/* * 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.runtime.manager.concurrent; import bitronix.tm.resource.jdbc.PoolingDataSource; import org.drools.core.command.SingleSessionCommandService; import org.drools.core.command.impl.CommandBasedStatefulKnowledgeSession; import org.hibernate.StaleObjectStateException; import org.jbpm.runtime.manager.util.TestUtil; import org.jbpm.services.task.exception.PermissionDeniedException; import org.jbpm.services.task.identity.JBossUserGroupCallbackImpl; import org.jbpm.test.util.AbstractBaseTest; import org.jbpm.workflow.instance.node.HumanTaskNodeInstance; import org.junit.After; import org.junit.Before; 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.io.ResourceType; 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.manager.RuntimeManagerFactory; import org.kie.api.runtime.manager.audit.AuditService; import org.kie.api.runtime.manager.audit.ProcessInstanceLog; import org.kie.api.runtime.process.ProcessInstance; import org.kie.api.runtime.process.WorkflowProcessInstance; 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.context.EmptyContext; import org.kie.internal.runtime.manager.context.ProcessInstanceIdContext; import org.kie.internal.task.api.UserGroupCallback; import javax.naming.InitialContext; import javax.persistence.OptimisticLockException; import javax.transaction.UserTransaction; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Properties; import static org.junit.Assert.*; @RunWith(Parameterized.class) public class SessionTest extends AbstractBaseTest { private long maxWaitTime = 60*1000; // max wait to complete operation is set to 60 seconds to avoid build hangs private int nbThreadsProcess = 10; private int nbThreadsTask = 10; private int nbInvocations = 10; private transient int completedStart = 0; private transient int completedTask = 0; private PoolingDataSource pds; private UserGroupCallback userGroupCallback; private RuntimeManager manager; private boolean useLocking; public SessionTest(boolean locking) { this.useLocking = locking; } @Parameters public static Collection<Object[]> persistence() { Object[][] data = new Object[][] { { false } }; return Arrays.asList(data); }; @Before public void setup() { TestUtil.cleanupSingletonSessionId(); Properties properties= new Properties(); properties.setProperty("mary", "HR"); properties.setProperty("john", "HR"); userGroupCallback = new JBossUserGroupCallbackImpl(properties); pds = TestUtil.setupPoolingDataSource(); } @After public void teardown() { pds.close(); if (manager != null) { manager.close(); } } @Test @Ignore public void testSingletonSessionMemory() throws Exception { for (int i = 0; i < 1000; i++) { RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .userGroupCallback(userGroupCallback) .addAsset(ResourceFactory.newClassPathResource("sample.bpmn"), ResourceType.BPMN2) .get(); RuntimeManager manager = RuntimeManagerFactory.Factory.get().newSingletonRuntimeManager(environment); RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); manager.disposeRuntimeEngine(runtime); manager.close(); System.gc(); Thread.sleep(100); System.gc(); logger.info("Used memory {}", Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()); } } @Test public void testSingletonSession() throws Exception { RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .userGroupCallback(userGroupCallback) .addAsset(ResourceFactory.newClassPathResource("sample.bpmn"), ResourceType.BPMN2) .get(); if( useLocking ) { environment.getEnvironment().set(EnvironmentName.USE_PESSIMISTIC_LOCKING, true); } long startTimeStamp = System.currentTimeMillis(); long maxEndTime = startTimeStamp + maxWaitTime; manager = RuntimeManagerFactory.Factory.get().newSingletonRuntimeManager(environment); completedStart = 0; for (int i=0; i<nbThreadsProcess; i++) { new Thread(new StartProcessRunnable(manager, i)).start(); } completedTask = 0; for (int i=0; i<nbThreadsTask; i++) { new Thread(new CompleteTaskRunnable(manager, i)).start(); } while (completedStart < nbThreadsProcess || completedTask < nbThreadsTask) { Thread.sleep(100); if (System.currentTimeMillis() > maxEndTime) { fail("Failure, did not finish in time most likely hanging"); } } Thread.sleep(1000); //make sure all process instance were completed RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); AuditService logService = runtime.getAuditService(); //active List<? extends ProcessInstanceLog> logs = logService.findActiveProcessInstances("com.sample.bpmn.hello"); assertNotNull(logs); assertEquals(0, logs.size()); // completed logs = logService.findProcessInstances("com.sample.bpmn.hello"); assertNotNull(logs); assertEquals(nbThreadsProcess*nbInvocations, logs.size()); logger.debug("Done"); manager.disposeRuntimeEngine(runtime); } @Test public void testNewSession() throws Exception { RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .userGroupCallback(userGroupCallback) .addAsset(ResourceFactory.newClassPathResource("sample.bpmn"), ResourceType.BPMN2) .get(); if( useLocking ) { environment.getEnvironment().set(EnvironmentName.USE_PESSIMISTIC_LOCKING, true); } long startTimeStamp = System.currentTimeMillis(); long maxEndTime = startTimeStamp + maxWaitTime; manager = RuntimeManagerFactory.Factory.get().newPerRequestRuntimeManager(environment); completedStart = 0; for (int i=0; i<nbThreadsProcess; i++) { new StartProcessRunnable(manager, i).run(); } completedTask = 0; for (int i=0; i<nbThreadsTask; i++) { new Thread(new CompleteTaskRunnable(manager, i)).start(); } while (completedStart < nbThreadsProcess || completedTask < nbThreadsTask) { Thread.sleep(100); if (System.currentTimeMillis() > maxEndTime) { fail("Failure, did not finish in time most likely hanging"); } } //make sure all process instance were completed RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); AuditService logService = runtime.getAuditService(); //active List<? extends ProcessInstanceLog> logs = logService.findActiveProcessInstances("com.sample.bpmn.hello"); assertNotNull(logs); assertEquals(0, logs.size()); // completed logs = logService.findProcessInstances("com.sample.bpmn.hello"); assertNotNull(logs); assertEquals(nbThreadsProcess*nbInvocations, logs.size()); logger.debug("Done"); manager.disposeRuntimeEngine(runtime); } @Test public void testSessionPerProcessInstance() throws Exception { RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .userGroupCallback(userGroupCallback) .addAsset(ResourceFactory.newClassPathResource("sample.bpmn"), ResourceType.BPMN2) .get(); if( useLocking ) { environment.getEnvironment().set(EnvironmentName.USE_PESSIMISTIC_LOCKING, true); } long startTimeStamp = System.currentTimeMillis(); long maxEndTime = startTimeStamp + maxWaitTime; manager = RuntimeManagerFactory.Factory.get().newPerProcessInstanceRuntimeManager(environment); completedStart = 0; for (int i=0; i<nbThreadsProcess; i++) { new StartProcessPerProcessInstanceRunnable(manager, i).run(); } completedTask = 0; for (int i=0; i<nbThreadsTask; i++) { new Thread(new CompleteTaskPerProcessInstanceRunnable(manager, i)).start(); } while (completedStart < nbThreadsProcess || completedTask < nbThreadsTask) { Thread.sleep(100); if (System.currentTimeMillis() > maxEndTime) { fail("Failure, did not finish in time most likely hanging"); } } RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); //make sure all process instance were completed AuditService logService = runtime.getAuditService(); //active List<? extends ProcessInstanceLog> logs = logService.findActiveProcessInstances("com.sample.bpmn.hello"); assertNotNull(logs); assertEquals(0, logs.size()); // completed logs = logService.findProcessInstances("com.sample.bpmn.hello"); assertNotNull(logs); assertEquals(nbThreadsProcess*nbInvocations, logs.size()); logger.debug("Done"); manager.disposeRuntimeEngine(runtime); } @Test public void testNewSessionSuccess() throws Exception { RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .userGroupCallback(userGroupCallback) .addAsset(ResourceFactory.newClassPathResource("sample.bpmn"), ResourceType.BPMN2) .get(); if( useLocking ) { environment.getEnvironment().set(EnvironmentName.USE_PESSIMISTIC_LOCKING, true); } manager = RuntimeManagerFactory.Factory.get().newPerRequestRuntimeManager(environment); RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); KieSession ksession = runtime.getKieSession(); UserTransaction ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" ); ut.begin(); ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello", null); logger.debug("Started process instance {}", processInstance.getId()); long workItemId = ((HumanTaskNodeInstance) ((WorkflowProcessInstance) processInstance).getNodeInstances().iterator().next()).getWorkItemId(); long taskId = runtime.getTaskService().getTaskByWorkItemId(workItemId).getId(); runtime.getTaskService().claim(taskId, "mary"); ut.commit(); List<Status> statusses = new ArrayList<Status>(); statusses.add(Status.Reserved); runtime = manager.getRuntimeEngine(EmptyContext.get()); assertNotNull(runtime.getKieSession().getProcessInstance(processInstance.getId())); List<TaskSummary> tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(1, tasks.size()); taskId = tasks.get(0).getId(); ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" ); ut.begin(); runtime.getTaskService().start(taskId, "mary"); runtime.getTaskService().complete(taskId, "mary", null); ut.commit(); assertNull(runtime.getKieSession().getProcessInstance(processInstance.getId())); tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(0, tasks.size()); manager.disposeRuntimeEngine(runtime); runtime = manager.getRuntimeEngine(EmptyContext.get()); ksession = runtime.getKieSession(); ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" ); ut.begin(); processInstance = ksession.startProcess("com.sample.bpmn.hello", null); workItemId = ((HumanTaskNodeInstance) ((WorkflowProcessInstance) processInstance).getNodeInstances().iterator().next()).getWorkItemId(); taskId = runtime.getTaskService().getTaskByWorkItemId(workItemId).getId(); runtime.getTaskService().claim(taskId, "mary"); logger.debug("Started process instance {}", processInstance.getId()); ut.commit(); assertNotNull(ksession.getProcessInstance(processInstance.getId())); tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(1, tasks.size()); taskId = tasks.get(0).getId(); ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" ); ut.begin(); runtime.getTaskService().start(taskId, "mary"); runtime.getTaskService().complete(taskId, "mary", null); ut.commit(); assertNull(ksession.getProcessInstance(processInstance.getId())); tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(0, tasks.size()); manager.disposeRuntimeEngine(runtime); } @Test public void testNewSessionFail() throws Exception { RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .userGroupCallback(userGroupCallback) .addAsset(ResourceFactory.newClassPathResource("sample.bpmn"), ResourceType.BPMN2) .get(); if( useLocking ) { environment.getEnvironment().set(EnvironmentName.USE_PESSIMISTIC_LOCKING, true); } manager = RuntimeManagerFactory.Factory.get().newPerRequestRuntimeManager(environment); RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); KieSession ksession = runtime.getKieSession(); UserTransaction ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" ); ut.begin(); ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello", null); logger.debug("Started process instance {}", processInstance.getId()); long workItemId = ((HumanTaskNodeInstance) ((WorkflowProcessInstance) processInstance).getNodeInstances().iterator().next()).getWorkItemId(); long taskId = runtime.getTaskService().getTaskByWorkItemId(workItemId).getId(); runtime.getTaskService().claim(taskId, "mary"); ut.rollback(); logger.debug("Rolled back"); // TODO: whenever transaction fails, do we need to dispose? can we? // sessionManager.dispose(); List<Status> statusses = new ArrayList<Status>(); statusses.add(Status.Reserved); runtime = manager.getRuntimeEngine(EmptyContext.get()); assertNull(runtime.getKieSession().getProcessInstance(processInstance.getId())); List<TaskSummary> tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(0, tasks.size()); ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" ); ut.begin(); processInstance = runtime.getKieSession().startProcess("com.sample.bpmn.hello", null); workItemId = ((HumanTaskNodeInstance) ((WorkflowProcessInstance) processInstance).getNodeInstances().iterator().next()).getWorkItemId(); taskId = runtime.getTaskService().getTaskByWorkItemId(workItemId).getId(); runtime.getTaskService().claim(taskId, "mary"); logger.debug("Started process instance {}", processInstance.getId()); ut.commit(); assertNotNull(runtime.getKieSession().getProcessInstance(processInstance.getId())); tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(1, tasks.size()); taskId = tasks.get(0).getId(); ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" ); ut.begin(); runtime.getTaskService().start(taskId, "mary"); runtime.getTaskService().complete(taskId, "mary", null); ut.rollback(); manager.disposeRuntimeEngine(runtime); runtime = manager.getRuntimeEngine(EmptyContext.get()); assertNotNull(runtime.getKieSession().getProcessInstance(processInstance.getId())); tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(1, tasks.size()); taskId = tasks.get(0).getId(); ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" ); ut.begin(); runtime.getTaskService().start(taskId, "mary"); runtime.getTaskService().complete(taskId, "mary", null); ut.commit(); assertNull(runtime.getKieSession().getProcessInstance(processInstance.getId())); tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(0, tasks.size()); manager.disposeRuntimeEngine(runtime); } @Test public void testNewSessionFailBefore() throws Exception { RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .userGroupCallback(userGroupCallback) .addAsset(ResourceFactory.newClassPathResource("sampleFailBefore.bpmn"), ResourceType.BPMN2) .get(); if( useLocking ) { environment.getEnvironment().set(EnvironmentName.USE_PESSIMISTIC_LOCKING, true); } manager = RuntimeManagerFactory.Factory.get().newPerRequestRuntimeManager(environment); RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); try{ ProcessInstance processInstance = runtime.getKieSession().startProcess("com.sample.bpmn.hello", null); fail("Started process instance " + processInstance.getId()); } catch (RuntimeException e) { // do nothing } // TODO: whenever transaction fails, do we need to dispose? can we? // sessionManager.dispose(); manager.disposeRuntimeEngine(runtime); List<Status> statusses = new ArrayList<Status>(); statusses.add(Status.Reserved); runtime = manager.getRuntimeEngine(EmptyContext.get()); List<TaskSummary> tasks = runtime.getTaskService().getTasksAssignedAsPotentialOwner("mary", "en-UK"); assertEquals(0, tasks.size()); manager.disposeRuntimeEngine(runtime); } @Test public void testNewSessionFailAfter() throws Exception { RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .userGroupCallback(userGroupCallback) .addAsset(ResourceFactory.newClassPathResource("sampleFailAfter.bpmn"), ResourceType.BPMN2) .get(); if( useLocking ) { environment.getEnvironment().set(EnvironmentName.USE_PESSIMISTIC_LOCKING, true); } manager = RuntimeManagerFactory.Factory.get().newPerRequestRuntimeManager(environment); RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); ProcessInstance processInstance = runtime.getKieSession().startProcess("com.sample.bpmn.hello.fa", null); long workItemId = ((HumanTaskNodeInstance) ((WorkflowProcessInstance) processInstance).getNodeInstances().iterator().next()).getWorkItemId(); long taskId = runtime.getTaskService().getTaskByWorkItemId(workItemId).getId(); runtime.getTaskService().claim(taskId, "mary"); List<Status> statusses = new ArrayList<Status>(); statusses.add(Status.Reserved); List<TaskSummary> tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(1, tasks.size()); taskId = tasks.get(0).getId(); UserTransaction ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" ); try { ut.begin(); runtime.getTaskService().start(taskId, "mary"); runtime.getTaskService().complete(taskId, "mary", null); fail("Task completed"); } catch (RuntimeException e) { // do nothing e.printStackTrace(); } try { ut.rollback(); } catch(Exception e) { } // TODO: whenever transaction fails, do we need to dispose? can we? // sessionManager.dispose(); manager.disposeRuntimeEngine(runtime); runtime = manager.getRuntimeEngine(EmptyContext.get()); tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(1, tasks.size()); manager.disposeRuntimeEngine(runtime); } @Test public void testNewSessionFailAfter2() throws Exception { RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .userGroupCallback(userGroupCallback) .addAsset(ResourceFactory.newClassPathResource("sampleFailAfter.bpmn"), ResourceType.BPMN2) .get(); if( useLocking ) { environment.getEnvironment().set(EnvironmentName.USE_PESSIMISTIC_LOCKING, true); } manager = RuntimeManagerFactory.Factory.get().newPerRequestRuntimeManager(environment); RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); ProcessInstance processInstance = runtime.getKieSession().startProcess("com.sample.bpmn.hello.fa", null); long workItemId = ((HumanTaskNodeInstance) ((WorkflowProcessInstance) processInstance).getNodeInstances().iterator().next()).getWorkItemId(); long taskId = runtime.getTaskService().getTaskByWorkItemId(workItemId).getId(); runtime.getTaskService().claim(taskId, "mary"); runtime.getTaskService().start(taskId, "mary"); List<Status> statusses = new ArrayList<Status>(); statusses.add(Status.InProgress); List<TaskSummary> tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(1, tasks.size()); taskId = tasks.get(0).getId(); try { runtime.getTaskService().complete(taskId, "mary", null); fail("Task completed"); } catch (RuntimeException e) { // do nothing } // TODO: whenever transaction fails, do we need to dispose? can we? // sessionManager.dispose(); manager.disposeRuntimeEngine(runtime); runtime = manager.getRuntimeEngine(EmptyContext.get()); tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); assertEquals(1, tasks.size()); manager.disposeRuntimeEngine(runtime); } private void testStartProcess(RuntimeEngine runtime) throws Exception { long taskId; synchronized((SingleSessionCommandService) ((CommandBasedStatefulKnowledgeSession) runtime.getKieSession()).getRunner()) { UserTransaction ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" ); ut.begin(); logger.debug("Starting process on ksession {}", runtime.getKieSession().getIdentifier()); ProcessInstance processInstance = runtime.getKieSession().startProcess("com.sample.bpmn.hello", null); logger.debug("Started process instance {} on ksession {}", processInstance.getId(), runtime.getKieSession().getIdentifier()); long workItemId = ((HumanTaskNodeInstance) ((WorkflowProcessInstance) processInstance).getNodeInstances().iterator().next()).getWorkItemId(); taskId = runtime.getTaskService().getTaskByWorkItemId(workItemId).getId(); logger.debug("Created task {}", taskId); runtime.getTaskService().claim(taskId, "mary"); ut.commit(); } } public class StartProcessRunnable implements Runnable { private RuntimeManager manager; @SuppressWarnings("unused") private int counter; public StartProcessRunnable(RuntimeManager manager, int counter) { this.manager = manager; this.counter = counter; } public void run() { try { for (int i=0; i<nbInvocations; i++) { RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); logger.trace("Thread {} doing call {}", counter, i); testStartProcess(runtime); manager.disposeRuntimeEngine(runtime); } logger.trace("Process thread {} completed", counter); completedStart++; } catch (Throwable t) { t.printStackTrace(); } } } private boolean testCompleteTask(RuntimeEngine runtime) throws InterruptedException, Exception { boolean result = false; List<Status> statusses = new ArrayList<Status>(); statusses.add(Status.Reserved); List<TaskSummary> tasks = null; tasks = runtime.getTaskService().getTasksOwnedByStatus("mary", statusses, "en-UK"); if (tasks.isEmpty()) { logger.debug("Task thread found no tasks"); Thread.sleep(1000); } else { long taskId = tasks.get(0).getId(); logger.debug("Completing task {}", taskId); boolean success = false; try { runtime.getTaskService().start(taskId, "mary"); success = true; } catch (PermissionDeniedException e) { // TODO can we avoid these by doing it all in one transaction? logger.debug("Task thread was too late for starting task {}", taskId); } catch (RuntimeException e) { if (isCausedByOptimisticLockingFailure(e)) { logger.debug("Task thread got in conflict when starting task {}", taskId); } else { throw e; } } if (success) { try { runtime.getTaskService().complete(taskId, "mary", null); logger.debug("Completed task {}", taskId); result = true; } catch (RuntimeException e) { if (isCausedByOptimisticLockingFailure(e)) { logger.debug("Task thread got in conflict when completing task {}", taskId); } else { throw e; } } } } return result; } private boolean testCompleteTaskByProcessInstance(RuntimeEngine runtime, long piId) throws InterruptedException, Exception { boolean result = false; List<Status> statusses = new ArrayList<Status>(); statusses.add(Status.Reserved); List<TaskSummary> tasks = null; tasks = runtime.getTaskService().getTasksByStatusByProcessInstanceId(piId, statusses, "en-UK"); if (tasks.isEmpty()) { logger.debug("Task thread found no tasks"); Thread.sleep(1000); } else { long taskId = tasks.get(0).getId(); logger.debug("Completing task {}", taskId); boolean success = false; try { runtime.getTaskService().start(taskId, "mary"); success = true; } catch (PermissionDeniedException e) { // TODO can we avoid these by doing it all in one transaction? logger.debug("Task thread was too late for starting task {}", taskId); } catch (RuntimeException e) { if (isCausedByOptimisticLockingFailure(e)) { logger.debug("Task thread got in conflict when starting task {}", taskId); } else { throw e; } } if (success) { runtime.getTaskService().complete(taskId, "mary", null); logger.debug("Completed task {}", taskId); result = true; } } return result; } public class CompleteTaskRunnable implements Runnable { private RuntimeManager manager; @SuppressWarnings("unused") private int counter; public CompleteTaskRunnable(RuntimeManager manager, int counter) { this.manager = manager; this.counter = counter; } public void run() { try { int i = 0; while (i < nbInvocations) { RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); boolean success = testCompleteTask(runtime); manager.disposeRuntimeEngine(runtime); if (success) { i++; } } completedTask++; logger.trace("Task thread {} completed", counter); } catch (Throwable t) { t.printStackTrace(); } } } public class StartProcessPerProcessInstanceRunnable implements Runnable { private RuntimeManager manager; @SuppressWarnings("unused") private int counter; public StartProcessPerProcessInstanceRunnable(RuntimeManager manager, int counter) { this.manager = manager; this.counter = counter; } public void run() { try { for (int i=0; i<nbInvocations; i++) { RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get()); logger.trace("Thread {} doing call {}", counter, i); testStartProcess(runtime); manager.disposeRuntimeEngine(runtime); } logger.trace("Process thread {} completed", counter); completedStart++; } catch (Throwable t) { t.printStackTrace(); } } } public class CompleteTaskPerProcessInstanceRunnable implements Runnable { private RuntimeManager manager; private int counter; public CompleteTaskPerProcessInstanceRunnable(RuntimeManager manager, int counter) { this.manager = manager; this.counter = counter; } public void run() { try { int i = 0; while (i < nbInvocations) { long processInstanceId = (nbInvocations *counter)+1 + i; logger.trace("pi id {} counter {}", processInstanceId, counter); RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstanceId)); boolean success = false; success = testCompleteTaskByProcessInstance(runtime, processInstanceId); manager.disposeRuntimeEngine(runtime); if (success) { i++; } } completedTask++; logger.trace("Task thread {} completed", counter); } catch (Throwable t) { t.printStackTrace(); } } } protected boolean isCausedByOptimisticLockingFailure(Throwable throwable) { while (throwable != null) { if (OptimisticLockException.class.isAssignableFrom(throwable.getClass()) || StaleObjectStateException.class.isAssignableFrom(throwable.getClass())) { return true; } else { throwable = throwable.getCause(); } } return false; } }