Creating a web application with Play, Akka and Java

In this blog I will explain how I have build a web application using Akka, Play and Java.
Full source code is available at https://github.com/robbertvdzon/playwebapp-contactdb

The application consists of 3 separate play services. There is one REST service which exposes a user database, one REST service which exposes the contact database and a web application which presents the contactmanager.

play-java-web-app-architecture

The decision to use a separate REST service for the user database and for the contact database seems a bit overkill, but this is done to demonstrate how to simultaneously call the two REST services, and asynchronously return the result back to the client when both responses are available.

This blog only describes the implementation of the webservice since the implementation of the user service is described in this blog: http://www.vdzon.com/2015/08/17/create-a-user-rest-service-using-play-with-java. The the implementation of the contact database is similar as the user service.

Prepare development system
Go to https://www.playframework.com/ and download and install the Typesafe Activator which I will use in building the REST service.
When using IntelliJ, make sure that you have the SBT plugin, the SBT Executor and Scala plugin  installed in IntelliJ.

Create the web application from a template using activator
Use the activator to create a project named “user-service” from the template “play-java”.

cd c:workspaceakka-contacts
activator new contactmanager play-java

When the project is created you can start the application.

cd c:workspaceakka-contactscontactmanager
activator run

You can now open this project in IntelliJ so we can modify the sample play application to be able to run it as our contactmanager web application.

Rendering of the home page
In the default implementation of the template, the home page is declared in the routes file as follows:
GET / controllers.Application.index()

The Application.index() method will load and render a view and return to the client.

public Result index() {
 return ok(index.render());
}

The index object which is used to render the page, is created from the index.scala.html which is a scala file that generates the web page.

The index.scala.html file
The index.scala.html file is a scala file which passes the body of the html page to the main.scala.html file class. The main.scala.html then add’s a header and a footer around the body.

The index.scala.html structure looks like this:
@()
@main("Welcome to my contact manager!") {
// html body code
}
I will not show the whole html body here, but it consists of loading the following javascript files:
– jquery-2.1.1.min.js (we use jquery in this application)
– index.js (our own javascript file)

The jquery-2.1.1.min.js is not provided in the template, so we need to add that to the publicjavascripts folder.

The index.js file is generated from a coffeescript file and described later in this blog.

The html further consists of a dropdown list which will contain all users:


Then a part where all details of the selected user is shown:


