Skip to content

Commit

Permalink
BZ807640 - Conditional sequence flow is not supported by jBPM engine
Browse files Browse the repository at this point in the history
  • Loading branch information
mswiderski committed Jun 13, 2012
1 parent 4e36f76 commit e19270b
Show file tree
Hide file tree
Showing 13 changed files with 373 additions and 45 deletions.
65 changes: 42 additions & 23 deletions jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/ProcessHandler.java
Expand Up @@ -298,30 +298,18 @@ public static void linkConnections(NodeContainer nodeContainer, List<SequenceFlo
target, NodeImpl.CONNECTION_DEFAULT_TYPE);
result.setMetaData("bendpoints", connection.getBendpoints());
result.setMetaData("UniqueId", connection.getId());
if (source instanceof Split) {
Split split = (Split) source;
Constraint constraint = new ConstraintImpl();
String defaultConnection = (String) split.getMetaData("Default");
if (defaultConnection != null && defaultConnection.equals(connection.getId())) {
constraint.setDefault(true);
}
if (connection.getName() != null) {
constraint.setName(connection.getName());
} else {
constraint.setName("");
}
if (connection.getType() != null) {
constraint.setType(connection.getType());
} else {
constraint.setType("code");
}
if (connection.getLanguage() != null) {
constraint.setDialect(connection.getLanguage());

if (System.getProperty("jbpm.enable.multi.con") != null){
NodeImpl nodeImpl = (NodeImpl) source;
Constraint constraint = buildConstraint(connection, nodeImpl);
if (constraint != null) {
nodeImpl.addConstraint(new ConnectionRef(target.getId(), NodeImpl.CONNECTION_DEFAULT_TYPE),
constraint);
}
if (connection.getExpression() != null) {
constraint.setConstraint(connection.getExpression());
}
constraint.setPriority(connection.getPriority());

} else if (source instanceof Split) {
Split split = (Split) source;
Constraint constraint = buildConstraint(connection, split);
split.addConstraint(
new ConnectionRef(target.getId(), NodeImpl.CONNECTION_DEFAULT_TYPE),
constraint);
Expand Down Expand Up @@ -632,5 +620,36 @@ private void assignLanes(NodeContainer nodeContainer, Map<String, String> laneMa
}
}
}

private static Constraint buildConstraint(SequenceFlow connection, NodeImpl node) {
if (connection.getExpression() == null) {
return null;
}

Constraint constraint = new ConstraintImpl();
String defaultConnection = (String) node.getMetaData("Default");
if (defaultConnection != null && defaultConnection.equals(connection.getId())) {
constraint.setDefault(true);
}
if (connection.getName() != null) {
constraint.setName(connection.getName());
} else {
constraint.setName("");
}
if (connection.getType() != null) {
constraint.setType(connection.getType());
} else {
constraint.setType("code");
}
if (connection.getLanguage() != null) {
constraint.setDialect(connection.getLanguage());
}
if (connection.getExpression() != null) {
constraint.setConstraint(connection.getExpression());
}
constraint.setPriority(connection.getPriority());

return constraint;
}

}
19 changes: 19 additions & 0 deletions jbpm-bpmn2/src/test/java/org/jbpm/bpmn2/SimpleBPMNProcessTest.java
Expand Up @@ -2533,6 +2533,25 @@ public void afterProcessStarted(ProcessStartedEvent event) {
System.clearProperty("jbpm.enable.multi.con");
}

/**
* Conditional sequence flow is supported in Guvnor BPMN designer (even in minimal perspective), but jBPM engine cannot
* parse this definition:
* "This type of node cannot have more than one outgoing connection!"
*/
public void testConditionalFlow() throws Exception {
System.setProperty("jbpm.enable.multi.con", "true");
String processId = "designer.conditional-flow";

KnowledgeBase kbase = createKnowledgeBase("BPMN2-ConditionalFlowWithoutGateway.bpmn2");
StatefulKnowledgeSession ksession = createKnowledgeSession(kbase);

WorkflowProcessInstance wpi = (WorkflowProcessInstance) ksession.startProcess(processId);

assertProcessInstanceCompleted(wpi.getId(), ksession);
assertNodeTriggered(wpi.getId(), "start", "script", "end1");
System.clearProperty("jbpm.enable.multi.con");
}

