Class loader issues with JB 6.0 and HornetQ
cdollar393 Feb 7, 2011 7:05 PMHi All,
I am migrating my application from JBoss 4.2 to JBoss 6.0 Final and am running into some classloading issues with HornetQ.
I deploy my application as a single .ear file. It is a typical .ear with multiple .sar, .war, and .jar files. I keep all my common files, interfaces, etc. in their own .jar file, and that .jar goes into the lib/ folder in the .ear.
One of my .war files has an application scoped backing bean that also implements MessageListener. It receives ObjectMessage (I know this is not the best for serialization) JMS messages.
My issue comes in when this class receives a message. As soon as I call the getObject() method on the ObjectMessage I get an error:
{code}16:36:03,890 ERROR [com.rydindecal.PermitExpress.web.beans.UserBean] Error while receiving JMS message: javax.jms.JMSException: com.rydindecal.PermitExpress.common.ObjectChangeNotification
at java.net.URLClassLoader$1.run(URLClassLoader.java:217) [:1.6.0_18]
at java.security.AccessController.doPrivileged(Native Method) [:1.6.0_18]
at java.net.URLClassLoader.findClass(URLClassLoader.java:205) [:1.6.0_18]
at java.lang.ClassLoader.loadClass(ClassLoader.java:321) [:1.6.0_18]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294) [:1.6.0_18]
at java.lang.ClassLoader.loadClass(ClassLoader.java:266) [:1.6.0_18]
at java.lang.Class.forName0(Native Method) [:1.6.0_18]
at java.lang.Class.forName(Class.java:264) [:1.6.0_18]
at org.jboss.classloader.spi.base.BaseClassLoaderDomain.loadClass(BaseClassLoaderDomain.java:284) [jboss-classloader.jar:2.2.0.GA]
at org.jboss.classloader.spi.base.BaseClassLoaderDomain.loadClass(BaseClassLoaderDomain.java:1152) [jboss-classloader.jar:2.2.0.GA]
at org.jboss.classloader.spi.base.BaseClassLoader.loadClassFromDomain(BaseClassLoader.java:886) [jboss-classloader.jar:2.2.0.GA]
at org.jboss.classloader.spi.base.BaseClassLoader.doLoadClass(BaseClassLoader.java:505) [jboss-classloader.jar:2.2.0.GA]
at org.jboss.classloader.spi.base.BaseClassLoader.loadClass(BaseClassLoader.java:450) [jboss-classloader.jar:2.2.0.GA]
at java.lang.ClassLoader.loadClass(ClassLoader.java:266) [:1.6.0_18]
at java.lang.Class.forName0(Native Method) [:1.6.0_18]
at java.lang.Class.forName(Class.java:264) [:1.6.0_18]
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:621) [:1.6.0_18]
at org.hornetq.utils.ObjectInputStreamWithClassLoader.resolveClass(ObjectInputStreamWithClassLoader.java:69) [:6.0.0.Final]
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1592) [:1.6.0_18]
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1513) [:1.6.0_18]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1749) [:1.6.0_18]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346) [:1.6.0_18]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:368) [:1.6.0_18]
at org.hornetq.jms.client.HornetQObjectMessage.getObject(HornetQObjectMessage.java:158) [:6.0.0.Final]
at com.rydindecal.PermitExpress.web.beans.UserBean.onMessage(UserBean.java:163)
at org.hornetq.jms.client.JMSMessageListenerWrapper.onMessage(JMSMessageListenerWrapper.java:91) [:6.0.0.Final]
at org.hornetq.core.client.impl.ClientConsumerImpl.callOnMessage(ClientConsumerImpl.java:822) [:6.0.0.Final]
at org.hornetq.core.client.impl.ClientConsumerImpl.access$100(ClientConsumerImpl.java:46) [:6.0.0.Final]
at org.hornetq.core.client.impl.ClientConsumerImpl$Runner.run(ClientConsumerImpl.java:940) [:6.0.0.Final]
at org.hornetq.utils.OrderedExecutorFactory$OrderedExecutor$1.run(OrderedExecutorFactory.java:100) [:6.0.0.Final]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) [:1.6.0_18]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) [:1.6.0_18]
at java.lang.Thread.run(Thread.java:636) [:1.6.0_18]{code}
In this case "ObjectChangeNotification" is the actual object in the ObjectMessage.
At this point I tried to change my code to use a MapMessage rather than ObjectMessage. This is when I ran into my second problem... In the onMessage() method in my class I was using JNDI and an interface to lookup an EJB and call one of it's methods. When the onMessage() was called it would fail with this error at the step that made the EJB call:
{code}
16:16:27,065 WARN [org.hornetq.jms.client.JMSMessageListenerWrapper] Unhandled exception thrown from onMessage: java.lang.RuntimeException: Specified calling class, com.rydindecal.PermitExpress.interfaces.ClientManager could not be found for BaseClassLoader@4b4b50{vfs:///home/chris/java/projects/jboss-6.0.0.Final.git/server/permitexpress/conf/jboss-service.xml}
{code}
Now I'm really believing it is a classloading issue, so to test this I changed the "isolated" property in JBOSS_HOME/server/< servername>/deployer/ear-deployer-jboss-beans.xml to false as per this thread. This got it working.
Then I went back and changed my MapMessage stuff back to ObjectMessage and tried again -- this also worked.
As soon as I change "isolated" back to true it doesn't work.
Now for my question... What is the best approach to fix this? The thread I linked to above states that setting that value to false is not recommended for production, so I'm hesitant to think this is a long-term solution. Based on the exception it appears that the onMessage() call was using the sytem-scoped classloader that JBoss uses (and is defined in JBOSS_HOME/server/< servername>/conf/jboss-service.xml) rather than the deployed .ear's "isolated" classloader, but I could be wrong there. I'm guessing that if I took my .jar of common objects and interfaces and put it in JBOSS_HOME/common/lib that things would work with "isolated" set to true, but this would be really inconvenient.
Any thoughts or ideas?
Thanks!
Chris