Tag Archives: how-to

Functional tests in Play! 2 with REST-assured

In the Play! documentation, several flavours of functional testing are mentioned, including starting a HTTP server for the duration of the test – this is very useful for testing web services. In code, this looks like

import static play.test.Helpers.*;
...
@Test
public void testInServer() {
  running(testServer(3333), new Callback0() {
      public void invoke() {
         assertThat(
           WS.url("http://localhost:3333").get().get().status
         ).isEqualTo(OK);
      }
  });
}

A few days ago, when I was looking for a good way to test applications built on a CXF stack, I came across REST-assured. It was simple, elegant and useful so it’s the one I picked. In that case, I had to build some extra support for handling Jetty start-up and shutdown, application loading, etc.

With the full-stack approach of Play, however, you get this support for free out of the box, so all that is left is to integrate the tool of your choice. To add REST-assured to your project, edit the project/Build.scala file and add the dependency. If you already have Play running, don’t forget to reload the project for the changes to be visible.

val appDependencies = Seq(
    "com.jayway.restassured" % "rest-assured" % "1.7" % "test"
)

The above example re-written to use REST-assured then looks like this:

import static play.test.Helpers.*;
import com.jayway.restassured.RestAssured;
...
@Test
public void testInServer() {
  running(testServer(3333), new Runnable() {
      public void run() {
         RestAssured.expect().statusCode(200).when().get("http://localhost:3333");
      }
  });
}

REST-assured defaults to localhost for requests, so it’s possible to cut down on the boilerplate and use relative URLs by tweaking the configuration a little.

import static play.test.Helpers.*;
import com.jayway.restassured.RestAssured;
...
private static final int PORT = 3333;

@Before
public void setUp()
{
    RestAssured.port = PORT;
}
...
@Test
public void testInServer() {
  running(testServer(PORT), new Runnable() {
      public void run() {
         RestAssured.expect().statusCode(200).when().get("/");
      }
  });
}

I’ve written a small example that’s available on github, and here’s a sample from it which features an entity being persisted, and then retrieved for testing.

@Test
public void testGetBeer_present()
{
    running(testServer(PORT), new Runnable()
    {
        @Override
        public void run()
        {
            // Create the beer
            RestAssured.given()
                       .contentType(ContentType.JSON)
                       .content("{\"name\":\"Westmalle\"}")
                       .expect()
                       .statusCode(200)
                       .when()
                       .post("/");

            // Retrieve it by name, and bind the response body into a Beer instance
            Beer beer = RestAssured.expect()
                                   .statusCode(200)
                                   .when()
                                   .get("/Westmalle")
                                   .andReturn()
                                   .body()
                                   .as(Beer.class);
            Assert.assertEquals("Westmalle",
                                beer.name);
        }
    });
}

To run the tests, just start up Play in on your command line, and use the test command:

[play-rest-assured] $ test
[info] controllers.BeersTest
[info] + controllers.BeersTest.testGetAll_emptyDatabase
[info] + controllers.BeersTest.testGetAll_populatedDatabase
[info] + controllers.BeersTest.testGetBeer_emptyDatabase
[info] + controllers.BeersTest.testGetBeer_present
[info] + controllers.BeersTest.testUpdate
[info] 
[info] 
[info] Total for test controllers.BeersTest
[info] Finished in 6.954 seconds
[info] 5 tests, 0 failures, 0 errors
[info] Passed: : Total 5, Failed 0, Errors 0, Passed 5, Skipped 0
[success] Total time: 7 s, completed Oct 29, 2012 7:37:52 AM

Resources

Play 2 – modules, plugins, what’s the difference?

There seems to be some confusion regarding Play 2 modules and plugins. I imagine this is because the two are often synonymous. In Play (both versions – 1 and 2) there are distinct differences. In this post, I’m going to look at what a plugin is, how to implement one in Java and Scala, and how to import plugins from modules.

Plugins

A Play 2 plugin is a class that extends the Java class play.Plugin or has the Scala trait play.api.Plugin. This class may be something you have written in your own application, or it may be a plugin from a module.

Writing a plugin in Java

Create a new class, and have it extend play.Plugin. There are three methods available to override – onStart(), onStop() and enabled(). You can also add a constructor that takes a play.Application argument.

To have some functionality occur when the application starts, override onStart(). To have functionality occur when the application stops, override onStop(). It’s that simple! Here’s an example implementation which doesn’t override enabled().

package be.objectify.example;

import play.Application;
import play.Configuration;
import play.Logger;
import play.Plugin;

/**
 * An example Play 2 plugin written in Java.
 */
public class MyExamplePlugin extends Plugin
{
    private final Application application;

    public MyExamplePlugin(Application application)
    {
        this.application = application;
    }

    @Override
    public void onStart()
    {
        Configuration configuration = application.configuration();
        // you can now access the application.conf settings, including any custom ones you have added
        Logger.info("MyExamplePlugin has started");
    }

    @Override
    public void onStop()
    {
        // you may want to tidy up resources here
        Logger.info("MyExamplePlugin has stopped");
    }
}

Writing a plugin in Scala

Create a new Scala class, and have it extends play.api.Plugin. Just as in the Java version, there are onStart(), onStop() and enabled() methods along with an play.api.Application constructor argument. Here’s the Scala implementation:

package be.objectify.example

import play.api.{Logger, Application, Plugin}