public void testMultipleInOutgoingSequenceFlowsDisable() throws Exception {

try {
Expand Down
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.omg.org/bpmn20" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:drools="http://www.jboss.org/drools" id="_OSAkwHM-EeGROt7AmBKBUg" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" targetNamespace="http://www.omg.org/bpmn20">
<bpmn2:itemDefinition id="_xItem" structureRef="Integer"/>
<bpmn2:process id="designer.conditional-flow" drools:packageName="org.jboss.qa.brms.jbpm.functional.bpmn2.designer" name="conditional-flow" isExecutable="true">
<bpmn2:property id="x" itemSubjectRef="_xItem"/>
<bpmn2:startEvent id="_CF9831F9-AFE1-4F57-86C4-662C73090C42" drools:bgcolor="#ffffff" name="start">
<bpmn2:outgoing>_1897CC4D-9026-42F4-A3E9-663EBD1B00F3</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:scriptTask id="_E4D6AD1A-F7EC-445A-B43E-12566A22221E" name="script" scriptFormat="http://www.java.com/java">
<bpmn2:incoming>_1897CC4D-9026-42F4-A3E9-663EBD1B00F3</bpmn2:incoming>
<bpmn2:outgoing>_99179D07-2AD3-4F1D-9851-11A6F92179C5</bpmn2:outgoing>
<bpmn2:outgoing>_53A779A0-66A0-4A12-BD82-6CCDDE3D5CE9</bpmn2:outgoing>
<bpmn2:outgoing>_0DD13F4B-C5E8-4AA3-84EE-B98461099FC4</bpmn2:outgoing>
<bpmn2:script><![CDATA[kcontext.setVariable("x", 5);]]></bpmn2:script>
</bpmn2:scriptTask>
<bpmn2:endEvent id="_5DC17F4C-389B-4150-A034-39D599347FB2" drools:bgcolor="#ffffff" name="end1">
<bpmn2:incoming>_99179D07-2AD3-4F1D-9851-11A6F92179C5</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:endEvent id="_F5220DC7-5A9A-460C-8125-FB17947EA334" drools:bgcolor="#ffffff" name="end2">
<bpmn2:incoming>_53A779A0-66A0-4A12-BD82-6CCDDE3D5CE9</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:endEvent id="_40A0F353-CEA3-4EE9-850C-260D1D48BB13" drools:bgcolor="#ffffff" name="end3">
<bpmn2:incoming>_0DD13F4B-C5E8-4AA3-84EE-B98461099FC4</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:sequenceFlow id="_1897CC4D-9026-42F4-A3E9-663EBD1B00F3" sourceRef="_CF9831F9-AFE1-4F57-86C4-662C73090C42" targetRef="_E4D6AD1A-F7EC-445A-B43E-12566A22221E"/>
<bpmn2:sequenceFlow id="_99179D07-2AD3-4F1D-9851-11A6F92179C5" sourceRef="_E4D6AD1A-F7EC-445A-B43E-12566A22221E" targetRef="_5DC17F4C-389B-4150-A034-39D599347FB2">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression" id="_OSAkwXM-EeGROt7AmBKBUg" language="http://www.java.com/java"><![CDATA[return x > 1;]]></bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="_53A779A0-66A0-4A12-BD82-6CCDDE3D5CE9" sourceRef="_E4D6AD1A-F7EC-445A-B43E-12566A22221E" targetRef="_F5220DC7-5A9A-460C-8125-FB17947EA334">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression" id="_OSAkwnM-EeGROt7AmBKBUg" language="http://www.java.com/java"><![CDATA[return x > 10;]]></bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="_0DD13F4B-C5E8-4AA3-84EE-B98461099FC4" sourceRef="_E4D6AD1A-F7EC-445A-B43E-12566A22221E" targetRef="_40A0F353-CEA3-4EE9-850C-260D1D48BB13">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression" id="_OSAkw3M-EeGROt7AmBKBUg" language="http://www.java.com/java"><![CDATA[return x > 100;]]></bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
</bpmn2:process>
<bpmndi:BPMNDiagram id="_OSAkxHM-EeGROt7AmBKBUg">
<bpmndi:BPMNPlane id="_OSAkxXM-EeGROt7AmBKBUg" bpmnElement="designer.conditional-flow">
<bpmndi:BPMNShape id="_OSAkxnM-EeGROt7AmBKBUg" bpmnElement="_CF9831F9-AFE1-4F57-86C4-662C73090C42">
<dc:Bounds height="30.0" width="30.0" x="135.0" y="168.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_OSAkx3M-EeGROt7AmBKBUg" bpmnElement="_E4D6AD1A-F7EC-445A-B43E-12566A22221E">
<dc:Bounds height="80.0" width="100.0" x="270.0" y="143.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_OSAkyHM-EeGROt7AmBKBUg" bpmnElement="_5DC17F4C-389B-4150-A034-39D599347FB2">
<dc:Bounds height="28.0" width="28.0" x="493.0" y="59.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_OSAkyXM-EeGROt7AmBKBUg" bpmnElement="_F5220DC7-5A9A-460C-8125-FB17947EA334">
<dc:Bounds height="28.0" width="28.0" x="493.0" y="167.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_OSAkynM-EeGROt7AmBKBUg" bpmnElement="_40A0F353-CEA3-4EE9-850C-260D1D48BB13">
<dc:Bounds height="28.0" width="28.0" x="493.0" y="268.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="_OSAky3M-EeGROt7AmBKBUg" bpmnElement="_1897CC4D-9026-42F4-A3E9-663EBD1B00F3">
<di:waypoint xsi:type="dc:Point" x="150.0" y="183.0"/>
<di:waypoint xsi:type="dc:Point" x="320.0" y="183.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="_OSAkzHM-EeGROt7AmBKBUg" bpmnElement="_99179D07-2AD3-4F1D-9851-11A6F92179C5">
<di:waypoint xsi:type="dc:Point" x="320.0" y="183.0"/>
<di:waypoint xsi:type="dc:Point" x="430.0" y="79.0"/>
<di:waypoint xsi:type="dc:Point" x="507.0" y="73.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="_OSAkzXM-EeGROt7AmBKBUg" bpmnElement="_53A779A0-66A0-4A12-BD82-6CCDDE3D5CE9">
<di:waypoint xsi:type="dc:Point" x="320.0" y="183.0"/>
<di:waypoint xsi:type="dc:Point" x="507.0" y="181.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="_OSAkznM-EeGROt7AmBKBUg" bpmnElement="_0DD13F4B-C5E8-4AA3-84EE-B98461099FC4">
<di:waypoint xsi:type="dc:Point" x="320.0" y="183.0"/>
<di:waypoint xsi:type="dc:Point" x="432.0" y="284.0"/>
<di:waypoint xsi:type="dc:Point" x="507.0" y="282.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>
Expand Up @@ -20,6 +20,7 @@
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

Expand All @@ -36,6 +37,7 @@
import org.drools.compiler.ParserError;
import org.drools.compiler.ProcessBuilder;
import org.drools.compiler.ProcessLoadError;
import org.drools.compiler.ReturnValueDescr;
import org.drools.definition.process.Connection;
import org.drools.definition.process.Node;
import org.drools.definition.process.NodeContainer;
Expand All @@ -49,6 +51,7 @@
import org.jbpm.compiler.xml.ProcessSemanticModule;
import org.jbpm.compiler.xml.XmlProcessReader;
import org.jbpm.compiler.xml.processes.RuleFlowMigrator;
import org.jbpm.process.builder.MultiConditionalSequenceFlowNodeBuilder;
import org.jbpm.process.builder.ProcessBuildContext;
import org.jbpm.process.builder.ProcessNodeBuilder;
import org.jbpm.process.builder.ProcessNodeBuilderRegistry;
Expand All @@ -62,11 +65,15 @@
import org.jbpm.process.core.impl.ProcessImpl;
import org.jbpm.process.core.validation.ProcessValidationError;
import org.jbpm.process.core.validation.ProcessValidator;
import org.jbpm.process.instance.impl.ReturnValueConstraintEvaluator;
import org.jbpm.process.instance.impl.RuleConstraintEvaluator;
import org.jbpm.ruleflow.core.RuleFlowProcess;
import org.jbpm.ruleflow.core.validation.RuleFlowProcessValidator;
import org.jbpm.workflow.core.Constraint;
import org.jbpm.workflow.core.impl.ConnectionRef;
import org.jbpm.workflow.core.impl.ConstraintImpl;
import org.jbpm.workflow.core.impl.DroolsConsequenceAction;
import org.jbpm.workflow.core.impl.NodeImpl;
import org.jbpm.workflow.core.impl.WorkflowProcessImpl;
import org.jbpm.workflow.core.node.ConstraintTrigger;
import org.jbpm.workflow.core.node.EventNode;
Expand Down Expand Up @@ -226,6 +233,13 @@ private void processNodes(
buildContexts( (ContextContainer) node,
context );
}

if (System.getProperty("jbpm.enable.multi.con") != null) {
builder = ProcessNodeBuilderRegistry.INSTANCE.getNodeBuilder( NodeImpl.class );
if (builder != null) {
builder.build(process, processDescr, context, node);
}
}
}
}

