JBoss classloader issues
- Published: 27 December 2008
By default JBoss (prior to version 3.2) uses a flat class loading model that avoids the need to redundantly include classes in different layers. WAR files would only have the web contents and servlets, EJBs their interfaces, implementations and types, etc.
From 4.0.2 JBoss has changed to the Servlet spec classloading model, i.e. it uses the Tomcat classloader.
One common issue with JBoss classloader happens when you deploy multiple applications each one attempting to access common interfaces. For example supposing you have deployed a web application named "Webapplication.war" which has references the EJB deployed in a "Application.ear".
Since the Web Application is not bundled in the .ear file you need to make the EJB visible by adding in your WebApplication the EJB interfaces. So we add an ejb-client.jar in your "Webapplication.war"
Ok. done. Now what happens if you try to invoke your EJB from the Web application ? since JBoss has already loaded the EJB classes from another branch of the Classloader (when you have deployed "EJBApplication.jar") a ClassCastException is raised.
Let's isolate all the applications!
The solution to this problem is not too complicated: you simply need to tell jBoss to load your applications into separated classloaders, so no conflict can arise.
You need to do a little modification into your deploy/ear-deployer.xml (If you are using JBoss 3.X The EARDeployer is configured in conf/jboss-service.xml )
<mbean code="org.jboss.deployment.EARDeployer" name="jboss.j2ee:service=EARDeployer"> <!-- Isolate all ears in their own classloader space --> <attribute name="Isolated">true</attribute> <!-- Enforce call by value to all remote interfaces --> <attribute name="CallByValue">true</attribute> </mbean>
So now you have enforced your ears to be in isolated classloader spaces using call by value for remote interfaces.
Ok now your problem is fixed but what about if we want other .ear file to respect the default policy ? the only solution is to isolate the single applications. How ? using JBoss deployment descriptors.
Let's isolate only our application
By default you don't need to use JBoss deployment descriptors when you are deploying an ejb or a web application: however if you need to tell to JBoss to load your application into an isolated Classloader, then just pick up the appropriate deployment descriptor.
In order to accomplish this you just need to add an element <loader-repository> with the name of the classloader repository to use.
All application refering to the same 'loader-repository' will share the same context classloader. And the will access to the classes of each other.
Important! When you add the loader-repository information, the application classloader will be isolated from all other application, even if the 'Isolated' parameter set to false!
1. Isolating the .ear files
For .ear files, in your jboss-app.xml, add the following descriptor fragment construct to enabled scoped class loading:
<jboss-app> <loader-repository> com.example:archive=unique-archive-name </loader-repository> </jboss-app>
2. Isolating the .war files
For .war files, in your jboss-web.xml, the following template applies:
<jboss-web> <loader-repository> com.example:archive=unique-archive-name </loader-repository> </jboss-web>
3. Isolating the EJB3 .jar files
For EJB3 deployed as .jar files you need to modify jboss.xml:
<?xml version="1.0" encoding="UTF-8" ?> <jboss> <loader-repository> com.example:archive=unique-archive-name </loader-repository> </jboss>
4. Isolating the .sar files
For .sar archives, in your jboss-service.xml, the following template applies:
<server> <loader-repository> com.example:archive=unique-archive-name </loader-repository> </server>