/**
 * An example Play 2 plugin written in Scala.
 */
class MyExamplePlugin(application: Application) extends Plugin
{
  override def onStart()
  {
    val configuration = application.configuration;
    // you can now access the application.conf settings, including any custom ones you have added
    Logger.info("MyExamplePlugin has started");
  }

  override def onStop()
  {
    // you may want to tidy up resources here
    Logger.info("MyExamplePlugin has stopped");
  }
}

Hooking a plugin into your application

Regardless of the implementation language, plugins are invoked directly by Play once you have added them to the conf/play.plugins file. This file isn’t created when you start a new application, so you need to add it yourself. The syntax is <priority>:<classname>. For example, to add the example plugin to your project, you would use

10000:be.objectify.example.MyExamplePlugin

The class name is that of your plugin. The priority determines the order in which plugins start up, and just needs to be a number that is larger or smaller than that of another plugin. If you have several plugins, you can explicitly order them:

5000:be.objectify.example.MyExamplePlugin
10000:be.objectify.example.MyOtherExamplePlugin

Modules

A module can be thought of as a reusable application that you can include in your own app. It’s analogous to a third-party library that adds specific functionality. A module can contain plugins, which you can hook into your app using the conf/play.plugins file.

For example, if you’re using Deadbolt 2 you would need to add the following to your play.plugins file:

10000:be.objectify.deadbolt.DeadboltPlugin

A list of Play 2 modules can be found on the Play 2 GitHub wiki.

You can read more on creating modules for Play 2 here and here.

jQuery AJAX with Play 2

I’ve just prepared a course on jQuery to give to my colleagues, and one of the sections covers jQuery’s AJAX features. To demonstrate these, I wrote a quick web app using Play 2. Since there have been a few questions asked in various places on how this works, I’m going to break it down here with some simple examples of the main $.ajax() method, plus the shorthand methods that simplify its use.

$.ajax()

jQuery’s $.ajax() method is part of the low-level interface, and can be configured to provide all your AJAX needs. For this example, we’re going to do a simple GET that will result in a 200 response from the server, along with a text message.

The JavaScript:

$.ajax(ajaxParameters({
      url: '/ajax',
      success: function(data, textStatus, jqXHR) {
        window.alert(data);
      },
      error: function(jqXHR, textStatus, errorThrown) {
        window.alert(textStatus);
      }
    }));

Note the URL – this results in a call to http://localhost:9000/ajax, and so must appear in the routes file:

GET     /ajax                       controllers.Application.ajax()

The server-side implementation is about as simple as it gets:

public static Result ajax()
{
    return ok("Here's my server-side data");
}

Responding to errors

Take a look at the JavaScript again – you will see there are handlers declared for success and error conditions. What’s the difference? A 200 response will trigger the success handler, and other codes trigger the error handler. Let’s tweak the code to show this working.

The JavaScript. The only thing that has changed here is the URL – it now points to a URL that is guaranteed to return a non-200 response.

$.ajax(ajaxParameters({
      url: '/ajaxWithError',
      success: function(data, textStatus, jqXHR) {
        window.alert(data);
      },
      error: function(jqXHR, textStatus, errorThrown) {
        window.alert(textStatus);
      }
    }));

The route:

GET     /ajaxWithError              controllers.Application.ajaxWithError()

And the server-side implementation:

public static Result ajaxWithError()
{
    return badRequest("Somehow, somewhere, you screwed up");
}

And that’s it – you’re now handling success and error conditions.

Shorthand methods

jQuery usefully provides shorthand methods that simplify usage of $.ajax(), and in some cases provide additional functionality such as parsing repsonse data into JSON objects, or loading content into elements.

$.get()

$.get() is, unsurprisingly, for issuing asynchronous GETs to the server. You can provide parameters, and give a single function that will be executed if the call is successful.

The JavaScript:

$.get('/get',
      {'foo': 'bar'},
      function(data) {
          window.alert(data);
      });

The route – note this doesn’t specify a method parameter!

GET     /get                        controllers.Application.get()

The server-side implementation:

public static Result get()
{
    Map queryParameters = request().queryString();
    return ok(String.format("Here's my server-side data using $.get(), and you sent me [%s]",
                            queryParameters.get("foo")[0]));
}

Handling errors

In the real world, you will receive errors sometimes and the above JavaScript doesn’t handle this. The key is to use jQuery’s event-handling features instead of providing callbacks. In this example, any success, error and completion events fired by $.get() are handled.

$.get('/getWithError')
      .success(function(data) {
        window.alert('Success: ' + data);
      })
      .error(function(data) {
        window.alert('Error: ' + data.responseText);
      })
      .complete(function(data) {
        window.alert('The call is now complete');
      });

The route:

GET     /getWithError               controllers.Application.getWithError()

The server-side implementation:

public static Result getWithError()
{
    return badRequest("Something went very, very wrong");
}

$.post()

So far, so good, but what if you want to POST data to the server? You can use $.post() for this. Interestingly, there are no shorthand methods for DELETE, PUT, etc – you have to use the $.ajax() method for these.

The semantics are the same as for $.get(), so I’m not going to provide an event-driven here. This is using a regular callback.

The JavaScript:

$.post('/post',
      {'foo':'bar'},
      function(data) {
          window.alert(data);
      });

The route – note the POST HTTP method is used.

POST    /post                       controllers.Application.post()

