8 Replies Latest reply on Jun 9, 2008 4:39 AM by nizzy

    Using end-complete-process attribute of end-state

    nizzy

      Hi,

      I have attached the update schema to the jira issue created by Tom.
      This fixed the exception I was seeing. However I'm still not seeing the behaviour from the process execution that I would expect.

      Could someone have a look at my test process definition and see if I'm doing something wrong



      <?xml version="1.0" encoding="UTF-8"?>
      
      <process-definition xmlns="" name="forktest">
      
      
       <start-state name="start">
       <transition to="doThis1"></transition>
       </start-state>
      
      
       <node name="doThis1">
       <transition to="fork1"></transition>
       </node>
      
       <fork name="fork1">
       <transition to="wait for a" name="a"></transition>
       <transition to="wait for b" name="b"></transition>
       </fork>
      
       <node name="doThis2">
       <action class="com.ecebs.sample.action.DoThis2ActionHandler"></action>
       <transition to="join1" name="success"></transition>
       <transition to="end - doThis2 failed" name="failure"></transition>
       </node>
      
       <node name="doThis3">
       <action class="com.ecebs.sample.action.DoThis2ActionHandler"></action>
       <transition to="join1" name="success"></transition>
       <transition to="end - doThis3 failed" name="failure"></transition>
       </node>
      
       <join name="join1">
       <transition to="wait state"></transition>
       </join>
      
       <state name="wait for a">
       <transition to="doThis2"></transition>
       </state>
      
       <state name="wait for b">
       <transition to="doThis3"></transition>
       </state>
      
       <state name="wait state">
       <transition to="end"></transition>
       </state>
      
      
       <end-state name="end" />
       <end-state name="end - doThis2 failed" end-complete-process='true'/>
       <end-state name="end - doThis3 failed" end-complete-process='true'/>
      
      
      </process-definition>




      I set in the ContextInstance a variable that I use to define success or failure, i.e. if success then I call


      ctx.leaveNode("success")




      from my action handler, if it fails then I calll same method passing in failure.

      I still dont see the process completing at the expected end-state which leads me to believe I'm doing something wrong, either in my process definition or my test code

      test code is



      @Test
       public void testFork() {
       if (log.isDebugEnabled()) {
       log.debug("inside testFork()");
       }
      
       // Create an instance of the process definition.
       ProcessInstance instance = new ProcessInstance(processDefinition);
       debugState("new ProcessInstance()", instance);
       log.debug("ProcessId is " + instance.getId());
      
       // Signal to Start Process
       log.info("Signaling: Start");
       signal(instance, null);
       debugState("1. Start", instance);
      
       ContextInstance ctxInst = instance.getContextInstance();
       ctxInst.setVariable("status", "failure");
      
       // Signal: Waiting for a
       log.info("Signaling: Waiting for a");
       signal(instance, "a");
       debugState("2. signal(instance, \"wait for a\")", instance);
      
       ctxInst.setVariable("status", "success");
      
       // Signal: Waiting for a
       log.info("Signaling: Waiting for b");
       signal(instance, "b");
       debugState("3. signal(instance, \"wait for b\")", instance);
      
       // Signal: Waiting for a
       log.info("Signaling: to exit wait");
       signal(instance, null);
       debugState("3. signal(instance, \"null\")", instance);
      
       Assert.assertEquals(processDefinition.getNode("end - doThis2 failed"), instance.getRootToken().getNode());
       //Assert.assertEquals(processDefinition.getNode("end"), instance.getRootToken().getNode());
       }
      
       private void debugState(String action, ProcessInstance instance) {
       log.info("**************************************************");
       log.info("just performed action: " + action);
       log.info("root token node: " + instance.getRootToken().getNode());
       log.info("root token active children: "
       + instance.getRootToken().getActiveChildren());
       log.info("**************************************************");
       }
      
       private void signal(ProcessInstance instance, String childKey) {
       if (log.isDebugEnabled()) {
       log.debug("inside signal(instance, childKey = " + childKey + ")");
       }
      
       Token rootToken = instance.getRootToken();
       if (rootToken.hasActiveChildren()) {
       Map<?, ?> children = rootToken.getActiveChildren();
       if (children.containsKey(childKey)) {
       Token childToken = (Token) children.get(childKey);
       childToken.signal();
       } else {
       // TODO handle this
       String msg = "Root Token does not contain, " + childKey;
       log.debug(msg);
       throw new RuntimeException(msg);
       }
       } else {
       rootToken.signal();
       }
       }





      Help is much appreciated!

        • 1. Re: Using end-complete-process attribute of end-state
          nizzy

          Hi

          Interestingly in my debug, I see that node-entered is the required end state however execution of the process does not finish at this point!

          
          10:47:20,613 [main] INFO ForkTest : Signaling: Waiting for a
          10:47:20,613 [main] DEBUG ForkTest : inside signal(instance, childKey = a)
          10:47:20,613 [main] DEBUG GraphElement : event 'before-signal' on 'State(wait for a)' for 'Token(/a)
          '
          10:47:20,613 [main] DEBUG GraphElement : event 'node-leave' on 'State(wait for a)' for 'Token(/a)'
          10:47:20,613 [main] DEBUG GraphElement : event 'transition' on 'Transition(148aa23)' for 'Token(/a)'
          
          10:47:20,613 [main] DEBUG GraphElement : event 'node-enter' on 'Node(doThis2)' for 'Token(/a)'
          10:47:20,613 [main] DEBUG GraphElement : executing action 'Action(a46701)'
          10:47:20,613 [main] DEBUG DoThis2ActionHandler : Calling leaveNode("failure")
          10:47:20,613 [main] DEBUG GraphElement : event 'node-leave' on 'Node(doThis2)' for 'Token(/a)'
          10:47:20,613 [main] DEBUG GraphElement : event 'transition' on 'Transition(failure)' for 'Token(/a)'
          
          10:47:20,613 [main] DEBUG GraphElement : event 'node-enter' on 'EndState(end - doThis2 failed)' for
          'Token(/a)'
          10:47:20,613 [main] DEBUG GraphElement : event 'process-end' on 'ProcessDefinition(forktest)' for 'T
          oken(/)'
          10:47:20,613 [main] DEBUG GraphElement : event 'after-signal' on 'State(wait for a)' for 'Token(/a)'
          
          10:47:20,613 [main] INFO ForkTest : **************************************************
          10:47:20,613 [main] INFO ForkTest : just performed action: 2. signal(instance, "wait for a")
          10:47:20,613 [main] INFO ForkTest : root token node: Fork(fork1)
          10:47:20,613 [main] INFO ForkTest : root token active children: {}
          10:47:20,613 [main] INFO ForkTest : **************************************************
          


          I also have noticed that ProcessInstance.hasEnded() is true, but I can still signal the process!!

          • 2. Re: Using end-complete-process attribute of end-state
            kukeltje

            nizzy,

            instead of using a 'debugstate' method, use asserts to show what you expect at certain points. Since I only have a slight clue what you want to achieve it is more difficult to help out.

            If you do create a testcase, put the processdefinition as a string inside of it, so I can also run it locally (also put the actionhandlers in as innerclasses)

            • 3. Re: Using end-complete-process attribute of end-state
              nizzy

              Thanks Ronald,

              Having a look at executing sub processes at the moment as soon as I get back onto it I will do as you suggest.

              • 4. Re: Using end-complete-process attribute of end-state
                nizzy

                Hi,

                Finally got back to this issue.

                I'm using the

                end-complete-process="true"
                attribute of end-state.

                My process definition has a fork, the fork has two paths of execution. At the end of each path, processing can either be successfull or fail. When execution fails I would expect to enter the relevant end-state and not to be able to signal the process again.

                This is not the observed behaviour.

                In fact the end-state is only entered if both paths of execution in the fork fail.

                Is this a problem or have I misunderstood what should be happening

                Below is a unit test containing the process definition

                Test1 is +ve test to show nothing wrong with process definition
                Test2 fails as expected, however only because both paths in fork have failed
                Test3 verifies that behaviour not as expected.
                package com.sample;
                
                
                import java.util.Map;
                
                import junit.framework.Assert;
                
                import org.apache.log4j.Logger;
                import org.apache.log4j.PropertyConfigurator;
                import org.jbpm.JbpmConfiguration;
                import org.jbpm.JbpmContext;
                import org.jbpm.context.exe.ContextInstance;
                import org.jbpm.graph.def.ProcessDefinition;
                import org.jbpm.graph.exe.ProcessInstance;
                import org.jbpm.graph.exe.Token;
                import org.junit.Before;
                import org.junit.Test;
                
                /**
                 * TODO description
                 *
                 * @author Alan Nisbet
                 * @version $Revision$
                 */
                
                /*
                 History
                 =======
                 $Log$
                 */
                public class EndStateTest {
                
                 /**
                 * Log4j Logger for this class
                 */
                 private static final Logger log = Logger.getLogger(EndStateTest.class);
                 private ProcessDefinition processDefinition;
                
                 @Before
                 public void setUp() throws Exception {
                 PropertyConfigurator.configure("log4j.properties");
                 JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
                
                 log.info("Parsing ProcessDefinition");
                 try {
                 processDefinition = ProcessDefinition
                 .parseXmlString(
                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                 "<process-definition xmlns=\"\" name=\"forktest\">" +
                 "<start-state name=\"start\">" +
                 "<transition to=\"doThis1\"></transition>" +
                 "</start-state>" +
                
                 "<node name=\"doThis1\">" +
                 "<transition to=\"fork1\"></transition>" +
                 "</node>" +
                
                 "<fork name=\"fork1\">" +
                 "<transition to=\"wait for a\" name=\"a\"></transition>" +
                 "<transition to=\"wait for b\" name=\"b\"></transition>" +
                 "</fork>" +
                
                 "<node name=\"doThis2\">" +
                 "<action class=\"com.ecebs.sample.action.DefaultActionHandler\"></action>" +
                 "<transition to=\"join1\" name=\"success\"></transition>" +
                 "<transition to=\"end - doThis2 failed\" name=\"failure\"></transition>" +
                 "</node>" +
                
                 "<node name=\"doThis3\">" +
                 "<action class=\"com.ecebs.sample.action.DefaultActionHandler\"></action>" +
                 "<transition to=\"join1\" name=\"success\"></transition>" +
                 "<transition to=\"end - doThis3 failed\" name=\"failure\"></transition>" +
                 "</node>" +
                
                 "<join name=\"join1\">" +
                 "<transition to=\"wait state\"></transition>" +
                 "</join>" +
                
                 "<state name=\"wait for a\">" +
                 "<transition to=\"doThis2\"></transition>" +
                 "</state>" +
                
                 "<state name=\"wait for b\">" +
                 "<transition to=\"doThis3\"></transition>" +
                 "</state>" +
                
                 "<state name=\"wait state\">" +
                 "<transition to=\"end\"></transition>" +
                 "</state>" +
                
                 "<end-state name=\"end\" />" +
                 "<end-state name=\"end - doThis2 failed\" end-complete-process=\"true\" />" +
                 "<end-state name=\"end - doThis3 failed\" end-complete-process=\"true\" />" +
                 "</process-definition>");
                 } finally {
                 ctx.close();
                 }
                 }
                
                 @Test
                 public void testEndStatePass() {
                 if (log.isDebugEnabled()) {
                 log.debug("inside testEndState()");
                 }
                
                 // Create an instance of the process definition.
                 ProcessInstance instance = new ProcessInstance(processDefinition);
                 log.debug("ProcessId is " + instance.getId());
                
                 // Signal to Start Process
                 log.info("Signaling: Start");
                 signal(instance, null);
                
                 ContextInstance ctxInst = instance.getContextInstance();
                 ctxInst.setVariable("status", "success");
                
                 // Signal: Waiting for a
                 log.info("Signaling: Waiting for a");
                 signal(instance, "a");
                
                 // Signal: Waiting for b
                 log.info("Signaling: Waiting for b");
                 signal(instance, "b");
                
                
                 // Signal: Exit Wait
                 log.info("Signaling: Exit Wait");
                 signal(instance, null);
                
                 Assert.assertEquals(processDefinition.getNode("end"), instance.getRootToken().getNode());
                 }
                
                 @Test
                 public void testEndStateFail_doThis2() {
                
                 // Create an instance of the process definition.
                 ProcessInstance instance = new ProcessInstance(processDefinition);
                 log.debug("ProcessId is " + instance.getId());
                
                 // Signal to Start Process
                 log.info("Signaling: Start");
                 signal(instance, null);
                
                 ContextInstance ctxInst = instance.getContextInstance();
                 ctxInst.setVariable("status", "failure");
                
                 // Signal: Waiting for a
                 log.info("Signaling: Waiting for a");
                 signal(instance, "a");
                
                 ctxInst.setVariable("status", "failure");
                
                 // Signal: Waiting for b
                 log.info("Signaling: Waiting for b");
                 signal(instance, "b");
                
                 // Signal: Exit Wait
                 log.info("Signaling: Exit Wait");
                 signal(instance, null);
                
                 Assert.assertEquals(processDefinition.getNode("end - doThis2 failed"), instance.getRootToken().getNode());
                 }
                
                 @Test
                 public void testEndStateFail_doThis3() {
                 if (log.isDebugEnabled()) {
                 log.debug("inside testEndStateFail_doThis3()");
                 }
                
                 // Create an instance of the process definition.
                 ProcessInstance instance = new ProcessInstance(processDefinition);
                 log.debug("ProcessId is " + instance.getId());
                
                 // Signal to Start Process
                 log.info("Signaling: Start");
                 signal(instance, null);
                
                 ContextInstance ctxInst = instance.getContextInstance();
                 ctxInst.setVariable("status", "success");
                
                 // Signal: Waiting for a
                 log.info("Signaling: Waiting for a");
                 signal(instance, "a");
                
                 ctxInst.setVariable("status", "failure");
                
                 // Signal: Waiting for b
                 log.info("Signaling: Waiting for b");
                 signal(instance, "b");
                
                
                 // Signal: Exit Wait
                 log.info("Signaling: Exit Wait");
                 signal(instance, null);
                
                 Assert.assertEquals(processDefinition.getNode("end - doThis3 failed"), instance.getRootToken().getNode());
                 }
                
                 private void signal(ProcessInstance instance, String childKey) {
                 if (log.isDebugEnabled()) {
                 log.debug("inside signal(instance, childKey = " + childKey + ")");
                 }
                
                 Token rootToken = instance.getRootToken();
                 if (rootToken.hasActiveChildren()) {
                 Map<?, ?> children = rootToken.getActiveChildren();
                 if (children.containsKey(childKey)) {
                 Token childToken = (Token) children.get(childKey);
                 childToken.signal();
                 } else {
                 // TODO handle this
                 String msg = "Root Token does not contain, " + childKey;
                 log.debug(msg);
                 throw new RuntimeException(msg);
                 }
                 } else {
                 rootToken.signal();
                 }
                 }
                }



                Cheers Alan

                • 5. Re: Using end-complete-process attribute of end-state
                  nizzy

                  Forgot ActionHandler

                  package com.ecebs.sample.action;
                  
                  import org.apache.log4j.Logger;
                  import org.jbpm.context.exe.ContextInstance;
                  import org.jbpm.graph.def.ActionHandler;
                  import org.jbpm.graph.exe.ExecutionContext;
                  
                  /**
                   * TODO description
                   *
                   * @author Alan Nisbet
                   * @version $Revision: 1.1 $
                   */
                  
                  /*
                   History
                   =======
                   $Log: DefaultActionHandler.java,v $
                   Revision 1.1 2008/06/06 13:35:10 alann
                   Periodic Checkin
                  
                   Revision 1.1 2008/06/05 10:15:08 alann
                   Intial Checkin
                  
                   */
                  public class DefaultActionHandler implements ActionHandler {
                  
                   private static final long serialVersionUID = 1L;
                   private static final Logger log = Logger
                   .getLogger(DefaultActionHandler.class);
                  
                   public void execute(ExecutionContext ctx) throws Exception {
                  
                   ContextInstance ctxInstance = ctx.getContextInstance();
                   String status = (String)ctxInstance.getVariable("status");
                   leaveNode(ctx, status);
                   }
                  
                   private void leaveNode(ExecutionContext ctx, String transitionName) {
                   if (transitionName != null) {
                  
                   if (log.isDebugEnabled()) {
                   log.debug("Calling leaveNode(\"" + transitionName + "\")");
                   }
                  
                   ctx.leaveNode(transitionName);
                   } else {
                  
                   if (log.isDebugEnabled()) {
                   log.debug("Calling leaveNode()");
                   }
                  
                   ctx.leaveNode();
                   }
                   }
                  }



                  • 6. Re: Using end-complete-process attribute of end-state
                    kukeltje

                    what is the node the root token is in when you run test 3? for checking ended processes try the 'isEnded()' method.

                    • 7. Re: Using end-complete-process attribute of end-state
                      nizzy

                      Hi Ronald,

                      After "waiting for b" the process has indeed ended!

                      However it is not in the end state I would have thought it should be in, and it is also possible to signal a process instance which has ended.

                      1. Should the process instance not be in end-state(end - doThis3 failed)?
                      2. Should an exception have been thrown if signalling a process instance that has already finished?

                      • 8. Re: Using end-complete-process attribute of end-state
                        nizzy

                        Hi Ronald,

                        After calling "signalling waiting for b" the root token node is Fork(fork1).