9 Replies Latest reply on Dec 3, 2009 12:23 PM by nizzy

    Variable missing in assignment handler

    lpiccoli

      hi all,

      my swimlane assignment handler is unable to retrieve variables that have been previous set in both root token and local token.

      the test involves a forkhandler, sub process and swimlane assignment handler.

      When inside the SwimlaneAssignmentHandler getVariable() returns null which is not what i was expecting

      public void assign(Assignable assignable, ExecutionContext executionContext) {
      
      assertNotNull("shouldnt be null", executionContext.getVariable(IDENTIFIER));
      String expression = (String)executionContext.getVariable(EXPRESSION);
      assertNotNull( "shouldnt be null", expression );
      


      as it was set in the previous ActivityForkActionHandler.execute()


      public void execute(final ExecutionContext executionContext) throws Exception {
      .....stuff
      Token(rootToken, "ITP" + item.getId()); //TODO -lp
      newToken.setTerminationImplicit(true);
       executionContext.getJbpmContext().getSession().save(newToken);
      final ExecutionContext newExecutionContext = new ExecutionContext(newToken);
      newExecutionContext.getContextInstance().createVariable(
       EXPRESSION,
       item.getResponsibleExpression(),
       newToken);
      
      



      I am not sure if the problem is with my process definition of the swimlan assignment

      <swimlane name='responsible' >
      <assignment class='com.asteriski.itpflow.action.ActivityForkActionHandlerTest$SwimlaneAssignmentHandler'/>
      </swimlane>
      


      or with how i am setting the local variables against token in the forkHandler above

      i have a attached the entire test case below. It is a bit tedious but i needed to include all the handler code.

      any help is most appreciated.

      public class ActivityForkActionHandlerTest extends AbstractJbpmTestCase {
      
       static JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance("jbpm.test.cfg.xml");
      
       public void testForkHandlerAndSubProcess() throws Exception {
      
       JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
      
       ProcessDefinition subProcessDefinition = ProcessDefinition.parseXmlString(
       "<process-definition name='sub'>" +
       " <start-state>" +
       " <transition to='wait' />" +
       " </start-state>" +
       " <swimlane name='responsible' >" +
       " <assignment class='com.asteriski.itpflow.action.ActivityForkActionHandlerTest$SwimlaneAssignmentHandler' />" +
       " </swimlane>" +
       " <task-node name='wait' >" +
       " <description>'just waiting'</description>" +
       " <task name='task-waiting' signal='block' swimlane='responsible'>"+
       " </task>" +
       " <transition to='end_sub'></transition>" +
       " </task-node>" +
       " <end-state name='end_sub' />" +
       "</process-definition>"
       );
       jbpmContext.deployProcessDefinition(subProcessDefinition);
      
       ProcessDefinition superProcessDefinition = ProcessDefinition.parseXmlString(
       "<process-definition" +
       " name='super'>" +
       "<start-state name='start'> " +
       "<transition name='to_state' to='forkActivities'>" +
       "</transition>" +
       "</start-state>" +
       " <node name='forkActivities'>" +
       " <action class='com.asteriski.itpflow.action.ActivityForkActionHandlerTest$ActivityForkActionHandler' config-type='bean' name='ActivityForkActionHandler'></action>" +
       " <description>" +
       " find all activities that have no blocking state and generate a testign request"+
       " </description>" +
       " <transition to='Sub Process'></transition>" +
       " </node>" +
       "<process-state name='Sub Process'>" +
       "<sub-process name='sub'/>" +
       "<transition to='task-node1'></transition>" +
       "</process-state>" +
       "<task-node name='task-node1'> " +
       " <task name='task1'>" +
       " </task>" +
       " <transition to='end' name='to_end'> " +
       " </transition>" +
       "</task-node>" +
       "<end-state name='end'></end-state> " +
       "</process-definition>");
      
      
       jbpmContext.deployProcessDefinition(superProcessDefinition);
       ProcessInstance processInstance = jbpmContext.newProcessInstance("super");
      
       ProcessState processState = (ProcessState) superProcessDefinition.getNode("Sub Process");
       processState.setSubProcessDefinition(subProcessDefinition);
      
       processInstance.getContextInstance().createVariable(Constant.ITP_ID_JBPM_CONTEXT_IDENTIFIER, 1);
      
       // After construction, the process execution has one main path
       // of execution (=the root token).
       Token rootToken = processInstance.getRootToken();
      
       assertEquals(
       "start",
       rootToken.getNode().getName());
      
      
       // Move the process instance to the task
       rootToken.signal();
       assertEquals(
       "Sub Process",
       rootToken.getNode().getName());
      
       //get the sub process
       ProcessInstance subProcessInstance = rootToken.getSubProcessInstance();
       assertSame(subProcessDefinition, subProcessInstance.getProcessDefinition());
       Token subToken = subProcessInstance.getRootToken();
      
      
       // Move the process instance to the task
       subToken.signal();
       assertEquals(
       "end_sub",
       subToken.getNode().getName());
      
      
       }
      
       public static class SwimlaneAssignmentHandler implements AssignmentHandler {
      
       Logger log = Logger.getLogger(ResponsibleSwimlaneAssignmentHandler.class);
      
       private static final long serialVersionUID = 1L;
      
       public void assign(Assignable assignable, ExecutionContext executionContext) {
      
      
       assertNotNull("shouldnt be null", executionContext.getVariable(Constant.ITP_ID_JBPM_CONTEXT_IDENTIFIER));
       String expression = (String)executionContext.getVariable(Constant.ASSIGNMENT_RESPONSIBLE_EXPRESSION_JBPM_CONTEXT_IDENTIFIER);
       assertNotNull( "shouldnt be null", expression );
      
       assignable.setPooledActors( expression );
       }
       }
      
      
       public static class ActivityForkActionHandler implements ActionHandler, Constant {
      
       Logger log = Logger.getLogger( ActivityForkActionHandler.class );
      
      
       /**
       * Create a new child token for each item in list.
       *
       * @param executionContext
       * @throws Exception
       */
       @SuppressWarnings("unchecked")
       public void execute(final ExecutionContext executionContext) throws Exception {
      
       log.debug("execute");
       long id =Long.valueOf( executionContext.getContextInstance().getVariable(ITP_ID_JBPM_CONTEXT_IDENTIFIER).toString() );
       log.debug("execute itp d:" + id);
      
       Collection<ITPItem> activities = getActivities( id );
      
       if( activities != null ){
       final Token rootToken = executionContext.getToken();
       final Node node = executionContext.getNode();
       log.debug( "node name:" + node.getName() );
       log.debug( "node getDescription:" + node.getDescription() );
       log.debug( "node getDefaultLeavingTransition:" + node.getDefaultLeavingTransition().getName() );
       final Transition transition = (Transition) node.getLeavingTransitions().get(0); //TODO must work out which transition to use
      
       log.debug( "transition name:" + transition.getName() );
      
       Collection<ExecutionContext> newContextList = new ArrayList<ExecutionContext>();
      
       //newExecutionContext.getContextInstance().createVariable(as, item, newToken);
       //
      
       for (Iterator iterator = activities.iterator(); iterator.hasNext();) {
      
       ITPItem item = (ITPItem) iterator.next();
       log.debug( "item id:" + item.getId() );
      
       final Token newToken = new Token(rootToken, "ITP" + item.getId()); //TODO -lp
       newToken.setTerminationImplicit(true);
       executionContext.getJbpmContext().getSession().save(newToken);
       final ExecutionContext newExecutionContext = new ExecutionContext(newToken);
       newExecutionContext.getContextInstance().createVariable(
       ASSIGNMENT_RESPONSIBLE_EXPRESSION_JBPM_CONTEXT_IDENTIFIER,
       item.getResponsibleExpression(),
       newToken);
       log.debug("execute getVariable:" + newExecutionContext.getVariable(ASSIGNMENT_RESPONSIBLE_EXPRESSION_JBPM_CONTEXT_IDENTIFIER));
      
       newContextList.add(newExecutionContext);
       }
      
       // Now, let each new token leave the node.
       //
       for (Iterator<ExecutionContext> iterator = newContextList.iterator(); iterator.hasNext();) {
       ExecutionContext context = iterator.next();
       //go via transition
       node.leave(context, transition);
      
       }
       }
      
       log.debug("leaving node" );
       executionContext.leaveNode();
       }
      
       /**
       * TODO the test version needs to be mocked somehow
       * @param id
       * @return
       */
       public Collection<ITPItem> getActivities( long id) {
      
       log.trace("getActivities");
       Collection<ITPItem> items = new ArrayList<ITPItem>();
      
       ITPItem itp1 = new ITPItem();
       itp1.setId(1);
       itp1.setResponsibleExpression("hello1");
       ITPItem itp2 = new ITPItem();
       itp2.setId(2);
       itp2.setResponsibleExpression("hello2");
       ITPItem itp3 = new ITPItem();
       itp3.setId(3);
       itp3.setResponsibleExpression("hello3");
      
       items.add(itp1);
       items.add(itp2);
       items.add(itp3);
       return items;
      
       }
       }
       }
      



      -lp

        • 1. Re: Variable missing in assignment handler
          kukeltje

          I'll have a look later today, but one compliment in advance. It looks like a unittest that can be run with just a copy/paste (ok, a quickfix for the imports). Everything IN it... great...

          • 2. Re: Variable missing in assignment handler
            lpiccoli

            Further testing reveals that there are NO variables at all available in the Assignment handler.

            I am using Jbpm3.2.6

            
            public void assign(Assignable assignable, ExecutionContext executionContext) {
            
            Map<Token, TokenVariableMap> tokenVariableMaps = executionContext.getContextInstance().getTokenVariableMaps();
            assertNotNull("tokenmap shouldnt be null", tokenVariableMaps);
            
            



            Q. Since the ExecutionContext has only one method to retrieve variable ExecutionContext.getVariable() which seem to be based on the root token. How can a local variable bound to a token be retrieved from the ExecutionContext?

            any help most appreciated.



            many thanks

            -lp

            • 3. Re: Variable missing in assignment handler
              lpiccoli

              i think i may have found the issue with why my sub process has now variable instances.

              it seems that the variables need to be 'explicit' copied into the new process definition. The following snippet now correctly sets the variable in the new sub process.

              
              <process-state name='Sub Process'>
               <variable name='itp.id' access='read' mapped-name='itp.id' />
               <sub-process name='sub'/>"
              
              


              From the docs
              http://docs.jboss.com/jbpm/v3.2/userguide/html_single/#graphorientedprogramming

              it describes this exact process.

              10.8. Process composition
              In general, When a subprocess is started, all variables with read access are read from the super process and fed into the newly created sub process before the signal is given to leave the start state. When the sub process instances is finished, all the variables with write access will be copied from the sub process to the super process. The mapped-name attribute of the variable element allows you to specify the variable name that should be used in the sub process.


              However i dont want to 'explicitly' copy all my variables over to the new process. I am hoping there is an 'implicit' copy that i have not found yet.

              I have not seen this functionality in any of the unit tests i have been viewing, so any help is appreciated.

              -lp

              • 4. Re: Variable missing in assignment handler
                kukeltje

                Implicit is possible by extending and overriding the subprocess node implementation.

                • 5. Re: Variable missing in assignment handler
                  nizzy

                  "Implicit is possible by extending and overriding the subprocess node implementation."
                  --
                  Why are super-process variables not by default copied into the sub-process?

                  Am I missing something or is this not the behaviour what most people require, should it not be included as part of Jboss JBPM core functionality?

                  It may just be the way we use JBPM but I always want the super-process variables to be available to the sub-process, without having to worry about overriding SubprocessNode implementation!

                  Indeed it is counter-intuitive that they are not available.

                  Defining them in the process definition is IMO not the best way to achieve this; since a dependecny is introduced between the business logic layer and the process definition xml.

                  I assume there is a very good reason for this limitation?

                  Could someone enlighten me please!!!!

                  • 6. Re: Variable missing in assignment handler
                    kukeltje

                    Well, most likely not everybody (we've not had many requests for this) wants ALL variables copied automatically. So it is the way it is because it just is. Don't think that there is a specific reason for it. Other than that maybe many users use a domain model and have all info in there and just pass on an id to that domain model

                    • 7. Re: Variable missing in assignment handler
                      nizzy

                      Yes all we pass is ids for the domain model objects, however to have to pre-define those in a process definition is restrictive and complicates code updates.

                      However obviously I will have to live with it, I'm surpirsed not many people have requested a change.

                      Thanks for the response!

                      • 8. Re: Variable missing in assignment handler
                        nizzy

                        Hi,

                        I have successfully copied the super-process variables into the sub-process using the "variable" element in the process definition.

                        Chapter 18 Table 18.8 of the User Guide states

                        "variable - specifies how data should be copied from the super process to the sub process at the start and from the sub process to the super process upon completion of the sub process."

                        In the process state (sub-process) below, I have "em-ref" and "card-id" copied from super-process to sub-process with no problems. However I also took it to mean that "barcode-data" would be copied from sub-process back to super-process on sub-process completion.

                        This is not the behaviour I'm experiencing!
                        Have I misunderstood the spec?

                         <process-state name="Encode Barcode App">
                         <sub-process name="encodebarcodeapp"></sub-process>
                         <variable name="em-ref" access="read" />
                         <variable name="card-id" access="read" />
                         <variable name="barcode-data" access="read" />
                         <transition to="join1"></transition>
                         </process-state>
                        


                        Any help appreciated!

                        • 9. Re: Variable missing in assignment handler
                          nizzy

                          Hi All,

                          I think the problem was that I had to mark the variables to be written back to the super-process as;

                          <variable name="barcode-data" access="read,write" />