And the server-side code:

public static Result post()
{
    Map parameters = request().body().asFormUrlEncoded();
    String message = parameters.get("foo")[0];

    return ok(message);
}

$.getJSON()

$.getJSON() loads JSON-encoded data from the server using a GET HTTP request (to steal a line from the jQuery docs). This means the data returned can be treated immediately as a JSON object without the need to process it further.

The JavaScript:

$.getJSON('/getJson',
          function(data) {
              // data is a JSON list, so we can iterate over it
              $.each(data, function(key, val) {
                  window.alert(key + ':' + val);
          });
       });

The route:

GET     /getJson                    controllers.Application.getJson()

Server-side code:

public static Result getJson()
{
    List data = Arrays.asList("foo", "bar");
    return ok(Json.toJson(data));
}

$.getScript()

$.getScript() loads a script from the server using a HTTP GET, and then executes it in the global context.

The JavaScript:

$.getScript('/getScript');

The route:

GET     /getScript                  controllers.Application.getScript()

Server-side code:

public static Result getScript()
{
    return ok("window.alert('hello');");
}

Handling events

This example couldn’t be simpler. It could, however, be slightly more complex. The events generated by $.getScript() are slightly different to the success, error, etc events of the other methods. For $.getScript(), there are done and fail events. For success, you have to use a callback.

$.getScript('/getScriptWithError',
            function() {
                window.alert('This is the success callback');
            })
            .done(function(script, textStatus) {
                window.alert('Done: ' + textStatus);
            })
            .fail(function(jqxhr, settings, exception) {
                window.alert('Fail: ' + exception);
            });

The route:

GET     /getScriptWithError         controllers.Application.getScriptWithError()

The server-side implementation. Note it’s returning a 200 response but with broken JavaScript.

public static Result getScriptWithError()
{
    return ok("this is not a valid script!");
}

.load

Last but not least is .load(). Note this isn’t $.load(), but rather $(a selector).load() as its purpose is to retrieve HTML from the server and place it into the matched element.

The JavaScript:

$('#loadContainer').load('/load');

The route:

GET     /load                       controllers.Application.load()

The server-side implementation:

public static Result load()
{
    return ok(snippet.render());
}

The template:

<div id="aTable">
    <table>
        <tr>
            <td>Foo</td>
            <td>Bar</td>
        </tr>
        <tr>
            <td>Hurdy</td>
            <td>Gurdy</td>
        </tr>
    </table>
</div>
<div id="aList">
    <ul>
        <li>Foo</li>
        <li>Bar</li>
    </ul>
</div>

Loading fragments

.load() can take a second parameter within the URL string, separated by a space. This parameter is a selector – if present, jQuery will extract the selected element from the HTML and insert only that into the target.

$('#loadFragmentContainer').load('/load #aList');

Note there is no change to the route, the server-side code or the template here – this is purely a client-side operation.

That’s all, folks

jQuery simplifies making AJAX calls, and Play simplifies receiving and responding to those calls. A perfect match!

An example implemtation of all these features is available here: jquery-ajax

Publishing Play 2 modules on github

Now you’ve doubtless followed my earlier guides on writing modules for Play 2, I imagine you’re keen to show your coding chops to the world. The problem is – and this is quite a big one, I’m afraid – there is no place to publish your modules to. When I first starting writing Play modules, the repository was already set-up and ready to go – however, it used a custom format (state of the art download-and-unzip). This changed to some degree with Play 1.2, but I have to say I’m not a fan of that dependency management mechanism; I’m just too damn old to learn these shiny new things.

With Play 2, things swung around again and the dependency mechanism is compliant with Maven and Ivy. I can hear enterprises up and down the planet rejoicing.

However, this still doesn’t help you achieve your well-deserved glory and fame, unless you happen to host your own Maven/Ivy repo or feel like hacking the auto-generated artifact models that Play generates. Help is hand, until Play 2 modules have an official release mechanism, using GitHub’s Pages feature. Very simply, we’ll use Play’s publish-local task to generate all the files we need in the correct directory structure, and then copy that structure as-is directly into GitHub pages. A publish in this manner can be done in less than a minute, after you’ve done the initial set-up in GitHub.

Creating the repository in GitHub

If your GitHub user name is, for example, foo, then creating a repository called foo.github.com hooks you into Pages – what you commit here will be available at http://foo.github.com. There’s no GitHubby goodness when it comes to displaying files here – what you commit is what’s available in its native form. Commit a HTML file (let’s call it bar.html), and point your browser at http://foo.github.com/bar.html and you’ll see the rendered HTML – not the source of it. This makes it ideal for exposing files that you want represented in their raw form – say, the jar file containing your Play 2 module.

So, go ahead and create a .github.com repository and clone it to your workspace, and cd into it. In here, you can replicate (kind of – see later) a meven repository.

To keep things nice, the first directory you should create is releases. If you also want to add a snapshot directory, do it at this level.

Note: When you first create the repository in GitHub, it can take up to 10 minutes for it to become available. If you haven’t changed your email preferences, you should receive a notification when it is ready. This doesn’t stop you committing to the repository in the meantime.

Publish the files locally

Let’s assume your module name is hurdy as the artifact name. Your Build.scala looks like this:

import sbt._
import Keys._
import PlayProject._

object ApplicationBuild extends Build {

    val appName         = "hurdy"
    val appVersion      = "1.0"

