Ian Page, Senior Consultant at Tier 2 Consulting, guides us through the creation and deployment of a JBoss module, and reasons for doing so.
JBoss Enterprise Application Platform 6 uses a completely new class loading system, based on modules. All of the JBoss application server functionality is based on modules, but how can you write your own and why would you want to?
This post assumes some basic knowledge of JEE (Java Enterprise Edition) applications, and that you have a working JBoss environment available. A separate Getting Started with JBoss EAP 6 post (coming soon) will cover setting up JBoss. The examples have been tested on version 6.0.1.GA of JBoss Enterprise Application Platform, although they should work on other revisions of EAP 6 as well as the community edition JBoss AS 7.
Eclipse projects containing the source code for this article are included at the end. They use Maven as a build tool so some familiarity with it will be useful. However you only need to deploy the archives for this article; it’s not necessary to build from the source. I’ve included it just for completeness.
Please e-mail me with any questions or comments!
What is a Module?
In simple terms, a module exists for each archive on the server. There are exceptions to that statement, but it’s good enough for now. For example, JBoss makes use of a lot of third-party functionality, such as Apache’s log4j (http://logging.apache.org/log4j/1.2/). This functionality is configured as modules. Actually, JBoss’ own functionality is also configured as modules. And, if you’ve deployed an application to JBoss, then you’ve already unknowingly written a module because JBoss treats deployments as modules. We’ll see next why you might want to knowingly write a module.
Why Use Modules?
Let’s assume that you’ve written a web application that you wish to deploy as a WAR file. Inside that application you will have Java classes in the WEB‑INF/classes directory. Now imagine that some of these classes represent functionality that you could potentially use in other applications. Maybe you’ve written some code to calculate the area of a circle, or (as in the example that we’ll see later) something that creates a greeting message based on a user’s name. To re-use the code you could just copy the classes into the WEB‑INF/classes directory of another application. However that’s not a particularly elegant solution, especially if there are lots of classes. Imagine if you find an error in your code, and have to go and make changes to every web application that contains it! It’s better to package the classes into a separate JAR file. Now you can maintain the code in one place, and make the JAR available to your web application. A simple way to do this would be to include the JAR file in the WEB‑INF/lib directory of the WAR file (this is one of the exceptions that I mentioned earlier: all of the archives in the WEB‑INF/lib directory of a WAR file will be treated as though they are in one module). However, this is still quite difficult to maintain: if you have to fix a bug in the JAR you would still have to update every WAR. In addition, if you have multiple WARs containing the same JAR, you will have deployed the same code multiple times effectively wasting space on your server and, more importantly, slowing down deployment and server start up. Deploying your code as a module will allow you to share the code between your applications. However, there’s a small problem with this approach: class loader isolation.
Modules and Class Loading
Each module has its own class loader, which is responsible for loading classes into memory. Generally these class loaders are isolated from each other. In other words classes loaded by one class loader can’t see the classes loaded by another class loader. In an application server such as JBoss this is usually a good thing; since multiple applications can be deployed there is a chance of the same class with the same name existing on the server in more than one place. Having isolated class loaders means that there won’t be class naming conflicts if this occurs. However, what happens if you want to reference a class in one module from a class in another module? Well, unless you tell JBoss otherwise, you can’t! Let’s see a small example of this.
I’ve created a couple of simple archives. One is a JAR (utils.jar) that contains a utility class; the other is a WAR (webapp1.war) that contains a servlet. The code for each is shown below.
I’ve left out some of the unimportant stuff like imports, so that we can see what the code actually does. The utility class GreetingMaker is very straightforward: it simply accepts a name in its constructor and creates a greeting based upon the name. It then returns the greeting in the getGreeting() method. In the doGet() method of the servlet class GreetingServlet a new GreetingMaker is created using a name passed in on the browser request. The generated greeting is then returned back to the browser in the response.
So, let’s deploy the two archives. If your JBoss server is already running, stop it now. This isn’t really necessary, but it makes viewing the log output a bit easier. The simplest way to deploy the archives is to drop them into the deployments directory in the standalone version of JBoss (<jboss-install-dir>/standalone/deployments). Then start JBoss in standalone mode with the <jboss-install-dir>/bin/standalone.sh script (this is for Linux or MacOS; Windows users should execute the <jboss-install-dir>binstandalone.bat script). The server will start, and you should see the following near the end of the log output:
Notice the bits in the middle about registering your two modules: deployment.utils.jar:main and deployment.webapp1.war:main. These are the names and versions of the modules, and we’ll refer back to one of these later. Just to be clear: the archives are standard archives; there’s nothing special that makes them modules. As I said before, JBoss treats all deployments as modules.
Let’s test our application. The web application context is by default derived from the name of the WAR (webapp1.war) and I’ve configured the servlet to map to /greet, and if you remember from the code above the servlet checks for a parameter called name. So let’s test the application using the following URL:
Did it work? Sadly not. You should have got an error similar to this:
This is the module class loader from the web application module complaining that it can’t find the class in the utility module. This is exactly what we’d expect: remember that modules are isolated by default. We need to tell JBoss that the web module is dependent on the utility module.
The simplest way to tell the web module that it is dependent on the utility jar is to add a Dependencies: entry to the WAR’s manifest file. The manifest file is a file named MANIFEST.MF contained within the META-INF directory of the WAR. Remember the name of the module when the utility JAR was deployed? It was deployment.utils.jar:main. Actually we don’t need the :main part; that’s the default and refers to the version of the deployment. I’ll come back to versions in the next article. The rest is how JBoss names its deployed modules, and takes the form deployment.<archive-name>.<archive-extension>. We just need to create a manifest file with the entry Dependencies: deployment.utils.jar in the WAR, and JBoss will look inside the utilities module for the classes it needs.
I’ve created another web archive (webapp2.war) that contains the manifest file. Actually the manifest file in this WAR contains some other stuff; this is because I used Maven to create it. Rest assured that it’s only the Dependencies: entry that’s required.
Deploy this archive by dropping it into the deployments directory (there’s no need to stop the server). JBoss will deploy it automatically, and you should see something similar to the following in the console:
Now, test the app by going to the new URL, which, because the new WAR is called webapp2.war, will be:
You should see the message Hello Fred in the browser. It worked! The web module is now able to see the classes in the utility module.
We’ve been able to create a re-usable module that can be shared with other deployments. This way we can be more efficient about what we deploy, and make maintenance easier.
In the next article I’ll deal with some slightly more advanced dependency techniques.
Find out more about JBoss and Tier 2 Consulting.