1 Reply Latest reply on Aug 31, 2010 12:25 PM by Bernd Ruecker

    Classloading of JNDI objects in ExternalActivityBehaviour impl

    Justin Couch Newbie

      Classloading seems to be the eternal bane of JBPM and I've run in to another fun problem

       

      Platform: JBPM 4.4, JBoss 5.1.0GA with JBPM as a process, JDK 1.6 u21, Win7 64bit

       

      I have a custom ExternalActivityBehaviour where we reference several remote beans through JNDI. We have JBoss set up for strict classloading so that each EAR file has it's own class loader. My code for the deployment registers all the necessary classes with the JBPM deployment instance as needed:

       

              ByteArrayInputStream bis = new ByteArrayInputStream(jbpm_config.getBytes());
      
              try
              {
                  ProcessEngine jbpm_engine = getProcessEngine();
                  ExecutionService exec_service = jbpm_engine.getExecutionService();
                  RepositoryService repo_service = jbpm_engine.getRepositoryService();
                  NewDeployment deployment = repo_service.createDeployment();
      
                  String workflow_name = workflow.getWorkflowName();
      
                  String jpdl_name = workflow_name + ".jpdl.xml";
                  deployment.addResourceFromInputStream(jpdl_name, bis);
      
                  InputStream[] is = new InputStream[RUNTIME_CLASSES.length];
      
                  ClassLoader cl = Thread.currentThread().getContextClassLoader();
      
                  for(int i = 0; i < RUNTIME_CLASSES.length; i++)
                  {
                      String cls = convertClassName(RUNTIME_CLASSES[i]);
                      InputStream class_input = cl.getResourceAsStream(cls);
      
                      if(class_input == null)
                      {
                          log(...)
      
                      }
                      else
                      {
                          is[i] = class_input;
      
                          deployment.addResourceFromInputStream(cls, class_input);
                      }
                  }
      
                  deployment.setName(workflow_name);
                  String deployment_id = deployment.deploy();
      
                  ProcessInstance process_instance =
                      exec_service.startProcessInstanceByKey(workflow_name, vars);
      
                  close(is);
              }
              catch(JbpmException jbe)
              {
              }
      
      
      

       

      where getProcessEngine() uses a JNDI lookup.

       

      That's fine - everything registers and puts the right sets of classes in to the DB - confirmed by manual inspection of the JBPM4_LOB table.

       

      My custom behaviour looks like this:

       

       

      public class JMSActivityBehaviour implements ExternalActivityBehaviour
      {
          private JobProcessorManagerRemote jobProcessor;
      
          public JMSActivityBehaviour()
          {
      
              ....
          }
      
          @Override
          public void signal(ActivityExecution exec,
                             String signal,
                             Map<String, ?> extVars)
              throws Exception
          {
              exec.setVariables(extVars);
              exec.take(signal);
          }
      
          /**
           * @inheritDoc
           */
          @Override
          public void execute(ActivityExecution exec)
          {
              // Locate the message queue first, to make sure we can send the
              // message.
              String jms_queue = null;
              QueueSession session = null;
              Queue message_queue = null;
              Context ctx = null;
              try
              {
                  ctx = new InitialContext();
      
                  QueueConnectionFactory fac =
                      (QueueConnectionFactory)ctx.lookup("ConnectionFactory");
      
                  QueueConnection conn = fac.createQueueConnection();
                  session = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
                  conn.start();
      
                  if(jobProcessor == null)
                  {
                      Object obj = ctx.lookup("ejb/mycompany/JobProcessorManager");
      
                      if(obj != null)
                          jobProcessor = (JobProcessorManagerRemote)PortableRemoteObject.narrow(obj,JobProcessorManagerRemote.class );
                  }
      
      ....
              }
              catch (NamingException ne)
      ....
      
      

       

      in the line where I look up the custom EJB (EJB3 SLSB), I get a ClassNotFoundException on the remote interface class. Note that the variable in the top of the class doesn't throw the CNFE on <init>, it is only during the JNDI lookup, from somewhere in the guts of JNDI. I've confirmed w test that that specific EJB end point can be located in JNDI in other EJBs, MBeans, and even external Junit tests, so I have narrowed it down to the way that JBPM does it's classloading specifically. Here's the important part of the stack trace:

       

      java.lang.RuntimeException: Can not find interface declared by Proxy in our CL + org.jboss.util.loading.DelegatingClassLoader@4f8ee7ad
          at org.jboss.ejb3.proxy.impl.objectfactory.ProxyObjectFactory.redefineProxyInTcl(ProxyObjectFactory.java:410)
          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:161)
          at javax.naming.spi.NamingManager.getObjectInstance(Unknown Source)
          at org.jnp.interfaces.NamingContext.getObjectInstance(NamingContext.java:1479)
          at org.jnp.interfaces.NamingContext.getObjectInstanceWrapFailure(NamingContext.java:1496)
          at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:822)
          at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:686)
          at javax.naming.InitialContext.lookup(Unknown Source)
          at com.mycompany.server.workflow.JMSActivityBehaviour.execute(JMSActivityBehaviour.java:124)
          at org.jbpm.pvm.internal.wire.usercode.UserCodeActivityBehaviour.execute(UserCodeActivityBehaviour.java:42)
          at org.jbpm.pvm.internal.model.op.ExecuteActivity.perform(ExecuteActivity.java:60)
      ....
      Caused by: java.lang.ClassNotFoundException: com.mycompany.server.ejb3.scheduler.JobProcessorManagerRemote from BaseClassLoader@bec2452{VFSClassLoaderPolicy@3c44899b{name=vfsfile:/D:/apps/jboss-5.1.0.GA/server/iICE/deploy/jbpm/jbpm-enterprise.jar/ domain=ClassLoaderDomain@7d858aa0{name=DefaultDomain parentPolicy=BEFORE parent=org.jboss.bootstrap.NoAnnotationURLClassLoader@2f754ad2} roots=[MemoryContextHandler@933641734[path= context=vfsmemory://3ia22z-wqetcv-gdhxpu8b-1-gdhxq6z0-2a real=vfsmemory://3ia22z-wqetcv-gdhxpu8b-1-gdhxq6z0-2a], FileHandler@53614709[path=jbpm/jbpm-enterprise.jar context=file:/D:/apps/jboss-5.1.0.GA/server/iICE/deploy/ real=file:/D:/apps/jboss-5.1.0.GA/server/iICE/deploy/jbpm/jbpm-enterprise.jar/]]  delegates=null exported=[META-INF] <IMPORT-ALL>NON_EMPTY}}
          at org.jboss.classloader.spi.base.BaseClassLoader.loadClass(BaseClassLoader.java:448)
          at java.lang.ClassLoader.loadClass(Unknown Source)
          at org.jboss.classloading.spi.DelegatingClassLoader.loadClass(DelegatingClassLoader.java:87)
          at java.lang.ClassLoader.loadClass(Unknown Source)
          at org.jboss.util.loading.DelegatingClassLoader.loadClass(DelegatingClassLoader.java:97)
          at java.lang.ClassLoader.loadClass(Unknown Source)
          at java.lang.Class.forName0(Native Method)
          at java.lang.Class.forName(Unknown Source)
          at org.jboss.ejb3.proxy.impl.objectfactory.ProxyObjectFactory.redefineProxyInTcl(ProxyObjectFactory.java:406)
          ... 175 more
      
      

       

      In reading through past documentation and forum threads, I've noticed that there used to be an option in JBPM to turn off it's internal classloader and delegate to JBoss's  classloader. With JBPM 4.3 and 4.4 that option no longer seems to exist. Anyone have ideas on places to look and other options to try. I've tried everything in the wiki, previous forum posts and such, just completely out of ideas.