14 Replies Latest reply on Aug 2, 2006 5:34 PM by kukeltje

    monitoring piece of default webapp not displaying the correc

    unome

      I have recently started using jBPM on a project.

      Problem Description:
      The following is a snippet of the process defn I'm using:

      <process-definition name="testWorkflow">
      
      ...
      ...
      
       <state name="stateBeforeFork">
       <event type="node-enter">
       <action class="JustLeaveActionHandler" />
       </event>
      
       <transition name="to_fork1" to="fork1"></transition>
       </state>
      
       <fork name="fork1">
       <transition name="to_stateA" to="stateA"></transition>
       <transition name="to_taskA" to="taskA"></transition>
       </fork>
      
       <state name="stateA">
       <event type="node-enter">
       <action class="ExternalServiceActionHandler" />
       </event>
      
       <transition name="to_join1" to="join1"></transition>
       </state>
      
       <task-node name="taskA">
       <task swimlane="shipper">
       <controller>
       <variable name="taskA Complete?" />
       </controller>
       </task>
       <transition name="to_join1" to="join1"></transition>
       </task-node>
      
       <join name="join1">
       <transition name="to_taskAfterJoin" to="taskAfterJoin"></transition>
       </join>
      
      
       <task-node name="taskAfterJoin">
       <task swimlane="accountant">
       <controller>
       <variable name="taskAfterJoin Complete?" />
       </controller>
       </task>
       <transition name="to_endState" to="endState"></transition>
       </task-node>
      
      ...
      ...
      </process-definition>
      



      The ExternalServiceHandler posts a JMS TextMessage (token id - in this case, the child token which
      goes to stateA) onto a JBoss JMS topic. There is an MDB listening on the topic which does some processing,
      loads the token (using the token id) and calls signal on it. The code below shows this:

      ...
      ...
       static JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
       JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
       GraphSession graphSession = jbpmContext.getGraphSession();
      
       Token token = graphSession.loadToken(tokenId);
       System.out.println("Got the token : " + token.getFullName());
      
       token.signal();
      ...
      ...
      


      I'm using the default webapp that comes with the starter kit to view the flow and interact with the above process.

      The behavior I see is that after the MDB calls token.signal(), the debug messages show that process moves to join
      (and assuming that task-node is marked complete using the webapp), and then moves to task-node "taskAfterJoin".
      But the webapp monitoring piece contradicts this by showing that control is still at stateA.

      Am I missing something here? If I take out the "post message to MDB and have MDB signal the token" and replace
      it with a one line of code "executionContext.getNode().leave(executionContext)", it works great.

      So I cannot understand why the webapp does not show what the debug messages are showing.
      Do I have to save the process instance (or token) after calling signal in the MDB?
      Do I need to save the process instance (or token) before posting a message in the Action Handler?

      Any direction, solution or thoughts on this problem is appreciated.

      Environment:
      Jboss AS: JBoss 4.0.4 GA w/ EJB3 support
      jBPM: jBPM 3.1.1
      OS: Windows XP
      Copied the jbpm.sar folder (which has the jBPM jars) to the deploy directory of "all" server.
      Created and copied a jbpm.ear which contains the jbpm.war (default webapp) and the MDB (in a jar) to the
      deploy directory of "all" server.


      Thanks.
      AS

        • 1. Re: monitoring piece of default webapp not displaying the co
          unome

          I'm also using ehcache-1.2.jar instead of ehcache-1.1.jar that comes bundled with the jbpm starter kit

          • 2. Re: monitoring piece of default webapp not displaying the co
            unome

            Another interesting behavior I noticed:

            If the MDB "signals" before the task-node is completed, the processing goes to join but realises that it cannot complete since the other token is still active. But on completion of the task, processing does not resume at join. It says that other token (to_stateA) is still active.

            I'm obviously not getting something here.

            The reason I say this is because if the Task-node completes before StateA, processing resumes at join and goes ahead (of course the monitoring piece of the webapp does not show the correct results)

            Any ideas?

            • 3. Re: monitoring piece of default webapp not displaying the co
              kukeltje

              Could it be the mdb runs in the transaction created by jBPM? In that case the behaviour you see may be slithgly explained. You signal the stateA in it's own node-enter, wich is not advised.

              If you can look into this, we can go further from there.

              could you create a pd.xml with the classes and an mdb.jar so if needed we can try to simulate? Maybe initially post the code of the mdb and the external service action handler

              • 4. Re: monitoring piece of default webapp not displaying the co
                unome

                I don't think the MDB runs in the transaction created by jBPM. But I'm not sure since I'm still picking up on jBPM. Maybe you are referring to the JbpmContext I create in the MDB to load the token and signal it.

                The MDB is started when JBoss starts up. It's subscribed to a JMS Topic.
                The ExternalServiceActionHandler calls a JMS client that creates a TextMessage (procesd defn id, process instance id, root token id, token id, token name, token full name) and posts it on the Topic. The MDB takes this TextMessage does some processing, creates a JbpmContext, gets the GraphSession, loads the token using the token id that was posted, and then calls signal on it.

                Below is the code. I can attach the files and process defn if required. Thanks for your help and time.

                ExternalServiceActionHandler:

                public class ExternalServiceActionHandler implements ActionHandler
                {
                 private static final long serialVersionUID = 1L;
                
                 private static final Logger log = Logger.getLogger(ExternalServiceActionHandler.class);
                
                 public void execute(ExecutionContext executionContext) throws Exception
                 {
                 String inputName = (String)executionContext.getVariable("inputName");
                
                 String defaultTransitionName = executionContext.getNode().getDefaultLeavingTransition().getName();
                 System.out.println("ExternalServiceActionHandler.execute : ########## DefaultTransitionName : " + defaultTransitionName);
                
                 long processDefnId = executionContext.getProcessInstance().getProcessDefinition().getId();
                 System.out.println("ExternalServiceActionHandler.execute : ########## getProcessInstance().getProcessDefinition().getId() PROCESS DEFINITION ID : " + processDefnId);
                
                 long processInstanceId = executionContext.getProcessInstance().getId();
                 System.out.println("ExternalServiceActionHandler.execute : ########## getProcessInstance().getId() PROCESS INSTANCE ID : " + processInstanceId);
                
                 long rootTokenId = executionContext.getProcessInstance().getRootToken().getId();
                 System.out.println("ExternalServiceActionHandler.execute : ########## getRootToken().getId() TOKEN ID : " + rootTokenId);
                
                 String tokenName = executionContext.getToken().getName();
                 String tokenFullName = executionContext.getToken().getFullName();
                 long tokenId = executionContext.getToken().getId();
                 System.out.println("ExternalServiceActionHandler.execute : ########## executionContext.getToken().getName() : " + tokenName);
                 System.out.println("ExternalServiceActionHandler.execute : ########## executionContext.getToken().getFullName() : " + tokenFullName);
                 System.out.println("ExternalServiceActionHandler.execute : ########## executionContext.getToken().getId() : " + tokenId);
                
                 TestProducer.send(inputName, processDefnId, processInstanceId, rootTokenId, tokenId, tokenName, tokenFullName);
                
                 }
                
                }
                


                TestProducer is a class that posts messages on to the topic.


                TestMessageBean (the MDB):
                
                @MessageDriven(activationConfig =
                {
                 @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Topic"),
                 @ActivationConfigProperty(propertyName="destination", propertyValue="topic/eventTopic"),
                })
                public class TestMessageBean implements MessageListener
                {
                 static Map<String, String> tokenMap = Collections.synchronizedMap(new HashMap());
                
                 static JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
                
                 public void onMessage(Message recvMsg)
                 {
                
                 String personName = "";
                 String button = "";
                
                 try
                 {
                 if (recvMsg instanceof TextMessage)
                 {
                 TextMessage message = (TextMessage)recvMsg;
                 String text = message.getText();
                
                 if (text != null && text.startsWith("INPUT_NAME:"))
                 savePerson(text);
                 }
                
                 if (recvMsg.propertyExists("PERSON_NAME"))
                 {
                 person = recvMsg.getStringProperty("PERSON_NAME");
                
                 if (tokenMap.containsKey(person))
                 {
                 String tokenId = tokenMap.get(person);
                 System.out.println("Person tokenId : " + tokenId);
                 System.out.println("Person tokenId (long) : " + Long.parseLong(tokenId));
                
                 JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
                 System.out.println("Got the JbpmContext");
                
                 GraphSession graphSession = jbpmContext.getGraphSession();
                 System.out.println("Got the GraphSession");
                
                 Token token = graphSession.loadToken(Long.parseLong(tokenId));
                 System.out.println("Got the token : " + token.getFullName());
                
                 token.signal();
                 /*
                 ProcessInstance processInstance = DbUtils.loadProcessInstance(Long.parseLong(tokenId));
                 System.out.println("Process Instance Id : " + processInstance.getId());
                 System.out.println("Process Instance getRootToken Id : " + processInstance.getRootToken().getId());
                 Token token = processInstance.getRootToken();
                 token.signal();
                 */
                 System.out.println("Called token.signal");
                
                 //DbUtils.saveToken(token);
                 jbpmContext.save(token);
                
                 //jbpmContext.close();
                
                 // remove the "matched" rfid=tokenId from the Map
                 tokenMap.remove(person);
                
                 }
                
                 }
                
                 }
                 catch (JMSException e)
                 {
                 e.printStackTrace();
                 }
                
                 }
                
                
                 private void savePerson(String text)
                 {
                 Pattern p = Pattern.compile(",");
                 String[] result = p.split(text);
                
                 if (result.length > 0)
                 {
                 String inputPerson = result[0]; // personName
                 String processDefnId = result[1]; // processDefnId
                 String processInstanceId = result[2]; // processInstanceId
                 String rootTokenId = result[3]; // rootTokenId
                 String tokenId = result[4]; // tokenId
                 String tokenName = result[5]; // tokenName
                 String tokenFullName = result[6]; // tokenFullName
                
                 tokenMap.put(inputPerson.substring(inputPerson.indexOf(":") + 1), tokenId.substring(tokenId.indexOf(":") + 1) );
                 }
                 }
                
                }
                
                


                ***************************


                You signal the stateA in it's own node-enter, wich is not advised.


                Is there a reason you stated the above? Is it a bad practice or is there something more to it?

                • 5. Re: monitoring piece of default webapp not displaying the co
                  olivier_debels

                  A remark that maybe can help:

                  If you are creating action handlers on node events (like in your case where you have ExternalServiceActionHandler on a node-enter event) you don't need to call node.signal().

                  The reason for this is that by default node.signal() will be called in the action of the node. So if you don't add an action directly on the node signalling is not necessary.

                  However when adding an action directly on a node you need to signal the node (by using executionContext.leaveNode()). See also the comments on this function:

                  /**
                   * leave this node over the default transition. This method is only available
                   * on node actions. Not on actions that are executed on events. Actions on
                   * events cannot change the flow of execution.
                   */
                   public void leaveNode() {
                   getNode().leave(this);
                   }


                  • 6. Re: monitoring piece of default webapp not displaying the co
                    kukeltje

                    @Olivier: That is not true for states, tasks, etc... only for nodes that have no implementation (custom nodes) AFAIK.

                    @unome: Yes, there is more to it. States etc have implementations do not expect to be signalled on events. De behaviour you get when signalling nodes in an event is undefined and in manyt cases leads to problems.

                    If you need something like async signalling, use the jBPM specific 'queueing' or JMS implementation, but WITH the jBPM async functionality.


                    I'll look at your code tonigh (CET)

                    • 7. Re: monitoring piece of default webapp not displaying the co
                      olivier_debels

                      You're right!

                      It only goes for nodes that don't override the execute method (simple nodes f.e.).

                      So forget my remark for this one.

                      • 8. Re: monitoring piece of default webapp not displaying the co
                        unome

                        Should I not be creating the JbpmConfiguration in the MDB and then getting the JbpmContext?

                        Is there a JbpmConfiguration created by the webapp that I should be using for creating JbpmContexts?


                        • 9. Re: monitoring piece of default webapp not displaying the co
                          kukeltje

                          creating the JbpmConfiguration each time is SLOW and should not be done. That is why I said that if you want ASYNC functionality look at what jBPM provides for you there, either custom or JMS based. That is optimized!

                          • 10. Re: monitoring piece of default webapp not displaying the co
                            kukeltje

                            I looked at your code and see that you use a static for the JbpmConfigiration. Good, so that part is optimized.

                            It could still be that since your code runs on the node-enter, that the async stuff is to quick and tries signalling the node/token while it is not persisted. Strange behaviour could occur then as well.

                            I advise again to look at the jBPM async stuff....

                            • 11. Re: monitoring piece of default webapp not displaying the co
                              unome

                              Ronald,
                              This is what I attempted:

                              I changed the "State" node "StateA" to a "node" node.
                              i.e: So one child token goes to a "node" while the other still goes to a "task-node". And then I added the action to the node instead of the event "node-enter". But I still see the same results.

                              i.e. If I manually (via the webapp) "Save and close task" first, the application waits until the MDB signals the child token (node). The debug messages show that the process resumes, goes into join, and past it to the next node, etc. But the image (monitoring peice) still highlights the child node.

                              Also, if the MDB signals the child token first and then I signal the task-node (via the webapp), the process does not go past the join. Debug messages say that there join is waiting on a concurrent token.

                              Strange behavior!

                              I even tried, saving the child token in the ActionHandler (ExternalServiceActionHandler) using jbpmContext.getCurrentJbpmContext().

                              Question: In the MDB, should I be saving the token after calling signal? I tried doing that. If I just did a jbpmContext.save(token) I don't get any errors. But according to the documentation if I add a jbpmContext.close(), the application throws up saying "cannot commit during a managed transaction".

                              I tried "async=true" as an attribute of the node. But didn't help. I'm not very clear on the async piece. So I'll have to investigate that further.

                              Anyway, I'm going to have to think of a different approach soon.
                              All I want to do is have a MDB (or some external program) signal whatever token (root token or token) is passed to it. I'm initiating the process through the default webapp. When I come to a "wait" state, I want an MDB (or external program) signal the token so when I monitor the process instance via the default webapp monitoring piece I can see the state change.

                              Ronald>> Thanks for taking a look at this and for your time.

                              • 12. Re: monitoring piece of default webapp not displaying the co
                                kukeltje

                                 

                                So one child token goes to a "node" while the other still goes to a "task-node". And then I added the action to the node instead of the event "node-enter". But I still see the same results.


                                ok, thanks for trying

                                i.e. If I manually (via the webapp) "Save and close task" first, the application waits until the MDB signals the child token (node). The debug messages show that the process resumes, goes into join, and past it to the next node, etc. But the image (monitoring peice) still highlights the child node.

                                Strange behavior!


                                Correct, that is why I pay attention to it.
                                It could be that the child token always stays in the node and that the webapp displays it incorrectly

                                Question: Do you save the PI after signalling it to go into the fork? I assume you do, but just want to be sure.

                                Closing the context could indeed be dependend on the configuration of hibernate. I've never used a managed transaction, so have to little knowledge on this subject. The behaviour you see in this area does not seem strange.

                                All I want to do is have a MDB (or some external program) signal whatever token (root token or token) is passed to it. I'm initiating the process through the default webapp. When I come to a "wait" state, I want an MDB (or external program) signal the token so when I monitor the process instance via the default webapp monitoring piece I can see the state change.


                                That is what the jbpm async stuff is for, in combination with the commmandExecutor. I've used that in a minimal config and it works.

                                I have to think further.... if you find additional info, please let us know



                                • 13. Re: monitoring piece of default webapp not displaying the co
                                  unome

                                   


                                  Question: Do you save the PI after signalling it to go into the fork? I assume you do, but just want to be sure.

                                  Earlier I was not. Shame on me. I had a "State" node earlier. To make things easy I changed it to a Task-node and let the webapp handle the save (using the "Save and Close Task"). The "Save and Close task" propagates the execution into the fork, etc.

                                  Still, same behavior.

                                  I should have tried this earlier, but I'm next going to really simplify the process defn to just a start state, node and end state. Have an action handler on the node which posts a message (with the token id) on a JMS queue, have an MDB listening on this queue which will do some processing and then signal the token using the token id that it picked up. Deploy the par with the default webapp and go from there.

                                  If you or anyone come across something stupid that I've been doing, please point it out. Always appreciate it.


                                  • 14. Re: monitoring piece of default webapp not displaying the co
                                    kukeltje

                                    Within the company I work for, We've done this (a process as your describe) with the jbpm async support (so no jms of our own). In that situation it just works, both with jms or the proprietary jBPM queue thing... For us there was no need to develop some thing like this ourselves.