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

37 thoughts on “Writing modules for Play 2, part 1: Get something working

  1. The 2.9.1 is the version of the Scala compiler that was used to compile the module (Play 2 uses SBT which compiles everything with the Scala compiler, which in the end uses javac to compile Java classes, so for Java-only module the version number is not very interesting; on the other hand it could be interesting for Scala code since Scala binary compatibility has tended to be much less respected)

  2. 2.9.1 is the scala version you are using and is added with artifact-id by sbt when publishing.

    You can simplify dependencies by using

    my_log %% my_log % version

    Omitting scala version and is implied.

  3. In regards to removing the routes file, I’ve noticed that the dependent application’s routes file will supersede the main applications’s only in dev mode. If you run in prod mode (by running ‘play start’), the main application’s routes file will be used instead of the dependents’ routes.

  4. @Nick – I hadn’t noticed this behaviour because I’ve been removing the routes files prior to running in production mode. This sounds like a definite bug – did you report it on lighthouse already?

    In the meantime, I still recommend removing the routes files otherwise you’ll have a hard time running in dev mode 🙂

  5. Hi Steve,
    What if I need UI of the module in my application? For doing this I need the routes of the module.
    e.g. If my module has a code:

    return redirect(routes.Application.hotellist())

    How to publish module in this case ?

  6. Meanwhile we can use following code since there is no routes available :

    return redirect("/path specified in the routes for the calling function");

  7. I’m not sure, and I don’t think it would have any effect when it came to creating the distribution. What’s your use case?

  8. @Soma It’s something Jorge Aliss has looked at as he’s been writing SecureSocial2, and he said Peter Hausel has confirmed it’s working in 2.1. If they’re covering it in the Devoxx session, I think it’s safe to assume it’ll be coming soon.

  9. i am getting action not found error while trying running these tutorial

    Action not found

    For request ‘GET /’

    These routes have been tried, in this order:

    any help will be appreciated
    thanks

  10. @Raj if you’re running the code from the zip file, you need to run the application in samples/mylogger-sample.

    The code in project-code is the actual module, which is only for use in other projects.

  11. In Play 1.x you could copy module code into the application ‘modules’ folder. It was great feature if you were developing and testing the module. With Play 2.x after every change you have to publish it into the local repository. Is there any way to avoid this clean, publish-local workflow and speed up this process?

  12. Great post. It’s work with Play 2.2.1. I have this sbt build file for the sample (essai) :
    name := “MyModule-essai”

    version := “1.0-SNAPSHOT”

    play.Project.playJavaSettings

    libraryDependencies ++= Seq(
    “mymodule” % “mymodule_2.10” % “1.0-SNAPSHOT”
    )

    —-
    @Lukasz, in a phase testing, you can make depend the sample-test from a subproject (the module) without publish-local with this on build.sbt file :

    lazy val module = project.in(file(“../module”))

    lazy val main = project.in(file(“.”))
    .dependsOn(module)
    .aggregate(module) –> compile the module too

    tested in an other project and subproject

  13. I have a play application(with views , routes and other conf files) which i want to behave as play module in other play application it is possible and if yes further guidance will really be helpful.

  14. Hi,
    i am not sure, but It is not working with play 2.2.3?
    Have done the steps as described, finally published-local it: yes is located in the local repository.

    When I try to use the module in a different play project, it is not going to add this module to the buildpath, which is supposed to be done by itself. Why is that?

    And how to do it?

    Anyhow, every other modules from the local repository are loaded.

    sincerely Knut

  15. One further question: Why is it important to add a “project-code” folder and why not keeping the structure?

  16. Regarding my buildpath problem: you have to run “play eclipse” again.

    Struggeling for hours, and then 10 minutes after questioning it… there was the solution. 💡

  17. Pingback: lacdev

Leave a Reply

Your email address will not be published. Required fields are marked *