Module CSC5002—ASR6: Middleware and software architecture for distributed applications

Portail informatique

Micro-project : 02—REST for microservice MiniSocs Frontend

Implement the REST access to the MiniSocs Frontend microservice. The implementation is made in what we call a “REST adapter” (See later in the course when introducing Design Pattern Ports & Adapters).
This work is part of Milestone 1, whose content is the following:
  • ▢ REST: Use cases “create a user”, “create a social network”, and “post a message”.
  • ◇ REST: The other use cases, which are implemented in the skeleton.
  • ▢ REST: A JUnit unit test class that demonstrates a client calling the application's endpoints (use cases) via REST.

Specification of the REST adapter of the MiniSocs Frontend microservice

Let us first observe that the methods of the Façade, that is the methods that implements MiniSocsAPI (in Maven module minisocs-frontend.common-api) in class MiniSocs (in Maven module minisocs-frontend.hexagon), are prepared in order to simplify the specification of the REST endpoints: the arguments (inputs) and the return values (outputs) are JAVA records. In addition, class UtilSerialiser (from now on, it's your job to look up the classes in the Maven modules yourself 😎) contains utility methods to serialise in and de-serialise from JSON these records: e.g. writeValueAsString, readValue, and listOfUserReturnValuesFromJson (the latter is non-trivial because of the list). Finally, in order to support JAVA classes such as Instant, we have already included the Maven dependency on artefact jackson-datatype-jsr310 (See JSR310).

Here follows the specification that we propose for the first three use cases of the MiniSocs microservice, namely “create a user”, “create a social network”, and “post a message”. The first endpoint in the table is interesting for checking that the REST server is ready for receiving (functional) requests: See the bottom of the page for its usage. In the URI column, the first part of the path corresponds to the class path of the REST resource while the second part corresponds to the method path of the REST resource.

Use case URI path Resource class HTTP method Inputs Outputs
/ / + isready ReadyResource GET / /
“create a user” /user/ + add UserResource POST AddUserCommand in a String /
“create a social network” /socialnetwork/ + create SocialNetworkResource POST CreateSocialNetworkCommand in a String /
“post a message” /participation/ + post ParticipationResource PUT PostMessageCommand in a String /

Please note! When creating the base path for a REST server, the table paths must be prefixed with the REST server path: there may be several REST servers on the same port, and they are distinguished by this path. Thus, when the MiniSocs REST server is located at “http://localhost:8083/MyServer”, the address of the “/user/add” endpoint is “http://localhost:8083/MyServer/user/add”.

Designing, Implementing, and Testing the REST adapter of the MiniSocs Frontend microservices

Design: Overview.

In the next Figure, we display an abstract view of the sequence of a REST call: the client requests a given REST endpoint, and then the REST server forwards the request to the appropriate REST resource, which calls the corresponding method of the Façade.

Fig. 1: Sequence diagram of the call to a REST endpoint.

You can check to make sure you have a good understanding of the sequence shown in the sequence diagram.

Implementation: Giving access to the façade of the microservice.

When writing a class that models a REST resource, we often wonder how to access the Façade to delegate processing to the application's business logic. We provide the following abtract class, from which all REST resource classes inherit. Method setApplicationFacade is the method that is called by class that instantiate the Façade and that “starts” the system.

... /** * This class defines the common behaviour of all the REST resources. */ public abstract class AbstractResource { private static MiniSocsAPI applicationFacade; protected AbstractResource() { super(); } public static void setApplicationFacade(final MiniSocsAPI facade) { applicationFacade = facade; } protected static MiniSocsAPI accessApplicationFacade() { if (applicationFacade == null) { throw new IllegalStateException("the reference to the facade has not been set"); } return applicationFacade; } }

You can create this class in your Maven project: at first glance and for now, in minisocs-frontend.hexagon.

In next question, we create the REST resource classes, which extends class AbstractResource.

Implementation: Writing the REST resource classes.

As a reminder, the methods of the façade take as inputs and outputs JAVA records, and class UtilSerialiser provides methods to serialise and de-serialise these records. Therefore, REST resource can simply consume and produce “plain text” strings.

The methods of the endpoints are organised around a call to UtilSerialiser::readValue, a call with accessApplicationFacade() to perform the delegation to the mehod of the façade, and a call to return the response, which potentially contains an entity for the outputs. The last instruction is then similar to either return Response.status(Status.OK).build(); or return Response.status(Status.OK).entity(UtilSerialiser.writeValueAsString(returnValue)).build();. Of course, you must manage exceptional cases.

Create REST resources classes (one or several classes) in order to implement the mehods for the first three use cases. This or these classes should extend class AbstractResource.

Testing: JUnit unit test class

To test REST resources, you can either follow the procedure shown in the examples provided in the Discovery Lab—by incorporating everything into the JUnit test class—or create a separate REST server class before creating the JUnit test class.

For the first option, displayed in Figure 2, everything in the JUnit test class, the method annotated @BeforeEach registers the REST resources, creates and start the Grizzly HTTP server, and then creates the HTTP client for the test. Then, the method @AfterEach closes and shutdown everything. Finally, a method annotated @Test can mimick the scenario in the MiniSocs test class TestScenario with a sequence of calls for creating users, social networks, and posting messages.

By using JAVA records, the client code for calling a MiniSocs endpoint typically looks like the following (restService is the WebTarget of the REST client used to access the REST server, and we have added a simple check on the response status):

command = new AddUserCommand(...); var response = restService.path("/user/add").request().post(Entity.text(UtilSerialiser.writeValueAsString(command))); Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());

The TestScenario test class includes the notification-related part. In this version, there is no notification handler (it is set to null), no notification consumers, and therefore no notifications. This topic will be covered in the next part on server-sent events (SSE).
Fig. 2: Sequence diagram of the test without the REST server.

For the second version, start by creating a class that implements a REST server, such as RestServer. Put this class in the src/main/java directory structure. Its constructor can handle registering the REST resources and creating the Grizzly HTTP server. Its start and shutdown methods can then be used to start and stop the HTTP server. Next, proceed with building the JUnit test class, but this time by instantiating your REST server class.

Fig. 2: Sequence diagram of the test with the REST server.
Before starting the sequence of calls to the REST endpoints, we advise you to wait until the server is ready. You can for instance use the following instructions.
Awaitility.await().until(() -> {
  try {
    var response = restService.path("/isready").request().get();
    Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
    var ok = response.readEntity(String.class);
    return ok.equals("isready");
  } catch (Exception ex) {
    return false;
  }
});
Congratulations, you are now ready to continue with the other tasks listed at the top of the page.