Play Framework Modules: Divide and Conquer

Play-framework

It’s usually the case that you start developing an application and go on fulfilling requirements. When your application grows bigger you start to realize the convenience of separating it into different components. Moreover, when you develop your second or third application, your begin to recognize certain features that could be reused across different applications.

These are two good reasons to modularize an application. Ideally we should aim at components with high cohesion and low coupling.

The Java language has proven itself quite fit to accomplish this kind of tasks. It provides general means to enforce the use of well defined API thru interfaces, abstract classes, etc.

Play framework developers think that this is perfectly fine for developing a general purpose library, but in the case of a web application reusability and modularization could be best achieved by other means. Have a look at this excerpt taken from play framework’s FAQ:

Java itself is a very generic programming language and not originally designed for web application development. It is a very different thing to write a generic and reusable Java library and to create a web application. A web application itself doesn’t need to be designed to be reusable. You need less abstraction, less configuration. Reusability does exist for web applications, but through web service APIs rather than language-level integration.

So when it comes to reusability, play provides us with a solution better suited for web applications.

Play modules

A module is just another Play framework application. The only difference is that a module is not meant to be run on his own, it needs to be included into a containing application.

Nevertheless, there area a couple of differences between a module and a regular application, mainly that a module has no conf file (it has to be provided by the main application) and everything in a module is optional.

Doing is better than saying, so as usual we will look for a fine opportunity to make a simple module to show how it works.

Creating a new play framework application and deploying it to the cloud

As many of you already know, we are working on the spanish translation of play framework site. We wanted to add web analytics to it so that we can see how people were using it.

So in order to follow this example, we’ll need a play framework app deployed somewhere on the Internet. Nowadays there are lots of Java hosting options available for free. Here you have a couple of tutorials for deploying on openshift, google application engine and heroku.

First let’s create a play framework application, I’ll create the app at ~/devel/apps/module-test, you can choose whatever location you like, just be sure to update the commands appropriately.

To create the app, run the following command at an os prompt:

sas@ubuntu:~/devel/apps/module-test$ play new analytics-app ~ _ _ ~ _ __ | | __ _ _ _| | ~ | '_ \| |/ _' | || |_| ~ | __/|_|\____|\__ (_) ~ |_| |__/ ~ ~ play! 1.2.4, http://www.playframework.org ~ ~ The new application will be created in /home/sas/Dropbox/Public/devel/play/apps/module-test/analytics-app ~ What is the application name? [analytics-app] ~ ~ OK, the application is created. ~ Start it with : play run analytics-app ~ Have fun!
Code language: JavaScript (javascript)

Now it would be a good time to deploy it somewhere. For this tutorial we will deploy it at openshift, you can use any host you want (For more information on setting up your environment for openshift deployment follow this tutorial)

Create a new directory at ~/devel/apps/module-test/openshift, go to that directory and run:

rhc-create-app -l [email protected] -p mypassword -t jbossas-7.0 -a analyticsapp Attempting to create remote application space: analyticsapp Now your new domain name is being propagated worldwide (this might take a minute)... Pulling new repo down [...] Successfully created application: analyticsapp
Code language: JavaScript (javascript)

Next, we’ll get rid of the demo app:

cd ~/devel/apps/module-test/openshift/analyticsapp rm -fr pom.xml src
Code language: JavaScript (javascript)

And we’ll compile and package the newly created app as an exploded war. Go to ~/devel/apps/module-test folder and run:

cd ~/devel/apps/module-test play war analytics-app -o openshift/analyticsapp/deployments/ROOT.war ~ _ _ ~ _ __ | | __ _ _ _| | ~ | '_ \| |/ _' | || |_| ~ | __/|_|\____|\__ (_) ~ |_| |__/ ~ ~ play! 1.2.4, http://www.playframework.org ~ JPDA port 8000 is already used. Will try to use any free port for debugging Listening for transport dt_socket at address: 53978 00:22:38,021 INFO ~ Starting /home/sas/Dropbox/Public/devel/play/apps/module-test/analytics-app 00:22:39,891 INFO ~ Precompiling ... 00:22:49,075 INFO ~ Done. ~ Packaging current version of the framework and the application to /home/sas/Dropbox/Public/devel/play/apps/module-test/openshift/analyticsapp/deployments/ROOT.war ... ~ Done ! ~ ~ You can now load /home/sas/Dropbox/Public/devel/play/apps/module-test/openshift/analyticsapp/deployments/ROOT.war as a standard WAR into your servlet container ~ You can't use play standard commands to run/stop/debug the WAR application... ~ ... just use your servlet container commands instead ~ ~ Have fun! ~
Code language: PHP (php)

