1 Reply Latest reply on Jun 11, 2012 5:24 AM by marbu

    methods getConstraint and setConstraint in Split nodes don't work.

    marbu

      Hello everyone! I'm developing my first project with jBPM, and stumbled with a problem I can't find a solution too. I hope you folks can lend me some help

       

      I have a requirement to be able to change the constraint of connections coming out of a Split node (splitting XOR gateway) at runtime. I have seen that this type of nodes have a setter and getter method for the constraints. However, when I use this methods on a Split node object, it seems that they do nothing!

       

      For instance, if I load the BPMN2-ExclusiveSplit.bpmn2 example [https://github.com/droolsjbpm/jbpm/blob/master/jbpm-examples/src/main/resources/junit/BPMN2-ExclusiveSplit.bpmn2], and create and start a new instance of the process passing it parameters x="First", y="Second" (as in the test class []), everything executes just fine.

       

      public void testExclusiveSplit() throws Exception {
              KnowledgeBase kbase = createKnowledgeBase("BPMN2-ExclusiveSplit.bpmn2");
              StatefulKnowledgeSession ksession = createKnowledgeSession(kbase);
              ksession.getWorkItemManager().registerWorkItemHandler("Email", new SystemOutWorkItemHandler());
              Map<String, Object> params = new HashMap<String, Object>();
              params.put("x", "First");
              params.put("y", "Second");
              ProcessInstance processInstance = ksession.startProcess("com.sample.test", params);
              assertTrue(processInstance.getState() == ProcessInstance.STATE_COMPLETED);
          }
      

       

       

      Notice the constraints associated with the gateway in the bpmn2 file mentioned before:

       

      <bpmn2:exclusiveGateway id="_D5523729-B575-4A2A-A7AC-D600586865CD" drools:bgcolor="#ffffff" drools:dg="" name="Split" gatewayDirection="Diverging">
            <bpmn2:incoming>_50273E3F-9A4D-4C60-957F-5FE65E6F2609</bpmn2:incoming>
            <bpmn2:outgoing>_DF37882A-350E-43F9-B776-47CC2AFF1CFB</bpmn2:outgoing>
            <bpmn2:outgoing>_1AECF2F4-884D-49F7-AEF1-6512E7495D80</bpmn2:outgoing>
          </bpmn2:exclusiveGateway>
       <bpmn2:sequenceFlow id="_50273E3F-9A4D-4C60-957F-5FE65E6F2609" sourceRef="_CDDCB4BD-7E33-4124-B9E8-7A5276160413" targetRef="_D5523729-B575-4A2A-A7AC-D600586865CD"/>
      
          <bpmn2:sequenceFlow id="_DF37882A-350E-43F9-B776-47CC2AFF1CFB" name="First" sourceRef="_D5523729-B575-4A2A-A7AC-D600586865CD" targetRef="_FC5AEDEA-84F5-4222-A581-1EBF4376B3A7">
            <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression" id="_P8LaOLFQEeG9sYc2b7U_cA" language="http://www.java.com/java"><![CDATA[return x == "First";]]></bpmn2:conditionExpression>
          </bpmn2:sequenceFlow>
      
          <bpmn2:sequenceFlow id="_1AECF2F4-884D-49F7-AEF1-6512E7495D80" name="Second" sourceRef="_D5523729-B575-4A2A-A7AC-D600586865CD" targetRef="_427976FF-F304-42EE-82CC-D75D4FBE3376">
            <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression" id="_P8LaObFQEeG9sYc2b7U_cA" language="http://www.java.com/java"><![CDATA[return x == "Second";]]></bpmn2:conditionExpression>
          </bpmn2:sequenceFlow>
      

       

      However!! if I extract the Split node from my process definition, get it's constraints, and try to get all the attributes out of them:

       

              Node[] processNodes = jbpmProcessDefinition.getNodes();
              log.debug("detected "+jbpmProcessDefinition.getNodes().length+" jbpm states");
              
              for(Node node : processNodes){
                  
                  log.debug("processing node "+node);
                  if (node instanceof Split){
                      log.debug("    it's a split node");
                      Split splitNode = (Split) node; 
                      
                      log.debug("Split node has type: "+splitNode.getType());
                      
                      Map<ConnectionRef, Constraint> constraints = splitNode.getConstraints();
                      for(ConnectionRef conn : constraints.keySet()){
                          log.debug("constraint name: "+constraints.get(conn).getName());
                          log.debug("contraint type: "+constraints.get(conn).getType());
                          log.debug("constraint lang: "+constraints.get(conn).getDialect());
                          log.debug("node constraint: "+constraints.get(conn).getConstraint()+" on connection "+conn.getNodeId());
                      }
                 }
             }
      

       

      what I get is this output from the logger:

      18:54:16,204DEBUG [ProcessDefinition] it's a split node

       

      18:54:16,206DEBUG [ProcessDefinition] Split node has type: 2

       

      18:54:16,208DEBUG [ProcessDefinition] constraint name: Second

      18:54:16,208DEBUG [ProcessDefinition] contraint type: null

      18:54:16,209DEBUG [ProcessDefinition] constraint lang: java

      18:54:16,209DEBUG [ProcessDefinition] node constraint: null on connection 4

       

      18:54:16,210DEBUG [ProcessDefinition] constraint name: First

      18:54:16,210DEBUG [ProcessDefinition] contraint type: null

      18:54:16,210DEBUG [ProcessDefinition] constraint lang: java

      18:54:16,211DEBUG [ProcessDefinition] node constraint: null on connection 3

       

      As you can see: everything is loading just fine, but "apparently" the constraint is not being loaded!! Notice the "apparently", because the constraint is in fact getting loaded, as the process is capable of selecting the correct transition when it reaches the split node, and also: if i change the values of the parameters passed to the process, or the constraints itselves in the bpmn2 file, then jbpm complains when it reaches the split node that none of the constraints evaluates to true.

       

      org.jbpm.workflow.instance.WorkflowRuntimeException: [com.sample.test:1 - Split:2] -- XOR split could not find at least one valid outgoing connection for split Split
      

       

      And there's more! If i try to change the constraint in my process instance for a new one using the setConstraint method, then I get a similar behaviour. When I do this, if I then do a getConstraint(), I don't get a null value anymore: I get the constraint string that I have just set.

       

      So "apparently", the constraint has been updated. However, again, this is not true, as when I execute the process instance, the behaviour I obtain is the one of the constraints that are ironed into the bpmn2 file, and the constraints I set manually with setConstraint("..") are being simply ignored, they do not even override the old ones.

       

      if I do: connection.getConstraint()  --->   returns null (and SHOULD return ' return x == "First" ', as it is in the bpmn2 file)

      if AFTER that, I do: connection.setConstraint("return true;"); connection.getConstraint()   --->   returns 'return true;', as it would be expected

      if AFTER that the split node containing that constraint gets executed   --->  behaves as if the constraint were ' return x == "First" ', and it should't since the constraint has been changed

       

      So my conclusion is: these two methods are useless! But then, how can I access the ACTUAL constraint? and how can I change it at runtime?

       

      I found another post commenting that the fluent API has not been recently updated [https://community.jboss.org/message/629023#629023], and disrecomends it's usage. I don't know if I stumbled upon a failure in the API, but I hope there is a way of getting and setting the constrains at runtime, because I really need this feature!

       

      Thanks very much in advance for your kind help. Regards!

        • 1. Re: methods getConstraint and setConstraint in Split nodes don't work.
          marbu

          Ok, I made some more findings on my own.

           

          Seems that whenever a split node get parsed (in org.jbpm.compiler.ProcessBuilderImpl), for every constraint it contains, a new drools rule is created, with the constraint condition:

           

              private String createSplitRule(Process process,
                                             Connection connection,
                                             String constraint) {
                  return 
                      "rule \"RuleFlow-Split-" + process.getId() + "-" +
                          ((org.jbpm.workflow.core.Node) connection.getFrom()).getUniqueId() + "-" + 
                          ((org.jbpm.workflow.core.Node) connection.getTo()).getUniqueId() + "-" +
                          connection.getToType() + "\" \n" +
                      "      ruleflow-group \"DROOLS_SYSTEM\" \n" + 
                      "    when \n" + 
                      "      " + constraint + "\n" + 
                      "    then \n" +
                      "end \n\n";
              }
          

           

          Then, when the node gets later evaluated at runtime, (at org.jbpm.workflow.instance.node.SplitInstance) the constraints get evaluated, with an or.jbpm.process.instance.impl.RuleConstraintEvaluator. This evaluation returns true if the drools rule created at parse time is active.

           

          So, efectively, the setConstraint method does absolutely nothing, as the evaluation only depends on the drools rule, and this doesn't get modified at all.

           

           

          HOWEVER! notice how in the createSplitRule method, it actually uses the text in the constraint to add it as a drools rule. This text seems that should in fact be present in the constraints in the split node, or the call to evaluate done in ProcessBuilderImpl could not work:

           

              private void generateRules(Node[] nodes, Process process, StringBuffer builder) {
                  for ( int i = 0; i < nodes.length; i++ ) {
                      if ( nodes[i] instanceof Split ) {
                          Split split = (Split) nodes[i];
                          if ( split.getType() == Split.TYPE_XOR || split.getType() == Split.TYPE_OR ) {
                              for ( Connection connection : split.getDefaultOutgoingConnections() ) {
                                  Constraint constraint = split.getConstraint( connection );
                                  if ( "rule".equals( constraint.getType() ) ) {
                                      builder.append( createSplitRule( process,
                                                                       connection,
                                                                       split.getConstraint( connection ).getConstraint() ) );
                                  }
                              }
                          }
          
          

           

          This connection, I reckon, is set at parse time in org.jbpm.bpmn2.xml.ProcessHandler, and it's obtained from a SequenceFlow object, wich is a direct mapping of a bpmn2 sequence flow (and completely inaccessible once the parsing is done, I assume).

           

                      for (SequenceFlow connection: connections) {
          
                          [ ... ]
          
                          if (source instanceof Split) {
                              Split split = (Split) source;
                              Constraint constraint = new ConstraintImpl();
          
                              [ ... ]
          
                              if (connection.getExpression() != null) {
                                  constraint.setConstraint(connection.getExpression());
                              }
          
                              [ ... ]
          
                              split.addConstraint(
                                  new ConnectionRef(target.getId(), NodeImpl.CONNECTION_DEFAULT_TYPE),
                                  constraint);
                          }
          

           

          What I still don't undestand is how the getConstraint() method at Split is returning null, if it seems reasonable that it should at least contain the constraint text (or else, the drools rule wouldn't get created, and the constraint wouldn't be evaluated at runtime).

           

          Please, any help would be appreciated.