TCCL used by EJBNamingContext is wrong when callstack passes through multiple OSGi modules
steffenwollscheid Mar 16, 2012 6:29 AMHi all,
we face the following problem in JBoss 7.1.0-Final (including the fix for AS7-3830):
We would like to be able to trigger a chain of events, say by JMX-Bean in on OSGi bundle [A].
[A] then calls up an OSGi Service/Class located in bundle [B] using an interface exported by [B].
Now [B] tries to make a remote EJB lookup into an ear [C] on an interface it imported from another OSGi Bundle [D].
This fails with the following stacktrace:
[Server:server-one] 15:46:32,245 ERROR [stderr] (RMI TCP Connection(4)-10.0.103.110) javax.naming.NamingException: Could not load ejb proxy class steffen.experimental.remote.ejb.RemoteCalculator [Root exception is java.lang.ClassNotFoundException: steffen.experimental.remote.ejb.RemoteCalculator from [Module "deployment.steffen.experimental.ejb-remote.twice-removed:0.0.1.SNAPSHOT" from Service Module Loader]]
[Server:server-one] 15:46:32,245 ERROR [stderr] (RMI TCP Connection(4)-10.0.103.110) at org.jboss.ejb.client.naming.ejb.EjbNamingContext.createEjbProxy(EjbNamingContext.java:108)
[Server:server-one] 15:46:32,246 ERROR [stderr] (RMI TCP Connection(4)-10.0.103.110) at org.jboss.ejb.client.naming.ejb.EjbNamingContext.lookup(EjbNamingContext.java:96)
[Server:server-one] 15:46:32,246 ERROR [stderr] (RMI TCP Connection(4)-10.0.103.110) at org.jboss.ejb.client.naming.ejb.EjbNamingContext.lookup(EjbNamingContext.java:76)
[Server:server-one] 15:46:32,246 ERROR [stderr] (RMI TCP Connection(4)-10.0.103.110) at org.jboss.as.naming.InitialContext.lookup(InitialContext.java:100)
[Server:server-one] 15:46:32,246 ERROR [stderr] (RMI TCP Connection(4)-10.0.103.110) at org.jboss.as.naming.NamingContext.lookup(NamingContext.java:213)
[Server:server-one] 15:46:32,246 ERROR [stderr] (RMI TCP Connection(4)-10.0.103.110) at org.apache.aries.jndi.DelegateContext.lookup(DelegateContext.java:161)
[Server:server-one] 15:46:32,246 ERROR [stderr] (RMI TCP Connection(4)-10.0.103.110) at steffen.experimental.client.jmx.service.LookupImpl.internal_InitialContextService(LookupImpl.java:63)
[Server:server-one] 15:46:32,247 ERROR [stderr] (RMI TCP Connection(4)-10.0.103.110) at steffen.experimental.client.jmx.service.TriggerLookup.doAddition_InitialContextService(TriggerLookup.java:85)
[Server:server-one] 15:46:32,247 ERROR [stderr] (RMI TCP Connection(4)-10.0.103.110) at steffen.experimental.indirect.jmx.ServiceCallerWrapper.doAddition_InitialContextService(ServiceCallerWrapper.java:30)
[Server:server-one] 15:46:32,247 ERROR [stderr] (RMI TCP Connection(4)-10.0.103.110) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Where “twice-removed” is [A] and the class TriggerLookup does reside in [B].
(We have aries.jndi running in our jboss, but the behavior described here occurs also without aries.jndi – in fact we had hoped that aries.jndi would solve our problems)
It is important to note, that the same code in [B] works alright, when the initiating JMX Bean resides in [B] instead of [A], because in this case the TCCL is bundle classloader of bundle [B], whereas in the other case it is the bundle classloader of [A] which of course has not knowledge of the interface class.
Furthermore it is important to note that this behavior occurs even though the flow of control from [A] to [B] is done using:
private TriggerLookupMBean getService()
{
ServiceReference sRef = TwiceRemovedActivator.getBundleContext().getServiceReference(TriggerLookupMBean.class.getName());
if( sRef != null ){
return (TriggerLookupMBean) TwiceRemovedActivator.getBundleContext().getService(sRef);
} else {
throw new IllegalStateException("Service TriggerLookupMBean was not found!");
}
}
public String doAddition_InitialContextService()
{
return getService().doAddition_InitialContextService();
}
So that the OSGi framework would have a chance to change the TCCL using an interceptor hooked into the service which is returned by getService.
But from what I see simply an instance of the implementation class from bundle [B] is returned.
Am I doing something wrong here?
Having aries.jndi installed, I can do a successful JNDI lookup for an OSGi Service regardless of the Bundle initiating the flow of control, while the same lookup, when done with a “ejb:” prefix fails.
This works:
AnOSGiService otherSvc = null;
ServiceReference sRef = Activator.getBundleContext()
.getServiceReference(JNDIContextManager.class.getName());
if (sRef != null)
{
JNDIContextManager contextMgr = (JNDIContextManager) Activator.getBundleContext().getService(sRef);
try
{
Properties props = new Properties();
props.put("osgi.service.jndi.bundleContext", Activator.getBundleContext());
Context ctx = contextMgr.newInitialContext(props);
System.out.println("doing JNDI lookup");
otherSvc = (AnOSGiService) ctx.lookup("osgi:service/" + AnOSGiService.class.getName());
System.out.println("lookup succeeded, calling service");
return "result:" + otherSvc.foo();
}
//...
This fails:
RemoteCalculator calc = null;
ServiceReference sRef = Activator.getBundleContext()
.getServiceReference(JNDIContextManager.class.getName());
if (sRef != null)
{
JNDIContextManager contextMgr = (JNDIContextManager) Activator.getBundleContext().getService(sRef);
try
{
Properties props = new Properties();
props.put("osgi.service.jndi.bundleContext", Activator.getBundleContext());
props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
Context ctx = contextMgr.newInitialContext(props);
System.out.println("doing lookup");
calc = (RemoteCalculator)ctx.lookup("ejb:application-ear-0.0.1-SNAPSHOT/ejb-definition-0.0.1-SNAPSHOT//CalculatorBean!steffen.experimental.remote.ejb.RemoteCalculator");
System.out.println("lookup succeeded, calling remote bean");
return "result:" + calc.add(1, 1);
}
//...
As I mentioned before when called from a JMX-Bean in the same bundle both work!
What am I missing?
Our current workaround is an aspect that changes the TCCL in exported public methods if required – but I believe this should not be necessary.