Now we just need to commit the application and push it to our git repo on openshift:

cd ~/devel/apps/module-test/openshift/analyticsapp touch deployments/ROOT.war.dodeploy git add -A git commit -m "deploy play framework app" git push origin
Code language: JavaScript (javascript)

Note: The first time it will take a couple of minutes to push the application, because of play framework libraries. Later pushes will be much quicker, git is smart enough to only send updated files.

And that’s it, you’ve just deployed your first app to red hat’s cloud. You can see it running at http://analyticsapp-opensas.rhcloud.com/ (of course, you’ll have to replace “opensas” with your own openshift user name).

Google web analytics & play framework

Adding Google web analytics to a play app is really easy. You just need a gmail account, then go to Google Analytics web site, click on “sign up”, login with your gmail account, and complete all the required data.

In the account name enter “analytics-app”, in the website’s url enter http://analyticsapp-opensas.rhcloud.com, agree to the terms and conditions and click on “Create account”.

You’ll be taken to your analytics-app account page, there you can see the tracking code. You’ll just have to paste it in your app. So open the file at ~/devel/apps/module-test/analytics-app/app/views/main.html and paste the tracking code before the closing head tag, like this:

[...] <script src="@{'/public/javascripts/jquery-1.6.4.min.js'}" type="text/javascript" charset="${_response_encoding}"></script> #{get 'moreScripts' /} <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-XXXXXXXX-1']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> </head> <body> [...]
Code language: HTML, XML (xml)

Note: Google will provide you with your own UA-XXXXXXXX-1 account code, so just copy and paste the code from your Google analytics account page, and NOT from this page!

Now you just have to generate the war folder, commit, and push it once again to openshift to deploy your changes. Every time you make a change you’ll have to follow these same steps to deploy it to openshift.

cd ~/devel/apps/module-test play war analytics-app/ -o openshift/analyticsapp/deployments/ROOT.war cd openshift/analyticsapp/ git add -A git commit -m "added tracking code" git push origin
Code language: JavaScript (javascript)

Visit again your page at http://analyticsapp-opensas.rhcloud.com/, and see the source of the page to check that the tracking code has been added. You can also see it in action on Google’s analytics page, click on “Home”, Real-Time (BETA), and the Overview. You should have one visitor (yes, it’s you!).

So far now we created a new play application and deployed it to openshift. Then we created a Google analytic account and added the tracking code the our play application. Everything is working ok and our app is being tracked by Google. Now we are going to move that functionality to a module so that we can reuse it from other apps.

Creating a module

To create a new module you have to use the “new-module” play command, like this:

cd /home/sas/devel/apps/module-test/ play new-module analytics
Code language: JavaScript (javascript)

Now, in order to tell our main application (in our case analytics-app) to include this module, we have to configure a local repository.

Edit ~/devel/apps/module-test/analytics-app/conf/dependencies.yml like this:

# Application dependencies require: - play - analytics -> analytics repositories: - My local modules: type: local artifact: ${application.path}/../[module] contains: - analytics
Code language: PHP (php)

and after that run the following command to tell play to resolve dependencies.

cd ~/devel/apps/module-test/analytics-app play dependencies ~ _ _ ~ _ __ | | __ _ _ _| | ~ | '_ \| |/ _' | || |_| ~ | __/|_|\____|\__ (_) ~ |_| |__/ ~ ~ play! 1.2.4, http://www.playframework.org ~ ~ Resolving dependencies using /home/sas/devel/apps/module-test/analytics-app/conf/dependencies.yml, ~ ~ analytics->analytics -> (from My local modules) ~ ~ Installing resolved dependencies, ~ ~ modules/analytics -> /home/sas/devel/apps/module-test/analytics/../analytics ~ ~ Done! ~
Code language: JavaScript (javascript)

You can now start the main application on your workstation:

cd ~/devel/apps/module-test/analytics-app play run
Code language: JavaScript (javascript)

You can see your app running at http://localhost:9000.

Moving the tracking code to a reusable tag

Now we will move the tracking code to a tag defined in the module, so we’ll create a file ~/devel/apps/module-test/analytics/app/views/analytics.html with the tracking code, like this:

<script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-XXXXXXXX-1']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script>
Code language: HTML, XML (xml)

