[jboss-user] [JBoss jBPM] - Re: Actions and async=

jeffj55374 do-not-reply at jboss.com
Thu Aug 16 13:40:04 EDT 2007


The details of my particular failure seem to be different than what is already documented in Jira.  
I have submitted http://jira.jboss.com/jira/browse/JBPM-1042

Here is a failing unit test.
This was created with jBPM 3.2.1

While the details of this test are different, I believe the problem may be related to things documented in:
http://jira.jboss.com/jira/browse/JBPM-633
http://jira.jboss.com/jira/browse/JBPM-983
http://jira.jboss.com/jira/browse/JBPM-626

package org.jbpm.job.executor;
  | 
  | import java.util.Collections;
  | import java.util.Set;
  | import java.util.Timer;
  | import java.util.TimerTask;
  | import java.util.TreeSet;
  | 
  | import junit.framework.TestCase;
  | 
  | import org.apache.commons.logging.Log;
  | import org.apache.commons.logging.LogFactory;
  | import org.hibernate.Session;
  | import org.jbpm.JbpmConfiguration;
  | import org.jbpm.JbpmContext;
  | import org.jbpm.graph.def.ActionHandler;
  | import org.jbpm.graph.def.ProcessDefinition;
  | import org.jbpm.graph.exe.ExecutionContext;
  | import org.jbpm.graph.exe.ProcessInstance;
  | 
  | 
  | /** This test verifies that a Node with an action handler can propagate execution to the next node
  |  * that is an async="true" node that also has an action handler.
  |  * 
  |  * An instance of the process below will run successfully if the action handler is removed from Node1,
  |  * or Node2 is not async="true". But the combination with Node1 
  |  * having and action handler that propagates the execution to the next node that is async="true" 
  |  * fails with the exception described below.
  |  * 
  |  * @author jpjohnson at digitalriver.com
  |  * 
  |  * Originally this test case failed in jBPM 3.2.1 with the following exception
  |  * org.jbpm.JbpmException: token '1' can't be locked by 'job[1]' cause it's already locked by 'token[1]'
  |   at org.jbpm.graph.exe.Token.lock(Token.java:646)
  |   at org.jbpm.graph.def.Node.enter(Node.java:316)
  |   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  |   at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
  |   at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
  |   at java.lang.reflect.Method.invoke(Unknown Source)
  |   at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
  |   at org.jbpm.graph.def.Node$$EnhancerByCGLIB$$cc789161.enter(<generated>)
  |   at org.jbpm.graph.def.Transition.take(Transition.java:151)
  |   at org.jbpm.graph.def.Node.leave(Node.java:393)
  |   at org.jbpm.graph.def.Node.leave(Node.java:357)
  |   at org.jbpm.graph.exe.ExecutionContext.leaveNode(ExecutionContext.java:120)
  |   at org.jbpm.job.executor.SimpleAsyncProcessTest$AsyncAction.execute(SimpleAsyncProcessTest.java:57)
  |   at org.jbpm.graph.def.Action.execute(Action.java:122)
  |   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  |   at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
  |   at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
  |   at java.lang.reflect.Method.invoke(Unknown Source)
  |   at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
  |   at org.jbpm.graph.def.Action$$EnhancerByCGLIB$$4852cc95.execute(<generated>)
  |   at org.jbpm.graph.def.GraphElement.executeAction(GraphElement.java:255)
  |   at org.jbpm.graph.def.Node.execute(Node.java:338)
  |   at org.jbpm.graph.def.Node.enter(Node.java:318)
  |   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  |   at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
  |   at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
  |   at java.lang.reflect.Method.invoke(Unknown Source)
  |   at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
  |   at org.jbpm.graph.def.Node$$EnhancerByCGLIB$$cc789161.enter(<generated>)
  |   at org.jbpm.graph.def.Transition.take(Transition.java:151)
  |   at org.jbpm.graph.def.Node.leave(Node.java:393)
  |   at org.jbpm.graph.node.StartState.leave(StartState.java:70)
  |   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  |   at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
  |   at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
  |   at java.lang.reflect.Method.invoke(Unknown Source)
  |   at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
  |   at org.jbpm.graph.def.Node$$EnhancerByCGLIB$$cc789161.leave(<generated>)
  |   at org.jbpm.graph.exe.Token.signal(Token.java:194)
  |   at org.jbpm.graph.exe.Token.signal(Token.java:139)
  |   at org.jbpm.graph.exe.ProcessInstance.signal(ProcessInstance.java:270)
  |   at org.jbpm.job.executor.SimpleAsyncProcessTest.launchProcess(SimpleAsyncProcessTest.java:112)
  |   at org.jbpm.job.executor.SimpleAsyncProcessTest.testConsecutiveAycnActionHandlers(SimpleAsyncProcessTest.java:69)
  |   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  |   at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
  |   at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
  |   at java.lang.reflect.Method.invoke(Unknown Source)
  |   at junit.framework.TestCase.runTest(TestCase.java:154)
  |   at junit.framework.TestCase.runBare(TestCase.java:127)
  |   at junit.framework.TestResult$1.protect(TestResult.java:106)
  |   at junit.framework.TestResult.runProtected(TestResult.java:124)
  |   at junit.framework.TestResult.run(TestResult.java:109)
  |   at junit.framework.TestCase.run(TestCase.java:118)
  |   at junit.framework.TestSuite.runTest(TestSuite.java:208)
  |   at junit.framework.TestSuite.run(TestSuite.java:203)
  |   at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
  |   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
  |   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
  |   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
  |   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
  |   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
  | 
  | 
  |  */
  | public class SimpleAsyncProcessTest
  |     extends TestCase {
  | 
  |   private static final long          serialVersionUID                 = 1L;
  | 
  |   static int                         nbrOfConcurrentProcessExecutions = 20;
  |   static int                         maxWaitTime                      = 20000;
  |   static Set                         collectedResults                 = Collections
  |                                                                           .synchronizedSet(new TreeSet());
  | 
  |   protected static JbpmConfiguration jbpmConfiguration                = JbpmConfiguration
  |                                                                           .getInstance("org/jbpm/jbpm.test.cfg.xml");
  | 
  |   static {
  |     jbpmConfiguration.getJobExecutor().nbrOfThreads = 5;
  |   }
  | 
  |   protected JobExecutor              jobExecutor;
  | 
  |   public static class AsyncAction
  |       implements ActionHandler {
  |     private static final long serialVersionUID = 1L;
  | 
  |     public void execute(ExecutionContext executionContext) throws Exception
  |     {
  |       // Normal would do stuff here
  |       // Leave via the default transition
  |       // When this test case was written, this method would throw when 
  |       // it tried to enter Node2 because it would try to lock the token but 
  |       // it is already locked.
  |       executionContext.leaveNode();
  |     }
  |   }
  |   /** This test verifies that a process with two consecutive nodes with async="true" 
  |    * and an action handler specified actually completes. 
  |    * This test was initially written against jBPM 3.2.1. It would fail in Token.lock() when
  |    * attempting to propagate execution to Node2
  |    */
  |   public void testConsecutiveAycnActionHandlers()
  |   {
  |     jbpmConfiguration.createSchema();
  |     deployProcess();
  |     long processID = launchProcess();
  |     processJobs(maxWaitTime);
  |     assertTrue(hasProcessInstanceEnded(processID));
  |     jbpmConfiguration.createSchema();
  |   }
  | 
  |   public void deployProcess()
  |   {
  |     ProcessDefinition processDefinition = ProcessDefinition
  |         .parseXmlString("<?xml version='1.0' encoding='UTF-8'?>" +
  |                         "<process-definition  xmlns=''  name='StandaloneSample'>" +
  |                            "<start-state name='start'>" + 
  |                               "<transition name='t0' to='node1'></transition>" +
  |                            "</start-state>" +
  |                            "<end-state name='end1'></end-state>" +
  |                            "<node  name='node1'>" +
  |                               "<action class='" + AsyncAction.class.getName() + "'></action>" +
  |                              "<transition name='t1' to='node2'></transition>" +
  |                            "</node>" +
  |                            "<node name='node2' async='true' >"+
  |                            "<action class='" + AsyncAction.class.getName() + "'></action>" +
  |                               "<transition name='t2' to='end1'></transition>"+
  |                             "</node>"+
  |                         "</process-definition>");
  | 
  |     JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
  |     try {
  |       jbpmContext.deployProcessDefinition(processDefinition);
  |     }
  |     finally {
  |       jbpmContext.close();
  |     }
  |   }
  |   /** Create a new process instance
  |    * 
  |    * @return The process instance ID
  |    */
  |   public long launchProcess()
  |   {
  |       JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
  |       try {
  |         ProcessInstance processInstance = jbpmContext
  |             .newProcessInstanceForUpdate("StandaloneSample");
  |         processInstance.signal();
  |         return processInstance.getId();
  |       }
  |       finally {
  |         jbpmContext.close();
  |       }
  |   }
  | 
  |   public boolean hasProcessInstanceEnded(long id) {
  |     JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
  |     try {
  |       ProcessInstance processInstance = jbpmContext.getProcessInstance(id);
  |       return processInstance.hasEnded();
  |     }
  |     finally {
  |       jbpmContext.close();
  |     }
  |     
  |   }
  |     protected void startJobExecutor()
  |   {
  |     jobExecutor = jbpmConfiguration.getJobExecutor();
  |     jobExecutor.start();
  |   }
  | 
  |   private void processAllJobs(final long maxWait)
  |   {
  |     boolean jobsAvailable = true;
  | 
  |     // install a timer that will interrupt if it takes too long
  |     // if that happens, it will lead to an interrupted exception and the test
  |     // will fail
  |     TimerTask interruptTask = new TimerTask() {
  |       Thread testThread = Thread.currentThread();
  | 
  |       public void run()
  |       {
  |         log
  |             .debug("test " + getName()
  |                 + " took too long. going to interrupt...");
  |         testThread.interrupt();
  |       }
  |     };
  |     Timer timer = new Timer();
  |     timer.schedule(interruptTask, maxWait);
  | 
  |     try {
  |       while (jobsAvailable) {
  |         log
  |             .debug("going to sleep for 200 millis, waiting for the job executor to process more jobs");
  |         Thread.sleep(200);
  |         jobsAvailable = areJobsAvailable();
  |       }
  |       jobExecutor.stopAndJoin();
  | 
  |     }
  |     catch (InterruptedException e) {
  |       fail("test execution exceeded treshold of " + maxWait + " milliseconds");
  |     }
  |     finally {
  |       timer.cancel();
  |     }
  |   }
  | 
  |   private int getNbrOfJobsAvailable()
  |   {
  |     int nbrOfJobsAvailable = 0;
  |     JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
  |     try {
  |       Session session = jbpmContext.getSession();
  |       Number jobs = (Number) session
  |           .createQuery("select count(*) from org.jbpm.job.Job").uniqueResult();
  |       log.debug("there are '" + jobs + "' jobs currently in the job table");
  |       if (jobs != null) {
  |         nbrOfJobsAvailable = jobs.intValue();
  |       }
  |     }
  |     finally {
  |       jbpmContext.close();
  |     }
  |     return nbrOfJobsAvailable;
  |   }
  | 
  |   protected boolean areJobsAvailable()
  |   {
  |     return (getNbrOfJobsAvailable() > 0);
  |   }
  | 
  |   protected void processJobs(long maxWait)
  |   {
  |     try {
  |       Thread.sleep(300);
  |     }
  |     catch (InterruptedException e) {
  |       e.printStackTrace();
  |     }
  |     startJobExecutor();
  |     try {
  |       processAllJobs(maxWait);
  |     }
  |     catch (Exception e) {
  |       e.printStackTrace();
  |       throw new RuntimeException(e);
  |     }
  |     finally {
  |       stopJobExecutor();
  |     }
  |   }
  | 
  |   protected void stopJobExecutor()
  |   {
  |     if (jobExecutor != null) {
  |       try {
  |         jobExecutor.stopAndJoin();
  |       }
  |       catch (InterruptedException e) {
  |         throw new RuntimeException(
  |             "waiting for job executor to stop and join got interrupted", e);
  |       }
  |     }
  |   }
  | 
  |   private static Log log = LogFactory.getLog(JobExecutorDbTest.class);
  | }
  | 

View the original post : http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4074927#4074927

Reply to the post : http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=4074927



More information about the jboss-user mailing list