Writing modules for Play 2, part 1: Get something working
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.
great post! But after we complete making a module, how can we publish to the public? Any suggestion?
This might help you – http://www.objectify.be/wordpress/?p=410