JavaEE (v5 and v6) has a commanding presence in both marketshare and (developer) mindshare in the enterprise software world. The specifications are well thought-out, battle-tested and highly relied upon. I started using JavaEE (v5) way back in 2007 with JBoss 4.x. The latest release, JavaEE-7, which was released close to a year ago brings with itself a lot of worthy changes to the specs and impl. To bring myself up to speed on it I went through few books and attended a conference (JUDCon, Bangalore). But I have also been coding and acquainting myself with Typesafe's Scala reactive stack. These two stacks are bound to compete with each more and more in the coming days. However I feel, they can be used in applications in complementary ways when carefully designed. The competition and challenge to JavaEE-7 stems from two tough requirements -
The JavaEE stack is broadly split into 3 tiers - web, business and persistence. JSF (broadly, including expression-lang, JSTL, JSP and Servlets) is the technology of choice (per the specs) in the web tier. And JSF, to me, seems most vulnerable of not being able to raise up to the above mentioned two challenges. JSF does feel like the loose brick in the JavaEE stack. And it feel ever more so after spending some time with the Play Framework!
This was a recent tweet by Peter Thomas -
Now to a quick primer on frameworks in JavaEE's web-tier. I like to group the Web Tier of JavaEE applications into 3 groups per a broad grouping of library's goals. A quick note on each of these...
With me having done quite a bit of programming in JSP and JSF, I found Play to be fresh breath of air. Web application programmers must spend some time reading and understanding this blog by Guillaume Bort on reasons behind the decision to not write yet another framework atop Java's HttpServlet. My experience with Play has been by building a lookalike for my blog with it (hosted on Heroku here). I have built my blog on NodeJS and RubyRails as well - and honestly, it took much lesser time to build it with Play. But more important is the question of should enterprise web-tiers be programmed with Play? Is Play up to the mark for projects of development scale and complexity? My answer is a thumping YES!!
Let me list the specific features that I found especially useful and important -
This one is a excellent presentation (and code) by Yevginy Brikman of LinkedIn (LinkedIn uses Play! for multiple web apps in its stack). The title is apt - building web apps that are composable and streamable. More and more, enterprise applications have UI requirements of the kind described here. And building these using JSF/Java would be too complex a web project and IMHO not worth the trouble!
For very good reasons enterprise applications are generally hosted on application containers. And application containers mostly come built with a servlet container for web frontend. Now since Play2 is not Servlet based does it mean using it in enterprise applications straightaway get vetoed? Not necessarily. If engineers have a little chutzpah, the gap can be bridged. Here is how I was able to host my Play2 application on JBoss-Wildfly -
|Artifact||GroupID||Version In Play v2.2.1||Version In Wildfly v8.0.0-Final||Newer|
|Jackson Core, Annotations and Databind||com.fasterxml.jackson.core.jackson*||v2.2.2||v2.3.0||Wildfly|
|Apache Commons Codec||org.apache.commons.codec||v1.6||v1.9||Wildfly|
|Apache Commons IO||org.apache.commons.io||v1.3.2||v2.4||Wildfly|
|Hibernate Commons Annotations||org.hibernate||v4.0.2||v4.0.4||Wildfly|
|Hibernate Core, Entity Manager||org.hibernate||v4.2.3||v4.3.1||Wildfly|
|Apache Commons Lang||org.apache.commons.lang||v3.1||v2.6||Play|
One can see from the above table that versions of many artefacts is newer in Wildfly. I decided to use the newer Wildfly versions and exclude these from the WAR generated by Play2War. So included this filtering statement in my project's build.sbt file -
Play2WarKeys.filteredArtifacts ++= Seq( ("com.google.guava", "guava"), ("com.google.code.findbugs", "findbugs"), ("com.fasterxml.jackson.core","jackson-annotations"), ("com.fasterxml.jackson.core","jackson-core"), ("com.fasterxml.jackson.core","jackson-databind"), ("com.fasterxml","classmate"), ("commons-codec","commons-codec"), ("commons-io","commons-io"), ("org.hibernate","hibernate-commons-annotations"), ("org.hibernate","hibernate-core"), ("org.hibernate","hibernate-entitymanager"), ("org.hibernate","hibernate-validator"), ("org.hibernate.common","hibernate-commons-annotations"), ("org.hibernate.javax.persistence","hibernate-jpa-2.0-api"), ("javax.validation","validation-api"), ("javax.persistence","persistence-api"), ("javax.transaction","transaction-api"), ("org.jboss.spec.javax.transaction","jboss-transaction-api_1.1_spec"), ("org.jboss.logging","jboss-logging"), ("org.jboss.logmanager", "log4j-jboss-logmanager"), ("org.springframework","spring-beans"), ("org.springframework","spring-context"), ("org.springframework","spring-core"), ("postgresql","postgresql"), ("org.javassist","javassist"), ("org.yaml","snakeyaml"), ("antlr","antlr"), ("com.h2database","h2"), ("dom4j","dom4j"), ("tyrex","tyrex") //("com.jolbox", "bonecp"), //("io.netty","netty"), )
app/ conf/ project/ war/ |--WEB-INF |--jboss-deployment-structure.xml
I used the following setting in this xml -
<jboss-deployment-structure> <deployment> <dependencies> <module name="com.google.guava"/> <module name="com.fasterxml.jackson.core.jackson-annotations"/> <module name="com.fasterxml.jackson.core.jackson-core"/> <module name="com.fasterxml.jackson.core.jackson-databind"/> <!--module name="com.h2database.h2"/--> <module name="org.apache.commons.codec"/> <module name="org.apache.commons.io"/> <!--module name="io.netty"/--> <module name="org.hibernate.commons-annotations"/> <module name="org.hibernate"/> <module name="org.javassist"/> <module name="org.jboss.logging"/> <module name="org.yaml.snakeyaml"/> <module name="org.antlr"/> <module name="org.dom4j"/> <module name="org.postgres"/> <module name="javax.validation.api"/> <module name="javax.persistence.api"/> <module name="javax.transaction.api"/> <module name="org.glassfish.javaeetutorial.helloservice-api"/> <module name="org.jboss.log4j.logmanager"/> </dependencies> </deployment> </jboss-deployment-structure>
war |--WEB-INF |--classes |--META-INF |--persistence.xml
Since the WAR will be deployed in Wildfly, make sure to read the persistence related docs from its wiki. 5. Logging - Play2War's GitHub wiki has a separate section for configuring logging with Wildfly. Make sure to read that. It basically asks for including this dependency in the build.sbt -
"com.github.play2war.ext" %% "redirect-playlogger" % "1.0.1"6. With this configuration the WAR built by Play2War for my Play2 project was around 50MB in size. One has to use JNDI lookup to access the EJB's in container. The looked up EJB's can be cached in a Scala Object to avoid repeats 7. One shortcoming that I realised while doing this work was that websockets will not work in this setup. Play2 uses Netty as is HTTP server and Wildfly uses Undertow. The websocket implementation in Wildfly (per the Websocket 1.0 spec) could be closely tied to to Undertow - but I have not read Wildfly's code to say so with certainty. Or maybe if one can make Wildfly to use Netty instead of Undertow as the underlying HTTP server then the websocket communication as provided by Play2 should become naturally available. Anyway, this is one shortcoming one has to put up if one takes this route
The Final Word - I really feel Play2 will be a really good fit even in the JavaEE stack a few years down the line when some more bridges will appear around it to make it easily compatible to JavaEE application servers. It can be done even now as I found out. However one should take this plunge very cautiously (at least when using Wildfly). But to me, this is the right way to go... and the right way is generally never easy!