You should understand the core architeture of SkyCave, have a development environment set up, and be able to add features to it that are validated though automated tests.
September 28th at 23.59. That is, after this time, none of the exercises in this iteration (on this webpage) that are marked M can be handed in on BrightSpace and thus cannot be awarded any points.
Form groups of two or three persons and register yourself in BrightSpace to get a groupName from those defined in BrightSpace: Alfa, Bravo, etc.
Gradle is used to execute the server ("daemon") and the client ("cmd") of SkyCave.
Read the README.md file in the root of the SkyCave folder for an explanation of how to start a server on localhost and next start a client to connect with it.
Play around with the command set of the 'cmd' - you can get help on the command set from the 'h' command.
SkyCave uses Gradle as build management tool for compiling, testing, execution, packaging, and much more. It is structured as a multi-project that separates SkyCave into three separate modules (which is called 'subprojects' in Gradle terminology): common, server, and client, reflecting a classic tiered architecture. You will later add more subprojects as you refactor the architecture into a microservice architecture, add integration tests, etc.
In the following exercises and mandatory project you will inspect code and write code. IntelliJ is installed in the course VM but you are free to use any other IDE if you prefer.
If you do not know IntelliJ it is as any other modern IDE: It is a swiss army knife that can do anything but the learning curve is steep, getting to know a zillion short-cut keys :(.
Go find a PDF on the www with the default keymap, but be sure to know
Consider the scenario:
"Mikkel logs into SkyCave with his credentials. He LOOKs around, and then moves NORTH. Here, he DIGs a new room in direction EAST with the description "You have entered a dark cave." Next, he moves EAST and LOOK to see the description of the new room. He then QUITs the cave.
Execute the scenario using the fully stubbed Cmd target (gradle cmd -Pcpf=local.cpf = run 'cmd' using configuration defined in 'local.cpf')
Your task in this exercise is to understand the flow of data and control from the client to the server and back as defined by the Broker pattern. As our Broker is configurable and as some of the code is default code from the FRDS.Broker library, we will take it in a few steps.
Next, spin up an application server ('daemon') on localhost ('gradle daemon -Pcpf=http.cpf') and perform the same scenario using the Cmd target configured for localhost communication ('gradle cmd -Pcpf=http.cpf').
Finally, run separate Cmd's for Mikkel, Mathilde, and Magnus, each in their own shell. ('gradle cmd -Pcpf=http.cpf -Pid=mathilde_aarskort -Ppwd=333', you will find the known users in the TestStubSubscriptionService.java source code as well as in the README.md).
Review the JavaDocs of the code and refresh you memory of the used patterns.
First, run the full test suite using gradle test from the command line.
Next, run the test cases in IntelliJ. You can do this one module at a time by right clicking the module root (or the src/test/ folder within) and select 'Run All Tests'. Or, better, you can make a configuration for running all tests by
Review the test cases used to develop and test SkyCave. Start in the server side tests of Cave and Player; next review the similar tests on the client side.
Next, try to 'Run All Tests with Coverage', first with the server, and next with the client. Ensure to select 'Add to active suits' for the latter run - it will accumulate the test coverage for both the server and client tests. Now a package browser appears and you can browse your code and see coverage statistics, and 'red/green' coloring of the Java production code.
Be puzzled that the teacher has not achieved 100% code coverage, inspect the weak spots, and explain why the teacher has not bothered.
Try to run coverage from Gradle instead using gradle test jacocoRootReport. You have to browse to the 'build/reports/jacoco/jacocoRootReport/html/index.html' to see statistics and browseable code.
Please appreciate the weird 'jacocoRootReport' target in the root build.gradle file. It has really cost me a lot of hours! If any gradle nerd out there spots anything that could be made more elegantly, I would appreciate it highly!
Review 'log4j.properties' in the respective client and server subprojects' resource folder. Find out what the configuration of logging is. Wondering why logging is going to the console and not to a logfile for the server? Docker prefers it that way, which we will return to in a few weeks...
Suggest improvements to the logging (which admittedly is not quite at production quality at the moment!)
Over the course you will configure the SkyCave daemon and cmd specifically for each and every exercise. For this to work, a strong dependency injection and configuration architecture is necessary which can instantiate and dependency inject the proper delegates. This is done though reading Cascading Property Files or 'CPF's, which is my own invention (and have been invented by several others before me :).
Revisit the FRS chapter on Abstract Factory and skim the paper on Object Manager. These two patterns form the core of SkyCave's dependency injection architecture.
Trace how the ObjectManager in the 'daemon' is populated with the proper delegates during execution of 'gradle daemon -Pcpf=http.cpf'.
An important feature of the used CPF library is the chaining feature, which allows one configuration file to inherit/chain all definitions from an ancestor CPF file.
SKYCAVE_QUOTESERVICE_SERVER_ADDRESS = www.theserver.dk:9999Run a daemon using this CPF, and contact it with a cmd. Use the 'sys' command to verify that the configuration is indeed set to 'www.theserver:9999' of the quote server.
Decide on a Git hosting provider and create a non-public repository for your group to share code and material.
Choosing BitBucket is a bit favorable, as my lessons will use bitbucket pipelines when we come to the Continuous Delivery topic.
The code and configurations you make for SkyCave is the exam and thus you are not allowed to share excessive code fragments nor entire codebases across teams. Make sure your repo is not public!
Spot checks will be made and violation of this rule is deemed very problematic. Downloading/copying from accidentially public repositories with SkyCave material is considered exam cheating.
Learning objective: You will review the abstractions/patterns for distributed method calls (Broker) between client and server; and use these to implement the missing bits to make the 'getQuote' method work from the client side.
The quote service related methods are not implemented on the server side (in PlayerServant), but instead a hard-coded test value is returned, which suffices for now to develop the broker interaction.
Implement test cases (in client project's 'TestQuoteClient') and proper client side proxy (PlayerProxy) and method dispatching (PlayerInvoker) so clients can retrieve the (hard-coded) quote from the server. Ensure high test coverage.
Hint: Seach for (some of) the TODO tags in the code base. In IntelliJ, you can use 'View->Tool Windows->TODO'.
Requirements:
public static final String GET_QUOTE_METHOD_KEY = PLAYER_TYPE_PREFIX + "get-quote";
Hand-in:
Evaluation:
Your submission is evaluated against the learning goals and adherence to the submission guidelines. The grading is explained in Grading Guidelines. I will use the grade sheet to evaluate your submission. The score counts towards your final grade.
The functionality will be tested by Crunch, later on.
Learning Goal | Assessment parameters |
Submission | Required artifacts (source files) are all present. |
ClientProxy Code | Client side proxy code obeys the Broker pattern. The proper MarshalingKeys constant is used. Robert Martin 'Clean Code' properties are generally kept. |
Invoker Code | PlayerServant Invoker code obeys the Broker pattern. The proper MarshalingKeys constant is used. Robert Martin 'Clean Code' properties are generally kept. |
Test Code | The test code is simple (no complex constructs, basically only assignments, simple private method calls, very basic for loop). The test code reflects using the TDD principles (Isolated Test, Evident Test, Evident Data, etc.). The production code is 'well' covered by test code (JaCoCo coverage is 'green' for most production code). |
Learning objective: Again, use the Broker pattern to handle client/server communication, this time transferring lists and complex objects over the wire.
The (very limited) 'social network' feature of Cave is the POST and READ commands in the Cmd, which allows a player to write messages on the wall of a room, and read all the messages posted. (Note: we specify that a room has only a single 'wall' = a whiteboard for posting messages.)
If a player 'Mikkel' POSTs 'Hello Cave!', and 'Mathilde' one minute later POSTs 'Great place.' then Mathilde (and Mikkel or anyone else) should read
0: [Mathilde, just now] Great place. 1: [Mikkel, 1 minutes ago] Hello Cave!
if she READs the wall. Postings to the wall are sorted, newest first. That is, they get older the longer they are down the list. The author of a message can overwrite it by the UPD command by referring to the index of the message (the number before the message.)
Wall messages are paginated, with page 0 being the page containing the newest/latest messages. (Player.WALL_PAGE_SIZE pages.)
Server side code is already implemented for the wall behaviour: Review test cases 'TestWallStorage' which tests the (fake) storage of wall messages, 'TestWall' which tests 'PlayerServant's behaviour, but no implementation has been made on the client side, in the 'PlayerProxy'.
Implement test cases (in client project's 'TestWallClient') and proper client side proxy (PlayerProxy) and method dispatching (PlayerInvoker) so clients can interact with the the server. Ensure high test coverage.
Hint: Look for (some of) the TODO tags in the code base.
Requirements:
public static final String ADD_MESSAGE_METHOD_KEY = PLAYER_TYPE_PREFIX + "add-message"; public static final String UPDATE_MESSAGE_METHOD_KEY = PLAYER_TYPE_PREFIX + "update-message"; public static final String GET_MESSAGE_LIST_METHOD_KEY = PLAYER_TYPE_PREFIX + "get-message-list";
Hand-in:
Evaluation:
Your submission is evaluated against the learning goals and adherence to the submission guidelines. The grading is explained in Grading Guidelines. I will use the grade sheet to evaluate your submission. The score counts towards your final grade.
The functionality will be tested by Crunch, later on.
Learning Goal | Assessment parameters |
Submission | Required artifacts (source files) are all present. |
ClientProxy Code | Client side proxy code obeys the Broker pattern. The proper MarshalingKeys constant is used. Robert Martin 'Clean Code' properties are generally kept. |
Invoker Code | PlayerServant Invoker code obeys the Broker pattern. The proper MarshalingKeys constant is used. Robert Martin 'Clean Code' properties are generally kept. |
Test Code | The test code is simple (no complex constructs, basically only assignments, simple private method calls, very basic for loop). The test code reflects using the TDD principles (Isolated Test, Evident Test, Evident Data, etc.). The production code is 'well' covered by test code (JaCoCo coverage is 'green' for most production code). |