And now, replace the tracking code in main.html with a call to the tag, like this:

[...] <script src="@{'/public/javascripts/jquery-1.6.4.min.js'}" type="text/javascript" charset="${_response_encoding}"></script> #{get 'moreScripts' /} #{analytics /} </head> [...]
Code language: HTML, XML (xml)

Getting module configuration from the application.conf file

Our module is almost ready, there’s just one thing that’s preventing us from really reusing it on another application: the Google analytics code is hardcoded in our tag!

So we will read it from the application.conf file. Just edit the analytics.html tag like this:

%{ String code = play.Play.configuration.getProperty("analytics.code", "") }% #{if code!=""} <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', '${code}}']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> #{/if}
Code language: HTML, XML (xml)

and add the following to your main application configuration file at ~/devel/apps/module-test/analytics-app/conf/application.conf

analytics.code=UA-XXXXXXXX-1

Prevent tracking in dev mode

This tag will will update the tracker every time the page is rendered, even when we are working on our development workstation!

So we will add a minor improvement to prevent the module from logging the page activity while working in development mode.

Just add the following condition to the code:

%{ String code = play.Play.configuration.getProperty("analytics.code", "") }% #{if play.mode.isProd() && code!=""} <script type="text/javascript"> var _gaq = _gaq || []; [...]
Code language: PHP (php)

Troubleshooting Openshift

Openshift won’t be able to resolve the relative reference to the module location (and in fact any war deployed application will have the same problem), so you’ll have to tell play to copy the module sources to the containing application before generating the war folder. Just issue:

cd ~/devel/apps/module-test/analytics-app play dependencies --forceCopy
Code language: JavaScript (javascript)

And that’s it, now you can deploy to openshift in the usual way:

cd ~/devel/apps/module-test play war analytics-app/ -o openshift/analyticsapp/deployments/ROOT.war cd openshift/analyticsapp/ git add -A git commit -m "added analytics module" git push origin
Code language: JavaScript (javascript)

Run your site locally with “play run”, and also open it from http://analyticsapp-opensas.rhcloud.com/, check the source code of both sites, you should see that the app running at openshift contains the tracking code, contrary to your local application.

Conclusion

In this post we saw how to deploy a play framework app to openshift and, more important, how to move features from your application to a module in order to reuse it from other applications.

You can learn more about modules on this article or reading the play framework documentation.

If you speak spanish you can help us with the translation, and you can also have a look at our work right here… You can be sure every click you make will be tracked!

From: http://playlatam.wordpress.com

Get our Articles via Email. Enter your email address.

You may also like...

6 Comments

  1. Javier Beneito Barquero says:

    Have you ever tried Spring Roo (http://www.springsource.org/spring-roo#try)?

    If so, which one do you consider better?

  2. Javier Beneito Barquero says:

    Apologies, yes, you know a lot of Roo.

    But the question remains for me due to debate in the Roo forum, which one is better for you?

  3. opensas says:

    Hi, I’m Sebastián, the author of this post. Viral has been kind enough to publish it on his blog.

    I come from php and dynamic language web development, and Play was the first java web framework that I felt comfortable with…

    Right now I’m giving my first steps with Spring (yea, I know, most people comes from spring, struts, grails to Play) and I’m trying to do it all by hand, later I’ll have a look at Roo

    But I’m also very interested about a comparison between Roo and Play, nevertheless I think they two completely differents approach to RAD. I mean, Roo generates code (that someone will have to understand and maintain), where as Play really needs little code…

  4. Rick Holland says:

    I have done a couple projects with Roo and lots of Strut1-2/Weblogic/JBoss/SpringMVC stuff and a Grails project. Play Framework is the most comprehensive and efficient Java framework I have ever used. Am looking forward to learning Play2/Scala. Couldn’t believe someone created a Java webapp framework independent of the Servlet spec (Sacrilege!!), but it works for ROR and Django. Some call Grails the Java ROR but actually Play is the Java ROR.

  5. Hi Viral,

    Your and mine sites are hosted on same server so very often I visit your site. Recently, I started working on Play framework and Java. I wanted to understand whether we can host a Play web application on any Linux hosting? If yes, then please guide.

  6. Bala Krishna.N says:

    Hi,

    I am a java guy, new to Play framework.

    Can you help me for the below scenario:

    My controller method needs to read the JSON request and return the JSON response.

    How to do this. Pls help

Leave a Reply

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