From 4e3295bd1e814894cc3f8ca73b0320c5629ec29d Mon Sep 17 00:00:00 2001 From: Enrique Gonzalez Martinez Date: Wed, 8 Jan 2020 09:36:04 +0100 Subject: [PATCH] [JBPM-8896] NPE during Process Migration when Boundary Timer is fired but UserTask not completed --- .../instance/node/StateBasedNodeInstance.java | 13 +++++++++ .../migration/TimerMigrationManagerTest.java | 28 +++++++++---------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java b/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java index 0ccaa91de4..739ce43dd2 100644 --- a/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java +++ b/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java @@ -32,6 +32,8 @@ import org.drools.core.spi.Activation; import org.drools.core.time.TimeUtils; import org.drools.core.time.impl.CronExpression; +import org.drools.core.time.impl.DefaultJobHandle; +import org.drools.core.time.impl.DefaultTimerJobInstance; import org.drools.core.util.MVELSafeHelper; import org.jbpm.process.core.ContextContainer; import org.jbpm.process.core.context.variable.Variable; @@ -345,9 +347,20 @@ private void triggerTimer(TimerInstance timerInstance) { for (Map.Entry entry : getEventBasedNode().getTimers().entrySet()) { if (entry.getKey().getId() == timerInstance.getTimerId()) { executeAction((Action) entry.getValue().getMetaData("Action")); + + // self remove timer instance from this node as it will remove itself + // from timer service once is triggered and there is no next + if (!(timerInstance.getJobHandle() instanceof DefaultJobHandle)) { + return; + } + DefaultTimerJobInstance defaultTimerInstance = (DefaultTimerJobInstance) ((DefaultJobHandle) timerInstance.getJobHandle()).getTimerJobInstance(); + if (defaultTimerInstance.getTrigger().hasNextFireTime() == null) { + timerInstances.remove(timerInstance.getId()); + } return; } } + } @Override diff --git a/jbpm-runtime-manager/src/test/java/org/jbpm/runtime/manager/impl/migration/TimerMigrationManagerTest.java b/jbpm-runtime-manager/src/test/java/org/jbpm/runtime/manager/impl/migration/TimerMigrationManagerTest.java index 34d6c0b993..256cb67a56 100644 --- a/jbpm-runtime-manager/src/test/java/org/jbpm/runtime/manager/impl/migration/TimerMigrationManagerTest.java +++ b/jbpm-runtime-manager/src/test/java/org/jbpm/runtime/manager/impl/migration/TimerMigrationManagerTest.java @@ -16,11 +16,6 @@ package org.jbpm.runtime.manager.impl.migration; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import java.time.Instant; import java.util.Arrays; import java.util.Collection; @@ -39,7 +34,6 @@ import org.jbpm.services.task.identity.JBossUserGroupCallbackImpl; import org.jbpm.test.listener.process.NodeLeftCountDownProcessEventListener; import org.jbpm.test.util.AbstractBaseTest; -import org.kie.test.util.db.PoolingDataSourceWrapper; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -63,7 +57,12 @@ import org.kie.internal.runtime.manager.context.EmptyContext; import org.kie.internal.runtime.manager.context.ProcessInstanceIdContext; import org.kie.internal.task.api.UserGroupCallback; +import org.kie.test.util.db.PoolingDataSourceWrapper; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.kie.api.runtime.process.ProcessInstance.STATE_ACTIVE; import static org.kie.api.runtime.process.ProcessInstance.STATE_COMPLETED; @@ -566,7 +565,7 @@ public void testMigrateUserTaskCompletedBoundaryTimerProcessInstance() throws Ex pid = startProcessTillBoundaryTimer(countdownListener); completeUserTask(managerV1, USER_JOHN); - migrateProcessUserTaskBoundary(); + migrateProcessUserTaskBoundary(managerV1); //Only needs to complete Mary's task after migration completeUserTask(managerV2, USER_MARY); @@ -580,7 +579,7 @@ public void testMigrateUserTaskNotCompletedBoundaryTimerProcessInstance() throws pid = startProcessTillBoundaryTimer(countdownListener); - migrateProcessUserTaskBoundary(); + migrateProcessUserTaskBoundary(managerV1); //Needs to complete both user tasks after migration completeUserTask(managerV2, USER_JOHN); @@ -669,13 +668,14 @@ private void createRuntimeManager(RuntimeEnvironment environment, RuntimeEnviron private long startProcessTillBoundaryTimer(NodeLeftCountDownProcessEventListener countdownListener) { auditService = new JPAAuditLogService(emf); - + createRuntimeManagers("migration/v1/BPMN2-UserTaskBoundary-v1.bpmn2", "migration/v2/BPMN2-UserTaskBoundary-v2.bpmn2", countdownListener); runtime = managerV1.getRuntimeEngine(EmptyContext.get()); - + long pid = startProcess(runtime, USERTASK_BOUNDARY_TIMER_ID_V1); checkProcess(pid, USERTASK_BOUNDARY_TIMER_ID_V1, DEPLOYMENT_ID_V1, STATE_ACTIVE); + managerV1.disposeRuntimeEngine(runtime); //wait for boundary timer countdownListener.waitTillCompleted(); countdownListener.reset("Goodbye v2", 1); @@ -700,11 +700,11 @@ private void checkProcess(long pid, String processId, assertEquals(status, log.getStatus().intValue()); } - private void migrateProcessUserTaskBoundary() { - managerV1.disposeRuntimeEngine(runtime); - + private void migrateProcessUserTaskBoundary(RuntimeManager manager) { + RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get()); + manager.disposeRuntimeEngine(runtime); MigrationSpec migrationSpec = new MigrationSpec(DEPLOYMENT_ID_V1, pid, DEPLOYMENT_ID_V2, USERTASK_BOUNDARY_TIMER_ID_V2); - + MigrationManager migrationManager = new MigrationManager(migrationSpec); migrationManager.migrate();