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?