Jars and Class Loading, Jboss v5.x

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.

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 (jboss/server/<configured instance>/lib). The instance libraries load up orders of magnitude more quickly than the prepackaged third party library strategy, but the downside is your deployable artifact is no longer completely self contained. This might not necessarily be a bad thing at the end of the day however, as long as your third party libraries can be easily managed in whatever application server you use. JBoss allows you to use an instance specific library folder, and it turns out to be a neat alternative to self contained libraries, especially when there is more than one deployed application, and they share a few common libraries.

If you want to go a step more global than an instance specific common library, you could use the jboss/common/lib directory. This location will be loaded before the instance specific library and provides a baseline for all available instances. Any libraries that are super global should be placed here.

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:

  1. XML, Xalan, Endorsed dirs and &..
  2. External deploy directories in JBoss
  3. Configuring Data Sources, JBoss 7

Comments (3)

  1. avatar

    11:12 am, October 3, 2011irfan  / Reply

    Hi,
    For enabling the scoped-class-loading configuration, you wrote:


    1.
    2.
    3. com.example:archive=name-of-your-deployable-artifact.ear
    4.
    5. java2ParentDelegation=false
    6.
    7.
    8.

    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..”

    I am trying to do this settings for multiple package names. I tried the following (didn’t work): How to do this?

    1.
    2.
    3. com.example1:archive=name-of-your-deployable-artifact.ear
    4.
    5. java2ParentDelegation=false
    6.
    7.

    2.
    3. com.example2:archive=name-of-your-deployable-artifact.ear
    4.
    5. java2ParentDelegation=false
    6.
    7.

    8.

    • avatar

      1:45 am, October 4, 2011Ant  / Reply

      Sure, I can try to help. I need more information about your deployments though – can you give the exact packages you are trying to configure as well as the layout of each deployable artifact your application uses to run?

  2. avatar

    11:44 am, November 30, 2011shan  / Reply

    Thanks! Very Useful.

Pingbacks (0)

› No pingbacks yet.