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:
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.
Possible scenarios
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.
Resources:
JBoss wiki on Class Loading
Jboss Wiki on Class Loading use cases
Related posts: