So where do I put all my jars?
As you write your applications you’re bound to leverage third party libraries to cut down on the amount of work; lets face it no one wants to reinvent the wheel. A downside is sometimes these third party libraries might not be the most mature or stable releases to date. As your product grows and matures, or you expand your client base or number of implementations, you’re bound to come across multiple third party library dependencies, even ones across the same library but different versions, what a headache! How can we organize these libraries in JBoss? Luckly we are provided with a few directories where you can stick library jars for use in your own application, here is a quick rundown:
- jboss/client - this folder contains all the jar files used by any client application for jboss, for example a swing application that needs to commnicarte with a remote JBoss instance would need to have the jar files in this directory in its classpath or it will not be able to communicate correctly. Generally, you don’t stick third party libraries used by your application here unless you’re writing some kind of jboss client application and you’re extending the server’s functionality in some way.
- jboss/common/lib – this folder is meant to hold jar files that should be made available to all JBoss instances. Jar files global to all applications go here.
- jboss/lib – this folder holds all the jar files used in order for the server to run correctly. You don’t stick your libraries in this directory unless you’re extending or adding functionality to the JBoss server itself.
- jboss/lib/endorsed – this folder holds all the endorsed libs that jboss uses where other implementations can be used to override. Xalan implementations other than the default go here if you want to override with a newer version. Since JBoss relies on these libraries also, be mindful that you might find xml parsing issues if you use an older xalan library (jboss uses many xml files for configuration)
- jboss/server/<configured instance>/lib – this is where you put any instance specific jar files
So for the most part, unless you’re going to be tinkering with extending or modifying the JBoss server itself, you’ll want to stick to one of thee locations, the server global lib, the instance global lib, or the lib in your deployable artifact.
Ultimately, you’re going to need to make a decision on how you’re manage your third party libraries, and its all based on your particular setup and installed application base. The JBoss loaders can do teasingly mysterious things, as the order of precedence might not be completely obvious. The most easy pitfalls include more than one library being loaded, but of a different version. Which one gets loaded if there is more than one? The answer depends on the strategy you used in your setup, and figuring out the best strategy for your particular application(s) is paramount to minimizing this risk. First, lets look at the viable options:
If you want to make your application portable and completely self contained – you’ll want to package all your third party libraries in the right lib directory for your war or ear file. The benefits include more complete portability by becoming completely self contained deployable artifacts, and therefore minimizing immediate class loading problems. The downside to packaging everything into your deployable artifact is that your instance startup times inflate. A full complement of third party libraries in a huge ear file containing multiple war files could end up taking minutes to deploy because each artifact deploys its own libraries; and if there are common libraries throughout, each one can be loaded separately if they’re not organized to minimize this inefficiency.
The converse of removing all the third party libraries and sticking them into the instance library (
If you want to go a step more global than an instance specific common library, you could use the
What the.. ?
So what happens if you have more than one library, say one in your war file, and another one in the instance lib, which one gets used by your code? It turns out that the order in which the classes are loaded matters and is the determining factor. The server global directory loads before the instance specific lib, and the instance specific lib loads before your deployable artifacts and their libraries. So basically, the more global libraries will outweigh the artifact local libraries.
Now what if you want your deployable artifact to override global libraries? Luckily, JBoss provides a way to scope the deployment. Scoping the deployment here means you’re making the libraries used by your deployment localized, superseding the global libraries with whats packaged in the artifact.
For War files you will need to add an entry to your jboss-web.xml file:
<jboss-web> <class-loading java2ClassLoadingCompliance="false"> <loader-repository> com.example:archive=name-of-your-deployable-artifact.war <loader-repository-config> java2ParentDelegation=false </loader-repository-config> </loader-repository> </class-loading> </jboss-web>
and for Ear files you’ll need to make your jboss-app.xml look like this:
<jboss-app> <loader-repository> com.example:archive=name-of-your-deployable-artifact.ear <loader-repository-config> java2ParentDelegation=false </loader-repository-config> </loader-repository> </jboss-app>
Where com.example is the package name for the specific class package (third party library) you want to override, and unique-archive-name.xxx is the name of the deployable artifact for which you want to localize the classes loading. Note that these descriptors only work for jboss and will not be honored by other vendors. Also worthy of mention is that for your deployable artifacts, the most global artifact’s deployment scope will be honored, so if you have an ear file, the jboss-app.xml in the ear file will override and cause any jboss-web.xml scoping configurations in any embedded war files to be ignored. java2ParentDelegation is supposed to be disabled by default, but its a good idea to explicitly set it to false anyway just to be on the safe side – enabling it to true will cause the classes referenced in this scope configuration to be loaded by the next most global scope (moving the loaded classes to the instance/lib if its in an ear or war, and to the most global jboss/common/lib if its in the instance specific lib).
It’s also a good practice to make sure your war files don’t start with the same first few letters as other deployed war files in the same instance. In JBoss 4.x it was possible to collide class loading when 2 or more war files started with the first few letters and the packaged class files in the WEB-INF directory shared a similar code base (ex: my_war.war vs my_warfile.war). The fix was to change the names of the war files so they were totally different. Whoever loaded first would be linked in the JBoss class loaders. If you run into a situation where old code keeps getting reloaded, keep this in mind.