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.