From 05835c051b57d3231e3ddf8dc160f1477a6494ca Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Thu, 21 Nov 2019 08:59:34 +0100 Subject: add stop condition to process-flow engine that can be set dynamically by tasks --- .../impl/idp/process/ExecutionContextImpl.java | 16 ++- .../core/impl/idp/process/ProcessEngineImpl.java | 117 +++++++++++++-------- .../core/impl/idp/process/ProcessInstance.java | 10 +- .../core/impl/idp/process/test/HalloWeltTask.java | 2 +- .../core/impl/idp/process/test/HelloWorldTask.java | 2 +- .../impl/idp/process/test/ProcessEngineTest.java | 96 ++++++++++++----- .../impl/idp/process/test/StopProcessFlagTask.java | 52 +++++++++ .../impl/idp/process/test/ThrowExceptionTask.java | 52 +++++++++ ...ingExpressionAwareProcessEngineTest-context.xml | 6 ++ .../process/test/SampleProcessDefinition4.xml | 24 +++++ .../process/test/SampleProcessDefinition5.xml | 24 +++++ .../core/api/idp/process/ExecutionContext.java | 15 +++ 12 files changed, 334 insertions(+), 82 deletions(-) create mode 100644 eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/StopProcessFlagTask.java create mode 100644 eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/ThrowExceptionTask.java create mode 100644 eaaf_core/src/test/resources/process/test/SampleProcessDefinition4.xml create mode 100644 eaaf_core/src/test/resources/process/test/SampleProcessDefinition5.xml diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ExecutionContextImpl.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ExecutionContextImpl.java index 88a95795..3cd696df 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ExecutionContextImpl.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ExecutionContextImpl.java @@ -44,9 +44,10 @@ public class ExecutionContextImpl implements ExecutionContext { private static final long serialVersionUID = 1L; - private Map ctxData = Collections.synchronizedMap(new HashMap()); + private final Map ctxData = Collections.synchronizedMap(new HashMap()); private String processInstanceId; + private boolean markedAsCancelled = false; /** * Creates a new instance. @@ -93,7 +94,7 @@ public class ExecutionContextImpl implements ExecutionContext { @Override public String toString() { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); builder.append("ExecutionContextImpl ["); builder.append("id=").append(processInstanceId); builder.append(", variables="); @@ -102,4 +103,15 @@ public class ExecutionContextImpl implements ExecutionContext { return builder.toString(); } + @Override + public boolean isProcessCancelled() { + return markedAsCancelled; + } + + @Override + public void setCanceleProcessFlag() { + markedAsCancelled = true; + + } + } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessEngineImpl.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessEngineImpl.java index b6b42850..53f50e1f 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessEngineImpl.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessEngineImpl.java @@ -64,14 +64,14 @@ import at.gv.egiz.eaaf.core.impl.idp.process.model.Transition; */ public class ProcessEngineImpl implements ProcessEngine { - private Logger log = LoggerFactory.getLogger(getClass()); + private final Logger log = LoggerFactory.getLogger(getClass()); @Autowired ProcessInstanceStoreDAO piStoreDao; @Autowired ApplicationContext context; - private ProcessDefinitionParser pdp = new ProcessDefinitionParser(); + private final ProcessDefinitionParser pdp = new ProcessDefinitionParser(); - private Map processDefinitions = new ConcurrentHashMap(); + private final Map processDefinitions = new ConcurrentHashMap(); private final static String MDC_CTX_PI_NAME = "processInstanceId"; private final static String MDC_CTX_TASK_NAME = "taskId"; @@ -86,7 +86,7 @@ public class ProcessEngineImpl implements ProcessEngine { @Override public String registerProcessDefinition(InputStream processDefinitionInputStream) throws ProcessDefinitionParserException{ - ProcessDefinition pd = pdp.parse(processDefinitionInputStream); + final ProcessDefinition pd = pdp.parse(processDefinitionInputStream); postValidationOfProcessDefintion(pd); @@ -104,7 +104,7 @@ public class ProcessEngineImpl implements ProcessEngine { */ public void setProcessDefinitions(Iterable processDefinitions) { this.processDefinitions.clear(); - for (ProcessDefinition pd : processDefinitions) { + for (final ProcessDefinition pd : processDefinitions) { if (this.processDefinitions.containsKey(pd.getId())) { throw new IllegalArgumentException("Duplicate process definition identifier '" + pd.getId() + "'."); } @@ -125,18 +125,18 @@ public class ProcessEngineImpl implements ProcessEngine { @Override public String createProcessInstance(String processDefinitionId, ExecutionContext executionContext) throws ProcessExecutionException { // look for respective process definition - ProcessDefinition pd = processDefinitions.get(processDefinitionId); + final ProcessDefinition pd = processDefinitions.get(processDefinitionId); if (pd == null) { throw new ProcessExecutionException("Unable to find process definition for process '" + processDefinitionId + "'."); } // create and keep process instance - ProcessInstance pi = new ProcessInstance(pd, executionContext); + final ProcessInstance pi = new ProcessInstance(pd, executionContext); log.info("Creating process instance from process definition '{}': {}", processDefinitionId, pi.getId()); try { saveOrUpdateProcessInstance(pi); - } catch (EAAFException e) { + } catch (final EAAFException e) { throw new ProcessExecutionException("Unable to persist process instance.", e); } @@ -158,7 +158,7 @@ public class ProcessEngineImpl implements ProcessEngine { + " includes NO 'ProcessInstanceId'"); } - ProcessInstance pi = loadProcessInstance(pendingReq.getProcessInstanceId()); + final ProcessInstance pi = loadProcessInstance(pendingReq.getProcessInstanceId()); if (pi == null ) { throw new ProcessExecutionException("Process instance '" + pendingReq.getProcessInstanceId() + "' does not exist."); @@ -179,7 +179,7 @@ public class ProcessEngineImpl implements ProcessEngine { if (!ProcessInstanceState.ENDED.equals(pi.getState())) saveOrUpdateProcessInstance(pi); - } catch (EAAFException e) { + } catch (final EAAFException e) { throw new ProcessExecutionException("Unable to load/save process instance.", e); } finally { @@ -198,7 +198,7 @@ public class ProcessEngineImpl implements ProcessEngine { + " includes NO 'ProcessInstanceId'"); } - ProcessInstance pi = loadProcessInstance(pendingReq.getProcessInstanceId()); + final ProcessInstance pi = loadProcessInstance(pendingReq.getProcessInstanceId()); if (pi == null ) { throw new ProcessExecutionException("Process instance '" + pendingReq.getProcessInstanceId() + "' does not exist."); @@ -223,7 +223,7 @@ public class ProcessEngineImpl implements ProcessEngine { if (!ProcessInstanceState.ENDED.equals(pi.getState())) saveOrUpdateProcessInstance(pi); - } catch (EAAFException e) { + } catch (final EAAFException e) { throw new ProcessExecutionException("Unable to load/save process instance.", e); } finally { @@ -239,7 +239,7 @@ public class ProcessEngineImpl implements ProcessEngine { * @throws ProcessExecutionException Thrown in case of error (when the referenced class does not implement {@link Task} for instance). */ private Task createTaskInstance(TaskInfo ti) throws ProcessExecutionException { - String clazz = StringUtils.trimToNull(ti.getTaskImplementingClass()); + final String clazz = StringUtils.trimToNull(ti.getTaskImplementingClass()); Task task = null; if (clazz != null) { @@ -248,7 +248,7 @@ public class ProcessEngineImpl implements ProcessEngine { try { instanceClass = context.getBean(clazz); - } catch (Exception e) { + } catch (final Exception e) { throw new ProcessExecutionException("Unable to get class '" + clazz + "' associated with task '" + ti.getId() + "' .", e); } @@ -259,7 +259,7 @@ public class ProcessEngineImpl implements ProcessEngine { try { task = (Task) instanceClass; - } catch (Exception e) { + } catch (final Exception e) { throw new ProcessExecutionException("Unable to instantiate class '" + clazz + "' associated with task '" + ti.getId() + "' .", e); } } @@ -277,19 +277,19 @@ public class ProcessEngineImpl implements ProcessEngine { if (ProcessInstanceState.ENDED.equals(pi.getState())) { throw new ProcessExecutionException("Process for instance '" + pi.getId() + "' has already been ended."); } - ProcessDefinition pd = pi.getProcessDefinition(); - ProcessNode processNode = pd.getProcessNode(pi.getNextId()); + final ProcessDefinition pd = pi.getProcessDefinition(); + final ProcessNode processNode = pd.getProcessNode(pi.getNextId()); log.debug("Processing node '{}'.", processNode.getId()); // distinguish process node types StartEvent, TaskInfo and EndEvent if (processNode instanceof TaskInfo) { // TaskInfo types need to be executed - TaskInfo ti = (TaskInfo) processNode; + final TaskInfo ti = (TaskInfo) processNode; MDC.put(MDC_CTX_TASK_NAME, ti.getId()); try { log.debug("Processing task '{}'.", ti.getId()); - Task task = createTaskInstance(ti); + final Task task = createTaskInstance(ti); if (task != null) { try { log.debug("Executing task implementation for task '{}'.", ti.getId()); @@ -297,35 +297,39 @@ public class ProcessEngineImpl implements ProcessEngine { pendingReq = task.execute(pendingReq, pi.getExecutionContext()); log.debug("Returned from execution of task '{}'.", ti.getId()); log.trace("Execution context after task execution: {}", pi.getExecutionContext().keySet()); - } catch (Throwable t) { + + } catch (final Throwable t) { throw new ProcessExecutionException("Error executing task '" + ti.getId() + "'.", t); + + } + + //check if process was cancelled dynamically by task + if (pi.getExecutionContext().isProcessCancelled()) { + log.debug("Processing task '{}' was cancelled by Task: '{}'.", pi.getId(), ti.getId()); + processFinishEvent(pi); + return; + } + } else { log.debug("No task implementing class set."); + } } finally { MDC.remove(MDC_CTX_TASK_NAME); + } } else if (processNode instanceof EndEvent) { - log.info("Finishing process instance '{}'.", pi.getId()); - - try { - piStoreDao.remove(pi.getId()); - - } catch (EAAFException e) { - throw new ProcessExecutionException("Unable to remove process instance.", e); - - } - pi.setState(ProcessInstanceState.ENDED); - log.debug("Final process context: {}", pi.getExecutionContext().keySet()); + processFinishEvent(pi); return; + } final ExpressionEvaluationContext expressionContext = new ExpressionEvaluationContextImpl(pi); // traverse pointer - Transition t = IterableUtils.find(processNode.getOutgoingTransitions(), new Predicate() { + final Transition t = IterableUtils.find(processNode.getOutgoingTransitions(), new Predicate() { @Override public boolean evaluate(Transition transition) { if (transitionConditionExpressionEvaluator != null && transition.getConditionExpression() != null) { @@ -356,7 +360,7 @@ public class ProcessEngineImpl implements ProcessEngine { execute(pi, pendingReq); } } - + @Override public ProcessInstance getProcessInstance(String processInstanceId) { @@ -364,7 +368,7 @@ public class ProcessEngineImpl implements ProcessEngine { try { processInstance = loadProcessInstance(processInstanceId); - } catch (EAAFException e) { + } catch (final EAAFException e) { throw new RuntimeException("The process instance '" + processInstanceId + "' could not be retrieved.", e); } @@ -381,12 +385,12 @@ public class ProcessEngineImpl implements ProcessEngine { * @throws MOADatabaseException Thrown if an error occurs while accessing the database. */ private void saveOrUpdateProcessInstance(ProcessInstance processInstance) throws EAAFException { - ProcessInstanceStore store = new ProcessInstanceStore(); + final ProcessInstanceStore store = new ProcessInstanceStore(); - ExecutionContext ctx = processInstance.getExecutionContext(); + final ExecutionContext ctx = processInstance.getExecutionContext(); - Map ctxData = new HashMap(); - for (String key : ctx.keySet()) { + final Map ctxData = new HashMap(); + for (final String key : ctx.keySet()) { ctxData.put(key, ctx.get(key)); } store.setExecutionContextData(ctxData); @@ -408,20 +412,20 @@ public class ProcessEngineImpl implements ProcessEngine { */ private ProcessInstance loadProcessInstance(String processInstanceId) throws EAAFException { - ProcessInstanceStore piStore = piStoreDao.load(processInstanceId); + final ProcessInstanceStore piStore = piStoreDao.load(processInstanceId); if (piStore == null) { return null; } - ExecutionContext executionContext = new ExecutionContextImpl(piStore.getProcessInstanceId()); + final ExecutionContext executionContext = new ExecutionContextImpl(piStore.getProcessInstanceId()); - Map executionContextData = piStore.getExecutionContextData(); - for (String key : executionContextData.keySet()) { + final Map executionContextData = piStore.getExecutionContextData(); + for (final String key : executionContextData.keySet()) { executionContext.put(key, executionContextData.get(key)); } - ProcessInstance pi = new ProcessInstance(processDefinitions.get(piStore.getProcessDefinitionId()), executionContext); + final ProcessInstance pi = new ProcessInstance(processDefinitions.get(piStore.getProcessDefinitionId()), executionContext); pi.setNextId(piStore.getNextTaskId()); pi.setState(piStore.getProcessState()); @@ -441,10 +445,31 @@ public class ProcessEngineImpl implements ProcessEngine { try { piStoreDao.remove(processInstanceId); - } catch (EAAFException e) { + } catch (final EAAFException e) { + throw new ProcessExecutionException("Unable to remove process instance.", e); + + } + + } + + /** + * Finish a process-flow and remove any process-flow related information + * + * @param pi + * @throws ProcessExecutionException + */ + private void processFinishEvent(ProcessInstance pi) throws ProcessExecutionException { + log.info("Finishing process instance '{}'.", pi.getId()); + + try { + piStoreDao.remove(pi.getId()); + + } catch (final EAAFException e) { throw new ProcessExecutionException("Unable to remove process instance.", e); } + pi.setState(ProcessInstanceState.ENDED); + log.debug("Final process context: {}", pi.getExecutionContext().keySet()); } @@ -458,11 +483,11 @@ public class ProcessEngineImpl implements ProcessEngine { */ private void postValidationOfProcessDefintion(ProcessDefinition pd) throws ProcessDefinitionParserException{ try { - for(TaskInfo task : pd.getTaskInfos().values()) { + for(final TaskInfo task : pd.getTaskInfos().values()) { createTaskInstance(task); } - } catch (ProcessExecutionException e) { + } catch (final ProcessExecutionException e) { log.error("Post-validation of process definition: {} find an error: {}", pd.getId(), e.getMessage()); throw new ProcessDefinitionParserException("Post-validation find an error in process definition:" + pd.getId(), e); diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessInstance.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessInstance.java index 17d8ea8f..6db1dc7d 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessInstance.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessInstance.java @@ -49,13 +49,13 @@ public class ProcessInstance implements Serializable { private static final long serialVersionUID = 1L; private static final int RND_ID_LENGTH = 22; - private ProcessDefinition processDefinition; + private final ProcessDefinition processDefinition; private String nextId; private Date lru; - private ExecutionContext executionContext; + private final ExecutionContext executionContext; private ProcessInstanceState state = ProcessInstanceState.NOT_STARTED; - private Logger log = LoggerFactory.getLogger(getClass()); + private final Logger log = LoggerFactory.getLogger(getClass()); /** * Creates a new process instance, based on a given process definition and a @@ -74,7 +74,7 @@ public class ProcessInstance implements Serializable { executionContext = new ExecutionContextImpl(); } if (executionContext.getProcessInstanceId() == null) { - String pdIdLocalPart = RandomStringUtils.random(RND_ID_LENGTH, 0, 0, true, true, null, + final String pdIdLocalPart = RandomStringUtils.random(RND_ID_LENGTH, 0, 0, true, true, null, SecureRandomHolder.getInstance()); executionContext.setProcessInstanceId(this.processDefinition.getId() + "-" + pdIdLocalPart); } else { @@ -169,7 +169,7 @@ public class ProcessInstance implements Serializable { @Override public String toString() { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); builder.append("ProcessInstance ["); builder.append("id=").append(executionContext.getProcessInstanceId()); builder.append(", idle since=").append( diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/HalloWeltTask.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/HalloWeltTask.java index 40399e58..743a61da 100644 --- a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/HalloWeltTask.java +++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/HalloWeltTask.java @@ -44,7 +44,7 @@ public class HalloWeltTask implements Task { @Override public IRequest execute(IRequest pendingReq, ExecutionContext executionContext) { System.out.println("Hallo Welt"); - return null; + return pendingReq; } } diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/HelloWorldTask.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/HelloWorldTask.java index a2d8b3fb..c6da16b4 100644 --- a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/HelloWorldTask.java +++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/HelloWorldTask.java @@ -44,7 +44,7 @@ public class HelloWorldTask implements Task { @Override public IRequest execute(IRequest pendingReq, ExecutionContext executionContext) { System.out.println("Hello World"); - return null; + return pendingReq; } } diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/ProcessEngineTest.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/ProcessEngineTest.java index 78fdde61..dc45534e 100644 --- a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/ProcessEngineTest.java +++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/ProcessEngineTest.java @@ -33,6 +33,7 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; import java.io.InputStream; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -44,6 +45,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import at.gv.egiz.eaaf.core.api.idp.process.ProcessEngine; import at.gv.egiz.eaaf.core.exceptions.ProcessExecutionException; +import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; import at.gv.egiz.eaaf.core.impl.idp.module.test.TestRequestImpl; import at.gv.egiz.eaaf.core.impl.idp.process.ProcessDefinitionParser; import at.gv.egiz.eaaf.core.impl.idp.process.ProcessDefinitionParserException; @@ -64,7 +66,7 @@ public class ProcessEngineTest { public void init() throws IOException, ProcessDefinitionParserException { if (!isInitialized) { - ProcessDefinitionParser pdp = new ProcessDefinitionParser(); + final ProcessDefinitionParser pdp = new ProcessDefinitionParser(); if (pe == null) { pe = applicationContext.getBean("processEngine", ProcessEngine.class); @@ -79,29 +81,19 @@ public class ProcessEngineTest { ((ProcessEngineImpl) pe).registerProcessDefinition(pdp.parse(in)); } + try (InputStream in = ProcessEngineTest.class.getResourceAsStream("/process/test/SampleProcessDefinition4.xml")) { + ((ProcessEngineImpl) pe).registerProcessDefinition(pdp.parse(in)); + } + + try (InputStream in = ProcessEngineTest.class.getResourceAsStream("/process/test/SampleProcessDefinition5.xml")) { + ((ProcessEngineImpl) pe).registerProcessDefinition(pdp.parse(in)); + } + //initHibernateForTesting(); isInitialized = true; } } - private static void initHibernateForTesting() throws IOException{ - -// InputStream in = ProcessEngineTest.class.getResourceAsStream("/at/gv/egovernment/moa/id/process/hibernate.configuration.test.properties"); -// Properties props = new Properties(); -// props.load(in); -// -// try { -// //ConfigurationDBUtils.initHibernate(props); -// Configuration config = new Configuration(); -// config.addProperties(props); -// //config.addAnnotatedClass(ProcessInstanceStore.class); -// config.addAnnotatedClass(AssertionStore.class); -// //MOASessionDBUtils.initHibernate(config, props); -// } catch (Exception e) { -// e.printStackTrace(); -// } - } - @Test public void wrongProcessDefinition() throws IOException { try (InputStream in = ProcessEngineTest.class.getResourceAsStream("/process/test/SampleProcessDefinition3.xml")) { @@ -109,7 +101,7 @@ public class ProcessEngineTest { ((ProcessEngineImpl) pe).registerProcessDefinition(in); Assert.fail(); - } catch (ProcessDefinitionParserException e) { + } catch (final ProcessDefinitionParserException e) { Assert.assertTrue(e.getMessage().contains("Post-validation find an error in process definition")); } } @@ -119,9 +111,9 @@ public class ProcessEngineTest { @Test public void testSampleProcess1() throws IOException, ProcessDefinitionParserException, ProcessExecutionException { - TestRequestImpl testReq = new TestRequestImpl(); + final TestRequestImpl testReq = new TestRequestImpl(); - String piId = pe.createProcessInstance("SampleProcess1"); + final String piId = pe.createProcessInstance("SampleProcess1"); ProcessInstance pi = pe.getProcessInstance(piId); assertEquals(NOT_STARTED, pi.getState()); @@ -139,7 +131,7 @@ public class ProcessEngineTest { throw new ProcessExecutionException("ProcessInstance should be removed already, but it was found."); //assertEquals(ENDED, pi.getState()); - } catch (IllegalArgumentException e) { + } catch (final IllegalArgumentException e) { // do nothing because processInstance should be already removed } @@ -148,9 +140,9 @@ public class ProcessEngineTest { @Test public void testSampleProcess2() throws IOException, ProcessDefinitionParserException, ProcessExecutionException { - TestRequestImpl testReq = new TestRequestImpl(); + final TestRequestImpl testReq = new TestRequestImpl(); - String piId = pe.createProcessInstance("SampleProcess2"); + final String piId = pe.createProcessInstance("SampleProcess2"); ProcessInstance pi = pe.getProcessInstance(piId); assertEquals(NOT_STARTED, pi.getState()); @@ -168,14 +160,64 @@ public class ProcessEngineTest { throw new ProcessExecutionException("ProcessInstance should be removed already, but it was found."); //assertEquals(ENDED, pi.getState()); - } catch (IllegalArgumentException e) { + } catch (final IllegalArgumentException e) { // do nothing because processInstance should be already removed } + + } + + @Test + public void testSampleProcess4() throws IOException, ProcessDefinitionParserException, ProcessExecutionException { + + final TestRequestImpl testReq = new TestRequestImpl(); + testReq.setPendingReqId(RandomStringUtils.randomAlphanumeric(5)); - + final String piId = pe.createProcessInstance("SampleProcess4"); + final ProcessInstance pi = pe.getProcessInstance(piId); + assertEquals(NOT_STARTED, pi.getState()); + + // start process + testReq.setProcessInstanceID(piId); + try { + pe.start(testReq); + Assert.fail("Task exception not handled"); + + } catch (final ProcessExecutionException e1) { + org.springframework.util.Assert.isInstanceOf(TaskExecutionException.class, e1.getCause(), "No TaskExecutionException"); + Assert.assertEquals("Wrong error-msg", "jUnit Test", e1.getCause().getMessage()); + Assert.assertEquals("Wrong pendingReqId", testReq.getPendingRequestId(), ((TaskExecutionException) e1.getCause()).getPendingRequestID()); + org.springframework.util.Assert.isInstanceOf(RuntimeException.class, e1.getCause().getCause(), "Wrong Exception in TaskExecutionException"); + } + + } + + @Test + public void testSampleProcess5() throws IOException, ProcessDefinitionParserException, ProcessExecutionException { + final TestRequestImpl testReq = new TestRequestImpl(); + + final String piId = pe.createProcessInstance("SampleProcess5"); + ProcessInstance pi = pe.getProcessInstance(piId); + assertEquals(NOT_STARTED, pi.getState()); + + // start process + testReq.setProcessInstanceID(piId); + pe.start(testReq); + + try { + pi = pe.getProcessInstance(piId); + + } catch (final IllegalArgumentException e) { + Assert.assertTrue("wrong error-msg", e.getMessage().contains("does not/no longer exist.")); + Assert.assertTrue("wrong process-instance-id", e.getMessage().contains(piId)); + + } + + + } + @Test(expected = IllegalArgumentException.class) public void testProcessInstanceDoesNotExist() { pe.getProcessInstance("does not exist"); diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/StopProcessFlagTask.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/StopProcessFlagTask.java new file mode 100644 index 00000000..8cd76eaa --- /dev/null +++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/StopProcessFlagTask.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ, + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.idp.process.test; + +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; +import at.gv.egiz.eaaf.core.api.idp.process.Task; + +/** + * Simple task that just outputs a "Hello World" text to the console. + * + * @author tknall + * + */ +@Service("HelloWorldTask") +public class StopProcessFlagTask implements Task { + + @Override + public IRequest execute(IRequest pendingReq, ExecutionContext executionContext) { + System.out.println("Stop process-flow dynamically from task"); + executionContext.setCanceleProcessFlag(); + + return pendingReq; + } + +} diff --git a/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/ThrowExceptionTask.java b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/ThrowExceptionTask.java new file mode 100644 index 00000000..ecd139c8 --- /dev/null +++ b/eaaf_core/src/test/java/at/gv/egiz/eaaf/core/impl/idp/process/test/ThrowExceptionTask.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright 2017 Graz University of Technology + * EAAF-Core Components has been developed in a cooperation between EGIZ, + * A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.idp.process.test; + +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; +import at.gv.egiz.eaaf.core.api.idp.process.Task; +import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; + +/** + * Simple task that just outputs a "Hello World" text to the console. + * + * @author tknall + * + */ +@Service("HelloWorldTask") +public class ThrowExceptionTask implements Task { + + @Override + public IRequest execute(IRequest pendingReq, ExecutionContext executionContext) throws TaskExecutionException { + System.out.println("Stop process-flow dynamically from task"); + throw new TaskExecutionException(pendingReq, "jUnit Test", new RuntimeException("jUnit test exception handling")); + + } + +} diff --git a/eaaf_core/src/test/resources/process/spring/test/SpringExpressionAwareProcessEngineTest-context.xml b/eaaf_core/src/test/resources/process/spring/test/SpringExpressionAwareProcessEngineTest-context.xml index ad94c6d4..ebbd89e9 100644 --- a/eaaf_core/src/test/resources/process/spring/test/SpringExpressionAwareProcessEngineTest-context.xml +++ b/eaaf_core/src/test/resources/process/spring/test/SpringExpressionAwareProcessEngineTest-context.xml @@ -30,6 +30,12 @@ + + + + diff --git a/eaaf_core/src/test/resources/process/test/SampleProcessDefinition4.xml b/eaaf_core/src/test/resources/process/test/SampleProcessDefinition4.xml new file mode 100644 index 00000000..c88afd05 --- /dev/null +++ b/eaaf_core/src/test/resources/process/test/SampleProcessDefinition4.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + diff --git a/eaaf_core/src/test/resources/process/test/SampleProcessDefinition5.xml b/eaaf_core/src/test/resources/process/test/SampleProcessDefinition5.xml new file mode 100644 index 00000000..e0072e62 --- /dev/null +++ b/eaaf_core/src/test/resources/process/test/SampleProcessDefinition5.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + diff --git a/eaaf_core_api/src/main/java/at/gv/egiz/eaaf/core/api/idp/process/ExecutionContext.java b/eaaf_core_api/src/main/java/at/gv/egiz/eaaf/core/api/idp/process/ExecutionContext.java index 38a66d4c..319db027 100644 --- a/eaaf_core_api/src/main/java/at/gv/egiz/eaaf/core/api/idp/process/ExecutionContext.java +++ b/eaaf_core_api/src/main/java/at/gv/egiz/eaaf/core/api/idp/process/ExecutionContext.java @@ -37,6 +37,21 @@ import java.util.Set; */ public interface ExecutionContext extends Serializable { + /** + * Flag that indicates that a Task canceled the current {@link ExecutionContext} + * + * @return true if the process-flow was marked as canceled, otherwise false + */ + boolean isProcessCancelled(); + + /** + * Mark this {@link ExecutionContext} as cancelled + * + * The process-flow engine will stop execution when the task that sets this flag is finished + * + */ + void setCanceleProcessFlag(); + /** * Returns the identifier of underlying process instance. * -- cgit v1.2.3