/* * 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.migration; import bitronix.tm.BitronixTransactionManager; import bitronix.tm.TransactionManagerServices; import org.drools.core.command.impl.ExecutableCommand; import org.drools.core.command.impl.RegistryContext; import org.jbpm.persistence.processinstance.ProcessInstanceInfo; import org.jbpm.test.JbpmTestCase; import org.jbpm.workflow.instance.WorkflowProcessInstance; import org.jbpm.workflow.instance.WorkflowProcessInstanceUpgrader; import org.jbpm.workflow.instance.node.WorkItemNodeInstance; import org.junit.Before; import org.junit.Test; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.manager.RuntimeEngine; import org.kie.api.runtime.manager.RuntimeManager; import org.kie.api.runtime.manager.audit.VariableInstanceLog; import org.kie.api.runtime.process.NodeInstance; 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.TaskSummary; import javax.persistence.EntityManager; import javax.persistence.Query; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class ProcessInstanceMigrationTest extends JbpmTestCase { public ProcessInstanceMigrationTest() { super(true, true); } private RuntimeManager manager; private RuntimeEngine engine; private KieSession ksession; private TaskService taskService; private BitronixTransactionManager transactionManager; @Before public void init() throws Exception { manager = createRuntimeManager("org/jbpm/test/functional/migration/sample.bpmn2", "org/jbpm/test/functional/migration/sample2.bpmn2", "org/jbpm/test/functional/migration/BPMN2-ProcessVersion-3.bpmn2", "org/jbpm/test/functional/migration/BPMN2-ProcessVersion-4.bpmn2", "org/jbpm/test/functional/migration/sample3.bpmn2", "org/jbpm/test/functional/migration/sample4.bpmn2", "org/jbpm/test/functional/migration/subprocess1.bpmn2", "org/jbpm/test/functional/migration/subprocess2.bpmn2"); engine = manager.getRuntimeEngine(null); ksession = engine.getKieSession(); taskService = engine.getTaskService(); transactionManager = TransactionManagerServices.getTransactionManager(); transactionManager.setTransactionTimeout(3600); // longer timeout // for a debugger } @Test public void testProcessInstanceMigration() throws Exception { ProcessInstance p = ksession.startProcess("com.sample.bpmn.migration"); long pid = p.getId(); assertEquals("com.sample.bpmn.migration", ksession.getProcessInstance(pid).getProcessId()); // let john execute Task 1 List<TaskSummary> list = assertTaskAssignedTo("john"); // upgrade to version to of the process UpgradeCommand c = new UpgradeCommand(pid, null, "com.sample.bpmn.migration2"); ksession.execute(c); completeTask(list.get(0)); // in second version of the process second user task is for mary while // for first version it's for john list = assertTaskAssignedTo("mary"); assertDefinitionChanged(pid, "com.sample.bpmn.migration2", false); } @Test public void testProcessInstanceMigrationWithGatewaysAndSameUniqueId() throws Exception { PersistenceWorkItemHandler handler = new PersistenceWorkItemHandler(); ksession.getWorkItemManager().registerWorkItemHandler("Human Task", handler); Map<String, Object> params = new HashMap<String, Object>(); params.put("x", 5); WorkflowProcessInstance processInstance = (WorkflowProcessInstance) ksession.startProcess("com.sample.BPMN2ProcessVersion3", params); long pid = processInstance.getId(); assertEquals(1, processInstance.getNodeInstances().size()); Map<String, String> mapping = new HashMap<String, String>(); mapping.put("UserTask", "StartTask"); // upgrade to version to of the process ExplicitUpgradeCommand c = new ExplicitUpgradeCommand(pid, mapping, "com.sample.BPMN2ProcessVersion4"); ksession.execute(c); assertEquals("com.sample.BPMN2ProcessVersion4", ksession.getProcessInstance(pid).getProcessId()); assertEquals(1, processInstance.getNodeInstances().size()); NodeInstance current = processInstance.getNodeInstances().iterator().next(); handler.completeWorkItem(((WorkItemNodeInstance)current).getWorkItem(), ksession.getWorkItemManager()); handler.completeWorkItem(handler.getWorkItem("FirstPath"), ksession.getWorkItemManager()); handler.completeWorkItem(handler.getWorkItem("SecondPath"), ksession.getWorkItemManager()); List<? extends VariableInstanceLog> vars = engine.getAuditService().findVariableInstances(pid, "x"); assertNotNull(vars); assertEquals(2, vars.size()); assertEquals("10", vars.get(1).getValue()); assertDefinitionChanged(pid, "com.sample.BPMN2ProcessVersion4", true); } @Test public void testProcessInstanceMigrationExplicit() throws Exception { ProcessInstance p = ksession.startProcess("com.sample.bpmn.migration"); long pid = p.getId(); assertEquals("com.sample.bpmn.migration", ksession.getProcessInstance(pid).getProcessId()); List<TaskSummary> list = assertTaskAssignedTo("john"); assertEquals(ProcessInstance.STATE_ACTIVE, p.getState()); Map<String, String> mapping = new HashMap<String, String>(); mapping.put("Task 1", "Task 2"); // upgrade to version to of the process ExplicitUpgradeCommand c = new ExplicitUpgradeCommand(pid, mapping, "com.sample.bpmn.migration2"); ksession.execute(c); completeTask(list.get(0)); assertDefinitionChanged(pid, "com.sample.bpmn.migration2", true); } @Test public void testProcessInstanceMigrationExplicitSubprocesses() throws Exception { PersistenceWorkItemHandler handler = new PersistenceWorkItemHandler(); ksession.getWorkItemManager().registerWorkItemHandler("Human Task", handler); WorkflowProcessInstance processInstance = (WorkflowProcessInstance) ksession.startProcess("com.sample.bpmn.migration3"); long pid = processInstance.getId(); assertEquals("com.sample.bpmn.migration3", ksession.getProcessInstance(pid).getProcessId()); assertEquals(ProcessInstance.STATE_ACTIVE, processInstance.getState()); Map<String, String> mapping = new HashMap<String, String>(); mapping.put("ForJohn", "ForMary"); ExplicitUpgradeCommand c = new ExplicitUpgradeCommand(pid, mapping, "com.sample.bpmn.migration4"); ksession.execute(c); assertEquals("com.sample.bpmn.migration4", ksession.getProcessInstance(pid).getProcessId()); handler.completeWorkItem(handler.getWorkItem("ForJohn"), ksession.getWorkItemManager()); handler.completeWorkItem(handler.getWorkItem("ForBill"), ksession.getWorkItemManager()); assertDefinitionChanged(pid, "com.sample.bpmn.migration4", true); } @Test public void testProcessInstanceMigrationImplicitSubprocess() throws Exception { ProcessInstance p = ksession.startProcess("com.sample.bpmn.migration.subprocess1"); long pid = p.getId(); assertEquals("com.sample.bpmn.migration.subprocess1", ksession.getProcessInstance(pid).getProcessId()); List<TaskSummary> list = assertTaskAssignedTo("john"); // upgrade to version to of the process UpgradeCommand c = new UpgradeCommand(pid, null, "com.sample.bpmn.migration.subprocess2"); ksession.execute(c); completeTask(list.get(0)); // in second version of the process second user task is for mary while // for first version it's for john list = assertTaskAssignedTo("mary"); assertDefinitionChanged(pid, "com.sample.bpmn.migration.subprocess2", false); } @Test public void testProcessInstanceMigrationExplicitBack() throws Exception { ProcessInstance p = ksession.startProcess("com.sample.bpmn.migration.subprocess1"); long pid = p.getId(); assertEquals("com.sample.bpmn.migration.subprocess1", ksession.getProcessInstance(pid).getProcessId()); List<TaskSummary> list = assertTaskAssignedTo("john"); completeTask(list.get(0)); Map<String, String> mapping = new HashMap<String, String>(); mapping.put("ForJohn2", "ForJohn1"); ExplicitUpgradeCommand c = new ExplicitUpgradeCommand(pid, mapping, "com.sample.bpmn.migration.subprocess2"); ksession.execute(c); list = assertTaskAssignedTo("john"); completeTask(list.get(0)); list = assertTaskAssignedTo("mary"); assertDefinitionChanged(pid, "com.sample.bpmn.migration.subprocess2", false); } private class PersistenceWorkItemHandler implements WorkItemHandler { private List<WorkItem> workItems = new ArrayList<WorkItem>(); public WorkItem getWorkItem(String nodeName) { for(WorkItem item : workItems) { if(((String) item.getParameter("NodeName")).compareTo(nodeName) == 0) { return item; } } return null; } public void completeWorkItem(WorkItem workItem, WorkItemManager manager) { manager.completeWorkItem(workItem.getId(), null); workItems.remove(workItem); } @Override public void executeWorkItem(WorkItem workItem, WorkItemManager manager) { workItems.add(workItem); } @Override public void abortWorkItem(WorkItem workItem, WorkItemManager manager) { workItems.remove(workItem); manager.abortWorkItem(workItem.getId()); } } private static class UpgradeCommand implements ExecutableCommand<Object> { private static final long serialVersionUID = -626809842544969669L; private long pid; private Map<String, Long> mapping; private String toProcessId; private UpgradeCommand(long pid, Map<String, Long> mapping, String toProcessId) { this.pid = pid; this.mapping = mapping; this.toProcessId = toProcessId; } public Object execute(org.kie.api.runtime.Context arg0) { KieSession ksession = ((RegistryContext) arg0).lookup( KieSession.class ); WorkflowProcessInstanceUpgrader.upgradeProcessInstance(ksession, pid, toProcessId, mapping); return null; } } private static class ExplicitUpgradeCommand implements ExecutableCommand<Object> { private static final long serialVersionUID = 8673518721648293556L; private long pid; private Map<String, String> mapping; private String toProcessId; private ExplicitUpgradeCommand(long pid, Map<String, String> mapping, String toProcessId) { this.pid = pid; this.mapping = mapping; this.toProcessId = toProcessId; } public Object execute(org.kie.api.runtime.Context arg0) { KieSession ksession = ((RegistryContext) arg0).lookup( KieSession.class ); WorkflowProcessInstanceUpgrader.upgradeProcessInstanceByNodeNames( ksession, pid, toProcessId, mapping); return null; } } private List<TaskSummary> assertTaskAssignedTo(String user) { List<TaskSummary> list = taskService.getTasksAssignedAsPotentialOwner(user, "en-UK"); assertNotNull(list); assertEquals(1, list.size()); return list; } private void completeTask(TaskSummary task) { taskService.start(task.getId(), "john"); taskService.complete(task.getId(), "john", null); } @SuppressWarnings("unchecked") private void assertDefinitionChanged(long pid, String sPid, boolean complete) throws InterruptedException { if(!complete) { assertEquals(sPid, ksession.getProcessInstance(pid).getProcessId()); } EntityManager em = getEmf().createEntityManager(); Query query = em.createQuery( "select p from ProcessInstanceInfo p where p.processInstanceId = :pid") .setParameter("pid", pid); List<ProcessInstanceInfo> found = query.getResultList(); int count = (complete) ? 0 : 1; assertNotNull(found); assertEquals(count, found.size()); if(!complete) { ProcessInstanceInfo instance = found.get(0); assertEquals(sPid, instance.getProcessId()); } manager.disposeRuntimeEngine(engine); } }