/*
* Copyright 2015 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.test.functional;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.naming.InitialContext;
import javax.transaction.Status;
import javax.transaction.UserTransaction;
import org.assertj.core.api.Assertions;
import org.jbpm.test.JbpmTestCase;
import org.jbpm.test.listener.TrackingAgendaEventListener;
import org.jbpm.test.listener.TrackingProcessEventListener;
import org.jbpm.test.wih.ListWorkItemHandler;
import org.junit.Before;
import org.junit.Test;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.process.ProcessInstance;
public class TransactionsTest extends JbpmTestCase {
private static final String TRANSACTIONS = "org/jbpm/test/functional/Transactions.bpmn";
private static final String TRANSACTIONS_ID = "org.jbpm.test.functional.Transactions";
private static final String TRANSACTIONS_DRL = "org/jbpm/test/functional/Transactions.drl";
private static final String HELLO_WORLD = "org/jbpm/test/functional/common/HelloWorldProcess1.bpmn";
private KieSession ksession;
private Map<String, ResourceType> resources;
@Before
public void init() throws Exception {
resources = new HashMap<String, ResourceType>();
resources.put(TRANSACTIONS, ResourceType.BPMN2);
resources.put(HELLO_WORLD, ResourceType.BPMN2);
resources.put(TRANSACTIONS_DRL, ResourceType.DRL);
ksession = createKSession(resources);
}
@Test(timeout = 60000)
public void testStartProcessCommit() throws Exception {
UserTransaction ut = getUserTransaction();
long processId;
try {
ut.begin();
processId = startProcess(ksession);
Assertions.assertThat(ksession.getProcessInstance(processId)).isNotNull();
Assertions.assertThat(ksession.getProcessInstance(processId).getState()).isEqualTo(ProcessInstance.STATE_ACTIVE);
ut.commit();
} catch (Exception ex) {
ut.rollback();
throw ex;
}
ksession = restoreKSession(resources);
assertProcessInstanceActive(processId);
}
@Test(timeout = 60000)
public void testStartProcessRollback() throws Exception {
UserTransaction ut = getUserTransaction();
long processId;
try {
ut.begin();
processId = startProcess(ksession);
assertProcessInstanceActive(processId);
} finally {
ut.rollback();
}
System.out.println(ksession.getId() + " " + ksession.toString());
ksession = restoreKSession(resources);
System.out.println(ksession.getId() + " " + ksession.toString());
try {
ProcessInstance pi = ksession.getProcessInstance(processId);
Assertions.assertThat(pi).isNull();
} catch (NullPointerException npe) {
logger.error("Non-XA database thrown NPE on process started before rollback", npe);
}
}
@Test(timeout = 60000)
public void testAbortProcessCommit() throws Exception {
long processId = startProcess(ksession);
TrackingProcessEventListener listener = new TrackingProcessEventListener();
ksession.addEventListener(listener);
assertProcessInstanceActive(processId);
UserTransaction ut = getUserTransaction();
try {
ut.begin();
ksession.abortProcessInstance(processId);
ut.commit();
} catch (Exception ex) {
ut.rollback();
throw ex;
}
ksession = restoreKSession(resources);
assertProcessInstanceAborted(processId);
}
@Test(timeout = 60000)
public void testAbortProcessRollback() throws Exception {
long processId = startProcess(ksession);
assertProcessInstanceActive(processId);
UserTransaction ut = getUserTransaction();
try {
ut.begin();
ksession.abortProcessInstance(processId);
} finally {
ut.rollback();
}
ksession = restoreKSession(resources);
assertProcessInstanceActive(processId);
}
@Test(timeout = 60000)
public void testScript() throws Exception {
TrackingProcessEventListener process = new TrackingProcessEventListener(false);
ksession.addEventListener(process);
long processId = startProcess(ksession);
UserTransaction ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", "script", processId);
Assertions.assertThat(process.wasNodeLeft("script")).isTrue();
} finally {
ut.rollback();
}
process.clear();
ksession = restoreKSession(resources);
ksession.addEventListener(process);
String scriptNodeName = "script";
ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", "script", processId);
assertTrue( "Node '" + scriptNodeName + "' was not left on time!", process.waitForNodeToBeLeft(scriptNodeName, 1000));
Assertions.assertThat(ut.getStatus()).isEqualTo(Status.STATUS_ACTIVE);
ut.commit();
} catch (Exception ex) {
ut.rollback();
throw ex;
}
Assertions.assertThat(process.wasNodeLeft(scriptNodeName)).isTrue();
ksession.signalEvent("finish", null, processId);
assertTrue( "Process was not completed on time!", process.waitForProcessToComplete(1000));
assertProcessInstanceCompleted(processId);
}
@Test(timeout = 60000)
public void testRuleflowGroup() throws Exception {
TrackingProcessEventListener process = new TrackingProcessEventListener(false);
TrackingAgendaEventListener agenda = new TrackingAgendaEventListener();
ksession.addEventListener(process);
ksession.addEventListener(agenda);
long processId = startProcess(ksession);
UserTransaction ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", "rfg", processId);
Assertions.assertThat(process.wasNodeLeft("rfg")).isTrue();
} finally {
ut.rollback();
}
Thread.sleep(600);
process.clear();
agenda.clear();
ksession = restoreKSession(resources);
ksession.addEventListener(process);
ksession.addEventListener(agenda);
ksession.fireAllRules();
Assertions.assertThat(agenda.isRuleFired("dummyRule")).isFalse();
agenda.clear();
process.clear();
String ruleFlowGroupNodeName = "rfg";
ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", "rfg", processId);
assertTrue( "Node '" + ruleFlowGroupNodeName + "' was not left on time!", process.waitForNodeToBeLeft(ruleFlowGroupNodeName, 1000));
ut.commit();
} catch (Exception ex) {
ut.rollback();
throw ex;
}
Assertions.assertThat(process.wasNodeLeft(ruleFlowGroupNodeName)).isTrue();
ksession.signalEvent("finish", null, processId);
ksession.fireAllRules();
assertTrue( "Process did not complete on time!", process.waitForProcessToComplete(1000));
Assertions.assertThat(agenda.isRuleFired("dummyRule")).isTrue();
assertProcessInstanceCompleted(processId);
}
@Test
public void testTimer() throws Exception {
TrackingProcessEventListener process = new TrackingProcessEventListener();
ksession.addEventListener(process);
long processId = startProcess(ksession);
UserTransaction ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", "timer", processId);
Assertions.assertThat(process.wasNodeLeft("timer")).isTrue();
} finally {
ut.rollback();
}
Thread.sleep(600);
process.clear();
ksession = restoreKSession(resources);
ksession.addEventListener(process);
ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", "timer", processId);
ut.commit();
} catch (Exception ex) {
ut.rollback();
throw ex;
}
String timerNodeName = "timer";
assertTrue( "Node '" + timerNodeName + "' was not left on time!", process.waitForNodeToBeLeft(timerNodeName, 1500));
Assertions.assertThat(process.wasNodeLeft(timerNodeName)).isTrue();
String finishScriptNodeName = "Finish-Script";
assertTrue( "Node '" + finishScriptNodeName + "' was not triggered on time!", process.waitForNodeTobeTriggered(finishScriptNodeName, 1500));
ksession.signalEvent("finish", null, processId);
assertTrue( "Process did not complete on time!", process.waitForProcessToComplete(1500));
assertProcessInstanceCompleted(processId);
}
@Test(timeout = 60000)
public void testUsertask() throws Exception {
TrackingProcessEventListener process = new TrackingProcessEventListener();
ksession.addEventListener(process);
ListWorkItemHandler handler = new ListWorkItemHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", handler);
long processId = startProcess(ksession);
UserTransaction ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", "usertask", processId);
Assertions.assertThat(process.wasNodeLeft("usertask")).isTrue();
Assertions.assertThat(handler.getWorkItems()).hasSize(1);
} finally {
ut.rollback();
}
// human tasks are not aborted (as that would not cause the task to be cancelled
process.clear();
ksession = restoreKSession(resources);
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", handler);
ksession.addEventListener(process);
ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", "usertask", processId);
ut.commit();
} catch (Exception ex) {
ut.rollback();
throw ex;
}
String lastUserTaskNodeName = "User Task";
assertTrue( "Node '" + lastUserTaskNodeName + "' was not left on time!", process.waitForNodeTobeTriggered(lastUserTaskNodeName, 1000));
Assertions.assertThat(handler.getWorkItems()).hasSize(2);
Assertions.assertThat(process.wasNodeLeft("usertask")).isTrue();
Assertions.assertThat(process.wasNodeTriggered(lastUserTaskNodeName)).isTrue();
Assertions.assertThat(process.wasNodeLeft(lastUserTaskNodeName)).isFalse();
Assertions.assertThat(process.wasProcessCompleted("transactions")).isFalse();
}
@Test(timeout = 60000)
public void testForLoop() throws Exception {
TrackingProcessEventListener process = new TrackingProcessEventListener(false);
ksession.addEventListener(process);
Map<String, Object> params = new HashMap<String, Object>();
params.put("collection", Arrays.asList("hello world", "25", "false", "1234567891011121314151617181920", ""));
long processId = ksession.startProcess(TRANSACTIONS_ID, params).getId();
String forLoopNodeName = "forloop";
UserTransaction ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", forLoopNodeName, processId);
assertTrue( "Node '" + forLoopNodeName + "' was not left on time!", process.waitForNodeToBeLeft(forLoopNodeName, 1000));
Assertions.assertThat(process.wasNodeLeft(forLoopNodeName)).isTrue();
} finally {
ut.rollback();
}
process.clear();
ksession = restoreKSession(resources);
ksession.addEventListener(process);
ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", "forloop", processId);
ut.commit();
} catch (Exception ex) {
ut.rollback();
throw ex;
}
String multipleInstancesNode = "Multiple Instances";
assertTrue( "Process did not complete on time!", process.waitForNodeToBeLeft(multipleInstancesNode, 1000));
Assertions.assertThat(process.wasNodeLeft(forLoopNodeName)).isTrue();
Assertions.assertThat(process.wasNodeLeft(multipleInstancesNode)).isTrue();
Assertions.assertThat(process.wasProcessCompleted("transactions")).isFalse();
}
@Test(timeout = 60000)
public void testEmbedded() throws Exception {
TrackingProcessEventListener process = new TrackingProcessEventListener(false);
ksession.addEventListener(process);
long processId = startProcess(ksession);
UserTransaction ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", "embedded", processId);
assertTrue( "Node 'embedded' was not left on time!", process.waitForNodeToBeLeft("embedded", 1000));
Assertions.assertThat(process.wasNodeLeft("embedded")).isTrue();
} finally {
ut.rollback();
}
process.clear();
ksession = restoreKSession(resources);
ksession.addEventListener(process);
ut = getUserTransaction();
try {
ut.begin();
ksession.signalEvent("start", "embedded", processId);
ut.commit();
} catch (Exception ex) {
ut.rollback();
throw ex;
}
assertTrue( "Node 'embedded' was not left on time!", process.waitForNodeToBeLeft("embedded", 1000));
Assertions.assertThat(process.wasNodeLeft("embedded")).isTrue();
Assertions.assertThat(process.wasProcessCompleted("transactions")).isFalse();
}
private long startProcess(KieSession ksession) {
return startProcess(ksession, null);
}
private long startProcess(KieSession ksession, String nodeType) {
ProcessInstance pi = ksession.startProcess(TRANSACTIONS_ID);
if (nodeType != null) {
ksession.signalEvent("start", nodeType);
}
return pi.getId();
}
private UserTransaction getUserTransaction() throws Exception {
UserTransaction tx = InitialContext.doLookup("java:comp/UserTransaction");
return tx;
}
public KieSession restoreKSession(Map<String, ResourceType> res) {
disposeRuntimeManager();
createRuntimeManager(res);
return getRuntimeEngine().getKieSession();
}
}