/* * 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.regression.event; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.assertj.core.api.Assertions; import org.jbpm.test.JbpmTestCase; import org.junit.Test; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.manager.audit.AuditService; import org.kie.api.runtime.process.ProcessInstance; import org.kie.api.runtime.process.WorkItem; import org.kie.api.runtime.process.WorkItemHandler; import org.kie.api.runtime.process.WorkItemManager; import org.kie.api.task.TaskService; import org.kie.api.task.model.Task; import qa.tools.ikeeper.annotation.BZ; public class TimerEventTest extends JbpmTestCase { private static final String EXCEPTION_AFTER_TIMER = "org/jbpm/test/regression/event/TimerEvent-exceptionAfter.bpmn2"; private static final String EXCEPTION_AFTER_TIMER_ID = "org.jbpm.test.regression.event.TimerEvent-exceptionAfter"; private static final String START_TIMER_CYCLE = "org/jbpm/test/regression/event/TimerEvent-startTimerCycle.bpmn2"; private static final String START_TIMER_CYCLE_ID = "org.jbpm.test.regression.event.TimerEvent-startTimerCycle"; private static final String CANCELLED_TIMER = "org/jbpm/test/regression/event/TimerEvent-cancelledTimer.bpmn"; private static final String CANCELLED_TIMER_ID = "org.jbpm.test.regression.event.TimerEvent-cancelledTimer"; private static final String TIMER_AND_GATEWAY = "org/jbpm/test/regression/event/TimerEvent-timerAndGateway.bpmn"; private static final String TIMER_AND_GATEWAY_ID = "org.jbpm.test.regression.event.TimerEvent-timerAndGateway"; public static final String BOUNDARY_MULTIPLE_INSTANCES = "org/jbpm/test/regression/event/TimerEvent-boundaryMultipleInstances.bpmn2"; public static final String BOUNDARY_MULTIPLE_INSTANCES_ID = "org.jbpm.test.regression.event.TimerEvent-boundaryMultipleInstances"; @Test @BZ({"958390", "1167738"}) public void testRuntimeExceptionAfterTimer() throws InterruptedException { KieSession ksession = createKSession(EXCEPTION_AFTER_TIMER); ProcessInstance pi = ksession.startProcess(EXCEPTION_AFTER_TIMER_ID); Thread.sleep(3000); assertProcessInstanceActive(pi.getId()); ksession.abortProcessInstance(pi.getId()); assertProcessInstanceAborted(pi.getId()); } @Test @BZ("1104563") public void testStartTimerCycle() throws InterruptedException { KieSession ksession = createKSession(START_TIMER_CYCLE); AuditService auditService = getLogService(); ksession.startProcess(START_TIMER_CYCLE_ID); Assertions.assertThat(auditService.findProcessInstances()).hasSize(1); Thread.sleep(5000); Assertions.assertThat(auditService.findProcessInstances()).hasSize(2); Thread.sleep(5000); Assertions.assertThat(auditService.findProcessInstances()).hasSize(3); } @Test @BZ("1148304") public void testCancelledTimerNotScheduled() { for (int i = 0; i < 5; i++) { createRuntimeManager(Strategy.PROCESS_INSTANCE, (String) null, CANCELLED_TIMER); KieSession ksession = getRuntimeEngine().getKieSession(); TaskService taskService = getRuntimeEngine().getTaskService(); Map<String, Object> params = new HashMap<String, Object>(); ProcessInstance pi = ksession.startProcess(CANCELLED_TIMER_ID, params); System.out.println("A process instance started : pid = " + pi.getId()); List<Long> list = taskService.getTasksByProcessInstanceId(pi.getId()); for (long taskId : list) { Task task = taskService.getTaskById(taskId); System.out.println("taskId = " + task.getId() + ", status = " + task.getTaskData().getStatus()); } Date before = new Date(); disposeRuntimeManager(); // Check if engine did not waited for timer Date after = new Date(); long seconds = (after.getTime() - before.getTime()) / 1000; Assertions.assertThat(seconds).as("Cancelled timer has been scheduled").isLessThan(5); } } @Test @BZ("1036761") public void testTimerAndGateway() throws Exception { KieSession ksession = createKSession(TIMER_AND_GATEWAY); int sessionId = ksession.getId(); TestAsyncWorkItemHandler handler1 = new TestAsyncWorkItemHandler(); TestAsyncWorkItemHandler handler2 = new TestAsyncWorkItemHandler(); ksession.getWorkItemManager().registerWorkItemHandler("task1", handler1); ksession.getWorkItemManager().registerWorkItemHandler("task2", handler2); ProcessInstance instance = ksession.createProcessInstance(TIMER_AND_GATEWAY_ID, new HashMap<String, Object>()); ksession.startProcessInstance(instance.getId()); WorkItem workItem1 = handler1.getWorkItem(); Assertions.assertThat(workItem1).isNotNull(); Assertions.assertThat(handler1.getWorkItem()).isNull(); // first safe state: task1 completed ksession.getWorkItemManager().completeWorkItem(workItem1.getId(), null); ksession = restoreKSession(TIMER_AND_GATEWAY); ksession.getWorkItemManager().registerWorkItemHandler("task1", handler1); ksession.getWorkItemManager().registerWorkItemHandler("task2", handler2); // second safe state: timer completed, waiting on task2 for (int i = 0; i < 7; i++) { Thread.sleep(1000); } WorkItem workItem2 = handler2.getWorkItem(); // Both sides of the join are completed. But on the process instance, there are two JoinInstance for the same // Join, and since it is an AND join, it never reaches task2. It fails after the next assertion Assertions.assertThat(workItem2).isNotNull(); Assertions.assertThat(handler1.getWorkItem()).isNull(); } @Test @BZ("1213209") public void testBoundaryTimerInMultipleInstancesSubprocess() throws InterruptedException { KieSession ksession = createKSession(BOUNDARY_MULTIPLE_INSTANCES); ksession.setGlobal("counter", 0); Set<Integer> runList = new HashSet<Integer>(); runList.add(1); runList.add(2); runList.add(3); Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put("runList", runList); ksession.startProcess(BOUNDARY_MULTIPLE_INSTANCES_ID, parameters); // wait for 3x 50ms timers to be triggered Thread.sleep(1000); Integer counter = (Integer) ksession.getGlobal("counter"); Assertions.assertThat(counter).isEqualTo(3); } private static class TestAsyncWorkItemHandler implements WorkItemHandler { private WorkItem workItem; private int activations = 0; public void executeWorkItem(WorkItem workItem, WorkItemManager manager) { System.out.println("Starting call to handler " + workItem.getName()); this.workItem = workItem; this.activations++; } public void abortWorkItem(WorkItem workItem, WorkItemManager manager) { this.workItem = null; } public WorkItem getWorkItem() { WorkItem result = workItem; workItem = null; return result; } public int getActivations() { return activations; } } }