Finally it contains a list of all contacts of this user:

    
    

    Besides these, there is also code to remove and add users and contacts, but this is left out of this blog.

    Create the coffeescript code
    The index.coffee is used to build our javascript file and uses jquery to perform the REST calls and fill the fields.
    The following code calls the /users REST call after the page is loaded get the list of all users.
    The result of the call is used to populate the users dropdown component:

    $ ->
     $.get "/users", (users) ->
       $.each users, (index,user) ->
         $('#users').append("#{user.name} ");
    
    

    The following code is called when a user is selected from the users dropdown component.
    The code calls the /user/{uuid} REST call to get the user details including the contact list.

     $('#users').change ->
      useruid = $('#users :selected').val()
        $('#contacts li').remove();
        $.get "/user/"+useruid, (user) ->
          $('#name').val user.name;
          $('#uuid').val user.uuid;
          $('#addContactUserUuid').val user.uuid;
          $.each user.contacts, (index,contact) ->
            $('#contacts').append $("
    • “).html contact.name+” – “+ contact.email;

    The users name and uuid are loaded in the two fields and all contacts are added to the contact list.

    The “index.coffee” must be created in the assetsjavascripts folder.
    When the code is compiled, the index.coffee is automatically compiled to index.js.

    The javascript code needs  the /users and /user/{uuid} REST calls so we need to implement these which I will explain in the next section.Update the routes file
    In the routes file we define which request must be service by which method of which controller.
    This file is already created by default to service a single page. We will add the REST calls here.
    GET    /users         controllers.Application.getUsers()
    GET    /user/:uuid    controllers.Application.getUser(uuid: String)
    Update the Controller class
    The routes file refers to the Application controller class so we need to implement the getUsers() and getUser(uuid: String) methods in the Application class.
    public F.Promise getUsers() {
     return F.Promise.wrap(ask(userActor, new UserActorProtocol.GetUsers(), 1000))
     .map(users -> ok(toJson(users)).as("text/json"));
    }
    
    public F.Promise getUser(String uuid) {
     return F.Promise.wrap(ask(userActor, new UserActorProtocol.GetUser(uuid), 1000))
     .map(user -> ok(toJson(user)).as("text/json"));
    }
    This code might need some explanation.
    The code forwards the requests to an actor which will asynchronously  process the request. This is why the method returns a Promise.
    As soon as the actor has replied the result, the response will be returned as json. This all works asynchronously so no threads are locked.
    The reason to use an actor is to make it possible to scale out or to run the actor on a remote machine.
    The actor can be injected into the constructor. I also added the @Singleton annotation to make sure the Application controller is created only once.
    @Singleton
    public class Application extends Controller {
    
    final ActorRef userActor;
    
    @Inject
    public Application(ActorSystem system) {
     userActor = system.actorOf(UserActor.props);
    }
    // remaining code
    Create the user Actor and the protocol class
    It is a good practice to define all messages that can be send to an actor into one protocol class.
    In our case, we have two messages (getUsers and getUser). These messages are created as static inner classes of the protocol class.
    Our protocol class looks like this:
    public class UserActorProtocol {
     public static class GetUsers {
     }
     public static class GetUser {
       public final String uuid;
       public GetUser(String uuid) {this.uuid = uuid;}
     }
    }
    Then we implement the actor itself.
    public class UserActor extends UntypedActor {
    
     public UserActor() {
     }
    
     public static Props props = Props.create(UserActor.class);
    
     public void onReceive(Object msg) throws Exception {
      final ActorRef sender = sender();
      if (msg instanceof GetUsers) {
        getUsers().map(wsResponses -> {
          sender.tell(buildUsers(wsResponses), self());
          return null;
        });
      } else if (msg instanceof GetUser) {
        String uuid = ((GetUser) msg).uuid;
        getUser(uuid).map(wsResponses -> {
          sender.tell(buildUser(wsResponses), self());
          return null;
        });
      }
      // remainder of the code
     }
    Let’s look how the onRecieve handles the GetUser message.
    First, the code call’s the getUser(uuid) method.
    This method returns a Promise and will asynchronously call the external user REST service and the external contact REST service. When both REST services have replied, these results are returned as a List.
    The onRecieve method uses the two REST responses to combine these to a single response using the buildUser() method.
    The getUser(String uuid) is implemented as follows:
    private F.Promise<List> getUser(String uuid) {
     final F.Promise userResponsePromise = WS.url("http://localhost:9001/user/" + uuid).get();
     final F.Promise contactResponsePromise = WS.url("http://localhost:9002/contact/" + uuid).get();
     F.Promise<List> sequence = F.Promise.sequence(userResponsePromise, contactResponsePromise);
     return sequence;
    }
    Then method the build the user from the to responses is implemented as follows:
    private User buildUser(List wsResponses) {
     JsonNode jsonUser = wsResponses.get(0).asJson();
     JsonNode jsonContacts = wsResponses.get(1).asJson();
     User user = fromJson(jsonUser, User.class);
     Contacts contacts = fromJson(jsonContacts, Contacts.class);
     user.setContacts(contacts.getContacts());
     return user;
    }
    Create our model classes
    In a new “model” package, we create the 4 model classes we need:
    – Contact (hold the contact properties: name, email, uuid and a reference to the userUuid)
    – Contacts (a wrapper around a list of contacts)
    – User (hold the user properties: name, uuid and a collection of all contacts of this user)
    – Users (a wrapper around a list of users)
    These are simple pojo’s but are annotated with @JsonIgnoreProperties(ignoreUnknown = true). I have done this to prevent getting runtime errors when the REST service that services e.g. the users contains more fields then available in our model.

    Run the sample application

    activator run
    This will start the http server and listen to port 9000.
    Starting it the first time takes a while.
    You can open this page in your browser on http://localhost:9000
    Note that any changes to the source code are compiles automatically when reloading the web page.