Quartz MDB integration and classloader issues
jaikiran Jul 24, 2009 7:42 PMThere have been more than a couple of users now, having trouble with getting the Quartz integration with MDB running. Here are the 2 instances, for example:
http://www.jboss.org/index.html?module=bb&op=viewtopic&t=158926
http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4229808
The real issue is classloading where the quartz thread triggers a job. The QuartzJob which does this (and is triggered through quartz-ra.rar) has the base classloader which does not have access to user deployment classloader. So if you have your MDB packaged in a deployment with its own classloader (which is practical), then the QuartzJob will not have access to the EJBs in the user deployment leading to exceptions like:
javax.ejb.EJBTransactionRolledbackException: Unable to inject jndi dependency: env/org.jboss.tutorial.quartz.bean.AnnotatedQuartzMDBBean/calc into property org.jboss.tutorial.quartz.bean.AnnotatedQuartzMDBBean.calc: org.jboss.tutorial.quartz.bean.Calculator from BaseClassLoader@1f172aa{VFSClassLoaderPolicy@9f3364{name=vfszip:/home/jpai/jboss-5.1.0.GA/server/default/deploy/quartz-ra.rar/ domain=ClassLoaderDomain@1f5eb7f{name=DefaultDomain parentPolicy=BEFORE parent=org.jboss.bootstrap.NoAnnotationURLClassLoader@1431340} roots=[MemoryContextHandler@31918682[path= context=vfsmemory://3j001-a542up-fxj4w94o-1-fxj4x19a-21 real=vfsmemory://3j001-a542up-fxj4w94o-1-fxj4x19a-21], DelegatingHandler@22593168[path=quartz-ra.rar context=file:/home/jpai/jboss-5.1.0.GA/server/default/deploy/ real=file:/home/jpai/jboss-5.1.0.GA/server/default/deploy/quartz-ra.rar], DelegatingHandler@21430906[path=quartz-ra.rar/quartz-ra.jar context=file:/home/jpai/jboss-5.1.0.GA/server/default/deploy/ real=file:/home/jpai/jboss-5.1.0.GA/server/default/deploy/quartz-ra.rar/quartz-ra.jar]] delegates=null exported=[META-INF, org.jboss.resource.adapter.quartz.inflow] <IMPORT-ALL>NON_EMPTY}}
at org.jboss.ejb3.tx.Ejb3TxPolicy.handleInCallerTx(Ejb3TxPolicy.java:115)
at org.jboss.aspects.tx.TxPolicy.invokeInCallerTx(TxPolicy.java:130)
at org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:194)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.ejb3.security.Ejb3AuthenticationInterceptorv2.invoke(Ejb3AuthenticationInterceptorv2.java:80)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.ejb3.BlockContainerShutdownInterceptor.invoke(BlockContainerShutdownInterceptor.java:67)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.aspects.currentinvocation.CurrentInvocationInterceptor.invoke(CurrentInvocationInterceptor.java:67)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.ejb3.mdb.MessagingContainer.localInvoke(MessagingContainer.java:282)
at org.jboss.ejb3.mdb.inflow.MessageInflowLocalProxy.delivery(MessageInflowLocalProxy.java:270)
at org.jboss.ejb3.mdb.inflow.MessageInflowLocalProxy.invoke(MessageInflowLocalProxy.java:140)
at $Proxy136.execute(Unknown Source)
at org.jboss.resource.adapter.quartz.inflow.QuartzJob.execute(QuartzJob.java:57)
at org.quartz.core.JobRunShell.run(JobRunShell.java:203)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:520)
Caused by: java.lang.RuntimeException: Unable to inject jndi dependency: env/org.jboss.tutorial.quartz.bean.AnnotatedQuartzMDBBean/calc into property org.jboss.tutorial.quartz.bean.AnnotatedQuartzMDBBean.calc: org.jboss.tutorial.quartz.bean.Calculator from BaseClassLoader@1f172aa{VFSClassLoaderPolicy@9f3364{name=vfszip:/home/jpai/jboss-5.1.0.GA/server/default/deploy/quartz-ra.rar/ domain=ClassLoaderDomain@1f5eb7f{name=DefaultDomain parentPolicy=BEFORE parent=org.jboss.bootstrap.NoAnnotationURLClassLoader@1431340} roots=[MemoryContextHandler@31918682[path= context=vfsmemory://3j001-a542up-fxj4w94o-1-fxj4x19a-21 real=vfsmemory://3j001-a542up-fxj4w94o-1-fxj4x19a-21], DelegatingHandler@22593168[path=quartz-ra.rar context=file:/home/jpai/jboss-5.1.0.GA/server/default/deploy/ real=file:/home/jpai/jboss-5.1.0.GA/server/default/deploy/quartz-ra.rar], DelegatingHandler@21430906[path=quartz-ra.rar/quartz-ra.jar context=file:/home/jpai/jboss-5.1.0.GA/server/default/deploy/ real=file:/home/jpai/jboss-5.1.0.GA/server/default/deploy/quartz-ra.rar/quartz-ra.jar]] delegates=null exported=[META-INF, org.jboss.resource.adapter.quartz.inflow] <IMPORT-ALL>NON_EMPTY}}
at org.jboss.injection.JndiPropertyInjector.lookup(JndiPropertyInjector.java:82)
at org.jboss.injection.JndiPropertyInjector.inject(JndiPropertyInjector.java:99)
at org.jboss.injection.JndiPropertyInjector.inject(JndiPropertyInjector.java:89)
at org.jboss.injection.JndiPropertyInjector.inject(JndiPropertyInjector.java:61)
at org.jboss.ejb3.injection.InjectionInvocation.invokeTarget(InjectionInvocation.java:89)
at org.jboss.ejb3.injection.InjectionInvocation.invokeNext(InjectionInvocation.java:83)
at org.jboss.aspects.currentinvocation.CurrentInvocationInterceptor.invoke(CurrentInvocationInterceptor.java:67)
at org.jboss.ejb3.injection.InjectionInvocation.invokeNext(InjectionInvocation.java:74)
at org.jboss.ejb3.EJBContainer.injectBeanContext(EJBContainer.java:1097)
at org.jboss.ejb3.pool.AbstractPool.create(AbstractPool.java:83)
at org.jboss.ejb3.pool.AbstractPool.create(AbstractPool.java:73)
at org.jboss.ejb3.pool.StrictMaxPool.get(StrictMaxPool.java:146)
at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:58)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
at org.jboss.aspects.tx.TxPolicy.invokeInCallerTx(TxPolicy.java:126)
... 17 more
Caused by: javax.naming.NamingException: Could not dereference object [Root exception is javax.naming.NamingException: Could not dereference object [Root exception is java.lang.RuntimeException: Can not find interface declared by Proxy in our CL + BaseClassLoader@1f172aa{vfszip:/home/jpai/jboss-5.1.0.GA/server/default/deploy/quartz-ra.rar/}]]
at org.jnp.interfaces.NamingContext.resolveLink(NamingContext.java:1352)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:817)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:686)
at org.jboss.ejb3.JndiUtil.lookup(JndiUtil.java:44)
at org.jboss.injection.JndiPropertyInjector.lookup(JndiPropertyInjector.java:75)
... 31 more
Caused by: javax.naming.NamingException: Could not dereference object [Root exception is java.lang.RuntimeException: Can not find interface declared by Proxy in our CL + BaseClassLoader@1f172aa{vfszip:/home/jpai/jboss-5.1.0.GA/server/default/deploy/quartz-ra.rar/}]
at org.jnp.interfaces.NamingContext.getObjectInstanceWrapFailure(NamingContext.java:1504)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:822)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:686)
at javax.naming.InitialContext.lookup(InitialContext.java:351)
at org.jnp.interfaces.NamingContext.resolveLink(NamingContext.java:1346)
... 35 more
Caused by: java.lang.RuntimeException: Can not find interface declared by Proxy in our CL + BaseClassLoader@1f172aa{vfszip:/home/jpai/jboss-5.1.0.GA/server/default/deploy/quartz-ra.rar/}
at org.jboss.ejb3.proxy.impl.objectfactory.ProxyObjectFactory.redefineProxyInTcl(ProxyObjectFactory.java:343)
at org.jboss.ejb3.proxy.impl.objectfactory.session.SessionProxyObjectFactory.createProxy(SessionProxyObjectFactory.java:134)
at org.jboss.ejb3.proxy.impl.objectfactory.session.stateless.StatelessSessionProxyObjectFactory.getProxy(StatelessSessionProxyObjectFactory.java:79)
at org.jboss.ejb3.proxy.impl.objectfactory.ProxyObjectFactory.getObjectInstance(ProxyObjectFactory.java:158)
at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:304)
at org.jnp.interfaces.NamingContext.getObjectInstance(NamingContext.java:1479)
at org.jnp.interfaces.NamingContext.getObjectInstanceWrapFailure(NamingContext.java:1496)
... 39 more
Looking at the code, i think the right way to fix this is to change the org.jboss.ejb3.mdb.inflow.MessageInflowLocalProxy.delivery to deliver the message as part of the container's classloader:
Index: src/main/java/org/jboss/ejb3/mdb/inflow/MessageInflowLocalProxy.java
===================================================================
--- src/main/java/org/jboss/ejb3/mdb/inflow/MessageInflowLocalProxy.java (revision 91150)
+++ src/main/java/org/jboss/ejb3/mdb/inflow/MessageInflowLocalProxy.java (working copy)
- return delivery(proxy, container, method, args);
+ {
+ ClassLoader earlierClassLoader = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ // Deliver the message through the container's classloader
+ Thread.currentThread().setContextClassLoader(container.getClassloader());
+ return delivery(proxy, container, method, args);
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(earlierClassLoader);
+ }
+
+ }
}
Thoughts? Anything obviously wrong with this approach?