/**
* Copyright 2010 Red Hat, Inc. and/or its affiliates.
*
* 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;
import static org.jbpm.process.test.NodeCreator.connect;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import org.drools.core.process.core.datatype.impl.type.ObjectDataType;
import org.jbpm.process.core.ContextContainer;
import org.jbpm.process.core.context.exception.CompensationHandler;
import org.jbpm.process.core.context.exception.CompensationScope;
import org.jbpm.process.core.context.variable.Variable;
import org.jbpm.process.core.event.EventFilter;
import org.jbpm.process.core.event.EventTypeFilter;
import org.jbpm.process.core.event.NonAcceptingEventTypeFilter;
import org.jbpm.process.instance.impl.Action;
import org.jbpm.process.test.NodeCreator;
import org.jbpm.process.test.TestWorkItemHandler;
import org.jbpm.ruleflow.core.RuleFlowProcess;
import org.jbpm.test.util.AbstractBaseTest;
import org.jbpm.workflow.core.DroolsAction;
import org.jbpm.workflow.core.Node;
import org.jbpm.workflow.core.impl.DroolsConsequenceAction;
import org.jbpm.workflow.core.node.ActionNode;
import org.jbpm.workflow.core.node.BoundaryEventNode;
import org.jbpm.workflow.core.node.CompositeContextNode;
import org.jbpm.workflow.core.node.CompositeNode;
import org.jbpm.workflow.core.node.EndNode;
import org.jbpm.workflow.core.node.EventSubProcessNode;
import org.jbpm.workflow.core.node.StartNode;
import org.jbpm.workflow.core.node.WorkItemNode;
import org.junit.After;
import org.junit.Test;
import org.kie.api.definition.process.NodeContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.process.ProcessContext;
import org.kie.api.runtime.process.ProcessInstance;
import org.slf4j.LoggerFactory;
public class CompensationTest extends AbstractBaseTest {
public void addLogger() {
logger = LoggerFactory.getLogger(this.getClass());
}
private KieSession ksession;
@After
public void cleanUp() {
if( ksession != null ) {
ksession.dispose();
ksession = null;
}
}
/*
* General HELPER methods
*/
private void addCompensationScope(final Node node, final org.kie.api.definition.process.NodeContainer parentContainer,
final String compensationHandlerId) {
ContextContainer contextContainer = (ContextContainer) parentContainer;
CompensationScope scope = null;
boolean addScope = false;
if (contextContainer.getContexts(CompensationScope.COMPENSATION_SCOPE) == null) {
addScope = true;
} else {
scope = (CompensationScope) contextContainer.getContexts(CompensationScope.COMPENSATION_SCOPE).get(0);
if (scope == null) {
addScope = true;
}
}
if (addScope) {
scope = new CompensationScope();
contextContainer.addContext(scope);
contextContainer.setDefaultContext(scope);
scope.setContextContainer(contextContainer);
}
CompensationHandler handler = new CompensationHandler();
handler.setNode(node);
scope.setExceptionHandler(compensationHandlerId, handler);
node.setMetaData("isForCompensation", Boolean.TRUE);
}
private Node findNode(RuleFlowProcess process, String nodeName) {
Node found = null;
Queue<org.kie.api.definition.process.Node> nodes = new LinkedList<org.kie.api.definition.process.Node>();
nodes.addAll(Arrays.asList(process.getNodes()));
while( ! nodes.isEmpty() ) {
org.kie.api.definition.process.Node node = nodes.poll();
if (node.getName().equals(nodeName) ) {
found = (Node) node;
}
if( node instanceof NodeContainer ) {
nodes.addAll(Arrays.asList(((NodeContainer) node).getNodes()));
}
}
assertNotNull("Could not find node (" + nodeName + ").", found);
return found;
}
/*
* TESTS
*/
@Test
public void testCompensationBoundaryEventSpecific() throws Exception {
String processId = "org.jbpm.process.compensation.boundary";
String[] workItemNames = { "Don-Quixote", "Sancho", "Ricote" };
List<String> eventList = new ArrayList<String>();
RuleFlowProcess process = createCompensationBoundaryEventProcess(processId, workItemNames, eventList);
// run process
ksession = createKieSession(process);
Node compensatedNode = findNode(process, "work1");
String compensationEvent = (String) compensatedNode.getMetaData().get("UniqueId");
runCompensationBoundaryEventSpecificTest(ksession, process, processId, workItemNames, eventList, compensationEvent);
}
public static void runCompensationBoundaryEventSpecificTest(KieSession ksession, RuleFlowProcess process, String processId,
String [] workItemNames, List<String> eventList, String compensationEvent) {
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
for (String workItem : workItemNames) {
ksession.getWorkItemManager().registerWorkItemHandler(workItem, workItemHandler);
}
ProcessInstance processInstance = ksession.startProcess(processId);
// call compensation on the uncompleted work 1 (which should not fire)
ksession.signalEvent("Compensation", compensationEvent, processInstance.getId());
assertEquals("Compensation should not have fired yet.", 0, eventList.size());
// complete work 1
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
assertEquals(ProcessInstance.STATE_ACTIVE, processInstance.getState());
// call compensation on work 1, which should now fire
ksession.signalEvent("Compensation", compensationEvent, processInstance.getId());
assertEquals("Compensation should have fired.", 1, eventList.size());
// complete work 2 & 3
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
assertEquals(ProcessInstance.STATE_COMPLETED, processInstance.getState());
}
@Test
public void testCompensationBoundaryEventGeneral() throws Exception {
String processId = "org.jbpm.process.compensation.boundary";
String[] workItemNames = { "Don-Quixote", "Sancho", "Ricote" };
List<String> eventList = new ArrayList<String>();
RuleFlowProcess process = createCompensationBoundaryEventProcess(processId, workItemNames, eventList);
// run process
ksession = createKieSession(process);
String compensationEvent = CompensationScope.IMPLICIT_COMPENSATION_PREFIX + processId;
runCompensationBoundaryEventGeneralTest(ksession, process, processId, workItemNames, eventList, compensationEvent);
}
public static void runCompensationBoundaryEventGeneralTest(KieSession ksession, RuleFlowProcess process, String processId,
String [] workItemNames, List<String> eventList, String compensationEvent) {
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
for (String workItem : workItemNames) {
ksession.getWorkItemManager().registerWorkItemHandler(workItem, workItemHandler);
}
ProcessInstance processInstance = ksession.startProcess(processId);
// general compensation should not cause anything to happen
ksession.signalEvent("Compensation", compensationEvent, processInstance.getId());
assertEquals("Compensation should not have fired yet.", 0, eventList.size());
// complete work 1 & 2
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
assertEquals(ProcessInstance.STATE_ACTIVE, processInstance.getState());
assertEquals("Compensation should not have fired yet.", 0, eventList.size());
// general compensation should now cause the compensation handlers to fire in reverse order
ksession.signalEvent("Compensation", compensationEvent, processInstance.getId());
assertEquals("Compensation should have fired.", 2, eventList.size());
// complete work 3 and finish
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
assertEquals(ProcessInstance.STATE_COMPLETED, processInstance.getState());
}
private RuleFlowProcess createCompensationBoundaryEventProcess(String processId, String[] workItemNames,
final List<String> eventList) throws Exception {
RuleFlowProcess process = new RuleFlowProcess();
process.setAutoComplete(true);
process.setId(processId);
process.setName("CESP Process");
process.setMetaData("Compensation", true);
List<Variable> variables = new ArrayList<Variable>();
Variable variable = new Variable();
variable.setName("event");
ObjectDataType personDataType = new ObjectDataType();
personDataType.setClassName("java.lang.String");
variable.setType(personDataType);
variables.add(variable);
process.getVariableScope().setVariables(variables);
NodeCreator<StartNode> startNodeCreator = new NodeCreator<StartNode>(process, StartNode.class);
NodeCreator<EndNode> endNodeCreator = new NodeCreator<EndNode>(process, EndNode.class);
NodeCreator<WorkItemNode> workItemNodeCreator = new NodeCreator<WorkItemNode>(process, WorkItemNode.class);
NodeCreator<BoundaryEventNode> boundaryNodeCreator = new NodeCreator<BoundaryEventNode>(process, BoundaryEventNode.class);
NodeCreator<ActionNode> actionNodeCreator = new NodeCreator<ActionNode>(process, ActionNode.class);
// Create process
StartNode startNode = startNodeCreator.createNode("start");
Node lastNode = startNode;
WorkItemNode[] workItemNodes = new WorkItemNode[3];
for (int i = 0; i < 3; ++i) {
workItemNodes[i] = workItemNodeCreator.createNode("work" + (i + 1));
workItemNodes[i].getWork().setName(workItemNames[i]);
connect(lastNode, workItemNodes[i]);
lastNode = workItemNodes[i];
}
EndNode endNode = endNodeCreator.createNode("end");
connect(workItemNodes[2], endNode);
// Compensation (boundary event) handlers
for (int i = 0; i < 3; ++i) {
createBoundaryEventCompensationHandler(process, workItemNodes[i], eventList, "" + i+1);
}
return process;
}
@Test
public void testCompensationEventSubProcessSpecific() throws Exception {
String processId = "org.jbpm.process.compensation.event.subprocess";
String[] workItemNames = { "kwik", "kwek", "kwak" };
List<String> eventList = new ArrayList<String>();
RuleFlowProcess process = createCompensationEventSubProcessProcess(processId, workItemNames, eventList);
Node toCompensateNode = findNode(process, "sub0");
String compensationEvent = (String) toCompensateNode.getMetaData().get("UniqueId");
// run process
ksession = createKieSession(process);
runCompensationEventSubProcessSpecificTest(ksession, process, processId, workItemNames, eventList, compensationEvent);
}
public static void runCompensationEventSubProcessSpecificTest(KieSession ksession, RuleFlowProcess process, String processId,
String [] workItemNames, List<String> eventList, String compensationEvent) {
// run process
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
for (String workItem : workItemNames) {
ksession.getWorkItemManager().registerWorkItemHandler(workItem, workItemHandler);
}
ProcessInstance processInstance = ksession.startProcess(processId);
// call compensation on the uncompleted work 1 (which should not fire)
ksession.signalEvent("Compensation", compensationEvent, processInstance.getId());
assertEquals("Compensation should not have fired yet.", 0, eventList.size());
// pre work item
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
// sub-process is active, but not complete
ksession.signalEvent("Compensation", compensationEvent, processInstance.getId());
assertEquals("Compensation should not have fired yet.", 0, eventList.size());
// sub process work item
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
// sub-process has completed
ksession.signalEvent("Compensation", compensationEvent, processInstance.getId());
assertEquals("Compensation should have fired once.", 1, eventList.size());
// post work item
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
assertEquals(ProcessInstance.STATE_COMPLETED, processInstance.getState());
}
@Test
public void testCompensationEventSubProcessGeneral() throws Exception {
String processId = "org.jbpm.process.compensation.event.subprocess.general";
String[] workItemNames = { "kwik", "kwek", "kwak" };
List<String> eventList = new ArrayList<String>();
RuleFlowProcess process = createCompensationEventSubProcessProcess(processId, workItemNames, eventList);
String compensationEvent = CompensationScope.IMPLICIT_COMPENSATION_PREFIX + process.getId();
// run process
ksession = createKieSession(process);
runCompensationEventSubProcessGeneralTest(ksession, process, processId, workItemNames, eventList, compensationEvent);
}
public static void runCompensationEventSubProcessGeneralTest(KieSession ksession, RuleFlowProcess process, String processId,
String [] workItemNames, List<String> eventList, String compensationEvent) {
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
for (String workItem : workItemNames) {
ksession.getWorkItemManager().registerWorkItemHandler(workItem, workItemHandler);
}
ProcessInstance processInstance = ksession.startProcess(processId);
// pre and sub process work item
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
// Call general compensation
ksession.signalEvent("Compensation", compensationEvent, processInstance.getId());
assertEquals("Compensation should have fired once.", 1, eventList.size());
// post work item
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItems().removeLast().getId(), null);
assertEquals(ProcessInstance.STATE_COMPLETED, processInstance.getState());
}
private RuleFlowProcess createCompensationEventSubProcessProcess(String processId, String[] workItemNames,
final List<String> eventList) throws Exception {
RuleFlowProcess process = new RuleFlowProcess();
process.setAutoComplete(true);
process.setId(processId);
process.setName("CESP Process");
process.setMetaData("Compensation", true);
NodeCreator<StartNode> startNodeCreator = new NodeCreator<StartNode>(process, StartNode.class);
NodeCreator<WorkItemNode> workItemNodeCreator = new NodeCreator<WorkItemNode>(process, WorkItemNode.class);
NodeCreator<CompositeContextNode> compNodeCreator = new NodeCreator<CompositeContextNode>(process, CompositeContextNode.class);
NodeCreator<EndNode> endNodeCreator = new NodeCreator<EndNode>(process, EndNode.class);
// outer process
StartNode startNode = startNodeCreator.createNode("start0");
WorkItemNode workItemNode = workItemNodeCreator.createNode("work0-pre");
workItemNode.getWork().setName(workItemNames[0]);
connect( startNode, workItemNode );
CompositeNode compositeNode = compNodeCreator.createNode("sub0");
connect( workItemNode, compositeNode );
workItemNode = workItemNodeCreator.createNode("work0-post");
workItemNode.getWork().setName(workItemNames[2]);
connect( compositeNode, workItemNode );
EndNode endNode = endNodeCreator.createNode("end0");
connect( workItemNode, endNode );
// 1rst level nested subprocess
startNodeCreator.setNodeContainer(compositeNode);
workItemNodeCreator.setNodeContainer(compositeNode);
endNodeCreator.setNodeContainer(compositeNode);
startNode = startNodeCreator.createNode("start1");
workItemNode = workItemNodeCreator.createNode("work1");
workItemNode.getWork().setName(workItemNames[1]);
connect( startNode, workItemNode );
endNode = endNodeCreator.createNode("end1");
connect( workItemNode, endNode );
// 2nd level nested event subprocess in 1rst level subprocess
NodeCreator<EventSubProcessNode> espNodeCreator = new NodeCreator<EventSubProcessNode>(compositeNode, EventSubProcessNode.class);
EventSubProcessNode espNode = espNodeCreator.createNode("eventSub1");
EventTypeFilter eventFilter = new NonAcceptingEventTypeFilter();
eventFilter.setType("Compensation");
espNode.addEvent(eventFilter);
addCompensationScope(espNode, process, (String) compositeNode.getMetaData("UniqueId"));
startNodeCreator.setNodeContainer(espNode);
endNodeCreator.setNodeContainer(espNode);
NodeCreator<ActionNode> actionNodeCreator = new NodeCreator<ActionNode>(espNode, ActionNode.class);
startNode = startNodeCreator.createNode("start1*");
ActionNode actionNode = actionNodeCreator.createNode("action1*");
actionNode.setName("Execute");
DroolsAction action = new DroolsConsequenceAction("java", null);
action.setMetaData("Action", new Action() {
public void execute(ProcessContext context) throws Exception {
eventList.add("Executed action");
}
});
actionNode.setAction(action);
connect( startNode, actionNode );
endNode = endNodeCreator.createNode("end1*");
connect( actionNode, endNode );
return process;
}
@Test
public void testNestedCompensationEventSubProcessSpecific() throws Exception {
String processId = "org.jbpm.process.compensation.event.nested.subprocess";
String[] workItemNames = { "kwik", "kwek", "kwak" };
List<String> eventList = new ArrayList<String>();
RuleFlowProcess process = createNestedCompensationEventSubProcessProcess(processId, workItemNames, eventList);
Node toCompensateNode = findNode(process, "sub1");
String compensationEvent = (String) toCompensateNode.getMetaData().get("UniqueId");
ksession = createKieSession(process);
runCompensationEventSubProcessSpecificTest(ksession, process, processId, workItemNames, eventList, compensationEvent);
}
@Test
public void testNestedCompensationEventSubProcessGeneral() throws Exception {
String processId = "org.jbpm.process.compensation.event.subprocess.general";
String[] workItemNames = { "apple", "banana", "orange" };
List<String> eventList = new ArrayList<String>();
RuleFlowProcess process = createNestedCompensationEventSubProcessProcess(processId, workItemNames, eventList);
Node toCompensateNode = findNode(process, "sub0");
String compensationEvent = CompensationScope.IMPLICIT_COMPENSATION_PREFIX + toCompensateNode.getMetaData().get("UniqueId");
ksession = createKieSession(process);
runCompensationEventSubProcessGeneralTest(ksession, process, processId, workItemNames, eventList, compensationEvent);
}
private RuleFlowProcess createNestedCompensationEventSubProcessProcess(String processId, String[] workItemNames,
final List<String> eventList) throws Exception {
RuleFlowProcess process = new RuleFlowProcess();
process.setAutoComplete(true);
process.setId(processId);
process.setName("CESP Process");
process.setMetaData("Compensation", true);
NodeCreator<StartNode> startNodeCreator = new NodeCreator<StartNode>(process, StartNode.class);
NodeCreator<WorkItemNode> workItemNodeCreator = new NodeCreator<WorkItemNode>(process, WorkItemNode.class);
NodeCreator<CompositeContextNode> compNodeCreator = new NodeCreator<CompositeContextNode>(process, CompositeContextNode.class);
NodeCreator<EndNode> endNodeCreator = new NodeCreator<EndNode>(process, EndNode.class);
// outer process
CompositeContextNode compositeNode = compNodeCreator.createNode("sub0");
{
StartNode startNode = startNodeCreator.createNode("start0");
WorkItemNode workItemNode = workItemNodeCreator.createNode("work0-pre");
workItemNode.getWork().setName(workItemNames[0]);
connect( startNode, workItemNode );
connect( workItemNode, compositeNode );
EndNode endNode = endNodeCreator.createNode("end0");
connect( compositeNode, endNode );
}
// 1rst level nested subprocess (contains compensation visibility scope)
CompositeContextNode compensationScopeContainerNode = compositeNode;
{
startNodeCreator.setNodeContainer(compositeNode);
workItemNodeCreator.setNodeContainer(compositeNode);
compNodeCreator.setNodeContainer(compositeNode);
endNodeCreator.setNodeContainer(compositeNode);
StartNode startNode = startNodeCreator.createNode("start1");
CompositeContextNode subCompNode = compNodeCreator.createNode("sub1");
connect( startNode, subCompNode );
WorkItemNode workItemNode = workItemNodeCreator.createNode("work1-post");
workItemNode.getWork().setName(workItemNames[2]);
connect( subCompNode, workItemNode );
EndNode endNode = endNodeCreator.createNode("end1");
connect( workItemNode, endNode );
compositeNode = subCompNode;
}
// 2nd level nested subprocess
{
startNodeCreator.setNodeContainer(compositeNode);
workItemNodeCreator.setNodeContainer(compositeNode);
endNodeCreator.setNodeContainer(compositeNode);
StartNode startNode = startNodeCreator.createNode("start2");
WorkItemNode workItemNode = workItemNodeCreator.createNode("work2");
workItemNode.getWork().setName(workItemNames[1]);
connect( startNode, workItemNode );
EndNode endNode = endNodeCreator.createNode("end2");
connect( workItemNode, endNode );
}
// 3nd level nested event subprocess in 2nd level subprocess
{
NodeCreator<EventSubProcessNode> espNodeCreator = new NodeCreator<EventSubProcessNode>(compositeNode, EventSubProcessNode.class);
EventSubProcessNode espNode = espNodeCreator.createNode("eventSub2");
startNodeCreator.setNodeContainer(espNode);
endNodeCreator.setNodeContainer(espNode);
NodeCreator<ActionNode> actionNodeCreator = new NodeCreator<ActionNode>(espNode, ActionNode.class);
EventTypeFilter eventFilter = new NonAcceptingEventTypeFilter();
eventFilter.setType("Compensation");
espNode.addEvent(eventFilter);
addCompensationScope(espNode, compensationScopeContainerNode, (String) compositeNode.getMetaData("UniqueId"));
StartNode startNode = startNodeCreator.createNode("start3*");
ActionNode actionNode = actionNodeCreator.createNode("action3*");
actionNode.setName("Execute");
DroolsAction action = new DroolsConsequenceAction("java", null);
action.setMetaData("Action", new Action() {
public void execute(ProcessContext context) throws Exception {
eventList.add("Executed action");
}
});
actionNode.setAction(action);
connect( startNode, actionNode );
EndNode endNode = endNodeCreator.createNode("end3*");
connect( actionNode, endNode );
}
return process;
}
@Test
public void testNestedCompensationBoundaryEventSpecific() throws Exception {
String processId = "org.jbpm.process.compensation.boundary.nested";
String[] workItemNames = { "Don-Quixote", "Sancho", "Ricote" };
List<String> eventList = new ArrayList<String>();
RuleFlowProcess process = createNestedCompensationBoundaryEventProcess(processId, workItemNames, eventList);
// run process
ksession = createKieSession(process);
Node compensatedNode = findNode(process, "work-comp-1");
String compensationEvent = (String) compensatedNode.getMetaData().get("UniqueId");
runCompensationBoundaryEventSpecificTest(ksession, process, processId, workItemNames, eventList, compensationEvent);
}
@Test
public void testNestedCompensationBoundaryEventGeneral() throws Exception {
String processId = "org.jbpm.process.compensation.boundary.general.nested";
String[] workItemNames = { "Jip", "Janneke", "Takkie" };
List<String> eventList = new ArrayList<String>();
RuleFlowProcess process = createNestedCompensationBoundaryEventProcess(processId, workItemNames, eventList);
// run process
ksession = createKieSession(process);
Node toCompensateNode = findNode(process, "sub2");
String compensationEvent = CompensationScope.IMPLICIT_COMPENSATION_PREFIX
+ (String) toCompensateNode.getMetaData().get("UniqueId");
runCompensationBoundaryEventGeneralTest(ksession, process, processId, workItemNames, eventList, compensationEvent);
}
private RuleFlowProcess createNestedCompensationBoundaryEventProcess(String processId, String[] workItemNames,
final List<String> eventList) throws Exception {
RuleFlowProcess process = new RuleFlowProcess();
process.setAutoComplete(true);
process.setId(processId);
process.setName("CESP Process");
process.setMetaData("Compensation", true);
List<Variable> variables = new ArrayList<Variable>();
Variable variable = new Variable();
variable.setName("event");
ObjectDataType personDataType = new ObjectDataType();
personDataType.setClassName("java.lang.String");
variable.setType(personDataType);
variables.add(variable);
process.getVariableScope().setVariables(variables);
NodeCreator<StartNode> startNodeCreator = new NodeCreator<StartNode>(process, StartNode.class);
NodeCreator<EndNode> endNodeCreator = new NodeCreator<EndNode>(process, EndNode.class);
NodeCreator<CompositeContextNode> compNodeCreator = new NodeCreator<CompositeContextNode>(process, CompositeContextNode.class);
// process level
CompositeContextNode compositeNode = compNodeCreator.createNode("sub0");
{
StartNode startNode = startNodeCreator.createNode("start0");
connect( startNode, compositeNode );
EndNode endNode = endNodeCreator.createNode("end0");
connect( compositeNode, endNode );
}
// 1rst level nested subprocess (contains compensation visibility scope)
{
startNodeCreator.setNodeContainer(compositeNode);
compNodeCreator.setNodeContainer(compositeNode);
endNodeCreator.setNodeContainer(compositeNode);
StartNode startNode = startNodeCreator.createNode("start1");
CompositeContextNode subCompNode = compNodeCreator.createNode("sub1");
connect( startNode, subCompNode );
EndNode endNode = endNodeCreator.createNode("end1");
connect( subCompNode, endNode );
compositeNode = subCompNode;
}
// 2nd level nested subprocess (contains compensation visibility scope)
NodeCreator<WorkItemNode> workItemNodeCreator = new NodeCreator<WorkItemNode>(compositeNode, WorkItemNode.class);
{
startNodeCreator.setNodeContainer(compositeNode);
compNodeCreator.setNodeContainer(compositeNode);
endNodeCreator.setNodeContainer(compositeNode);
StartNode startNode = startNodeCreator.createNode("start2");
CompositeContextNode subCompNode = compNodeCreator.createNode("sub2");
connect( startNode, subCompNode );
WorkItemNode workItemNode = workItemNodeCreator.createNode("work2");
workItemNode.getWork().setName(workItemNames[2]);
connect( subCompNode, workItemNode );
EndNode endNode = endNodeCreator.createNode("end2");
connect( workItemNode, endNode );
createBoundaryEventCompensationHandler(compositeNode, workItemNode, eventList, "2");
compositeNode = subCompNode;
}
// Fill 3rd level with process with compensation
{
startNodeCreator.setNodeContainer(compositeNode);
workItemNodeCreator.setNodeContainer(compositeNode);
endNodeCreator.setNodeContainer(compositeNode);
StartNode startNode = startNodeCreator.createNode("start");
Node lastNode = startNode;
WorkItemNode[] workItemNodes = new WorkItemNode[3];
for (int i = 0; i < 2; ++i) {
workItemNodes[i] = workItemNodeCreator.createNode("work-comp-" + (i + 1));
workItemNodes[i].getWork().setName(workItemNames[i]);
connect(lastNode, workItemNodes[i]);
lastNode = workItemNodes[i];
}
EndNode endNode = endNodeCreator.createNode("end");
connect(workItemNodes[1], endNode);
// Compensation (boundary event) handlers
for (int i = 0; i < 2; ++i) {
createBoundaryEventCompensationHandler(compositeNode, workItemNodes[i], eventList, "" + i+1);
}
}
return process;
}
private void createBoundaryEventCompensationHandler(org.jbpm.workflow.core.NodeContainer nodeContainer,
Node attachedToNode, final List<String> eventList, final String id) throws Exception {
NodeCreator<BoundaryEventNode> boundaryNodeCreator = new NodeCreator<BoundaryEventNode>(nodeContainer, BoundaryEventNode.class);
BoundaryEventNode boundaryNode = boundaryNodeCreator.createNode("boundary" + id);
String attachedTo = (String) attachedToNode.getMetaData().get("UniqueId");
boundaryNode.setMetaData("AttachedTo", attachedTo);
boundaryNode.setAttachedToNodeId(attachedTo);
EventTypeFilter eventFilter = new NonAcceptingEventTypeFilter();
eventFilter.setType("Compensation");
List<EventFilter> eventFilters = new ArrayList<EventFilter>();
boundaryNode.setEventFilters(eventFilters);
eventFilters.add(eventFilter);
addCompensationScope(boundaryNode, nodeContainer, attachedTo);
NodeCreator<ActionNode> actionNodeCreator = new NodeCreator<ActionNode>(nodeContainer, ActionNode.class);
ActionNode actionNode = actionNodeCreator.createNode("handlerAction" + id);
actionNode.setMetaData("isForCompensation", true);
actionNode.setName("Execute");
DroolsAction action = new DroolsConsequenceAction("java", null);
action.setMetaData("Action", new Action() {
public void execute(ProcessContext context) throws Exception {
eventList.add("action" + id);
}
});
actionNode.setAction(action);
connect( boundaryNode, actionNode );
}
}