3 Replies Latest reply on Jul 21, 2007 9:09 AM by luukey

    Timers and JBPM 3.2.1

    luukey

      Hello.

      I'm trying to ceate a simple console application with jbpm and timers. What I did is quite simple:

      public static void main(String[] args)
       {
       try
       {
       JbpmConfiguration conf = JbpmConfiguration.getInstance("src/main/config/jbpm.cfg.xml");
       JbpmContext ctx = conf.createJbpmContext();
       ZipInputStream zis = new ZipInputStream(new FileInputStream("/test1.par"));
       ProcessDefinition pDef = ProcessDefinition.parseParZipInputStream(zis);
       ctx.deployProcessDefinition(pDef);
       zis.close();
       conf.startJobExecutor();
      
       ProcessInstance pInst = ctx.newProcessInstance("test1");
       pInst.signal();
       }
       catch (Exception ex)
       {
       ex.printStackTrace();
       }
      and the process definition goes as follows:
      <process-definition
       xmlns="urn:jbpm.org:jpdl-3.2" name="test1">
       <start-state name="start-state1">
       <transition to="state1" name="to_state1"></transition>
       </start-state>
       <state name="state1">
       <timer duedate="2 seconds" name="timy_timer" repeat="yes">
       <action class="TimerActionHandler"></action>
       </timer>
       <transition to="state1" name="to_state1"></transition>
       </state>
      </process-definition>
      . The problem is, I'm unable to execute the handler. I've tried to find some answer for that in the posts, but one I've found, I guess, referred to the 3.1.x and if I got it right, the timers have been seriously refactored since that time. What I've discovered is that the jbpm-console does it right, I mean it somehow adds a new record to the jbpm_job table in the database. What i did, was a simple experiment: started JBoss, configured and deployed the console, deployed the same process and run it. Then killed JBoss with entries left int the database (I use PostgreSQL). My code is able now to run the orphaned timers, yet it is unable to create the new ones. What else should I do to make it happen ? My jbpm.cfg.xml is like this:
      <jbpm-configuration>
       <jbpm-context>
       <service name="persistence" factory="org.jbpm.persistence.db.DbPersistenceServiceFactory" />
       <service name="tx" factory="org.jbpm.tx.TxServiceFactory" />
       <service name="message" factory="org.jbpm.msg.db.DbMessageServiceFactory" />
       <service name="scheduler" factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" />
       <service name="logging" factory="org.jbpm.logging.db.DbLoggingServiceFactory" />
       <service name="authentication" factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" />
       </jbpm-context>
       <string name="resource.hibernate.cfg.xml" value="hibernate.cfg.xml" />
       <string name="resource.business.calendar" value="org/jbpm/calendar/jbpm.business.calendar.properties" />
       <string name="resource.default.modules" value="org/jbpm/graph/def/jbpm.default.modules.properties" />
       <string name="resource.converter" value="org/jbpm/db/hibernate/jbpm.converter.properties" />
       <string name="resource.action.types" value="org/jbpm/graph/action/action.types.xml" />
       <string name="resource.node.types" value="org/jbpm/graph/node/node.types.xml" />
       <string name="resource.parsers" value="org/jbpm/jpdl/par/jbpm.parsers.xml" />
       <string name="resource.varmapping" value="org/jbpm/context/exe/jbpm.varmapping.xml" />
       <string name="resource.mail.templates" value="jbpm.mail.templates.xml" />
       <int name="jbpm.byte.block.size" value="1024" singleton="true" />
       <string name="jbpm.mail.smtp.host" value="localhost" />
       <bean name="jbpm.task.instance.factory" class="org.jbpm.taskmgmt.impl.DefaultTaskInstanceFactoryImpl" singleton="true" />
       <bean name="jbpm.variable.resolver" class="org.jbpm.jpdl.el.impl.JbpmVariableResolver" singleton="true" />
       <bean name="jbpm.mail.address.resolver" class="org.jbpm.identity.mail.IdentityAddressResolver" singleton="true" />
       <bean name="jbpm.job.executor" class="org.jbpm.job.executor.JobExecutor">
       <field name="jbpmConfiguration"><ref bean="jbpmConfiguration" /></field>
       <field name="name"><string value="JbpmJobExector" /></field>
       <field name="nbrOfThreads"><int value="1" /></field>
       <field name="idleInterval"><int value="5000" /></field>
       <field name="maxIdleInterval"><int value="3600000" /></field> <!-- 1 hour -->
       <field name="historyMaxSize"><int value="20" /></field>
       <field name="maxLockTime"><int value="600000" /></field> <!-- 10 minutes -->
       <field name="lockMonitorInterval"><int value="60000" /></field> <!-- 1 minute -->
       <field name="lockBufferTime"><int value="5000" /></field> <!-- 5 seconds -->
       </bean>
      </jbpm-configuration>
      and Hibernate is set to use PostgreSQL, of course.

      I get no exceptions. The handler is simply not invoked (or invoked only when it is deployed through the jbpm-console). Thanks in advance for help.


        • 1. Re: Timers and JBPM 3.2.1
          koen.aers

          You probably did not start a TimerRunner in some other console... This TimerRunner will poll the database for expired timers and consume them. Check the docs.

          Regards,
          Koen

          • 2. Re: Timers and JBPM 3.2.1
            luukey

            You mean to clean the orphans ? OK. But how to correctly create a new entries ? I found, that the transaction must be commited after the ProcessInstance has been signaled, e.g. in the example above:

            // ...init code
            ProcessInstance pInst = ctx.newProcessInstance("test1");
            pInst.signal();
            ctx.getSession().getTransaction().commit();
            // ... some other code
            . Now, the threads of the JobExecutor are able to find the timer, and run it, but the transaction now is commited, which leads to the following problems:
            Let's assume now, a more complicated flow:
            <?xml version="1.0" encoding="UTF-8"?>
            
            <process-definition
             xmlns="urn:jbpm.org:jpdl-3.2" name="test1">
             <start-state name="start-state1">
             <transition to="state1" name="to_state1"></transition>
             </start-state>
             <state name="state1">
             <timer duedate="2 seconds" name="first_timer" repeat="2 seconds">
             <action class="test1.TimerActionHandler"></action>
             </timer>
             <transition to="state2" name="to_state2"></transition>
             <!-- <transition to="state1" name="to_state1"></transition> -->
             </state>
            
             <state name="state2">
             <timer duedate="5 seconds" name="second_timer" repeat="2 seconds">
             <action class="test1.TimerActionHandler"></action>
             </timer>
             <transition to="end-state1" name="to_end"></transition>
             </state>
            
            
             <end-state name="end-state1"></end-state>
            </process-definition>
            Now we have two timers, needed to start one after another. The first_timer stops, the second_timer starts. Let's see the code:
            // ...init code
            ProcessInstance pInst = ctx.newProcessInstance("test1");
            pInst.signal();
            ctx.getSession().getTransaction().commit();
            System.in.read(); // we're just waiting for the enter....the first_timer is running
            pInst.signal(); // we move to the next node....in the hope of stopping first and starting second
            System.in.read(); // ...but nothing happens
            //....


            OK, adding one more commit, e.g:
            pInst.signal();
             ctx.getSession().getTransaction().commit();
             System.in.read();
             pInst.signal();
             ctx.getSession().getTransaction().commit();
             System.in.read();
            leads to the TransactionException, which is pretty obvious:
            org.hibernate.TransactionException: Transaction not successfully started
             at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:100)
             at Test2.main(Test2.java:41)


            Beginning a new transaction, however, raises the StaleObjectStateException:
            // ....
            ProcessInstance pInst = ctx.newProcessInstance("test1");
            
             pInst.signal();
             ctx.getSession().getTransaction().commit();
             System.in.read();
             ctx.getSession().getTransaction().begin();
             pInst.signal();
             ctx.getSession().getTransaction().commit();
             System.in.read();
            //....

            13:36:40,736 [main] ERROR AbstractFlushingEventListener : Could not synchronize database state with session
            org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [org.jbpm.graph.exe.Token#3601]
             at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1765)

            which is probably realted to the fact, that the JobExecutor is still running in the background and locking the table with the long transaction (I guess).
            So the question remains: how the process definitions with timers should be deployed without the jbpm-console, to run as they should ?

            • 3. Re: Timers and JBPM 3.2.1
              luukey

              Ok. This code works for me:

              public class Test2
              {
               public static void main(String[] args)
               {
               try
               {
               JbpmConfiguration conf = JbpmConfiguration.getInstance("src/main/config/jbpm.cfg.xml");
               JbpmContext ctx = conf.createJbpmContext();
               ProcessDefinition pDef = ProcessDefinition.parseXmlInputStream(new FileInputStream("src/main/jpdl/test1/processdefinition.xml"));
              
               ctx.deployProcessDefinition(pDef);
               conf.startJobExecutor();
              
               ProcessInstance pInst = ctx.newProcessInstance("test1");
               long pId = pInst.getId();
               ctx.close();
              
               ctx = conf.createJbpmContext();
               pInst = ctx.loadProcessInstance(pId);
               pInst.signal();
               ctx.close();
              
               System.in.read();
              
               ctx = conf.createJbpmContext();
               pInst = ctx.loadProcessInstance(pId);
               pInst.signal();
               ctx.close();
              
               System.in.read();
              
               conf.getJobExecutor().stop();
              
               }
               catch (Exception ex)
               {
               ex.printStackTrace();
               }
               }
              }

              No direct transaction playing, just stopping and starting context. Is this the right way ?