    val appDependencies = Seq(
    )

    val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings(
    )
}

(The mainLang value may be something else, depending on your primary language).

In the root of your module, start up Play and publish it locally:

steve@hex:/tmp/hurdy$ play
[info] Loading project definition from /tmp/hurdy/project
[info] Set current project to hurdy (in build file:/tmp/hurdy/)
       _            _ 
 _ __ | | __ _ _  _| |
| '_ \| |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/ 
             
play! 2.0, http://www.playframework.org

> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.

[hurdy] $ clean
[success] Total time: 0 s, completed Apr 21, 2012 9:49:00 AM
[hurdy] $ compile
[info] Updating {file:/tmp/hurdy/}hurdy...
[info] Done updating.                                                                  
[info] Compiling 4 Scala sources and 2 Java sources to /tmp/hurdy/target/scala-2.9.1/classes...
[success] Total time: 7 s, completed Apr 21, 2012 9:49:08 AM
[hurdy] $ publish-local
[info] Packaging /tmp/hurdy/target/scala-2.9.1/hurdy_2.9.1-1.0-SNAPSHOT-sources.jar ...
[info] Done packaging.
[info] Wrote /tmp/hurdy/target/scala-2.9.1/hurdy_2.9.1-1.0-SNAPSHOT.pom
[info] :: delivering :: hurdy#hurdy_2.9.1;1.0-SNAPSHOT :: 1.0-SNAPSHOT :: release :: Sat Apr 21 09:49:12 CEST 2012
[info] 	delivering ivy file to /tmp/hurdy/target/scala-2.9.1/ivy-1.0-SNAPSHOT.xml
[info] Generating API documentation for main sources...
[info] Packaging /tmp/hurdy/target/scala-2.9.1/hurdy_2.9.1-1.0-SNAPSHOT.jar ...
[info] Done packaging.
model contains 23 documentable templates
[info] API documentation generation successful.
[info] Packaging /tmp/hurdy/target/scala-2.9.1/hurdy_2.9.1-1.0-SNAPSHOT-javadoc.jar ...
[info] Done packaging.
[info] 	published hurdy_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/hurdy/hurdy_2.9.1/1.0-SNAPSHOT/poms/hurdy_2.9.1.pom
[info] 	published hurdy_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/hurdy/hurdy_2.9.1/1.0-SNAPSHOT/jars/hurdy_2.9.1.jar
[info] 	published hurdy_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/hurdy/hurdy_2.9.1/1.0-SNAPSHOT/srcs/hurdy_2.9.1-sources.jar
[info] 	published hurdy_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/hurdy/hurdy_2.9.1/1.0-SNAPSHOT/docs/hurdy_2.9.1-javadoc.jar
[info] 	published ivy to /home/steve/development/play/play-2.0/framework/../repository/local/hurdy/hurdy_2.9.1/1.0-SNAPSHOT/ivys/ivy.xml
[success] Total time: 3 s, completed Apr 21, 2012 9:49:15 AM

Move the published files into GitHub

The files have been published to ${PLAY_HOME}/repository/local, and are contained in the hurdy directory. Move this entire directory to your new git repository

cp -rv ${PLAY_HOME}/repository/local/hurdy <your username>.github.com/releases

Change directory to .github.com, and add all the files to the git repository, commit and push them.

steve@hex:/tmp/schaloner.github.com$ git add .
steve@hex:/tmp/schaloner.github.com$ git commit -m "Added release 1.0 to repository"
steve@hex:/tmp/schaloner.github.com$ git push -u origin master

Use the dependency

Create a new project (or open up an old one), and add the dependency and its location.

object ApplicationBuild extends Build {

    val appName         = "my-cool-project"
    val appVersion      = "2.1"

    val appDependencies = Seq(
      "hurdy" %% "hurdy" % "1.0"
    )

    val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings(
      resolvers += Resolver.url("My GitHub Play Repository", url("http://<your username>.github.com/releases/"))(Resolver.ivyStylePatterns)
    )
}

Note: Play has published the files in an Ivy style pattern, so this needs to be specified in your Build.scala

Start Play in your application root, and run the “dependencies” task. If all goes well – and I’m sure you’ll let me know if it doesn’t – your module will be pulled from GitHub and made available to your application.

Specifiying an organisation

Because there’s no organisation specified in Build.scala, the organisation (repository/local/hurdy/hurdy_2.9.1) is taken to be the same as the module name. If you want an organisation, add it in the main definition:

val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings(
      organization := "com.example"
    )

Now, when you publish locally the files will be placed in repository/local/com.example/hurdy_2.9.1. In this case, it’s the com.example directory that should copied from the local repository to your GitHub repo. The Build.scala of any application using your dependency will be similarly different:

val appDependencies = Seq(
      "com.example" %% "hurdy" % "1.0"
    )

Real-world proof this works

Deadbolt 2 is currently available using this mechanism, using http://schaloner.github.com as the repository. Here, there are release and snapshot versions so the Build scala of the sample applications looks like this:

import sbt._
import Keys._
import PlayProject._

object ApplicationBuild extends Build {

    val appName         = "deadbolt-usage"
    val appVersion      = "1.1.3-SNAPSHOT"

    val appDependencies = Seq(
      "be.objectify" %% "deadbolt-2" % "1.1.3-SNAPSHOT"
    )