Expand Down
@@ -0,0 +1,90 @@
/*
* Copyright 2012 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jbpm.process.builder;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.drools.compiler.ReturnValueDescr;
import org.drools.definition.process.Connection;
import org.drools.definition.process.Node;
import org.drools.definition.process.Process;
import org.drools.lang.descr.ProcessDescr;
import org.jbpm.process.builder.dialect.ProcessDialect;
import org.jbpm.process.builder.dialect.ProcessDialectRegistry;
import org.jbpm.process.instance.impl.ReturnValueConstraintEvaluator;
import org.jbpm.process.instance.impl.RuleConstraintEvaluator;
import org.jbpm.workflow.core.Constraint;
import org.jbpm.workflow.core.impl.ConnectionRef;
import org.jbpm.workflow.core.impl.ConstraintImpl;
import org.jbpm.workflow.core.impl.NodeImpl;
import org.jbpm.workflow.core.node.Split;

public class MultiConditionalSequenceFlowNodeBuilder implements ProcessNodeBuilder {

public void build(Process process, ProcessDescr processDescr,
ProcessBuildContext context, Node node) {

Map<ConnectionRef, Constraint> constraints = ((NodeImpl) node).getConstraints();

// exclude split as it is handled with separate builder and nodes with non conditional sequence flows
if (node instanceof Split || constraints.size() == 0) {
return;
}

// we need to clone the map, so we can update the original while iterating.
Map<ConnectionRef, Constraint> map = new HashMap<ConnectionRef, Constraint>( constraints );
for ( Iterator<Map.Entry<ConnectionRef, Constraint>> it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<ConnectionRef, Constraint> entry = it.next();
ConnectionRef connection = entry.getKey();
ConstraintImpl constraint = (ConstraintImpl) entry.getValue();
Connection outgoingConnection = null;
for (Connection out: ((NodeImpl) node).getDefaultOutgoingConnections()) {
if (out.getToType().equals(connection.getToType())
&& out.getTo().getId() == connection.getNodeId()) {
outgoingConnection = out;
}
}
if (outgoingConnection == null) {
throw new IllegalArgumentException("Could not find outgoing connection");
}
if ( "rule".equals( constraint.getType() )) {
RuleConstraintEvaluator ruleConstraint = new RuleConstraintEvaluator();
ruleConstraint.setDialect( constraint.getDialect() );
ruleConstraint.setName( constraint.getName() );
ruleConstraint.setPriority( constraint.getPriority() );
ruleConstraint.setDefault( constraint.isDefault() );
((NodeImpl) node).setConstraint( outgoingConnection, ruleConstraint );
} else if ( "code".equals( constraint.getType() ) ) {
ReturnValueConstraintEvaluator returnValueConstraint = new ReturnValueConstraintEvaluator();
returnValueConstraint.setDialect( constraint.getDialect() );
returnValueConstraint.setName( constraint.getName() );
returnValueConstraint.setPriority( constraint.getPriority() );
returnValueConstraint.setDefault( constraint.isDefault() );
((NodeImpl) node).setConstraint( outgoingConnection, returnValueConstraint );

ReturnValueDescr returnValueDescr = new ReturnValueDescr();
returnValueDescr.setText( constraint.getConstraint() );

ProcessDialect dialect = ProcessDialectRegistry.getDialect( constraint.getDialect() );
dialect.getReturnValueEvaluatorBuilder().build( context, returnValueConstraint, returnValueDescr, (NodeImpl) node );
}
}

}

}
Expand Up @@ -4,6 +4,7 @@
import java.util.Map;

import org.drools.definition.process.Node;
import org.jbpm.workflow.core.impl.NodeImpl;
import org.jbpm.workflow.core.node.ActionNode;
import org.jbpm.workflow.core.node.CompositeContextNode;
import org.jbpm.workflow.core.node.EndNode;
Expand Down Expand Up @@ -53,6 +54,8 @@ public ProcessNodeBuilderRegistry() {
new EventBasedNodeBuilder() );
register( StateNode.class,
new EventBasedNodeBuilder() );
register( NodeImpl.class,
new MultiConditionalSequenceFlowNodeBuilder() );
}

public void register(Class< ? extends Node> cls,
Expand All @@ -64,4 +67,8 @@ public void register(Class< ? extends Node> cls,
public ProcessNodeBuilder getNodeBuilder(Node node) {
return this.registry.get( node.getClass() );
}

public ProcessNodeBuilder getNodeBuilder(Class< ? extends Node> cls) {
return this.registry.get( cls );
}
}
Expand Up @@ -18,12 +18,11 @@

import org.drools.definition.process.Connection;
import org.jbpm.workflow.core.Constraint;
import org.jbpm.workflow.instance.node.SplitInstance;
import org.jbpm.workflow.instance.NodeInstance;

public interface ConstraintEvaluator extends Constraint {

// TODO: make this work for more than only splits
public boolean evaluate(SplitInstance instance,
public boolean evaluate(NodeInstance instance,
Connection connection,
Constraint constraint);
}
Expand Up @@ -25,7 +25,9 @@
import org.drools.spi.CompiledInvoker;
import org.drools.spi.ProcessContext;
import org.drools.spi.Wireable;
import org.jbpm.process.instance.ProcessInstance;
import org.jbpm.workflow.core.Constraint;
import org.jbpm.workflow.instance.NodeInstance;
import org.jbpm.workflow.instance.node.SplitInstance;

/**
Expand Down Expand Up @@ -118,12 +120,12 @@ public ReturnValueEvaluator getReturnValueEvaluator() {
return this.evaluator;
}

public boolean evaluate(SplitInstance instance,
public boolean evaluate(NodeInstance instance,
Connection connection,
Constraint constraint) {
Object value;
try {
ProcessContext context = new ProcessContext(instance.getProcessInstance().getKnowledgeRuntime());
ProcessContext context = new ProcessContext(((ProcessInstance)instance.getProcessInstance()).getKnowledgeRuntime());
context.setNodeInstance( instance );
value = this.evaluator.evaluate( context );
} catch ( Exception e ) {
Expand Down

0 comments on commit e19270b

Please sign in to comment.