Okay, not really incarnate, since it’s not alive. And it might not really be about JBoss – could be a general J2EE problem, though I don’t have time to look at the spec.
The problem stems from the seemingly innocuous desire to run multiple enterprise applications in a single instance of JBoss/Tomcat. Seems innocent enough, doesn’t it? In the red corner, we have Liferay Enterprise Portal, ready to be deployed as it’s own pre-packaged EAR file. In the blue corner, we have several internal web apps bundled in an EAR that we’d really like to run in the same JBoss instance. Strangely, when I’d run our internal apps, it was trying to use Liferay’s version of Hibernate. How odd.
So what’s the problem? Well, it seems that the default behavior in JBoss is that everyone gets to share their classes – the first class loaded wins. WHY?!? If I have 2 different EAR files, with different versions of libraries, why in the world would I want to use ANY other version of that library than THE ONE I PACKAGED WITH MY APPLICATION! There is, by the way, a proprietary way to tell JBoss NOT to do this. To specify a fresh classloader, isolated from your other apps (per EAR, at least), add this to your jboss-app.xml:
PUBLIC "-//JBoss//DTD J2EE Application 1.3V2//EN"
That wasn’t so bad now, was it? But wait, this is but the beginning of your troubles. It seems that when you switch into this mode, the classloader suddenly becomes VERY picky. Things you were able to do before suddenly become VERY difficult. Parts of your application that used to Just Work suddenly stop working. And of course, classloader issues rarely actually seem to show up as a ClassNotFoundException. My favorites are always the InvocationTargetException and the NPE, though there are many more obtuse messages than that.
It turns out that you were probably depending on JBoss’ vaunted Universal Class Loader more than you ever realized. So in some ways, it did make your life easier. Now, your application throws a temper tantrum. If you have a log4j JAR in your application, JBoss won’t like that you loaded that instead of its version of log4J. That jar you had in a few of your web apps? Well, now it may throw a tantrum as it tries to use the version you loaded in WebAppA in WebAppB. I don’t think it’s actually more strict, rather it seems that there are many things that just work under the standard configuration that we never worried about before.
Now it’s entirely possible that I don’t know what I really want, and that I’m dead wrong, or it would kill performance. But what seems to MAKE SENSE is a full, hierarchal class loading inheritance system. If MyApp1.war exists in MyApps.ear, and MyApps.ear has a version of struts.jar, then MyApp2.war can use that version, but MyApp1.war can supercede that with its version. As things are, now that I can partition this EAR, I’m wading through all the changes it requires, and it STILL won’t fix everything. While I’ll at least be able to run someone else’s app and my app in the same instance, what happens when I get a second third-party application in there? Now, without tweaking, it and Liferay will try to load in the same classloader. Seems like an unnecessary and counter-intuitive mess.
8 thoughts on “The JBoss ClassLoader is pain incarnate”
Isolate packages and people complain that the performance sucks, don’t isolate and people complain that their packages are not isolated. Whatever. Classloaders are pluggable, write your own.
Um, if webapp1.war has struts, you DON’T WANT webapp2.war to use webapp1’s classloader. You want webapp2 to have its own parallel resources. Really.
The JBoss CL does suck. It’s reason enough to go and buy a real appserver.
webapp1 and webapp2 can use parallel resources like the author just showed above.
This problem has crop up in so many places where each victem of this is usually comming from commercial giants such as weblogic or webshpere and usually they spend hours racking thier head trying to figure out why things are just out of whack. It’s in the spec and there is even documentation on how to share resources between applications through the extension mechanism. Take a look at this link if you would like more information on j2ee classloading. It’s one of the best I’ve seen, quick to the point with some links to other good references. http://www.theserverside.com/articles/article.jsp?l=ClassLoading
From the spec…. The deployment tool must allow the Deployer to deploy the same J2EE application
multiple times, as multiple independent applications, possibly with different
configurations. For example, the enterprise beans in an ejb-jar file might
be deployed multiple times under different JNDI names and with different configurations
of their resources… With Jboss class loading scheme I do not believe this is possible.
It is possible, as shown in the blog entry. Same application multiple times, different resources. These are completely isolated classloaders.
My point is that my preference is to NOT share resources between web apps or EARs, but that seems to be the default behavior in JBoss.
In JBoss, webapp2 seems to try and use webapp1’s classes if webapp1 loaded them first. I fail to see the benefit here – seems like a recipe for disaster.
Comments are closed.