    val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings(
      // Change this to point to your local play repository
      resolvers += Resolver.url("Objectify Play Repository", url("http://schaloner.github.com/releases/"))(Resolver.ivyStylePatterns),
      resolvers += Resolver.url("Objectify Play Repository", url("http://schaloner.github.com/snapshots/"))(Resolver.ivyStylePatterns)
    )
}

If I change the required version from 1.1.3-SNAPSHOT (which is in the snapshots/ directory) to 1.1.3 (which will be in the releases/ directory), the dependency will be resolved correctly.

Writing modules for Play 2, part 2: Interceptors

In the first part of this tutorial, we looked at the bare basics for creating, publishing and calling a module. The module we created didn’t really do much, so now it’s time to look at expaning the functionality using some of Play’s features.

1. Interceptors

Interceptors allow you to intercept calls to controllers, and augment or block their behaviour. In the first sample application, we added an explicit call to MyLogger to log a message to the console. If we scale that up, and you want to use this oh-so-useful plugin for every controller method call, you’re going to be writing a lot of boilerplate code. Interceptors allow us to automatically apply actions, and so reduce boilerplate.

1.1 Add the code

In the app directory, create a new package called actions. In here, we’re going to add the LogMe annotation, and LogMeAction that will be executed whenever the annotation is present.

LogMe.java is, at this point, a very simple annotation that doesn’t take any parameters

package actions;

import play.mvc.With;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author Steve Chaloner (steve@objectify.be)
 */
