1 Reply Latest reply on Aug 19, 2011 10:33 AM by James Williams

    Completing a Task Fails after Calling statefulSession.dispose

    James Williams Newbie

      I have a problem which I think is similar to these but my scenario is slightly different.

             

      How to make a process instance go on with jBPM5?   

      http://community.jboss.org/message/600654

       

       

      Human task API, how to move forward in the workflow ?

      http://community.jboss.org/message/614986#614986

       

       

      Here is my problem. I create a process using the following:

       

       

          Environment env = KnowledgeBaseFactory.newEnvironment();

          env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, entityManagerFactory);

          InitialContext ctx = new InitialContext();

          UserTransaction transactionManager = (UserTransaction) ctx.lookup("java:comp/UserTransaction");

          env.set(EnvironmentName.TRANSACTION_MANAGER, transactionManager);

                 

          StatefulKnowledgeSession knowledgeSession = JPAKnowledgeService.newStatefulKnowledgeSession(knowledgeBase, null, env);

          WorkItemHandler handler = new CommandBasedWSHumanTaskHandler(knowledgeSession);

          knowledgeSession.getWorkItemManager().registerWorkItemHandler("Human Task", handler);

       

       

          statefulSession.startProcess(processDefinitionId, processVariables);

         

          statefulSession.dispose(); (Remember this line)

         

      This works fine. The process is created and the first Human Task exists as expected.

       

      My next step is to use the Mina Task Client, via the task client to assign and complete the task. The assign works perfectly however I get the following exception when completing the task.

       

       

          SEVERE: Could not commit session

          java.lang.NullPointerException

              at org.drools.persistence.jpa.JpaPersistenceContextManager.beginCommandScopedEntityManager(JpaPersistenceContextManager.java:67)

              at org.drools.persistence.SingleSessionCommandService.execute(SingleSessionCommandService.java:287)

              at org.drools.command.impl.CommandBasedStatefulKnowledgeSession$1.completeWorkItem(CommandBasedStatefulKnowledgeSession.java:149)

              at org.jbpm.process.workitem.wsht.CommandBasedWSHumanTaskHandler$GetResultContentResponseHandler.execute(CommandBasedWSHumanTaskHandler.java:295)

              at org.jbpm.task.service.TaskClientHandler.messageReceived(TaskClientHandler.java:153)

              at org.jbpm.task.service.mina.MinaTaskClientHandler.messageReceived(MinaTaskClientHandler.java:47)

              at org.apache.mina.core.filterchain.DefaultIoFilterChain$TailFilter.messageReceived(DefaultIoFilterChain.java:713)

       

       

      This results in the task being completed but the next task is not created.

       

       

      This exception is thrown because the org.drools.persistence.jpa.JpaPersistenceContextManager.appScopedEntityManager is null. This field is initialised when the JpaPersistenceContextManager is created and is nulled when the dispose method is called.

       

       

      I was able to have the task complete properly by commenting out the call to dispose.

       

           // statefulSession.dispose();

         

      Not a good solution. I can't keep sessions open forever.

       

       

      The crux of the problem is that when completing the task it attempts to use the JpaPersistenceContextManager that was used to create the process, which has already been disposed of.

       

       

      My question is, how do I re-intitialise the JpaPersistenceContextManager to use ensure it has an active entityManager? Remember I'm making a call via the Mina Task Client so have no access to the JpaPersistenceContextManager.

       

      Any help would be really appreciated.

        • 1. Re: Completing a Task Fails after Calling statefulSession.dispose
          James Williams Newbie

          OK. I've fixed it. I had to write my own version of the PersistenceContextManager.

           

          import javax.persistence.EntityManager;

          import javax.persistence.EntityManagerFactory;

           

           

          import org.drools.persistence.PersistenceContext;

          import org.drools.persistence.jpa.JpaPersistenceContext;

          import org.drools.runtime.Environment;

          import org.drools.runtime.EnvironmentName;

          import org.jbpm.persistence.JpaProcessPersistenceContext;

          import org.jbpm.persistence.ProcessPersistenceContext;

          import org.jbpm.persistence.ProcessPersistenceContextManager;

           

           

          public class MultipleUseJpaPersistenceContextManager implements ProcessPersistenceContextManager {

           

           

              Environment env;

              private EntityManagerFactory emf;

           

           

              private EntityManager appScopedEntityManager;

              protected EntityManager cmdScopedEntityManager;

           

           

              private boolean internalAppScopedEntityManager;

              private boolean internalCmdScopedEntityManager;

           

           

              public MultipleUseJpaPersistenceContextManager(Environment env) {

                  this.env = env;

                  this.emf = (EntityManagerFactory) env.get(EnvironmentName.ENTITY_MANAGER_FACTORY);

              }

           

           

              public PersistenceContext getApplicationScopedPersistenceContext() {

                  checkAppScopedEntityManager();

                  return new JpaPersistenceContext(appScopedEntityManager);

              }

           

           

              private void checkAppScopedEntityManager() {

                  if (this.appScopedEntityManager == null) {

                      // Use the App scoped EntityManager if the user has provided it, and it is open.

                      this.appScopedEntityManager = (EntityManager) this.env.get(EnvironmentName.APP_SCOPED_ENTITY_MANAGER);

                      if (this.appScopedEntityManager != null && !this.appScopedEntityManager.isOpen()) {

                          throw new RuntimeException("Provided APP_SCOPED_ENTITY_MANAGER is not open");

                      }

           

           

                      if (this.appScopedEntityManager == null) {

                          internalAppScopedEntityManager = true;

                          this.appScopedEntityManager = this.emf.createEntityManager();

           

           

                          this.env.set(EnvironmentName.APP_SCOPED_ENTITY_MANAGER, this.appScopedEntityManager);

                      } else {

                          internalAppScopedEntityManager = false;

                      }

                  }

              }

           

           

              public PersistenceContext getCommandScopedPersistenceContext() {

                  return new JpaPersistenceContext(this.cmdScopedEntityManager);

              }

           

           

              public void beginCommandScopedEntityManager() {

                  checkAppScopedEntityManager();

                  EntityManager cmdScopedEntityManager = (EntityManager) env.get(EnvironmentName.CMD_SCOPED_ENTITY_MANAGER);

                  if (cmdScopedEntityManager == null

                          || (this.cmdScopedEntityManager != null && !this.cmdScopedEntityManager.isOpen())) {

                      internalCmdScopedEntityManager = true;

                      this.cmdScopedEntityManager = this.emf.createEntityManager(); // no need to call joinTransaction as it will

                                                                                    // do so if one already exists

                      this.env.set(EnvironmentName.CMD_SCOPED_ENTITY_MANAGER, this.cmdScopedEntityManager);

                      cmdScopedEntityManager = this.cmdScopedEntityManager;

                  } else {

                      internalCmdScopedEntityManager = false;

                  }

                  cmdScopedEntityManager.joinTransaction();

                  appScopedEntityManager.joinTransaction();

              }

           

           

              public void endCommandScopedEntityManager() {

                  if (this.internalCmdScopedEntityManager) {

                      this.env.set(EnvironmentName.CMD_SCOPED_ENTITY_MANAGER, null);

                  }

              }

           

           

              public void dispose() {

                  if (this.internalAppScopedEntityManager) {

                      if (this.appScopedEntityManager != null && this.appScopedEntityManager.isOpen()) {

                          this.appScopedEntityManager.close();

                      }

                      this.internalAppScopedEntityManager = false;

                      this.env.set(EnvironmentName.APP_SCOPED_ENTITY_MANAGER, null);

                      this.appScopedEntityManager = null;

                  }

           

           

                  if (this.internalCmdScopedEntityManager) {

                      if (this.cmdScopedEntityManager != null && this.cmdScopedEntityManager.isOpen()) {

                          this.cmdScopedEntityManager.close();

                      }

                      this.internalCmdScopedEntityManager = false;

                      this.env.set(EnvironmentName.CMD_SCOPED_ENTITY_MANAGER, null);

                      this.cmdScopedEntityManager = null;

                  }

              }

               

              @Override

              public ProcessPersistenceContext getProcessPersistenceContext() {

                  if (cmdScopedEntityManager == null) {

                      this.emf.createEntityManager();;

                  }

                  return new JpaProcessPersistenceContext(cmdScopedEntityManager);

              }

           

           

          }

           

          This version checks that it has a valid appScopedEntityManager and creates one if necessary.

           

          I then make sure this one is used instead of the default one provided by JBPM when creating my knowledgeSession:

           

                      Environment env = KnowledgeBaseFactory.newEnvironment();

                      env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, entityManagerFactory);

                      InitialContext ctx = new InitialContext();

                      UserTransaction transactionManager = (UserTransaction) ctx.lookup("java:comp/UserTransaction");

                     env.set(EnvironmentName.TRANSACTION_MANAGER,

                              new JtaTransactionManager(transactionManager, null, transactionManager));

                      env.set(EnvironmentName.PERSISTENCE_CONTEXT_MANAGER,

                              new MultipleUseJpaPersistenceContextManager(env));

                     

                      StatefulKnowledgeSession knowledgeSession = JPAKnowledgeService.newStatefulKnowledgeSession(knowledgeBase, null, env);

           

          I hope this helps others.