3 Replies Latest reply on May 22, 2007 1:23 PM by estaub

    Confused about variable and token association

    mteira

      Hello. I'm just starting with jBPM and doing some JUnits to test if I understand how the thing works. I'm confused about how execution contexts and variables are bound.

      For example, I'm trying to setup a simple task with a fork node, the two branches of the fork are implementing a loop with two nodes: One that increments a variable and another one (a Condition node) where the value of that variable is compared with a maximum value and so , choose a transition to the previous node (the incrementer) or go to the join node.

      My idea was to use variables binded to the execution context, and so, taking in mind that any branch should use it's own token, I could share the same name for the counter variable in the two branches without interaction. But what I get is that it seems that the created variable is always bound to the root execution context. At least, the nodes in the two branches seem to see the same variable.

      I try to use token bound variables using:

      ContextInstance ci = executionContext.getContextInstance();
      logger.debug("Executing under Token: " + executionContext.getToken());
      ci.setVariable(varName, new Integer(0), executionContext.getToken());
      


      That log shows a different Token when executing from different branches, as:
      DEBUG IncrementTokenVar : Executing under Token: Token(/to_incr1)

      and
      DEBUG IncrementTokenVar : Executing under Token: Token(/to_incr2)


      But the log from VariableContainer always shows:
      VariableContainer : update variable 'localCounter' in 'TokenVariableMap914f6a' to value 'n'


      Always showing 'TokenVariableMap914f6a' that I suppose is the root token variable map.

      I'm also confused because ContextInstance has a method:
      public Object getLocalVariable(String name, Token token);


      but there's not a complementary one as setLocalVariable or so.

      What I'm doing wrong?

      Regards.



      This is how my ActionHandler for the increment nodes action looks:

      public class IncrementTokenVar implements ActionHandler {
       static private Logger logger = Logger.getLogger(IncrementTokenVar.class);
       private static final long serialVersionUID = 1L;
       private String varName;
       public void execute(ExecutionContext executionContext) throws Exception {
       ContextInstance ci = executionContext.getContextInstance();
       logger.debug("Executing under Token: " + executionContext.getToken());
       if (!ci.hasVariable(varName, executionContext.getToken())) {
       ci.setVariable(varName, new Integer(0), executionContext.getToken());
       }
       Integer counter = (Integer) ci.getVariable(varName, executionContext.getToken());
       Integer increment = new Integer(counter.intValue() + 1);
       ci.setVariable(varName, increment, executionContext.getToken());
       }
      
      }
      


      And this is the process XML:

      <?xml version="1.0" encoding="UTF-8"?>
      
      <process-definition
       xmlns="urn:jbpm.org:jpdl-3.1" name="Forker">
      
       <!-- start transictions unconditionally to fork1 -->
       <start-state name="start">
       <transition name="to_fork" to="fork1"></transition>
       </start-state>
      
       <!-- Forks the process into two branches, doing the same -->
       <fork name="fork1">
       <transition name="to_incr1" to="incr1"></transition>
       <transition name="to_incr2" to="incr2"></transition>
       </fork>
      
       <!-- The first node of the branch where we increment the variable
       injected as varName using an ActionHandler-->
       <node name="incr1">
       <event type="node-enter">
       <action name="incrAction" class="com.jbay.IncrementTokenVar">
       <varName>localCounter</varName>
       </action>
       </event>
       <transition name="to_repeat1" to="repeat1"></transition>
       </node>
      
       <!-- The second node of the branch, where we decide to go to the
       previous node or exit to the join1, if the localCounter is greater
       or equal than 10 -->
       <decision name="repeat1">
       <transition name="repeatLoop" to="incr1"></transition>
       <transition name="endLoop1" to="join1">
       <condition>#{localCounter >= 10}</condition>
       </transition>
       </decision>
      
       <!-- The second branch, parallel to the first one -->
       <node name="incr2">
       <event type="node-enter">
       <action name="incrAction" class="com.jbay.IncrementTokenVar">
       <varName>localCounter</varName>
       </action>
       </event>
       <transition name="to_repeat2" to="repeat2"></transition>
       </node>
       <decision name="repeat2">
       <transition name="repeatLoop" to="incr2"></transition>
       <transition name="endLoop" to="join1">
       <condition>#{localCounter >= 10}</condition>
       </transition>
       </decision>
      
       <!-- Join the two branches -->
       <join name="join1">
       <transition name="to_end" to="end1"></transition>
       </join>
      
      
       <!-- End the process -->
       <end-state name="end1"></end-state>
      
      </process-definition>
      
      



        • 1. Re: Confused about variable and token association
          mteira

          Hello again.
          I think that I've found the problem. It's how the variable is initialized in the ActioHandler:

          if (!ci.hasVariable(varName, executionContext.getToken())) {
           ci.setVariable(varName, new Integer(0), executionContext.getToken());
          }


          ContextInstance.setVariable(String name, Object value, Token token) resolves the TokenVariableMap using getOrCreateTokenVariableMap, that delegates on the parent Token when the var doesn't exist on the current token:

          // if the given token has a variable map
           TokenVariableMap tokenVariableMap = null;
           if ( (tokenVariableMaps!=null)
           && (tokenVariableMaps.containsKey(token))
           ) {
           tokenVariableMap = (TokenVariableMap) tokenVariableMaps.get(token);
          
           } else if (!token.isRoot()) {
           tokenVariableMap = getOrCreateTokenVariableMap(token.getParent());
          
           } else {
           tokenVariableMap = createTokenVariableMap(token);
           }
          


          So, the only way to force a variable to be created in the current Token is to use the function:
          public void createVariable(String name, Object value, Token token)
          that uses createTokenVariableMap(Token token) to do the work.

          So, using ContextInstance.setVariable(String name, Object value, Token token) is always delegating into the parent context when the variable name doesn't exist in the current Token tokenVariableMap.

          I find this a little confusing. Is it the expected behaviour?

          Regards.


          • 2. Re: Confused about variable and token association
            clandestino_bgd

            Dear community,
            Since I have used several hours trying to understand why my invocation of:

            void ContextInstance.setVariable(String variableName, Object value, Token token);

            does not set variable in token scope, but in process scope instead of that.
            it seemed to me logical that it will be created in token scope if it does not exist.

            But it WON'T and people should be careful.

            User Guide indeed says:


            When a non-existing variable is set on a token, the variable is created on the root-token. This means that each variable has by default process scope. To make a variable token-local, you have to create it explicitly with:

            ContextInstance.createVariable(String name, Object value, Token token);


            Regards
            Milan

            • 3. Re: Confused about variable and token association

              Thanks!

              All you folks who've been learning about ForEachForkActionHandler in the last couple of weeks - nota bene!

              -Ed Staub