/*
* 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.
*
* 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.timer;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.naming.InitialContext;
import javax.persistence.EntityManagerFactory;
import javax.transaction.UserTransaction;
import org.drools.core.command.impl.ExecutableCommand;
import org.drools.core.command.impl.RegistryContext;
import org.drools.core.impl.EnvironmentFactory;
import org.drools.core.time.TimerService;
import org.drools.core.time.impl.TimerJobInstance;
import org.drools.persistence.api.TransactionManager;
import org.jbpm.persistence.JpaProcessPersistenceContextManager;
import org.jbpm.persistence.jta.ContainerManagedTransactionManager;
import org.jbpm.process.core.timer.GlobalSchedulerService;
import org.jbpm.process.core.timer.TimerServiceRegistry;
import org.jbpm.process.core.timer.impl.QuartzSchedulerService;
import org.jbpm.runtime.manager.impl.AbstractRuntimeManager;
import org.jbpm.runtime.manager.impl.SimpleRuntimeEnvironment;
import org.jbpm.runtime.manager.impl.jpa.EntityManagerFactoryManager;
import org.jbpm.services.task.identity.JBossUserGroupCallbackImpl;
import org.jbpm.services.task.persistence.JPATaskPersistenceContextManager;
import org.jbpm.test.functional.timer.TimerBaseTest.TestRegisterableItemsFactory;
import org.jbpm.test.listener.CountDownProcessEventListener;
import org.jbpm.workflow.instance.WorkflowProcessInstance;
import org.jbpm.workflow.instance.node.HumanTaskNodeInstance;
import org.junit.Ignore;
import org.junit.Test;
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.event.process.ProcessStartedEvent;
import org.kie.api.event.rule.AgendaEventListener;
import org.kie.api.event.rule.BeforeMatchFiredEvent;
import org.kie.api.event.rule.DefaultAgendaEventListener;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.Context;
import org.kie.api.runtime.Environment;
import org.kie.api.runtime.EnvironmentName;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.manager.RuntimeEngine;
import org.kie.api.runtime.manager.RuntimeEnvironment;
import org.kie.api.runtime.manager.RuntimeEnvironmentBuilder;
import org.kie.api.runtime.manager.RuntimeManager;
import org.kie.api.runtime.process.NodeInstance;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.api.task.UserGroupCallback;
import org.kie.api.task.model.Status;
import org.kie.api.task.model.TaskSummary;
import org.kie.internal.io.ResourceFactory;
import org.kie.internal.runtime.manager.SessionNotFoundException;
import org.kie.internal.runtime.manager.context.ProcessInstanceIdContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class GlobalTimerServiceBaseTest extends TimerBaseTest{
protected static final Logger logger = LoggerFactory.getLogger(GlobalTimerServiceBaseTest.class);
protected GlobalSchedulerService globalScheduler;
protected RuntimeManager manager;
protected RuntimeEnvironment environment;
protected EntityManagerFactory emf;
protected abstract RuntimeManager getManager(RuntimeEnvironment environment, boolean waitOnStart);
public void cleanup() {
if (manager != null) {
manager.close();
}
if (environment != null) {
EntityManagerFactory emf = ((SimpleRuntimeEnvironment) environment).getEmf();
if (emf != null) {
emf.close();
}
}
if (emf != null && emf.isOpen()) {
emf.close();
}
}
@Test(timeout=20000)
public void testInterediateTimerWithGlobalTestService() throws Exception {
CountDownProcessEventListener countDownListener = new CountDownProcessEventListener("timer", 3);
// prepare listener to assert results
final List<Long> timerExporations = new ArrayList<Long>();
ProcessEventListener listener = new DefaultProcessEventListener(){
@Override
public void afterNodeLeft(ProcessNodeLeftEvent event) {
if (event.getNodeInstance().getNodeName().equals("timer")) {
timerExporations.add(event.getProcessInstance().getId());
}
}
};
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/IntermediateCatchEventTimerCycle3.bpmn2"), ResourceType.BPMN2)
.schedulerService(globalScheduler)
.registerableItemsFactory(new TestRegisterableItemsFactory(listener, countDownListener))
.get();
manager = getManager(environment, true);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();
ProcessInstance processInstance = ksession.startProcess("IntermediateCatchEvent");
assertTrue(processInstance.getState() == ProcessInstance.STATE_ACTIVE);
// now wait for 1 second for first timer to trigger
Thread.sleep(1500);
// dispose session to force session to be reloaded on timer expiration
manager.disposeRuntimeEngine(runtime);
Thread.sleep(2000);
try {
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
ksession = runtime.getKieSession();
processInstance = ksession.getProcessInstance(processInstance.getId());
assertNull(processInstance);
} catch (SessionNotFoundException e) {
// expected for PerProcessInstanceManagers since process instance is completed
}
// let's wait to ensure no more timers are expired and triggered
countDownListener.waitTillCompleted();
assertEquals(3, timerExporations.size());
manager.disposeRuntimeEngine(runtime);
}
@Test(timeout=20000)
public void testTimerStart() throws Exception {
CountDownProcessEventListener countDownListener = new CountDownProcessEventListener("StartProcess", 5);
// prepare listener to assert results
final List<Long> timerExporations = new ArrayList<Long>();
ProcessEventListener listener = new DefaultProcessEventListener(){
@Override
public void beforeProcessStarted(ProcessStartedEvent event) {
timerExporations.add(event.getProcessInstance().getId());
}
};
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/TimerStart2.bpmn2"), ResourceType.BPMN2)
.schedulerService(globalScheduler)
.registerableItemsFactory(new TestRegisterableItemsFactory(listener, countDownListener))
.get();
manager = getManager(environment, false);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();
assertEquals(0, timerExporations.size());
countDownListener.waitTillCompleted();
manager.disposeRuntimeEngine(runtime);
assertEquals(5, timerExporations.size());
}
@Test@Ignore
public void testTimerRule() throws Exception {
int badNumTimers = 6;
final CountDownLatch timerCompleted = new CountDownLatch(badNumTimers);
// prepare listener to assert results
final List<String> timerExporations = new ArrayList<String>();
AgendaEventListener listener = new DefaultAgendaEventListener(){
@Override
public void beforeMatchFired(BeforeMatchFiredEvent event) {
timerExporations.add(event.getMatch().getRule().getId());
timerCompleted.countDown();
}
};
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/timer-rules.drl"), ResourceType.DRL)
.schedulerService(globalScheduler)
.registerableItemsFactory(new TestRegisterableItemsFactory(listener))
.get();
manager = getManager(environment, true);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
assertEquals(0, timerExporations.size());
boolean didNotWait = timerCompleted.await(6, TimeUnit.SECONDS);
assertTrue("Too many timers elapsed: " + (badNumTimers - timerCompleted.getCount()), ! didNotWait );
manager.disposeRuntimeEngine(runtime);
assertEquals(5, timerExporations.size());
}
@Test(timeout=20000)
public void testInterediateTimerWithHTAfterWithGlobalTestService() throws Exception {
CountDownProcessEventListener countDownListener = new CountDownProcessEventListener("timer", 3);
// prepare listener to assert results
final List<Long> timerExpirations = new ArrayList<Long>();
ProcessEventListener listener = new DefaultProcessEventListener(){
@Override
public void afterNodeLeft(ProcessNodeLeftEvent event) {
if (event.getNodeInstance().getNodeName().equals("timer")) {
timerExpirations.add(event.getProcessInstance().getId());
}
}
};
Properties properties= new Properties();
properties.setProperty("mary", "HR");
properties.setProperty("john", "HR");
UserGroupCallback userGroupCallback = new JBossUserGroupCallbackImpl(properties);
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/IntermediateCatchEventTimerCycleWithHT.bpmn2"), ResourceType.BPMN2)
.schedulerService(globalScheduler)
.registerableItemsFactory(new TestRegisterableItemsFactory(listener, countDownListener))
.userGroupCallback(userGroupCallback)
.get();
manager = getManager(environment, true);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", "R3/PT1S");
ProcessInstance processInstance = ksession.startProcess("IntermediateCatchEvent", params);
assertTrue(processInstance.getState() == ProcessInstance.STATE_ACTIVE);
logger.debug("Disposed after start");
// dispose session to force session to be reloaded on timer expiration
manager.disposeRuntimeEngine(runtime);
countDownListener.waitTillCompleted();
countDownListener.reset(1);
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
ksession = runtime.getKieSession();
// get tasks
List<Status> statuses = new ArrayList<Status>();
statuses.add(Status.Reserved);
List<TaskSummary> tasks = runtime.getTaskService().getTasksAssignedAsPotentialOwnerByStatus("john", statuses, "en-UK");
assertNotNull(tasks);
assertEquals(3, tasks.size());
for (TaskSummary task : tasks) {
runtime.getTaskService().start(task.getId(), "john");
runtime.getTaskService().complete(task.getId(), "john", null);
}
processInstance = ksession.getProcessInstance(processInstance.getId());
assertNull(processInstance);
// let's wait to ensure no more timers are expired and triggered
countDownListener.waitTillCompleted(3000);
manager.disposeRuntimeEngine(runtime);
assertEquals(3, timerExpirations.size());
}
@Test(timeout=20000)
public void testInterediateTimerWithHTBeforeWithGlobalTestService() throws Exception {
CountDownProcessEventListener countDownListener = new CountDownProcessEventListener("timer", 3);
// prepare listener to assert results
final List<Long> timerExporations = new ArrayList<Long>();
ProcessEventListener listener = new DefaultProcessEventListener(){
@Override
public void afterNodeLeft(ProcessNodeLeftEvent event) {
if (event.getNodeInstance().getNodeName().equals("timer")) {
timerExporations.add(event.getProcessInstance().getId());
}
}
};
Properties properties= new Properties();
properties.setProperty("mary", "HR");
properties.setProperty("john", "HR");
UserGroupCallback userGroupCallback = new JBossUserGroupCallbackImpl(properties);
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/IntermediateCatchEventTimerCycleWithHT2.bpmn2"), ResourceType.BPMN2)
.schedulerService(globalScheduler)
.registerableItemsFactory(new TestRegisterableItemsFactory(listener, countDownListener))
.userGroupCallback(userGroupCallback)
.get();
manager = getManager(environment, true);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", "R3/PT1S");
ProcessInstance processInstance = ksession.startProcess("IntermediateCatchEvent", params);
assertTrue(processInstance.getState() == ProcessInstance.STATE_ACTIVE);
// get tasks
List<Status> statuses = new ArrayList<Status>();
statuses.add(Status.Reserved);
List<TaskSummary> tasks = runtime.getTaskService().getTasksAssignedAsPotentialOwnerByStatus("john", statuses, "en-UK");
assertNotNull(tasks);
assertEquals(1, tasks.size());
for (TaskSummary task : tasks) {
runtime.getTaskService().start(task.getId(), "john");
runtime.getTaskService().complete(task.getId(), "john", null);
}
// dispose session to force session to be reloaded on timer expiration
manager.disposeRuntimeEngine(runtime);
// now wait for 1 second for first timer to trigger
countDownListener.waitTillCompleted();
countDownListener.reset(1);
try {
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
ksession = runtime.getKieSession();
processInstance = ksession.getProcessInstance(processInstance.getId());
assertNull(processInstance);
} catch (SessionNotFoundException e) {
// expected for PerProcessInstanceManagers since process instance is completed
}
// let's wait to ensure no more timers are expired and triggered
countDownListener.waitTillCompleted(3000);
manager.disposeRuntimeEngine(runtime);
assertEquals(3, timerExporations.size());
}
@Test(timeout=20000)
public void testInterediateTimerWithGlobalTestServiceRollback() throws Exception {
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/IntermediateCatchEventTimerCycle3.bpmn2"), ResourceType.BPMN2)
.schedulerService(globalScheduler)
.get();
manager = getManager(environment, true);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();
long ksessionId = ksession.getIdentifier();
ProcessInstance processInstance;
UserTransaction ut = InitialContext.doLookup("java:comp/UserTransaction");
try {
ut.begin();
processInstance = ksession.startProcess("IntermediateCatchEvent");
} finally {
ut.rollback();
}
manager.disposeRuntimeEngine(runtime);
try {
// two types of checks as different managers will treat it differently
// per process instance will fail on getting runtime
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
// where singleton and per request will return runtime but there should not be process instance
processInstance = runtime.getKieSession().getProcessInstance(processInstance.getId());
assertNull(processInstance);
} catch (SessionNotFoundException e) {
}
TimerService timerService = TimerServiceRegistry.getInstance().get(manager.getIdentifier()+TimerServiceRegistry.TIMER_SERVICE_SUFFIX);
Collection<TimerJobInstance> timerInstances = timerService.getTimerJobInstances(ksessionId);
assertNotNull(timerInstances);
assertEquals(0, timerInstances.size());
if (runtime != null) {
manager.disposeRuntimeEngine(runtime);
}
}
@Test(timeout=20000)
public void testInterediateTimerWithHTBeforeWithGlobalTestServiceRollback() throws Exception {
// prepare listener to assert results
Properties properties= new Properties();
properties.setProperty("mary", "HR");
properties.setProperty("john", "HR");
UserGroupCallback userGroupCallback = new JBossUserGroupCallbackImpl(properties);
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/IntermediateCatchEventTimerCycleWithHT2.bpmn2"), ResourceType.BPMN2)
.schedulerService(globalScheduler)
.userGroupCallback(userGroupCallback)
.get();
manager = getManager(environment, true);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();
long ksessionId = ksession.getIdentifier();
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", "R3/PT1S");
ProcessInstance processInstance = ksession.startProcess("IntermediateCatchEvent", params);
assertTrue(processInstance.getState() == ProcessInstance.STATE_ACTIVE);
// get tasks
List<Status> statuses = new ArrayList<Status>();
statuses.add(Status.Reserved);
List<TaskSummary> tasks = runtime.getTaskService().getTasksAssignedAsPotentialOwnerByStatus("john", statuses, "en-UK");
assertNotNull(tasks);
assertEquals(1, tasks.size());
TaskSummary task = tasks.get(0);
runtime.getTaskService().start(task.getId(), "john");
UserTransaction ut = InitialContext.doLookup("java:comp/UserTransaction");
try {
ut.begin();
runtime.getTaskService().complete(task.getId(), "john", null);
} finally {
ut.rollback();
}
processInstance = ksession.getProcessInstance(processInstance.getId());
Collection<NodeInstance> activeNodes = ((WorkflowProcessInstance)processInstance).getNodeInstances();
assertNotNull(activeNodes);
assertEquals(1, activeNodes.size());
assertTrue(activeNodes.iterator().next() instanceof HumanTaskNodeInstance);
TimerService timerService = TimerServiceRegistry.getInstance().get(manager.getIdentifier()+TimerServiceRegistry.TIMER_SERVICE_SUFFIX);
Collection<TimerJobInstance> timerInstances = timerService.getTimerJobInstances(ksessionId);
assertNotNull(timerInstances);
assertEquals(0, timerInstances.size());
// clean up
ksession.abortProcessInstance(processInstance.getId());
manager.disposeRuntimeEngine(runtime);
}
@Test(timeout=20000)
public void testInterediateBoundaryTimerWithGlobalTestServiceRollback() throws Exception {
Properties properties= new Properties();
properties.setProperty("mary", "HR");
properties.setProperty("john", "HR");
UserGroupCallback userGroupCallback = new JBossUserGroupCallbackImpl(properties);
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/HumanTaskWithBoundaryTimer.bpmn"), ResourceType.BPMN2)
.schedulerService(globalScheduler)
.userGroupCallback(userGroupCallback)
.get();
manager = getManager(environment, true);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();
long ksessionId = ksession.getIdentifier();
ProcessInstance processInstance;
UserTransaction ut = InitialContext.doLookup("java:comp/UserTransaction");
try {
ut.begin();
Map<String, Object> params = new HashMap<String, Object>();
params.put("test", "john");
processInstance = ksession.startProcess("PROCESS_1", params);
} finally {
ut.rollback();
}
manager.disposeRuntimeEngine(runtime);
try {
// two types of checks as different managers will treat it differently
// per process instance will fail on getting runtime
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
// where singleton and per request will return runtime but there should not be process instance
processInstance = runtime.getKieSession().getProcessInstance(processInstance.getId());
assertNull(processInstance);
} catch (SessionNotFoundException e) {
}
TimerService timerService = TimerServiceRegistry.getInstance().get(manager.getIdentifier()+TimerServiceRegistry.TIMER_SERVICE_SUFFIX);
Collection<TimerJobInstance> timerInstances = timerService.getTimerJobInstances(ksessionId);
assertNotNull(timerInstances);
assertEquals(0, timerInstances.size());
if (runtime != null) {
manager.disposeRuntimeEngine(runtime);
}
}
@Test
public void testHumanTaskDeadlineWithGlobalTimerService() throws Exception {
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/HumanTaskWithDeadlines.bpmn"), ResourceType.BPMN2)
.schedulerService(globalScheduler)
.get();
manager = getManager(environment, true);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();
ProcessInstance processInstance = ksession.startProcess("htdeadlinetest");
assertTrue(processInstance.getState() == ProcessInstance.STATE_ACTIVE);
List<TaskSummary> krisTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("krisv", "en-UK");
assertEquals(1, krisTasks.size());
List<TaskSummary> johnTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("john", "en-UK");
assertEquals(0, johnTasks.size());
List<TaskSummary> maryTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("mary", "en-UK");
assertEquals(0, maryTasks.size());
manager.disposeRuntimeEngine(runtime);
// now wait for 2 seconds for first reassignment
Thread.sleep(3000);
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
krisTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("krisv", "en-UK");
assertEquals(0, krisTasks.size());
johnTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("john", "en-UK");
assertEquals(1, johnTasks.size());
maryTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("mary", "en-UK");
assertEquals(0, maryTasks.size());
runtime.getTaskService().start(johnTasks.get(0).getId(), "john");
manager.disposeRuntimeEngine(runtime);
// now wait for 2 more seconds for second reassignment
Thread.sleep(2000);
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
krisTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("krisv", "en-UK");
assertEquals(0, krisTasks.size());
johnTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("john", "en-UK");
assertEquals(1, johnTasks.size());
maryTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("mary", "en-UK");
assertEquals(0, maryTasks.size());
manager.disposeRuntimeEngine(runtime);
// now wait for 1 seconds to make sure that reassignment did not happen any more since task was already started
Thread.sleep(3000);
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
krisTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("krisv", "en-UK");
assertEquals(0, krisTasks.size());
johnTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("john", "en-UK");
assertEquals(0, johnTasks.size());
maryTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("mary", "en-UK");
assertEquals(1, maryTasks.size());
runtime.getTaskService().start(maryTasks.get(0).getId(), "mary");
runtime.getTaskService().complete(maryTasks.get(0).getId(), "mary", null);
manager.disposeRuntimeEngine(runtime);
// now wait for 2 seconds to make sure that reassignment did not happen any more since task was completed
Thread.sleep(2000);
try {
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
ksession = runtime.getKieSession();
processInstance = ksession.getProcessInstance(processInstance.getId());
assertNull(processInstance);
} catch (SessionNotFoundException e) {
// this can be thrown for per process instance strategy as instance has already been completed
}
manager.disposeRuntimeEngine(runtime);
}
@Test
public void testHumanTaskDeadlineWithGlobalTimerServiceMultipleInstances() throws Exception {
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/HumanTaskWithDeadlines.bpmn"), ResourceType.BPMN2)
.schedulerService(globalScheduler)
.get();
manager = getManager(environment, true);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();
ProcessInstance processInstance = ksession.startProcess("htdeadlinetest");
manager.disposeRuntimeEngine(runtime);
RuntimeEngine runtime2 = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession2 = runtime2.getKieSession();
ProcessInstance processInstance2 = ksession2.startProcess("htdeadlinetest");
// abort second instance to trigger unschedule of deadlines
ksession2.abortProcessInstance(processInstance2.getId());
manager.disposeRuntimeEngine(runtime2);
assertTrue(processInstance.getState() == ProcessInstance.STATE_ACTIVE);
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
List<TaskSummary> krisTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("krisv", "en-UK");
assertEquals(1, krisTasks.size());
List<TaskSummary> johnTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("john", "en-UK");
assertEquals(0, johnTasks.size());
List<TaskSummary> maryTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("mary", "en-UK");
assertEquals(0, maryTasks.size());
manager.disposeRuntimeEngine(runtime);
// now wait for 2 seconds for first reassignment
Thread.sleep(3000);
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
krisTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("krisv", "en-UK");
assertEquals(0, krisTasks.size());
johnTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("john", "en-UK");
assertEquals(1, johnTasks.size());
maryTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("mary", "en-UK");
assertEquals(0, maryTasks.size());
runtime.getTaskService().start(johnTasks.get(0).getId(), "john");
manager.disposeRuntimeEngine(runtime);
// now wait for 2 more seconds for second reassignment
Thread.sleep(2000);
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
krisTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("krisv", "en-UK");
assertEquals(0, krisTasks.size());
johnTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("john", "en-UK");
assertEquals(1, johnTasks.size());
maryTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("mary", "en-UK");
assertEquals(0, maryTasks.size());
manager.disposeRuntimeEngine(runtime);
// now wait for 1 seconds to make sure that reassignment did not happen any more since task was already started
Thread.sleep(3000);
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
krisTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("krisv", "en-UK");
assertEquals(0, krisTasks.size());
johnTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("john", "en-UK");
assertEquals(0, johnTasks.size());
maryTasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("mary", "en-UK");
assertEquals(1, maryTasks.size());
runtime.getTaskService().start(maryTasks.get(0).getId(), "mary");
runtime.getTaskService().complete(maryTasks.get(0).getId(), "mary", null);
manager.disposeRuntimeEngine(runtime);
// now wait for 2 seconds to make sure that reassignment did not happen any more since task was completed
Thread.sleep(2000);
try {
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
ksession = runtime.getKieSession();
processInstance = ksession.getProcessInstance(processInstance.getId());
assertNull(processInstance);
} catch (SessionNotFoundException e) {
// this can be thrown for per process instance strategy as instance has already been completed
}
manager.disposeRuntimeEngine(runtime);
}
@Test(timeout=20000)
public void testInterediateTimerWithGlobalTestServiceSimulateCMT() throws Exception {
CountDownProcessEventListener countDownListener = new CountDownProcessEventListener("timer", 3);
// prepare listener to assert results
final List<Long> timerExporations = new ArrayList<Long>();
ProcessEventListener listener = new DefaultProcessEventListener(){
@Override
public void afterNodeLeft(ProcessNodeLeftEvent event) {
if (event.getNodeInstance().getNodeName().equals("timer")) {
timerExporations.add(event.getProcessInstance().getId());
}
}
};
Properties properties= new Properties();
properties.setProperty("mary", "HR");
properties.setProperty("john", "HR");
UserGroupCallback userGroupCallback = new JBossUserGroupCallbackImpl(properties);
EntityManagerFactory emf = EntityManagerFactoryManager.get().getOrCreate("org.jbpm.test.persistence");
TransactionManager tm = new ContainerManagedTransactionManager();
Environment env = EnvironmentFactory.newEnvironment();
env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, emf);
env.set(EnvironmentName.TRANSACTION_MANAGER, tm);
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/IntermediateCatchEventTimerCycleWithHT2.bpmn2"), ResourceType.BPMN2)
.addEnvironmentEntry(EnvironmentName.TRANSACTION_MANAGER, tm)
.addEnvironmentEntry(EnvironmentName.PERSISTENCE_CONTEXT_MANAGER, new JpaProcessPersistenceContextManager(env))
.addEnvironmentEntry(EnvironmentName.TASK_PERSISTENCE_CONTEXT_MANAGER, new JPATaskPersistenceContextManager(env))
.schedulerService(globalScheduler)
.registerableItemsFactory(new TestRegisterableItemsFactory(listener, countDownListener))
.userGroupCallback(userGroupCallback)
.get();
RuntimeEngine runtime;
KieSession ksession;
ProcessInstance processInstance;
UserTransaction ut = InitialContext.doLookup("java:comp/UserTransaction");
try {
ut.begin();
manager = getManager(environment, true);
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
ksession = runtime.getKieSession();
Map<String, Object> params = new HashMap<String, Object>();
params.put("x", "R3/PT1S");
processInstance = ksession.startProcess("IntermediateCatchEvent", params);
ut.commit();
} catch (Exception ex) {
ut.rollback();
throw ex;
}
assertTrue(processInstance.getState() == ProcessInstance.STATE_ACTIVE);
ut = InitialContext.doLookup("java:comp/UserTransaction");
try {
ut.begin();
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
ksession = runtime.getKieSession();
// get tasks
List<Status> statuses = new ArrayList<Status>();
statuses.add(Status.Reserved);
List<TaskSummary> tasks = runtime.getTaskService().getTasksAssignedAsPotentialOwnerByStatus("john", statuses, "en-UK");
assertNotNull(tasks);
assertEquals(1, tasks.size());
for (TaskSummary task : tasks) {
runtime.getTaskService().start(task.getId(), "john");
runtime.getTaskService().complete(task.getId(), "john", null);
}
ut.commit();
} catch (Exception ex) {
ut.rollback();
throw ex;
}
// now wait for 1 second for first timer to trigger
countDownListener.waitTillCompleted();
countDownListener.reset(1);
ut = InitialContext.doLookup("java:comp/UserTransaction");
try {
ut.begin();
try {
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
ksession = runtime.getKieSession();
processInstance = ksession.getProcessInstance(processInstance.getId());
assertNull(processInstance);
} catch (SessionNotFoundException e) {
// expected for PerProcessInstanceManagers since process instance is completed
}
ut.commit();
} catch (Exception ex) {
ut.rollback();
throw ex;
}
// let's wait to ensure no more timers are expired and triggered
countDownListener.waitTillCompleted(3000);
assertEquals(3, timerExporations.size());
}
@Test(timeout=20000)
public void testTimerFailureAndRetrigger() throws Exception {
CountDownProcessEventListener countDownListener = new CountDownProcessEventListener("Timer_1m", 3);
final List<Long> timerExporations = new ArrayList<Long>();
ProcessEventListener listener = new DefaultProcessEventListener(){
@Override
public void afterNodeTriggered(ProcessNodeTriggeredEvent event) {
if (event.getNodeInstance().getNodeName().equals("Timer_1m")) {
timerExporations.add(event.getNodeInstance().getId());
}
}
};
environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("org/jbpm/test/functional/timer/helloretrigger.bpmn2"), ResourceType.BPMN2)
.schedulerService(globalScheduler)
.registerableItemsFactory(new TestRegisterableItemsFactory(listener, countDownListener))
.get();
manager = getManager(environment, false);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();
Map<String, Object> params = new HashMap<>();
ProcessInstance pi = ksession.startProcess("rescheduletimer.helloretrigger", params);
assertEquals("Process instance should be active", ProcessInstance.STATE_ACTIVE, pi.getState());
manager.disposeRuntimeEngine(runtime);
final long processInstanceId = pi.getId();
// let the timer (every 2 sec) fire three times as third will fail on gateway
countDownListener.waitTillCompleted(8000);
assertEquals("There should be only 3 nodes as there third is failing", 3, timerExporations.size());
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstanceId));
ksession = runtime.getKieSession();
ksession.execute(new ExecutableCommand<Void>() {
@Override
public Void execute(Context context) {
KieSession ksession = ((RegistryContext) context).lookup( KieSession.class );
ProcessInstance pi = (ProcessInstance) ksession.getProcessInstance(processInstanceId);
((WorkflowProcessInstance) pi).setVariable("fixed", true);
return null;
}
});
manager.disposeRuntimeEngine(runtime);
countDownListener.reset(1);
countDownListener.waitTillCompleted(5000);
assertEquals("There should be 3 expirations as the failing one should finally proceed", 3, timerExporations.size());
try {
runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstanceId));
ksession = runtime.getKieSession();
pi = ksession.getProcessInstance(processInstanceId);
assertNull(pi);
} catch (SessionNotFoundException e) {
// expected for PerProcessInstanceManagers since process instance is completed
}
((AbstractRuntimeManager)manager).close(true);
}
public static void cleanupSingletonSessionId() {
File tempDir = new File(System.getProperty("java.io.tmpdir"));
if (tempDir.exists()) {
String[] jbpmSerFiles = tempDir.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith("-jbpmSessionId.ser");
}
});
for (String file : jbpmSerFiles) {
new File(tempDir, file).delete();
}
}
}
}