/**
* 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.bpmn2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jbpm.bpmn2.objects.TestWorkItemHandler;
import org.jbpm.process.core.context.exception.CompensationScope;
import org.jbpm.process.instance.impl.demo.SystemOutWorkItemHandler;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.kie.api.event.process.DefaultProcessEventListener;
import org.kie.api.event.process.ProcessEventListener;
import org.kie.api.event.process.ProcessNodeLeftEvent;
import org.kie.api.event.process.ProcessNodeTriggeredEvent;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.api.runtime.process.WorkItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RunWith(Parameterized.class)
public class CompensationTest extends JbpmBpmn2TestCase {
@Parameters
public static Collection<Object[]> persistence() {
Object[][] data = new Object[][] { { false }, { true } };
return Arrays.asList(data);
};
private KieSession ksession;
public CompensationTest(boolean persistence) {
super(persistence);
}
private Logger logger = LoggerFactory
.getLogger(CompensationTest.class);
private ProcessEventListener LOGGING_EVENT_LISTENER = new DefaultProcessEventListener() {
@Override
public void afterNodeLeft(ProcessNodeLeftEvent event) {
logger.info("After node left {}", event.getNodeInstance().getNodeName());
}
@Override
public void afterNodeTriggered(ProcessNodeTriggeredEvent event) {
logger.info("After node triggered {}", event.getNodeInstance().getNodeName());
}
@Override
public void beforeNodeLeft(ProcessNodeLeftEvent event) {
logger.info("Before node left {}", event.getNodeInstance().getNodeName());
}
@Override
public void beforeNodeTriggered(ProcessNodeTriggeredEvent event) {
logger.info("Before node triggered {}", event.getNodeInstance().getNodeName());
}
};
@BeforeClass
public static void setup() throws Exception {
setUpDataSource();
}
@Before
public void prepare() {
clearHistory();
}
@After
public void dispose() {
if (ksession != null) {
ksession.dispose();
ksession = null;
}
}
/**
* TESTS
*/
@Test
public void compensationViaIntermediateThrowEventProcess() throws Exception {
KieSession ksession = createKnowledgeSession("compensation/BPMN2-Compensation-IntermediateThrowEvent.bpmn2");
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", workItemHandler);
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", "0");
ProcessInstance processInstance = ksession.startProcess("CompensateIntermediateThrowEvent", params);
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItem().getId(), null);
// compensation activity (assoc. with script task) signaled *after* script task
assertProcessInstanceCompleted(processInstance.getId(), ksession);
assertProcessVarValue(processInstance, "x", "1" );
}
@Test
public void compensationTwiceViaSignal() throws Exception {
KieSession ksession = createKnowledgeSession("compensation/BPMN2-Compensation-IntermediateThrowEvent.bpmn2");
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", workItemHandler);
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", "0");
String processId = "CompensateIntermediateThrowEvent";
ProcessInstance processInstance = ksession.startProcess(processId, params);
// twice
ksession.signalEvent("Compensation", CompensationScope.IMPLICIT_COMPENSATION_PREFIX + processId, processInstance.getId());
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItem().getId(), null);
// compensation activity (assoc. with script task) signaled *after* script task
assertProcessInstanceCompleted(processInstance.getId(), ksession);
assertProcessVarValue(processInstance, "x", "2");
}
@Test
public void compensationViaEventSubProcess() throws Exception {
KieSession ksession = createKnowledgeSession("compensation/BPMN2-Compensation-EventSubProcess.bpmn2");
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", workItemHandler);
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", "0");
ProcessInstance processInstance = ksession.startProcess("CompensationEventSubProcess", params);
assertProcessInstanceActive(processInstance.getId(), ksession);
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItem().getId(), null);
assertProcessVarValue(processInstance, "x", "1");
}
@Test
public void compensationOnlyAfterAssociatedActivityHasCompleted() throws Exception {
KieSession ksession = createKnowledgeSession("compensation/BPMN2-Compensation-UserTaskBeforeAssociatedActivity.bpmn2");
ksession.addEventListener(LOGGING_EVENT_LISTENER);
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", workItemHandler);
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", "0");
ProcessInstance processInstance = ksession.startProcess("CompensateIntermediateThrowEvent", params);
// should NOT cause compensation since compensated activity has not yet completed (or started)!
ksession.signalEvent("Compensation", "_3", processInstance.getId());
// user task -> script task (associated with compensation) --> intermeidate throw compensation event
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItem().getId(), null);
// compensation activity (assoc. with script task) signaled *after* to-compensate script task
assertProcessInstanceCompleted(processInstance.getId(), ksession);
assertProcessVarValue(processInstance, "x", "1");
}
@Test
public void orderedCompensation() throws Exception {
KieSession ksession = createKnowledgeSession("compensation/BPMN2-Compensation-ParallelOrderedCompensation-IntermediateThrowEvent.bpmn2");
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", workItemHandler);
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", "");
ProcessInstance processInstance = ksession.startProcess("CompensateParallelOrdered", params);
List<WorkItem> workItems = workItemHandler.getWorkItems();
List<Long> workItemIds = new ArrayList<Long>();
for( WorkItem workItem : workItems ) {
if( "Thr".equals(workItem.getParameter("NodeName")) ) {
workItemIds.add(workItem.getId());
}
}
for( WorkItem workItem : workItems ) {
if( "Two".equals(workItem.getParameter("NodeName")) ) {
workItemIds.add(workItem.getId());
}
}
for( WorkItem workItem : workItems ) {
if( "One".equals(workItem.getParameter("NodeName")) ) {
workItemIds.add(workItem.getId());
}
}
for( Long id : workItemIds ) {
ksession.getWorkItemManager().completeWorkItem(id, null);
}
// user task -> script task (associated with compensation) --> intermeidate throw compensation event
String xVal = getProcessVarValue(processInstance, "x");
// Compensation happens in the *REVERSE* order of completion
// Ex: if the order is 3, 17, 282, then compensation should happen in the order of 282, 17, 3
assertEquals("Compensation did not fire in the same order as the associated activities completed.", "_171:_131:_141:_151:", xVal );
}
@Test
public void compensationInSubSubProcesses() throws Exception {
KieSession ksession = createKnowledgeSession("compensation/BPMN2-Compensation-InSubSubProcess.bpmn2");
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", workItemHandler);
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", "0");
ProcessInstance processInstance = ksession.startProcess("CompensateSubSubSub", params);
ksession.signalEvent("Compensation", "_C-2", processInstance.getId());
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItem().getId(), null);
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItem().getId(), null);
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItem().getId(), null);
// compensation activity (assoc. with script task) signaled *after* script task
assertProcessInstanceCompleted(processInstance.getId(), ksession);
assertProcessVarValue(processInstance, "x", "2");
}
@Test
public void specificCompensationOfASubProcess() throws Exception {
KieSession ksession = createKnowledgeSession("compensation/BPMN2-Compensation-ThrowSpecificForSubProcess.bpmn2");
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", workItemHandler);
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", 1);
ProcessInstance processInstance = ksession.startProcess("CompensationSpecificSubProcess", params);
// compensation activity (assoc. with script task) signaled *after* to-compensate script task
assertProcessInstanceCompleted(processInstance.getId(), ksession);
if( ! isPersistence() ) {
assertProcessVarValue(processInstance, "x", null);
} else {
assertProcessVarValue(processInstance, "x", "");
}
}
@Test
@Ignore
public void compensationViaCancellation() throws Exception {
KieSession ksession = createKnowledgeSession("compensation/BPMN2-Compensation-IntermediateThrowEvent.bpmn2");
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", workItemHandler);
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", "0");
ProcessInstance processInstance = ksession.startProcess("CompensateIntermediateThrowEvent", params);
ksession.signalEvent("Cancel", null, processInstance.getId());
ksession.getWorkItemManager().completeWorkItem(workItemHandler.getWorkItem().getId(), null);
// compensation activity (assoc. with script task) signaled *after* script task
assertProcessInstanceCompleted(processInstance.getId(), ksession);
assertProcessVarValue(processInstance, "x", "1");
}
@Test
public void compensationInvokingSubProcess() throws Exception {
KieSession ksession = createKnowledgeSession("compensation/BPMN2-UserTaskCompensation.bpmn2");
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", new SystemOutWorkItemHandler());
Map<String, Object> params = new HashMap<String, Object>();
params.put("compensation", "True");
ProcessInstance processInstance = ksession.startProcess("UserTaskCompensation", params);
assertProcessInstanceCompleted(processInstance.getId(), ksession);
assertProcessVarValue(processInstance, "compensation", "compensation");
}
/**
* Test to demonstrate that Compensation Events work with Reusable
* Subprocesses
*
* @throws Exception
*/
@Test
public void compensationWithReusableSubprocess() throws Exception {
KieSession ksession = createKnowledgeSession("compensation/BPMN2-Booking.bpmn2",
"compensation/BPMN2-BookResource.bpmn2", "compensation/BPMN2-CancelResource.bpmn2");
ProcessInstance processInstance = ksession.startProcess("Booking");
assertProcessInstanceCompleted(processInstance.getId(), ksession);
}
}