Sebastián has posted 2 posts at DZone. View Full User Profile

Divide and conquer: Play framework modules

01.20.2012
| 3673 views |
  • submit to reddit

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 highcohesion 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

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 openshiftgoogle 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 athttp://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 charset="${_response_encoding}" type="text/javascript" src="@{'/public/javascripts/jquery-1.6.4.min.js'}"></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>
[...]

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 charset="${_response_encoding}" type="text/javascript" src="@{'/public/javascripts/jquery-1.6.4.min.js'}"></script>
        #{get 'moreScripts' /}
 
#{analytics /}
[...]

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/ (go on, give it a try, every click counts), 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.

In the meantime, I’ve also added google analytics support to Play! framework spanish translation site. Here you can see it in action:

Google analytics realtime in action
Tracking visits from Colombia, Argentina, Perú, El Salvador
Venezuela, Costa Rica, Ecuador, México, Spain… in realtime

You can learn more about modules on this article or reading play’s documentation.

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

Check the original article at http://playlatam.wordpress.com/2011/12/21/divide-and-conquer-play-framework-modules/ 

Published at DZone with permission of its author, Sebastián Open.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Nikolay Skachkov replied on Sun, 2012/01/22 - 1:48am

"These are two good reasons to modularize an application" - right! Maybe even more than two. This topic was a real hit when I was a student. Some time affter 1970. "a module is not meant to be run on his own" - nice to finally know it about modules.

"Java itself is a very generic programming language and not originally designed for web application development" - what a revelation!

"deploying it to the cloud" is a relatively new concept (or at least new term). In this article it is same as "play framework app deployed somewhere on the Internet". Now I understand what "cloud" is.

... skipping valuable information about "cd" command and Google web analytics ... which seems to be a typical client-side javascript "module". Next is the instructionon how to desactivate the previous ...

"play provides us with a solution better suited for web applications". Apparently better than solution that Java provides - as long as this statement immedeately follows the "Java itself ..." statement already quoted above. The only example provided in the article is javascript example. Now I know that javascript is "better suited for web applications" than "Java itself". WOW!

Sebastián Open replied on Thu, 2012/01/26 - 8:22pm in response to: Nikolay Skachkov

Hi Alex, thanks for your comments

I'm sorry you failed to see the main intention of the article, may be I should have been more explicit about it.

I'm not trying to teach you how to put some javascript code in you web page, nor I'm discussing if javascript is better suited for web development than java, and I'm not talking about javascript "modules", whatever they might be.

The central point of the article is play modules ("Divide and conquer: Play framework modules", remember?)

So I just picked some very basic example (some client js code, in this case) and explained the steps to turn it into a play framework module, you know, play new module, dependency management, making a reusable tag, moving the configuration to the application.conf file, and as a bonus, and just to let google analytics track something, I deployed it to openshift.

The rest, was just an excuse to explain all those things. 

saludos

sas 

 

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.