Master Your Portlet Packaging in JBoss
eXo Platform 4.0 has a Tomcat 7 version and a JBoss EAP 6.1 version. In the JBoss version, eXo is packaged as a unique (exploded) EAR containing all the JARs and all the WARs. To install your portlets, the default procedure is to install your WARs and your JARs inside the eXo EAR. This can be done manually (copy the WARs into extension manager.
, copy the JARs into , then update the ) or with theThis method has the benefit of being really easy, especially with the extension manager, but it also has some drawbacks:
- eXo artifacts and custom portlets artifacts are not well separated
- portlets cannot be hot deployed
- it can lead to class-path issues if your portlet needs a different version of a JAR already present in eXo
Hopefully, there are solutions to avoid all these drawbacks. With JBoss AS 7 (JBoss EAP 6), a new class-path system has been introduced: JBoss Modules. Its goal is to get rid of the classic monolithic class-loader system and replace it by a more modular one. In this system, each library is a module that is linked to other modules that it depends on. So every library has its own made-to-measure class loader, which avoids many of the class-loader issues.
Thanks to this system, one EAR/WAR can depend on a second EAR/WAR, for example, and this makes all the classes of the second EAR/WAR available to the first. This is used to package portlets in separate WARs or EARs.
Portlet in a WAR or EAR alongside platform.ear
To make a portlet hot deployable, it needs to be packaged in a separate artifact other than the eXo EAR (platform.ear). This can be done in a WAR or EAR, which will be deployed alongside the eXo EAR (in the deployments folder).
Building an EAR containing a portlet is really simple, thanks to the Maven EAR plugin. There are many examples available (here or here). The file only contains the WAR of the portlet (and the application.xml file, of course, which declares the WAR). But simply deploying this EAR in the deployments folder, alongside the eXo EAR, is not sufficient, since the portlet needs libraries present in the eXo EAR and because the class paths of EARs are isolated by default. This is where we use JBoss Modules. As we said before, each EAR is a JBoss module. So all that is required is to make the portlet EAR depend on the eXo EAR.
JBoss provides two ways for declaring dependencies in an artifact: in
or in . Both ways are equivalent.The syntax for
is:Dependencies: deployment.platform.ear
Note: deployment.platform.ear is the JBoss module name of the eXo EAR. Every artifact deployed in JBoss is a module and can be referenced with
The syntax for
is:<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2"> <deployment> <dependencies> <module name="deployment.platform.ear"/> </dependencies> </deployment> </jboss-deployment-structure>
Once one of these files has been added into the EAR, the portlet can be deployed (and hot deployed) successfully.
If the portlet project is a Maven project, the
file can be generated automatically with the following lines in your :<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <archive> <manifestEntries> <Dependencies>deployment.platform.ear</Dependencies> </manifestEntries> </archive> </configuration> </plugin> </plugins> </build>
Portlet in a WAR or an EAR, alongside platform.ear, but where JARs conflicts with eXo
Now that our portlet is in its own EAR and can be hot deployed, let’s deal with a more tricky case.
eXo comes with a bunch of third-party libraries. Thanks to the dependency on deployment.platform.ear, the portlet sees all of them. This can be useful, as they can be used without being embedded in the portlet EAR, but it can also be an issue if the portlet needs to use a different version of a library. For example, let’s say I want to create a Groovy portlet and use the latest Groovy version to benefit from its new features. eXo comes with version 1.7.6 and I want to use a feature available from version 2: the new String methods “takeWhile” and “dropWhile”.
If the Groovy 2 JAR is simply added into the libraries of the WAR (
) or the EAR ( ), then since eXo EAR is set as a dependency, JBoss gives priority to it, so it will use the version embedded in eXo. So if the portlet is deployed with this structure, this error will show up:17:39:08,267 ERROR [portal:UIPortletLifecycle] (http-/127.0.0.1:8080-1) Error processing the action: No signature of method: java.lang.String.dropWhile() is applicable for argument types: (org.exoplatform.portlet.groovy.GroovyPortlet$_processAction_closure1) values: [org.exoplatform.portlet.groovy.GroovyPortlet$_processAction_closure1@3417595d]: groovy.lang.MissingMethodException: No signature of method: java.lang.String.dropWhile() is applicable for argument types: (org.exoplatform.portlet.groovy.GroovyPortlet$_processAction_closure1) values: [org.exoplatform.portlet.groovy.GroovyPortlet$_processAction_closure1@3417595d]
The Groovy JAR of the portlet must have priority. The first thing to do is to make a JBoss module of this JAR (remember that a JAR deployed in
of a WAR or in the lib folder of an EAR is not considered as a standalone JBoss module). To do this, the JAR can be set as an EAR Java module (which will turn it automatically into a JBoss module) or declared as a server JBoss module.A Java module can be declared in an EAR with the following XML snippet in the
file of the EAR:<module> <java>groovy-all-2.2.1.jar</java> </module>
Here, the Groovy JAR, located at the root of the EAR, is available to all the artifacts of the EAR, and is automatically registered as a JBoss module with the name
.So it can be now set as a dependency in the MANIFEST.MF of the EAR, in order to give it a higher priority than the eXo EAR:
Dependencies: deployment.<name-of-the-ear>.ear.groovy-all-2.2.1.jar, deployment.platform.ear
The order of the names in the Dependencies field is important. It is from the highest to the lowest priority. With this method, EAR artifacts will first look in the
before looking in the eXo EAR libs. So the portlet will use version 2.2.1 of the Groovy library and will run successfully, while eXo will still use its own embedded Groovy version.Conclusion
The new JBoss Modules class-path system is very powerful and is used to package portlets as needed. Hot redeployment, resolution of library conflicts and well-separated artifacts are now possible. And JBoss Modules has many more options for resolving tricky cases (but be careful not to fall into the JBoss Modules class-path hell ;-))!
Enjoy!