3 Replies Latest reply on Sep 11, 2013 8:35 AM by herwix

    JBPM6 Spring Persistence and Runtime Manager

    herwix

      Hey guys,

       

      I have been trying to integrate JBPM6 in my Spring-Hibernate based Grails application, but it seems that I'm stuck and maybe you guys here could point me in the right direction.

       

      I am trying to get the RuntimeManager working with a local spring transaction scheme. I have tried a lot of stuff but can't seem to find the right approach.

       

      I have set up a EntityManagerFactory bean, which loads the persistence-unit org.jbpm.presistence.jpa (with transaction-type "Resource_local") with the org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter.

      I have also set up a org.springframework.orm.jpa.JpaTransactionManager bean.

       

      When I now try to get a runtimeEngine via the RuntimeEnvironmentBuilder:

       

       

      RuntimeEnvironment environment = RuntimeEnvironmentBuilder.getDefaultInMemory()
                      .userGroupCallback(getUserGroupCallback())
                      .entityManagerFactory(entityManagerFactory)
                      .registerableItemsFactory(new KModuleRegisterableItemsFactory(kcontainer,''))
                      .addEnvironmentEntry(EnvironmentName.TRANSACTION_MANAGER,jbpmTransactionManager)
                      .get();
        
      RuntimeManager manager = RuntimeManagerFactory.Factory.get().newPerProcessInstanceRuntimeManager(environment);
      
      RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
      
      

       

      I get the following error:

       

      NamingException occurred when processing request: [GET] /bpm/test/
      Cannot create resource instance. Stacktrace follows:
      javax.naming.NamingException: Cannot create resource instance
                at org.apache.naming.factory.TransactionFactory.getObjectInstance(TransactionFactory.java:116)
                at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:321)
                at org.apache.naming.NamingContext.lookup(NamingContext.java:843)
                at org.apache.naming.NamingContext.lookup(NamingContext.java:154)
                at org.apache.naming.NamingContext.lookup(NamingContext.java:831)
                at org.apache.naming.NamingContext.lookup(NamingContext.java:168)
                at org.apache.naming.SelectorContext.lookup(SelectorContext.java:158)
                at javax.naming.InitialContext.lookup(InitialContext.java:411)
                at org.drools.persistence.jta.JtaTransactionManager.findUserTransaction(JtaTransactionManager.java:122)
                at org.drools.persistence.jta.JtaTransactionManager.<init>(JtaTransactionManager.java:69)
                at org.jbpm.shared.services.impl.JbpmJTATransactionManager.<init>(JbpmJTATransactionManager.java:39)
                at org.jbpm.runtime.manager.impl.factory.LocalTaskServiceFactory.newTaskService(LocalTaskServiceFactory.java:48)
                at org.jbpm.runtime.manager.impl.PerProcessInstanceRuntimeManager.getRuntimeEngine(PerProcessInstanceRuntimeManager.java:94)
                at org.jbpm.runtime.manager.impl.PerProcessInstanceRuntimeManager.init(PerProcessInstanceRuntimeManager.java:258)
                at org.jbpm.runtime.manager.impl.RuntimeManagerFactoryImpl.newPerProcessInstanceRuntimeManager(RuntimeManagerFactoryImpl.java:103)
                at org.jbpm.runtime.manager.impl.RuntimeManagerFactoryImpl.newPerProcessInstanceRuntimeManager(RuntimeManagerFactoryImpl.java:94)
                at bpm.TestController$$EOFBIT6u.index(TestController.groovy:82)
                at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:200)
                at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
                at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
                at java.lang.Thread.run(Thread.java:722)
      

       

      Looking at the stacktrace I found the the LocalTaskServiceFactory where the documentation says:

       

      /**

      * Regular <code>TaskServiceFactory</code> implementation that shall be used for non CDI environments.

      * Creates new <code>TaskService</code> instance for every call to the factory.

      * <code>TaskService</code> instance will be equipped with <code>JbpmJTATransactionManager</code>

      * for transaction management, this is mandatory as it must participate in already active

      * transaction if such exists.

      */

       

      So it seems it's not possible to use the RuntimeManager without a JTA Transaction Manager? I thought it might be possible to supply the localTransactionManager via the Environment? But I am fishing in the dark.

       

      If I am totally off the mark here in what I am doing, I would appreciate any pointer into the recommended way to integrate jbpm6 (and especially the runtime manager) into a spring-hibernate based application. I have looked at kie-spring, but there is little jbpm specific stuff.

       

      Cheers, Alex

        • 1. Re: JBPM6 Spring Persistence and Runtime Manager
          herwix

          #EDIT#

           

          Alright, I basically get it now, local transactions are obviously not possible. However, the SpringTransactionManager should be able to be integrated, so that the RuntimeManager can work with spring persistence. The following code:

           

          @Override
              public TaskService newTaskService() {
                  EntityManagerFactory emf = ((SimpleRuntimeEnvironment)runtimeEnvironment).getEmf();
                  if (emf != null) {
                     
                      TaskService internalTaskService =   HumanTaskServiceFactory.newTaskServiceConfigurator()
                      .transactionManager(new JbpmJTATransactionManager())
                      .entityManagerFactory(emf)
                      .userGroupCallback(runtimeEnvironment.getUserGroupCallback())
                      .getTaskService();
                                           
                      return internalTaskService;
                  } else {
                      return null;
                  }
              }
          
          

           

          hard codes the JbpmJTATransactionManager. In the normal Environment initialization in the SingleSessionCommandService this is handled via the initTransactionManager functions, which already implements a SpringTransactionManager. I know that this exact code is probably not applicable in this case but it should be a great starting point for a jbpm provided solution.

           

          public void initTransactionManager(Environment env) {
                  Object tm = env.get( EnvironmentName.TRANSACTION_MANAGER );
                  if ( env.get( EnvironmentName.PERSISTENCE_CONTEXT_MANAGER ) != null &&
                       env.get( EnvironmentName.TRANSACTION_MANAGER ) != null ) {
                      this.txm = (TransactionManager) tm;
                      this.jpm = (PersistenceContextManager) env.get( EnvironmentName.PERSISTENCE_CONTEXT_MANAGER );
                  } else {
                      if ( tm != null && isSpringTransactionManager(tm.getClass()) ) {
                          try {
                              Class< ? > cls = Class.forName( "org.kie.spring.persistence.KieSpringTransactionManager" );
                              Constructor< ? > con = cls.getConstructors()[0];
                              this.txm = (TransactionManager) con.newInstance( tm );
                              logger.debug( "Instantiating  KieSpringTransactionManager" );
                              cls = Class.forName( "org.kie.spring.persistence.KieSpringJpaManager" );
                              con = cls.getConstructors()[0];
                              this.jpm = (PersistenceContextManager) con.newInstance( new Object[]{this.env} );
                          } catch ( Exception e ) {
                              //fall back for drools5-legacy spring module
                              logger.warn( "Could not instantiate KieSpringTransactionManager. Trying with DroolsSpringTransactionManager." );
                              try {
                                  Class< ? > cls = Class.forName( "org.drools.container.spring.beans.persistence.DroolsSpringTransactionManager" );
                                  Constructor< ? > con = cls.getConstructors()[0];
                                  this.txm = (TransactionManager) con.newInstance( tm );
                                  logger.debug( "Instantiating  DroolsSpringTransactionManager" );
          
          
                                  // configure spring for JPA and local transactions
                                  cls = Class.forName( "org.drools.container.spring.beans.persistence.DroolsSpringJpaManager" );
                                  con = cls.getConstructors()[0];
                                  this.jpm = (PersistenceContextManager) con.newInstance( new Object[]{this.env} );
                              } catch ( Exception ex ) {
                                  logger.warn( "Could not instantiate DroolsSpringTransactionManager" );
                                  throw new RuntimeException( "Could not instatiate org.kie.container.spring.beans.persistence.DroolsSpringTransactionManager", ex );
                              }
                          }
                      } else {
                          logger.debug( "Instantiating  JtaTransactionManager" );
                          this.txm = new JtaTransactionManager( env.get( EnvironmentName.TRANSACTION ),
                                                                env.get( EnvironmentName.TRANSACTION_SYNCHRONIZATION_REGISTRY ),
                                                                tm );
                          try {
                              Class< ? > jpaPersistenceCtxMngrClass = Class.forName( "org.jbpm.persistence.JpaProcessPersistenceContextManager" );
                              Constructor< ? > jpaPersistenceCtxMngrCtor = jpaPersistenceCtxMngrClass.getConstructors()[0];
                              this.jpm = (PersistenceContextManager) jpaPersistenceCtxMngrCtor.newInstance( new Object[]{this.env} );
                          } catch ( ClassNotFoundException e ) {
                              this.jpm = new JpaPersistenceContextManager( this.env );
                          } catch ( Exception e ) {
                              throw new RuntimeException( "Error creating JpaProcessPersistenceContextManager",
                                                          e );
                          }
                      }
                      env.set( EnvironmentName.PERSISTENCE_CONTEXT_MANAGER,
                               this.jpm );
                      env.set( EnvironmentName.TRANSACTION_MANAGER,
                               this.txm );
                  }
                  Boolean lock = (Boolean) env.get(EnvironmentName.USE_PESSIMISTIC_LOCKING);
                  if( lock != null && lock ) { 
                      this.pessimisticLocking = true;
                  }
              }
          

           

          In the meantime I will try to implement a custom TaskServiceFactory that works with my spring configuration. If there is anyone with helpful pointers that would be gladly appreciated

           

          Cheers, Alex

          • 2. Re: JBPM6 Spring Persistence and Runtime Manager
            swiderski.maciej

            Alex, most likely having spring specific (local transaction based) TaskServiceFactory implementation will be the best option. Then RuntimeManager should be able to take advantage of it via RuntimeEnvironment instance instead of using always JTA based one. I guess then both transaction will be controlled by spring and spring will ensure that both runtime engine and task service operations will be rolled back, right?

             

            Would be great if you could open a jira for it and since you already work on the TaskServiceFactory implementation that might be good contribution, wdyt?

             

            Cheers

            • 3. Re: Re: JBPM6 Spring Persistence and Runtime Manager
              herwix

              Hey Maciej,

               

              to your first question: Yes, as I understand it from the spring documentation that's how it works. It's basically a common abstraction on tx-mgmt apis, that is handled by spring. The documentation states:

              Spring resolves the disadvantages of global and local transactions. It enables application developers to use a consistent programming model in any environment. You write your code once, and it can benefit from different transaction management strategies in different environments. The Spring Framework provides both declarative and programmatic transaction management. Most users prefer declarative transaction management, which is recommended in most cases.

              With programmatic transaction management, developers work with the Spring Framework transaction abstraction, which can run over any underlying transaction infrastructure. With the preferred declarative model, developers typically write little or no code related to transaction management, and hence do not depend on the Spring Framework transaction API, or any other transaction API.

              It looked like there was already a lot of stuff in place (e.g. KieSpringTransactionManager, HumanTaskSpringTransactionManager) that could be used to fully integrate the runtime manager with spring. However, I just don't have enough knowledge about the project to figure this out in a timely manner.

               

              So, I actually decided to switch to a standalone JTA implementation and registered the txmg via SimpleNamingContextBuilder (I know a bit hacky , so I might switch to SpringInitialContextFactory) in jndi so that I can stay completely within spring/my app for the setup of my dev environment and it shouldn't be much work to switch to server managed jta for deployment. It looks like this will work for me at the moment, even though native support for spring transactions would be awesome and give spring users more options to choose from (e.g. completely local transactions). I will open a jira for it and if I need to do some more work on this to support my usecase or find some time, I will gladly contribute what I come up with.

               

              Cheers, Alex

               

              EDIT: Opened the Jira: [#JBPM-4125] Support for Human Task Spring Transactions in RuntimeManager - JBoss Issue Tracker