13 Replies Latest reply: Jul 18, 2012 3:43 AM by Thomas Diesler RSS

    TCCL used by EJBNamingContext is wrong when callstack passes through multiple OSGi modules

    Steffen Wollscheid Newbie

      Hi 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.