@With(LogMeAction.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Inherited
@Documented
public @interface LogMe
{
}

Take a look at the annotations, and you’ll With(LogInAction.class) – this lets Play know that when this annotation is encountered, it should execute an LogInAction before the actual target.

package actions;

import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Result;

/**
 * @author Steve Chaloner (steve@objectify.be)
 */
public class LogMeAction extends Action<LogMe>
{
    @Override
    public Result call(Http.Context context) throws Throwable
    {
        System.out.println("MyLogger: " + context.request().path());
        return delegate.call(context);
    }
}

This is pretty elegant stuff – the action has a generic parameter type of LogMe, which gives access to any parameters given to the LogMe annotation. This allows you to customise behaviour of the action. We’ll see this in action when we add some extra features. Once your code – in this case, another class to System.out, is done then you return the result of delegate.class(context) to resume the normal execution flow. In the meantime, if @LogMe is added to a controller method, the path of the action will be logged to the console; if @LogMe is added to a controller, the invocation of any method in that controller will result in the path being logged to the console.

1.2 Update Build.scala

Since we have a new version of mylogger, we should change the version number. Open project/Build.scala and change

val appVersion      = "1.0-SNAPSHOT"

to

val appVersion      = "1.1"

1.3 Make sure your project changes are detected

If you’re already running the Play console in mylogger/project-code, you need to execute “reload” for the changes to Build.scala to be picked up. If don’t have the console open, open it now – the changes will be picked up automatically on start-up.

[mylogger] $ reload
[info] Loading project definition from C:\Temp\mylogger\project-code\project
[info] Set current project to mylogger (in build file:/C:/Temp/mylogger/project-code/)

1.4 Clean and publish

As noted earlier, it’s always a good idea to clean before publishing to ensure you’re not pushing out any objects that shouldn’t be there.

[mylogger] $ clean
[success] Total time: 0 s, completed Mar 19, 2012 9:17:25 PM
[mylogger] $ publish-local
[info] Packaging /tmp/mylogger/project-code/target/scala-2.9.1/mylogger_2.9.1-1.1-sources.jar ...
[info] Done packaging.
[info] Wrote /tmp/mylogger/project-code/target/scala-2.9.1/mylogger_2.9.1-1.1.pom
[info] Updating {file:/tmp/mylogger/project-code/}mylogger...
[info] Done updating.                                                                  
[info] :: delivering :: mylogger#mylogger_2.9.1;1.1 :: 1.1 :: release :: Mon Mar 19 21:17:30 CET 2012
[info] Generating API documentation for main sources...
[info] Compiling 3 Java sources to /tmp/mylogger/project-code/target/scala-2.9.1/classes...
[info] 	delivering ivy file to /tmp/mylogger/project-code/target/scala-2.9.1/ivy-1.1.xml
model contains 7 documentable templates
[info] API documentation generation successful.
[info] Packaging /tmp/mylogger/project-code/target/scala-2.9.1/mylogger_2.9.1-1.1-javadoc.jar ...
[info] Done packaging.
[info] Packaging /tmp/mylogger/project-code/target/scala-2.9.1/mylogger_2.9.1-1.1.jar ...
[info] Done packaging.
[info] 	published mylogger_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/mylogger/mylogger_2.9.1/1.1/poms/mylogger_2.9.1.pom
[info] 	published mylogger_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/mylogger/mylogger_2.9.1/1.1/jars/mylogger_2.9.1.jar
[info] 	published mylogger_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/mylogger/mylogger_2.9.1/1.1/srcs/mylogger_2.9.1-sources.jar
[info] 	published mylogger_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/mylogger/mylogger_2.9.1/1.1/docs/mylogger_2.9.1-javadoc.jar
[info] 	published ivy to /home/steve/development/play/play-2.0/framework/../repository/local/mylogger/mylogger_2.9.1/1.1/ivys/ivy.xml
[success] Total time: 3 s, completed Mar 19, 2012 9:17:31 PM

Note the version of the module has changed in the logging. If you still see 1.0-SNAPSHOT, make sure you reloaded the project before publishing!

1.5 Update the sample application

Back in the sample application, change the module version you require in project/Build.scala

    val appDependencies = Seq(
      "mylogger" % "mylogger_2.9.1" % "1.1"
    )

Reload, and run “dependencies” to ensure you have the correct version. You can now update app/controllers/Application.java to use this new code:

package controllers;

import actions.LogMe;
import play.mvc.Controller;
import play.mvc.Result;
import views.html.index;

@LogMe
public class Application extends Controller
{
    public static Result index()
    {
        return ok(index.render("Your new application is ready."));
    }
}

Run this example, and you’ll now see MyLogger output applied through the annotation.

2. Added interceptor parameters

Just having the path of the request logged is not particulary useful or exciting. What if a specific log message should be given for each controller or controller method? In this case, we need to add some parameters.

2.1 Change the annotation signature

Upload actions/LogMe.java to take a value() parameter – this is the default annotation parameter, and so doesn’t need to be explicitly named when used. The value defaults to an empty string, so a standard message can be provided in the action if one isn’t present here.

public @interface LogMe
{
    String value() default "";
}

In the action, the inherited configuration field is typed to the generic parameter (in this case, LogMe) and gives access to the parameters. Update the call(Http.Context) method to take advantage of this.

public Result call(Http.Context context) throws Throwable
{
    String value = configuration.value();
    if (value == null || value.isEmpty())
    {
        value = context.request().path();
    }
    System.out.println("MyLogger: " + value);
    return delegate.call(context);
}

2.2 Publish the changes

Repeat steps 1.2 to 1.4 again, this time changing appVersion to 1.2

2.3 Update the sample application

Just like before, update the dependency version in Build.scala, reload and confirm with “dependencies”. Now you can add a message to the LogMe annotation:

@LogMe("This is my log message")
public class Application extends Controller

Run the application, and now you’ll see your annotation message in the console.

[info] play - Application started (Dev)
MyLogger: This is my log message

3. Make the interceptors interact

Now you (hopefully) have the hang of this, we’re going to speed up a bit. In this section, we’re going to look at how interceptors can interact with each other. Play applies interceptors first to the method, and then to controller, so if the same annotation is present at both the method and controller level it will be executed twice. The LogMe annotation can be applied to both the class level and the method level, but what if you have a general logging message for the entire controller except for one method that requires a different message? Also, we only want one logging message invocation per invocation. To achieve this, we can use the context that’s passed into each action.

3.1 Update the module

Update LogMeAction to give it awareness of previous invocations:

package actions;

import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Result;

/**
 * @author Steve Chaloner (steve@objectify.be)
 */
public class LogMeAction extends Action<LogMe>
{
    public static final String ALREADY_LOGGED = "already-logged";
    
    @Override
    public Result call(Http.Context context) throws Throwable
    {
        Result result;
        
        if (context.args.containsKey(ALREADY_LOGGED))
        {
            // skip the logging, just continue the execution
            result = delegate.call(context);
        }
        else
        {
            // we're not using the value here, only the key, but this
            // mechanism can also be used to pass objects
            context.args.put(ALREADY_LOGGED, "");
            
            String value = configuration.value();
            if (value == null || value.isEmpty())
            {
                value = context.request().path();
            }
            System.out.println("MyLogger: " + value);
            
            result = delegate.call(context);
        }
        
        return result;
    }
}

Update the version number, clean, reload, and publish-local.

3.2 Update the sample application

We’re going to add a second annotation, this time to the index method. This will override the controller-level annotation. So, update the dependency number in Build.scala, reload and run.

package controllers;

import actions.LogMe;
import play.mvc.Controller;
import play.mvc.Result;
import views.html.index;

@LogMe("This is my log message")
public class Application extends Controller
{
    @LogMe("This is my method-specific log message")
    public static Result index()
    {
        return ok(index.render("Your new application is ready."));
    }
}

When you access http://localhost:9000, you will now see this in the console:

@LogMe("This is my log message")
[info] play - Application started (Dev)
MyLogger: This is my method-specific log message

4 It’s beer time again

You now have an infrastructure that supports parameterised actions. Remember that lots of things can be passed as annotation parameters, but – crucially – not everything. You may need to get creative for some of your tasks!

You can download the complete source code here.

A note on progress so far

This was originally planned to be a three-part tutorial. However, now I see how much detail you can go into when discussing just one subject, I think it’s going to be more of an ongoing series. If you have any feedback or questions on what there is so far, please let me know at steve@objectify.be

Other parts to this tutorial series

Writing modules for Play 2, part 1: Get something working

A couple of weeks ago, I migrated the Play! framework 1.x version of Deadbolt to the Play 2 platform, and was surprised at the lack of information on creating modules. This topic was covered in detail in the Play 1.x docs, and this made creating modules very simple. Clearly, something needed to be done – and so this is the first in a three-part series on creating modules and plugins for Play 2.

It’s important to note this is not a definitive guide – instead, it’s a collection of the information and techniques I’ve used when working on my own modules.

I’m going to assume you already have a working installation of Play 2 – if you don’t, head over to http://www.playframework.org/documentation/2.0/Installing for details on how to set this up.

In this first part, we’re going to cover the fundamentals of creating and publishing the module, and adding a sample application. The next two parts will go into greater depth on plugins, interceptors, tags and other useful tools.

First, a small note on the difference between a module and a library. In Play 1.x, a module was created using “play new-module” and distributed via the module repository. Up until Play 1.1, modules were controlled through application.conf entries, and libraries were added locally. From Play 1.2, both modules and libraries were controlled through the dependency management mechanism based on Ivy. In both cases, though, there was a clear concept of module (something tightly integrated with Play, which followed its conventions on package structure, etc) and a library (a general third-party library).

In Play 2, the line is blurred to some degree. Modules are now distributed in the same way as libraries, though Ivy or Maven, and the package structure can be arbitrary so you can have a traditional com.example.whatever structure. From this point of view, the only real difference between modules and libraries is that modules use the Play API directly.

Secondly, a note on language. Since both Java and Scala are natively supported by Play 2, you can implement your module in either language. If the module has its own API that applications can use, the superb interoperability between Java and Scala means your language choice is – in most cases – irrelevant to whichever application uses your API.

1. Getting started

As a simple introduction, we’re going to create a basic logging module that – following industry best practices – writes output to the console window. This module is called mylogger, because it’s Monday and I’m not feeling very creative right now. This module is going to be (mainly) written in Java.

You can think of a Play 2 module as being a Play 2 application with a couple of files missing. So, you create a module in the same way as you would an application. Go to, or create, a directory where you keep your projects, and use “play new mylogger” to create the app. Choose option 2 when prompted, to create a simple Java app.

steve@hex:/tmp$ play new mylogger
       _            _
 _ __ | | __ _ _  _| |
| '_ \| |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/

play! 2.0, http://www.playframework.org

The new application will be created in /tmp/mylogger

What is the application name?
> mylogger

Which template do you want to use for this new application?

  1 - Create a simple Scala application
  2 - Create a simple Java application
  3 - Create an empty project

> 2

OK, application mylogger is created.

Have fun!

Because we’re going to have a sample application alongside the module, we’re going to change the directory structure a little. At the moment, it looks like this:

mylogger
– app
– conf
– project
– public
– target
– .gitignore
– README

In the mylogger directory, create two new directories, project-code and samples. Copy all the files listed above into the project-code directory. You should now have

mylogger
– samples
– project-code
     – app
     – conf
     – project
     – public
     – target
     – .gitignore
     – README

The conf directory contains two files – routes, and application.conf.

- application.conf needs to be present for Play to recognise mylogger/project-code as a Play application, so we can’t delete it but we can remove everything it contains. Any configuration your module needs should be added to the application.conf of the “real” application.
- routes *must* be deleted. If you don’t do this, it may/will supercede the routes file of whichever application uses it, and this is A Bad Thing (mainly because nothing will work).

With application.conf emptied and routes deleted, type “play” in the project-code to start the Play console.

steve@hex:/tmp/mylogger/project-code$ play
[info] Loading project definition from /tmp/mylogger/project-code/project
[info] Set current project to mylogger (in build file://tmp/mylogger/project-code/)
       _            _
 _ __ | | __ _ _  _| |
| '_ \| |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/

play! 2.0, http://www.playframework.org

> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.

[mylogger] $

We now have a valid Play 2 module (that, admittedly, doesn’t do anything).

If you use an IDE, this is a good time to create the project – this tutorial is IDE-agnostic, however, so you can use the guide at http://www.playframework.org/documentation/2.0/IDE if you want to do this.

2. Adding some functionality

As I mentioned above, Play 2 modules can have a more traditional package structure of com.example.whatever, but a default Play application has the tradtional app/controllers, app/models, etc, structure. We’ll keep this for now, and change it in later parts of this tutorial.

2.1 Slash and burn

In the app folder, we have the following structure as created for us by the “play new” command:

app
– controllers
     – Application.java
– views
     – index.scala.html
     – main.scala.html

For the initial iteration of this module, we don’t need any views so you can delete the views package.

You can also delete Application.java, as we’re writing this from scratch.

2.2 Add some module code

In the controllers package, create a new class called MyLogger.java. It doesn’t need to extend or implement anything, and it contains a single method:

package controllers;

/**
 * @author Steve Chaloner
 */
public class MyLogger
{
    public static void log(String message)
    {
        System.out.println("MyLogger: " + message);
    }
}

2.3 Have a beer

You’ve just written a module. Go have a beer.

2.4 Post-beer realisation

As you gaze into your now-empty glass, abuzz with the joy of creation and impending feeling of industry-wide fame, you might realize that no-one can actually get to your module because it’s sitting on your computer. You need to publish it.

3. Publish and be damned

For this example, we’re just going to publish to your local repository. In the root of your Play installation, there is a repository directory, and this is where you wil first push your module.

Before publishing, always ensure you have run a “clean”, otherwise some classes/files that you have removed from the source tree may still exist in compiled form and end up in your module jar file. If the class matches one that’s in your actual application, it can be called in place of your actual class. Which sucks.

In the Play console, use “clean” and then “publish-local” to package up your module and publish it to the local repo:

[mylogger] $ publish-local
[info] Updating {file:/tmp/mylogger/project-code/}mylogger...
[info] Packaging /tmp/mylogger/project-code/target/scala-2.9.1/mylogger_2.9.1-1.0-SNAPSHOT-sources.jar ...
[info] Done packaging.
[info] Wrote /tmp/mylogger/project-code/target/scala-2.9.1/mylogger_2.9.1-1.0-SNAPSHOT.pom
[info] Done updating.                                                                  
[info] :: delivering :: mylogger#mylogger_2.9.1;1.0-SNAPSHOT :: 1.0-SNAPSHOT :: release :: Mon Mar 19 20:57:26 CET 2012
[info] 	delivering ivy file to /tmp/mylogger/project-code/target/scala-2.9.1/ivy-1.0-SNAPSHOT.xml
[info] Compiling 1 Java source to /tmp/mylogger/project-code/target/scala-2.9.1/classes...
[info] Generating API documentation for main sources...
model contains 4 documentable templates
[info] Packaging /tmp/mylogger/project-code/target/scala-2.9.1/mylogger_2.9.1-1.0-SNAPSHOT.jar ...
[info] Done packaging.
[info] API documentation generation successful.
[info] Packaging /tmp/mylogger/project-code/target/scala-2.9.1/mylogger_2.9.1-1.0-SNAPSHOT-javadoc.jar ...
[info] Done packaging.
[info] 	published mylogger_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/mylogger/mylogger_2.9.1/1.0-SNAPSHOT/poms/mylogger_2.9.1.pom
[info] 	published mylogger_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/mylogger/mylogger_2.9.1/1.0-SNAPSHOT/jars/mylogger_2.9.1.jar
[info] 	published mylogger_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/mylogger/mylogger_2.9.1/1.0-SNAPSHOT/srcs/mylogger_2.9.1-sources.jar
[info] 	published mylogger_2.9.1 to /home/steve/development/play/play-2.0/framework/../repository/local/mylogger/mylogger_2.9.1/1.0-SNAPSHOT/docs/mylogger_2.9.1-javadoc.jar
[info] 	published ivy to /home/steve/development/play/play-2.0/framework/../repository/local/mylogger/mylogger_2.9.1/1.0-SNAPSHOT/ivys/ivy.xml
[success] Total time: 4 s, completed Mar 19, 2012 8:57:28 PM

If you take a look in $PLAY_HOME/repository/local, you’ll now see a directory called mylogger. Since we didn’t give an organisation name, the organisation is taken to be the same name as the module itself. Go into mylogger, and you’ll see the artifact – mylogger_2.9.1. The 2.9.1 part of the filename comes from Play itself, and is (appears to be) a versioning thing. If anyone knows more about this, please comment and let me know.

Inside mylogger_2.9.1, we have the module version, which in this case is 1.0-SNAPSHOT, and this in turn contains jar files, source jars, Maven and Iyy info, etc.

Where does all this information come from? It’s based on the project/Build.scala file. In here, you can give the name of the module, the organisation, the version, and various other pieces of information. For now, we’ll keep things as they are, but this extremely important file will be updated as we get deeper into some issues.

4. Providing a sample

You can write the best, most incredibly useful module in the world, but without a sample application to a) show it works, and b) show HOW it works, you’re going to have trouble convincing people of that. This is why we changed the directory structure back when we first created the module. Open another terminal, and go to the mylogger/samples diretory – it’s time to show what mylogger can do.

