6 Replies Latest reply on Aug 16, 2010 11:55 PM by Craig Bensemann

    Jbpm woes

    Craig Bensemann Novice

      Hi All,


      Have been using seam 2.2.1.CR2 + jbpm-jpdl 3.2.6.SP1. I have defined a business process whereby a user can create tasks and assign them to other users. To achieve this I have used the ForEachForkActionHandler which is described on the jbpm site. At first glance this appears to do what I want (i.e. if you create two tasks on the screen then two jbpm tasks are created and once those are completed the whole business process joins and continues on to the next step). What happens however is that both tasks get initialized with the same variables (should become clearer once I post code below). That is to say that both tasks end up being assigned to the same user with the same descriptions etc.



      "Executive Summary"


      For those that might get lost in my code/comments below. Simply put when two different tasks are created inside the ForEachForkActionHandler and then transitioned to the next step in the business proccess each task gets assigned to the same one user and given the same description when I would expect that each one be assigned to different users (if the tasked were created for different people) and have their own descriptions.


      Lots of words and details below.


      the relevant part of my business process


      <node name="corrective_actions">
             <action
                  class="nz.co.softwarefactory.esafensound.util.ForEachForkActionHandler"
                  config-type="bean">
                  <listVariable>correctiveActions</listVariable>
                  <as>action</as>
              </action>
              <transition to="complete_corrective_action" />
          </node>
      
          <task-node name="complete_corrective_action" >
             <task notify="true" duedate="1 business week" description="#{action.summary}" >
                  <assignment actor-id="#{action.owner.email}" />
              </task>
              <transition to="corrective_actions_join" />
          </task-node>
      
          <join name="corrective_actions_join">
              <transition to="signoff" />
          </join>
      



      the ForEachForkActionHandler


      public class ForEachForkActionHandler implements ActionHandler {
      
          protected List<?> list;
          protected String as;
          private String listVariable;
          protected static final String FOREACH_PREFIX = "foreach.";
      
          /**
           * Create a new child token for each item in list.
           *
           * @param executionContext
           * @throws Exception
           */
          public void execute(final ExecutionContext executionContext) throws Exception {
              initializeList(executionContext);
      
              final Token rootToken = executionContext.getToken();
              final Node node = executionContext.getNode();
      
              final List<Object[]> argSets = new LinkedList<Object[]>();
      
              //
              // First, create a new token and execution context for each item in
              // list.
              //
              for (int i = 0; i < node.getLeavingTransitions().size(); i++) {
                  final Transition transition = (Transition) node.getLeavingTransitions().get(i);
      
                  for (int j = 0; list != null && j < list.size(); j++) {
                      final Object item = list.get(j);
      
                      final Token newToken = new Token(rootToken, ForEachForkActionHandler.FOREACH_PREFIX + node.getId()
                              + "." + j);
                      newToken.setTerminationImplicit(true);
                      executionContext.getJbpmContext().getSession().save(newToken);
                      final ExecutionContext newExecutionContext = new ExecutionContext(newToken);
                      newExecutionContext.getContextInstance().createVariable(as, item, newToken);
                      argSets.add(new Object[] { newExecutionContext, transition });
                  }
              }
      
              //
              // Now, let each new token leave the node.
              //
              for (int i = 0; i < argSets.size(); i++) {
                  final Object[] args = argSets.get(i);
                  node.leave((ExecutionContext) args[0], (Transition) args[1]);
              }
          }
      
          public List<?> getList() {
              return list;
          }
      
          public void setList(final List<?> list) {
              this.list = list;
          }
      
          public String getAs() {
              return as;
          }
      
          public void setAs(final String as) {
              this.as = as;
          }
      
          public String getListVariable() {
              return listVariable;
          }
      
          public void setListVariable(final String listVariable) {
              this.listVariable = listVariable;
          }
      
          protected void initializeList(final ExecutionContext executionContext) {
              if (list == null && listVariable != null) {
                  list = (List<?>) executionContext.getContextInstance().getVariable(listVariable);
              }
          }
      }
      



      My understanding of the ForEachForkActionHandler is that the variables in list will be iterated over and for each one a new task is created and the then a locally scoped variable created to store the item. Then when the node is transistioned to the next state the actor should be assigned to #{action.owner.email} and the description to #{action.summary} and both of these variables should come from the varaibles scoped to that task instance


      I have spent quite a bit of time trying to understand how all of this works and debugging through the code and you can see that two tasks are created and two copies of action are persisted in the jbpm database and both have different values. I could be on the wrong track as I did get a little confused but think the problem comes when the SeamExpressionEvaluator tries to evaluate the expressions. It always returns one value. At this point I stopped debugging as I have spent far too much time on this and hope someone else can shed some light on the problem.

        • 2. Re: Jbpm woes
          Tony Herstell Master

          We really are seeing that Seam(expression evaluator) is still just returning the first item in the list for JBPM.


          <rich:dataTable var="task" value="#{cachedTaskInstanceList}" width="100%">



          <rich:column styleClass="#{manageTasksController.rowClass(task)} task_description">
             <f:facet name="header">
               <h:outputText value="#{messages.manage_tasks_task_description}" />
             </f:facet>
             <h:outputText value="#{task.description}" />
          </rich:column>




          Is this a know problem with Seam2?


          • 3. Re: Jbpm woes
            Leo van den berg Master

            Hi,


            I am succesfully using jBPM in combination with Seam and it took me some time to get there. The integration of both world is the least documented part of Seam and can give you some headaches.
            I normally handle all jBPM interaction within the bean-code, but for human tasks (the Task node) I use the Seam annotations


            The first which comes to my mind is: why you're using specific action handlers and why you're NOT using a Seam managed bean?


            The second is that you should check if Seam and jBPM are synchronized; You're using the jBPM without notifying Seam that things have changed. I can't see the rest of you code, but if you use the cachedtaskInstanceList (which is a Seam managed component; a factory), you problably are missing the additional task inserts.


            leo


            • 4. Re: Jbpm woes
              Tony Herstell Master

              Leo van den Berg wrote on Aug 16, 2010 08:04:


              Hi,

              I am succesfully using jBPM in combination with Seam and it took me some time to get there. The integration of both world is the least documented part of Seam and can give you some headaches.
              I normally handle all jBPM interaction within the bean-code, but for human tasks (the Task node) I use the Seam annotations

              The first which comes to my mind is: why you're using specific action handlers and why you're NOT using a Seam managed bean?

              The second is that you should check if Seam and jBPM are synchronized; You're using the jBPM without notifying Seam that things have changed. I can't see the rest of you code, but if you use the cachedtaskInstanceList (which is a Seam managed component; a factory), you problably are missing the additional task inserts.

              leo




              Thanks Leo,


              We will look into this and will report progress.


              We have wasted far too much time on this one problem and if possible, may want to chat through this with you (skype etc.) if these leads can't shed light on the problem.


              I wonder if the Seamsters would approach you to document your findings (and possibly remunerate you in some way).

              • 5. Re: Jbpm woes
                Leo van den berg Master

                Hi Tony,


                As already stated, the doc on the jBPM/SEAM integration is not really 100%, but to be honest, we're talking open-source here and RedHat makes money with additional professional support.


                The best source still is the Seam In Action book which has a freely downloadable chpater on jBPM.


                There are a number of things which are mentioned in 
                my other thread and most of my experience comes from try-and-error.



                Possibly some useful tips with some code snippets:


                One of the first things I did was connecting Seam and jBPM with a simple logging function whiche enabled me to see what happens when running jBPM calling Seam. At the same time it is used to pass messages to the User.


                An example: A part of a workflow where a transaction is taken- As you can see I am directly using a Seam bean. At the same time you pass a local scripting variable processInstance (took me some too figure out this can be done!!)




                <transition to="HandlingTime Present?" name="officerNeeded">
                     <action expression="#{trafficIncidentWorkflow.sendMessageToOperator('', 'workflow.officerNeeded' , processInstance, true)}"></action>
                </transition>




                The piece of code of the staful bean which is called. The message to the operator is a so called growl component which receives its header and detail info from the workflow. As you can see we pick up the operator and the location ,of an incident, from the processInstance. The variable jbpmContext is managed by Seam and is injected with the In-Annotation. Be aware that this only works when there is an active transaction (so you need to put an transactional annotation on the entry point of this call).




                     
                public void sendMessageToOperator(String header, String detail, Object o,               boolean toOperator) {
                     if (o instanceof ProcessInstance) {
                     ProcessInstance pi = (ProcessInstance) o;
                     ProcessInstance processInstance = jbpmContext.getProcessInstance(pi
                                         .getId());
                     String location = (String) processInstance.getContextInstance()
                                         .getVariable("location");
                     String operator = (String) pi.getContextInstance().getVariable(
                                         "actualOperator");
                     if (toOperator)
                          events.raiseAsynchronousEvent(
                                              "es.esam.im4u.incidentmessage.new", operator, location,
                                              detail);
                     log.info("EVENT: es.esam.im4u.incidentmessage.new OPERATOR: "
                                         + operator + " LOCATION: " + location + " DETAIL: "
                                         + detail);
                     } else
                          log.info("No a useable processdefinition....");
                     }
                
                }
                



                Another tricky one is starting up additional procesdefintions. This is a nice way to split up a complex workflow in smaller manageable steps. I found out that jBPM parses everthing st startup, but you need to bind actions/variables at the moment you need them. This can work by defining a late binding as follows:



                <process-state name="WhateverYouWantToDo" binding="late" >
                etc etc,
                </...
                



                Another hint for decisions-nodes for a to be taken transition:




                <decision name="OfficerNeeded?" expression="#{trafficIncidentWorkflow.checkOfficer(processInstance)}">
                          <description>
                               Checks if there is a need for an OVD
                          </description>
                          <transition to="HandlingTime Present?" name="officerNeeded">
                               <action expression="#{trafficIncidentWorkflow.sendMessageToOperator('', 'workflow.officerNeeded' , processInstance, true)}"></action>
                          </transition>
                          <transition to="HandlingTime Present?" name="noOfficerNeeded"></transition>
                     </decision>
                
                
                



                The called method for the decision returns the name of the decision. As you can see this combines a decision taken in a Seam.bean with a message of its outcome to the operator I am still working on replacing the hard-coded decsion with a Drools decision, which further improves the extensibility/flexibility of the framework.



                As you can see I mix the Seam and jBPM environments without a probleme and it works like a charm. The only thing you need is a lot of patience and try and error. For the latter I advice testing all workflows first in a Jave-client environment and when everything works, too make the switch to Seam-



                Hopefully this gives some ideas. Don't hesitate to coontact me if you need further help. You can alos contact me through LinkedIn.com


                Leo

                • 6. Re: Jbpm woes
                  Craig Bensemann Novice

                  Hi Leo,


                  I'm the developer working on this at the moment and will look into it again when I am back in the office tomorrow. I am using the ForEachForkActionHandler instead of a Seam component as it came directly from the Jbpm wiki. The purpose of it is to fork off new nodes programatically. So for example I am logged in as User A and am working on a task within the jbpm flow. As part of this I create a list of new tasks that I want to assign to different users in order to complete my business objectives. So I create a task and assign it to User B and another task and assign it to User C. The action handler creates new tokens and execution contexts for each of these tasks and then transitions them to the next step in the process (my goal here is to branch from a single user to multiple users performing tasks and once they have all finished join back so the original user (A in this case) can review what the others have done). This mostly works the way I want - new tasks are created but all tasks will be assigned to either User B or User C and task.description will be set to the same value (either the description of the task meant for User B or the description for the task meant for User C).


                  So what should happen (as far as I understand) is that the EL which is used to set the task owner and the task description should be resolved to different values (these should come from task scoped variables set when the action handler creates the task) but what is happening is that it is always resolved to a single value.


                  Not sure if that describes what I'm trying to do any better or just confuses the issue.


                  Craig