Enterprise Java

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

Reference: Writing modules for Play 2, part 1: Get something working from our JCG partner Steve Chaloner at the Objectify blog.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
mingderwang
11 years ago

great post! But after we complete making a module, how can we publish to the public? Any suggestion?

Steve Chaloner
11 years ago
Reply to  mingderwang
Back to top button