4.1 A sample application is…a Play application

Since we’re writing a Play module, it makes sense to provide a Play application as the sample. Now we’re in mylogger/samples, using “play new mylogger-sample” to create the sample application. Again, choose option 2 to make a simple Java application.

4.2 Declaring dependencies

In order to use mylogger, we must declare a dependency for it in mylogger-sample/project/Build.scala. Open this file, and change

val appDependencies = Seq(
      // Add your project dependencies here,
    )

to

val appDependencies = Seq(
      "mylogger" % "mylogger_2.9.1" % "1.0-SNAPSHOT"
    )

You can see this matches the repository path of mylogger/mylogger_2.9.1/1.0-SNAPSHOT.

In a brazen act of laziness, we’re also going to declare the local repository as the place to find our module. Change

val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings(
      // Add your own project settings here      
    )

to

  val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings(
    resolvers += "Local Play Repository" at "file://path/to/play-2.0/repository/local"
  )

(but change the path to be correct for your local installation)

In the mylogger/samples/mylogger-sample directory, start up the Play console using “play”. If you use the “dependencies” command, you’ll see that mylogger is now a dependency of the application.

IMPORTANT NOTE! Since we started the Play console after changing Build.scala, the changes were automatically picked up. If you change this file while the console is open, use “reload” to make sure the changes are used.

4.2 Using your module

In your new default Play application, we’re going to add a single line to controllers/Application.java to call MyLogger:

package controllers;

import play.mvc.Controller;
import play.mvc.Result;
import views.html.index;

public class Application extends Controller
{
    public static Result index()
    {
        MyLogger.log("Here's my log message");
        return ok(index.render("Your new application is ready."));
    }
}

Note that we don’t need to import MyLogger as it’s also in the controllers package.

“run” the application and go to http://localhost:9000. A brief time later, the page will render and in the console you’ll see

[info] play - Application started (Dev)
MyLogger: Here's my log message

Reload the page a few times, and you’ll see the log message appear every time.

5. Have another beer

Congratulations, you now have a module and a working sample. It doesn’t actually add much value to your sample app, but that’s something that will be addressed in part 2. In the meantime, head over the fridge and grab yourself another beer.

You can download the complete source code for this example here

Other parts to this tutorial series