Play framework modules: Divide and conquer
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!
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 mymail@mail.com -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
Next, we’ll get rid of the demo app:
cd ~/devel/apps/module-test/openshift/analyticsapp rm -fr pom.xml src
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! ~
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
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> [...]
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
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
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
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! ~
You can now start the main application on your workstation:
cd ~/devel/apps/module-test/analytics-app play run
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>
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> [...]
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}
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 || []; [...]
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
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
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!
Reference: Play framework modules: Divide and conquer from our JCG partner Sebastian Scarano at the Having fun with Play framework! blog
Related Articles :