package org.camunda.bpm.camel.spring;/* 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. */ import static org.camunda.bpm.camel.component.CamundaBpmConstants.EXCHANGE_HEADER_ATTEMPTSSTARTED; import static org.camunda.bpm.camel.component.CamundaBpmConstants.EXCHANGE_HEADER_PROCESS_INSTANCE_ID; import static org.camunda.bpm.camel.component.CamundaBpmConstants.EXCHANGE_HEADER_RETRIESLEFT; import static org.fest.assertions.api.Assertions.assertThat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; import org.apache.camel.Expression; import org.apache.camel.Processor; import org.apache.camel.ProducerTemplate; import org.apache.camel.component.mock.MockEndpoint; import org.camunda.bpm.camel.component.CamundaBpmConstants; import org.camunda.bpm.camel.component.externaltasks.SetExternalTaskRetries; import org.camunda.bpm.engine.ExternalTaskService; import org.camunda.bpm.engine.HistoryService; import org.camunda.bpm.engine.RuntimeService; import org.camunda.bpm.engine.externaltask.ExternalTask; import org.camunda.bpm.engine.externaltask.LockedExternalTask; import org.camunda.bpm.engine.history.HistoricProcessInstance; import org.camunda.bpm.engine.history.HistoricVariableInstance; import org.camunda.bpm.engine.runtime.ProcessInstance; import org.camunda.bpm.engine.test.Deployment; import org.camunda.bpm.engine.test.ProcessEngineRule; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:consume-external-tasks-config.xml") public class ConsumeExternalTasksTest { @SetExternalTaskRetries(retries = 0) public static class CreateIncidentException extends Exception { private static final long serialVersionUID = 1L; public CreateIncidentException(final String message) { super(message); } }; @SetExternalTaskRetries(retries = 0, relative = true) public static class DontChangeRetriesException extends Exception { private static final long serialVersionUID = 1L; public DontChangeRetriesException(final String message) { super(message); } }; private MockEndpoint mockEndpoint; @Autowired(required = true) private CamelContext camelContext; @Autowired(required = true) private RuntimeService runtimeService; @Autowired(required = true) private HistoryService historyService; @Autowired(required = true) private ExternalTaskService externalTaskService; @Autowired(required = true) @Rule public ProcessEngineRule processEngineRule; @Before public void setUp() throws Exception { mockEndpoint = (MockEndpoint) camelContext.getEndpoint("mock:endpoint"); mockEndpoint.reset(); // start consumer if stopped by previous test (see "tearDown()") // ((BatchConsumer) // camelContext.getRoute("firstRoute").getConsumer()).start(); } @After public void tearDown() throws Exception { // avoid accessing during shutdown of Camunda engine // ((BatchConsumer) // camelContext.getRoute("firstRoute").getConsumer()).stop(); } @SuppressWarnings("unchecked") @Test @Deployment(resources = { "process/StartExternalTask.bpmn20.xml" }) public void testSetProcessVariables() throws Exception { // variables to be set by the Camel-endpoint processing the external // task mockEndpoint.returnReplyBody(new Expression() { @Override public <T> T evaluate(Exchange exchange, Class<T> type) { Map<String, Object> variables = exchange.getIn().getBody(Map.class); final String var2 = (String) variables.get("var2"); final HashMap<String, Object> result = new HashMap<String, Object>(); result.put("var2", var2 + "bar"); result.put("var3", "bar3"); return (T) result; } }); // start process final Map<String, Object> processVariables = new HashMap<String, Object>(); processVariables.put("var1", "foo"); processVariables.put("var2", "bar"); final ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startExternalTaskProcess", processVariables); assertThat(processInstance).isNotNull(); // wait for the external task to be completed Thread.sleep(1000); // external task is not open any more final long externalTasksCount = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).active().count(); assertThat(externalTasksCount).isEqualTo(0); // assert that the camunda BPM process instance ID has been added as a // property to the message assertThat(mockEndpoint.assertExchangeReceived(0).getProperty(EXCHANGE_HEADER_PROCESS_INSTANCE_ID)).isEqualTo( processInstance.getId()); // all process instance variables are loaded since no "variablesToFetch" // parameter was given assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody()).isNotNull(); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody()).isInstanceOf(Map.class); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody(Map.class).size()).isEqualTo(2); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody(Map.class)).containsKey("var1"); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody(Map.class)).containsKey("var2"); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody(Map.class)).containsValue("foo"); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody(Map.class)).containsValue("bar"); // assert that the variables sent in the response-message has been set // into the process final List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables.size()).isEqualTo(3); final HashMap<String, Object> variablesAsMap = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables) { variablesAsMap.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap.containsKey("var1")).isTrue(); assertThat(variablesAsMap.get("var1")).isEqualTo("foo"); assertThat(variablesAsMap.containsKey("var2")).isTrue(); assertThat(variablesAsMap.get("var2")).isEqualTo("barbar"); assertThat(variablesAsMap.containsKey("var3")).isTrue(); assertThat(variablesAsMap.get("var3")).isEqualTo("bar3"); // assert that process in end event "HappyEnd" assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("HappyEnd").singleResult()).isNotNull(); // assert that process ended not due to error boundary event 4711 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End4711").singleResult()).isNull(); // assert that process ended not due to error boundary event 0815 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End0815").singleResult()).isNull(); } @SuppressWarnings("unchecked") @Test @Deployment(resources = { "process/StartExternalTask3.bpmn20.xml" }) public void testLoadNoProcessVariablesAndAsyncIsFalse() throws Exception { // variables to be set by the Camel-endpoint processing the external // task mockEndpoint.returnReplyBody(new Expression() { @Override public <T> T evaluate(Exchange exchange, Class<T> type) { final HashMap<String, Object> result = new HashMap<String, Object>(); result.put("var1", "foo1"); result.put("var2", "bar2"); return (T) result; } }); // start process final Map<String, Object> processVariables = new HashMap<String, Object>(); processVariables.put("var1", "foo"); processVariables.put("var2", "bar"); final ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startExternalTaskProcess3", processVariables); assertThat(processInstance).isNotNull(); // wait for the external task to be completed Thread.sleep(1000); // external task is not open any more final long externalTasksCount = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).active().count(); assertThat(externalTasksCount).isEqualTo(0); // assert that the camunda BPM process instance ID has been added as a // property to the message assertThat(mockEndpoint.assertExchangeReceived(0).getProperty(EXCHANGE_HEADER_PROCESS_INSTANCE_ID)).isEqualTo( processInstance.getId()); // no process instance variables are loaded since an empty // "variablesToFetch" parameter was given assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody()).isNotNull(); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody()).isInstanceOf(Map.class); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody(Map.class).size()).isEqualTo(0); // assert that the variables sent in the response-message has been set // into the process final List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables.size()).isEqualTo(2); final HashMap<String, Object> variablesAsMap = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables) { variablesAsMap.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap.containsKey("var1")).isTrue(); assertThat(variablesAsMap.get("var1")).isEqualTo("foo1"); assertThat(variablesAsMap.containsKey("var2")).isTrue(); assertThat(variablesAsMap.get("var2")).isEqualTo("bar2"); // assert that process in end event "HappyEnd" assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("HappyEnd").singleResult()).isNotNull(); // assert that process ended not due to error boundary event 4711 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End4711").singleResult()).isNull(); // assert that process ended not due to error boundary event 0815 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End0815").singleResult()).isNull(); } @SuppressWarnings("unchecked") @Test @Deployment(resources = { "process/StartExternalTask4.bpmn20.xml" }) public void testCompleteTaskOnCompletionSucessfully() throws Exception { // variables returned but must not be set since task will not be // completed mockEndpoint.returnReplyBody(new Expression() { @Override public <T> T evaluate(Exchange exchange, Class<T> type) { final HashMap<String, Object> result = new HashMap<String, Object>(); result.put("var2", "bar2"); result.put("var3", "bar3"); return (T) result; } }); // start process final Map<String, Object> processVariables = new HashMap<String, Object>(); processVariables.put("var1", "foo"); processVariables.put("var2", "bar"); processVariables.put("var3", "foobar"); final ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startExternalTaskProcess2", processVariables); assertThat(processInstance).isNotNull(); // external task is still not resolved and not locked final List<ExternalTask> externalTasks1 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks1).isNotNull(); assertThat(externalTasks1.size()).isEqualTo(1); assertThat(externalTasks1.get(0).getWorkerId()).isNull(); // find external task and lock final List<LockedExternalTask> locked = externalTaskService.fetchAndLock(1, "0815", true).topic("topic4", 5000).execute(); assertThat(locked).isNotNull(); assertThat(locked.size()).isEqualTo(1); final LockedExternalTask lockedExternalTask = locked.get(0); // call route "direct:testRoute" final ProducerTemplate template = camelContext.createProducerTemplate(); template.requestBodyAndHeader("direct:firstTestRoute", null, "CamundaBpmExternalTaskId", lockedExternalTask.getId()); // ensure endpoint has been called assertThat(mockEndpoint.assertExchangeReceived(0)).isNotNull(); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getHeader(EXCHANGE_HEADER_ATTEMPTSSTARTED)).isEqualTo( 0); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getHeader(EXCHANGE_HEADER_RETRIESLEFT)).isEqualTo(2); // assert that process in end event "HappyEnd" assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("HappyEnd").singleResult()).isNotNull(); // assert that process ended not due to error boundary event 4711 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End4711").singleResult()).isNull(); // assert that process ended not due to error boundary event 0815 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End0815").singleResult()).isNull(); // assert that the variables sent in the response-message has been set // into the process final List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables.size()).isEqualTo(3); final HashMap<String, Object> variablesAsMap = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables) { variablesAsMap.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap.containsKey("var1")).isTrue(); assertThat(variablesAsMap.get("var1")).isEqualTo("foo"); assertThat(variablesAsMap.containsKey("var2")).isTrue(); assertThat(variablesAsMap.get("var2")).isEqualTo("bar2"); assertThat(variablesAsMap.containsKey("var3")).isTrue(); assertThat(variablesAsMap.get("var3")).isEqualTo("bar3"); } @Test @Deployment(resources = { "process/StartExternalTask4.bpmn20.xml" }) public void testCompleteTaskOnCompletionFailure() throws Exception { final String FAILURE = "Failure"; // variables returned but must not be set since task will not be // completed mockEndpoint.whenAnyExchangeReceived(new Processor() { @Override public void process(Exchange exchange) throws Exception { throw new Exception(FAILURE); } }); // start process final Map<String, Object> processVariables = new HashMap<String, Object>(); processVariables.put("var1", "foo"); processVariables.put("var2", "bar"); processVariables.put("var3", "foobar"); final ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startExternalTaskProcess2", processVariables); assertThat(processInstance).isNotNull(); // external task is still not resolved and not locked final List<ExternalTask> externalTasks1 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks1).isNotNull(); assertThat(externalTasks1.size()).isEqualTo(1); assertThat(externalTasks1.get(0).getWorkerId()).isNull(); assertThat(externalTasks1.get(0).getRetries()).isNull(); // find external task and lock final List<LockedExternalTask> locked = externalTaskService.fetchAndLock(1, "0815", true).topic("topic4", 5000).execute(); assertThat(locked).isNotNull(); assertThat(locked.size()).isEqualTo(1); final LockedExternalTask lockedExternalTask = locked.get(0); // call route "direct:testRoute" final ProducerTemplate template = camelContext.createProducerTemplate(); try { template.requestBodyAndHeader("direct:firstTestRoute", null, "CamundaBpmExternalTaskId", lockedExternalTask.getId()); Assert.fail("Expected an exception, but Camel route succeeded!"); } catch (Exception e) { // expected } // ensure endpoint has been called assertThat(mockEndpoint.assertExchangeReceived(0)).isNotNull(); // external task is still not resolved final List<ExternalTask> externalTasks2 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks2).isNotNull(); assertThat(externalTasks2.size()).isEqualTo(1); assertThat(externalTasks2.get(0).getRetries()).isEqualTo(2); // assert that process not in the end event "HappyEnd" assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("HappyEnd").singleResult()).isNull(); // assert that process ended not due to error boundary event 4711 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End4711").singleResult()).isNull(); // assert that process ended not due to error boundary event 0815 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End0815").singleResult()).isNull(); // assert that the variables sent in the response-message has been set // into the process final List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables.size()).isEqualTo(3); final HashMap<String, Object> variablesAsMap = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables) { variablesAsMap.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap.containsKey("var1")).isTrue(); assertThat(variablesAsMap.get("var1")).isEqualTo("foo"); assertThat(variablesAsMap.containsKey("var2")).isTrue(); assertThat(variablesAsMap.get("var2")).isEqualTo("bar"); assertThat(variablesAsMap.containsKey("var3")).isTrue(); assertThat(variablesAsMap.get("var3")).isEqualTo("foobar"); // complete task to make test order not relevant externalTaskService.complete(externalTasks2.get(0).getId(), "0815"); } @SuppressWarnings("unchecked") @Test @Deployment(resources = { "process/StartExternalTask4.bpmn20.xml" }) public void testCompleteTaskSucessfully() throws Exception { // variables returned but must not be set since task will not be // completed mockEndpoint.returnReplyBody(new Expression() { @Override public <T> T evaluate(Exchange exchange, Class<T> type) { final HashMap<String, Object> result = new HashMap<String, Object>(); result.put("var2", "bar2"); result.put("var3", "bar3"); return (T) result; } }); // start process final Map<String, Object> processVariables = new HashMap<String, Object>(); processVariables.put("var1", "foo"); processVariables.put("var2", "bar"); processVariables.put("var3", "foobar"); final ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startExternalTaskProcess2", processVariables); assertThat(processInstance).isNotNull(); // external task is still not resolved and not locked final List<ExternalTask> externalTasks1 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks1).isNotNull(); assertThat(externalTasks1.size()).isEqualTo(1); assertThat(externalTasks1.get(0).getWorkerId()).isNull(); // find external task and lock final List<LockedExternalTask> locked = externalTaskService.fetchAndLock(1, "0815", true).topic("topic4", 5000).execute(); assertThat(locked).isNotNull(); assertThat(locked.size()).isEqualTo(1); final LockedExternalTask lockedExternalTask = locked.get(0); // call route "direct:testRoute" final ProducerTemplate template = camelContext.createProducerTemplate(); template.requestBodyAndHeader("direct:secondTestRoute", null, "CamundaBpmExternalTaskId", lockedExternalTask.getId()); // ensure endpoint has been called assertThat(mockEndpoint.assertExchangeReceived(0)).isNotNull(); // assert that process in end event "HappyEnd" assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("HappyEnd").singleResult()).isNotNull(); // assert that process ended not due to error boundary event 4711 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End4711").singleResult()).isNull(); // assert that process ended not due to error boundary event 0815 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End0815").singleResult()).isNull(); // assert that the variables sent in the response-message has been set // into the process final List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables.size()).isEqualTo(3); final HashMap<String, Object> variablesAsMap = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables) { variablesAsMap.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap.containsKey("var1")).isTrue(); assertThat(variablesAsMap.get("var1")).isEqualTo("foo"); assertThat(variablesAsMap.containsKey("var2")).isTrue(); assertThat(variablesAsMap.get("var2")).isEqualTo("bar2"); assertThat(variablesAsMap.containsKey("var3")).isTrue(); assertThat(variablesAsMap.get("var3")).isEqualTo("bar3"); } @Test @Deployment(resources = { "process/StartExternalTask4.bpmn20.xml" }) public void testCompleteTaskFailure() throws Exception { // start process final Map<String, Object> processVariables = new HashMap<String, Object>(); processVariables.put("var1", "foo"); processVariables.put("var2", "bar"); processVariables.put("var3", "foobar"); final ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startExternalTaskProcess2", processVariables); assertThat(processInstance).isNotNull(); // external task is still not resolved and not locked final List<ExternalTask> externalTasks1 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks1).isNotNull(); assertThat(externalTasks1.size()).isEqualTo(1); assertThat(externalTasks1.get(0).getWorkerId()).isNull(); assertThat(externalTasks1.get(0).getRetries()).isNull(); // find external task and lock final List<LockedExternalTask> locked = externalTaskService.fetchAndLock(1, "0815", true).topic("topic4", 5000).execute(); assertThat(locked).isNotNull(); assertThat(locked.size()).isEqualTo(1); final LockedExternalTask lockedExternalTask = locked.get(0); /* * test CamundaBpmConstants.EXCHANGE_RESPONSE_IGNORE */ // variables returned but must not be set since task will not be // completed mockEndpoint.returnReplyBody(new Expression() { @SuppressWarnings("unchecked") @Override public <T> T evaluate(Exchange exchange, Class<T> type) { return (T) CamundaBpmConstants.EXCHANGE_RESPONSE_IGNORE; } }); // second route does not recognize exceptions // call route "direct:secondTestRoute" final ProducerTemplate template = camelContext.createProducerTemplate(); template.requestBodyAndHeader("direct:secondTestRoute", null, "CamundaBpmExternalTaskId", lockedExternalTask.getId()); // ensure endpoint has been called assertThat(mockEndpoint.assertExchangeReceived(0)).isNotNull(); // external task is still not resolved final List<ExternalTask> externalTasks2 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks2).isNotNull(); assertThat(externalTasks2.size()).isEqualTo(1); // Exception aborted processing so retries could not be set! assertThat(externalTasks2.get(0).getRetries()).isNull(); assertThat(externalTasks2.get(0).getErrorMessage()).isNull(); // assert that process not in the end event "HappyEnd" assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("HappyEnd").singleResult()).isNull(); // assert that process ended not due to error boundary event 4711 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End4711").singleResult()).isNull(); // assert that process ended not due to error boundary event 0815 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End0815").singleResult()).isNull(); // assert that the variables unchanged final List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables.size()).isEqualTo(3); final HashMap<String, Object> variablesAsMap = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables) { variablesAsMap.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap.containsKey("var1")).isTrue(); assertThat(variablesAsMap.get("var1")).isEqualTo("foo"); assertThat(variablesAsMap.containsKey("var2")).isTrue(); assertThat(variablesAsMap.get("var2")).isEqualTo("bar"); assertThat(variablesAsMap.containsKey("var3")).isTrue(); assertThat(variablesAsMap.get("var3")).isEqualTo("foobar"); /* * test common exception */ mockEndpoint.reset(); final String FAILURE = "FAILURE"; // variables returned but must not be set since task will not be // completed mockEndpoint.whenAnyExchangeReceived(new Processor() { @Override public void process(Exchange exchange) throws Exception { throw new Exception(FAILURE); } }); // call route "direct:testRoute" final ProducerTemplate template2 = camelContext.createProducerTemplate(); try { template2.requestBodyAndHeader("direct:firstTestRoute", null, "CamundaBpmExternalTaskId", lockedExternalTask.getId()); Assert.fail("Expected an exception, but Camel route succeeded!"); } catch (Exception e) { // expected } // ensure endpoint has been called assertThat(mockEndpoint.assertExchangeReceived(0)).isNotNull(); // external task is still not resolved final List<ExternalTask> externalTasks4 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks4).isNotNull(); assertThat(externalTasks4.size()).isEqualTo(1); // Exception aborted processing so retries could not be set! assertThat(externalTasks4.get(0).getRetries()).isEqualTo(2); assertThat(externalTasks4.get(0).getErrorMessage()).isEqualTo(FAILURE); // assert that process not in the end event "HappyEnd" assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("HappyEnd").singleResult()).isNull(); // assert that process ended not due to error boundary event 4711 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End4711").singleResult()).isNull(); // assert that process ended not due to error boundary event 0815 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End0815").singleResult()).isNull(); // assert that the variables unchanged final List<HistoricVariableInstance> variables2 = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables2.size()).isEqualTo(3); final HashMap<String, Object> variablesAsMap2 = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables2) { variablesAsMap2.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap2.containsKey("var1")).isTrue(); assertThat(variablesAsMap2.get("var1")).isEqualTo("foo"); assertThat(variablesAsMap2.containsKey("var2")).isTrue(); assertThat(variablesAsMap2.get("var2")).isEqualTo("bar"); assertThat(variablesAsMap2.containsKey("var3")).isTrue(); assertThat(variablesAsMap2.get("var3")).isEqualTo("foobar"); // complete task to make test order not relevant externalTaskService.complete(externalTasks2.get(0).getId(), "0815"); } @Test @Deployment(resources = { "process/StartExternalTask4.bpmn20.xml" }) public void testSetExternalTaskRetriesAnnotation() throws Exception { // start process final Map<String, Object> processVariables = new HashMap<String, Object>(); processVariables.put("var1", "foo"); processVariables.put("var2", "bar"); processVariables.put("var3", "foobar"); final ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startExternalTaskProcess2", processVariables); assertThat(processInstance).isNotNull(); // external task is still not resolved and not locked final List<ExternalTask> externalTasks1 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks1).isNotNull(); assertThat(externalTasks1.size()).isEqualTo(1); assertThat(externalTasks1.get(0).getWorkerId()).isNull(); assertThat(externalTasks1.get(0).getRetries()).isNull(); // find external task and lock final List<LockedExternalTask> locked = externalTaskService.fetchAndLock(1, "0815", true).topic("topic4", 5000).execute(); assertThat(locked).isNotNull(); assertThat(locked.size()).isEqualTo(1); final LockedExternalTask lockedExternalTask = locked.get(0); // set retries artificially externalTaskService.handleFailure(lockedExternalTask.getId(), "0815", "Blabal", 2, 0); /* * DontChangeRetriesException */ mockEndpoint.reset(); final String DONTCHANGEMSG = "DoNotChange"; // variables returned but must not be set since task will not be // completed mockEndpoint.whenAnyExchangeReceived(new Processor() { @Override public void process(Exchange exchange) throws Exception { throw new DontChangeRetriesException(DONTCHANGEMSG); } }); // call route "direct:testRoute" final ProducerTemplate template2 = camelContext.createProducerTemplate(); try { template2.requestBodyAndHeader("direct:firstTestRoute", null, "CamundaBpmExternalTaskId", lockedExternalTask.getId()); Assert.fail("Expected an exception, but Camel route succeeded!"); } catch (Exception e) { // expected } // ensure endpoint has been called assertThat(mockEndpoint.assertExchangeReceived(0)).isNotNull(); // external task is still not resolved final List<ExternalTask> externalTasks4 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks4).isNotNull(); assertThat(externalTasks4.size()).isEqualTo(1); // Exception aborted processing so retries could not be set! assertThat(externalTasks4.get(0).getRetries()).isEqualTo(2); assertThat(externalTasks4.get(0).getErrorMessage()).isEqualTo(DONTCHANGEMSG); // assert that process not in the end event "HappyEnd" assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("HappyEnd").singleResult()).isNull(); // assert that process ended not due to error boundary event 4711 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End4711").singleResult()).isNull(); // assert that process ended not due to error boundary event 0815 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End0815").singleResult()).isNull(); // assert that the variables unchanged final List<HistoricVariableInstance> variables2 = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables2.size()).isEqualTo(3); final HashMap<String, Object> variablesAsMap2 = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables2) { variablesAsMap2.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap2.containsKey("var1")).isTrue(); assertThat(variablesAsMap2.get("var1")).isEqualTo("foo"); assertThat(variablesAsMap2.containsKey("var2")).isTrue(); assertThat(variablesAsMap2.get("var2")).isEqualTo("bar"); assertThat(variablesAsMap2.containsKey("var3")).isTrue(); assertThat(variablesAsMap2.get("var3")).isEqualTo("foobar"); /* * DontChangeRetriesException */ mockEndpoint.reset(); final String CREATEINCIDENT = "Incident"; // variables returned but must not be set since task will not be // completed mockEndpoint.whenAnyExchangeReceived(new Processor() { @Override public void process(Exchange exchange) throws Exception { throw new CreateIncidentException(CREATEINCIDENT); } }); // call route "direct:testRoute" final ProducerTemplate template3 = camelContext.createProducerTemplate(); try { template3.requestBodyAndHeader("direct:firstTestRoute", null, "CamundaBpmExternalTaskId", lockedExternalTask.getId()); Assert.fail("Expected an exception, but Camel route succeeded!"); } catch (Exception e) { // expected } // ensure endpoint has been called assertThat(mockEndpoint.assertExchangeReceived(0)).isNotNull(); // external task is still not resolved final List<ExternalTask> externalTasks3 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks3).isNotNull(); assertThat(externalTasks3.size()).isEqualTo(1); // Exception aborted processing so retries could not be set! assertThat(externalTasks3.get(0).getRetries()).isEqualTo(0); assertThat(externalTasks3.get(0).getErrorMessage()).isEqualTo(CREATEINCIDENT); // assert that process not in the end event "HappyEnd" assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("HappyEnd").singleResult()).isNull(); // assert that process ended not due to error boundary event 4711 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End4711").singleResult()).isNull(); // assert that process ended not due to error boundary event 0815 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End0815").singleResult()).isNull(); // assert that the variables unchanged final List<HistoricVariableInstance> variables3 = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables3.size()).isEqualTo(3); final HashMap<String, Object> variablesAsMap3 = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables3) { variablesAsMap3.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap3.containsKey("var1")).isTrue(); assertThat(variablesAsMap3.get("var1")).isEqualTo("foo"); assertThat(variablesAsMap3.containsKey("var2")).isTrue(); assertThat(variablesAsMap3.get("var2")).isEqualTo("bar"); assertThat(variablesAsMap3.containsKey("var3")).isTrue(); assertThat(variablesAsMap3.get("var3")).isEqualTo("foobar"); // complete task to make test order not relevant externalTaskService.complete(externalTasks1.get(0).getId(), "0815"); } @SuppressWarnings("unchecked") @Test @Deployment(resources = { "process/StartExternalTask2.bpmn20.xml" }) public void testAsyncIsTrueAndLockDuration() throws Exception { // variables returned but must not be set since task will not be // completed mockEndpoint.returnReplyBody(new Expression() { @Override public <T> T evaluate(Exchange exchange, Class<T> type) { final HashMap<String, Object> result = new HashMap<String, Object>(); result.put("var2", "bar2"); result.put("var3", "bar3"); return (T) result; } }); // start process final Map<String, Object> processVariables = new HashMap<String, Object>(); processVariables.put("var1", "foo"); processVariables.put("var2", "bar"); processVariables.put("var3", "foobar"); final ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startExternalTaskProcess2", processVariables); assertThat(processInstance).isNotNull(); // wait for the external task to be completed Thread.sleep(1000); // external task is still not resolved and locked final List<ExternalTask> externalTasks1 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks1).isNotNull(); assertThat(externalTasks1.size()).isEqualTo(1); assertThat(externalTasks1.get(0).getWorkerId()).isEqualTo("0815"); assertThat(externalTasks1.get(0).getLockExpirationTime()).isAfter(new Date()); // wait for the task to unlock and refetch by Camel Thread.sleep(2000); // assert that the camunda BPM process instance ID has been added as a // property to the message for both exchanges received assertThat(mockEndpoint.assertExchangeReceived(0).getProperty(EXCHANGE_HEADER_PROCESS_INSTANCE_ID)).isEqualTo( processInstance.getId()); assertThat(mockEndpoint.assertExchangeReceived(1).getProperty(EXCHANGE_HEADER_PROCESS_INSTANCE_ID)).isEqualTo( processInstance.getId()); // only two process instance variables are loaded according configured // value of "variablesToFetch" parameter assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody()).isNotNull(); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody()).isInstanceOf(Map.class); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody(Map.class).size()).isEqualTo(2); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody(Map.class)).containsKey("var2"); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody(Map.class)).containsKey("var3"); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody(Map.class)).containsValue("bar"); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getBody(Map.class)).containsValue("foobar"); // assert that the variables sent in the response-message has NOT been // set into the process final List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables.size()).isEqualTo(3); final HashMap<String, Object> variablesAsMap = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables) { variablesAsMap.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap.containsKey("var1")).isTrue(); assertThat(variablesAsMap.get("var1")).isEqualTo("foo"); assertThat(variablesAsMap.containsKey("var2")).isTrue(); assertThat(variablesAsMap.get("var2")).isEqualTo("bar"); assertThat(variablesAsMap.containsKey("var3")).isTrue(); assertThat(variablesAsMap.get("var3")).isEqualTo("foobar"); // assert that process NOT in end event "HappyEnd" assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("HappyEnd").singleResult()).isNull(); // assert that process ended not due to error boundary event 4711 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End4711").singleResult()).isNull(); // assert that process ended not due to error boundary event 0815 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End0815").singleResult()).isNull(); } @SuppressWarnings("unchecked") @Test @Deployment(resources = { "process/StartExternalTask.bpmn20.xml" }) public void testBpmnError() throws Exception { // variables to be set by the Camel-endpoint processing the external // task mockEndpoint.returnReplyBody(new Expression() { @Override public <T> T evaluate(Exchange exchange, Class<T> type) { return (T) "4711"; } }); // start process final Map<String, Object> processVariables = new HashMap<String, Object>(); processVariables.put("var1", "foo"); processVariables.put("var2", "bar"); final ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startExternalTaskProcess", processVariables); assertThat(processInstance).isNotNull(); // wait for the external task to be completed Thread.sleep(1000); // external task is still not resolved final long externalTasksCount = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).active().count(); assertThat(externalTasksCount).isEqualTo(0); // assert that the camunda BPM process instance ID has been added as a // property to the message assertThat(mockEndpoint.assertExchangeReceived(0).getProperty(EXCHANGE_HEADER_PROCESS_INSTANCE_ID)).isEqualTo( processInstance.getId()); // assert that the variables sent in the response-message has been set // into the process final List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables.size()).isEqualTo(2); final HashMap<String, Object> variablesAsMap = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables) { variablesAsMap.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap.containsKey("var1")).isTrue(); assertThat(variablesAsMap.get("var1")).isEqualTo("foo"); assertThat(variablesAsMap.containsKey("var2")).isTrue(); assertThat(variablesAsMap.get("var2")).isEqualTo("bar"); // assert that process ended final HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId( processInstance.getId()).singleResult(); assertThat(historicProcessInstance.getEndTime()).isNotNull(); // assert that process ended due to error boundary event 4711 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End4711").singleResult()).isNotNull(); // assert that process not in end event "HappyEnd" assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("HappyEnd").singleResult()).isNull(); // assert that process ended not due to error boundary event 0815 assertThat(historyService.createHistoricActivityInstanceQuery().processInstanceId( processInstance.getId()).activityId("End0815").singleResult()).isNull(); } @Test @Deployment(resources = { "process/StartExternalTask.bpmn20.xml" }) public void testIncidentAndRetryTimeouts() throws Exception { // variables to be set by the Camel-endpoint processing the external // task mockEndpoint.whenAnyExchangeReceived(new Processor() { @Override public void process(final Exchange exchange) throws Exception { throw new RuntimeException("fail!"); } }); // count incidents for later comparison final long incidentCount = runtimeService.createIncidentQuery().count(); // start process final Map<String, Object> processVariables = new HashMap<String, Object>(); processVariables.put("var1", "foo"); processVariables.put("var2", "bar"); final ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("startExternalTaskProcess", processVariables); assertThat(processInstance).isNotNull(); // wait for the external task to be completed Thread.sleep(1000); // external task is still not resolved final List<ExternalTask> externalTasks1 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks1).isNotNull(); assertThat(externalTasks1.size()).isEqualTo(1); assertThat(externalTasks1.get(0).getRetries()).isEqualTo(2); // wait for the next try Thread.sleep(1000); // external task is still not resolved final List<ExternalTask> externalTasks2 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks2).isNotNull(); assertThat(externalTasks2.size()).isEqualTo(1); assertThat(externalTasks2.get(0).getRetries()).isEqualTo(1); // next try is 2 seconds so after 1 second nothing changes Thread.sleep(1000); // external task is still not resolved final List<ExternalTask> externalTasks3 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks3).isNotNull(); assertThat(externalTasks3.size()).isEqualTo(1); assertThat(externalTasks3.get(0).getRetries()).isEqualTo(1); // wait for the next try Thread.sleep(1000); // external task is still not resolved final List<ExternalTask> externalTasks4 = externalTaskService.createExternalTaskQuery().processInstanceId( processInstance.getId()).list(); assertThat(externalTasks4).isNotNull(); assertThat(externalTasks4.size()).isEqualTo(1); assertThat(externalTasks4.get(0).getRetries()).isEqualTo(0); // assert that the camunda BPM process instance ID has been added as a // property to the message assertThat(mockEndpoint.assertExchangeReceived(0).getProperty(EXCHANGE_HEADER_PROCESS_INSTANCE_ID)).isEqualTo( processInstance.getId()); // assert that in-headers are set properly assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getHeader(EXCHANGE_HEADER_ATTEMPTSSTARTED)).isEqualTo( 0); assertThat(mockEndpoint.assertExchangeReceived(0).getIn().getHeader(EXCHANGE_HEADER_RETRIESLEFT)).isEqualTo(2); assertThat(mockEndpoint.assertExchangeReceived(1).getIn().getHeader(EXCHANGE_HEADER_ATTEMPTSSTARTED)).isEqualTo( 1); assertThat(mockEndpoint.assertExchangeReceived(1).getIn().getHeader(EXCHANGE_HEADER_RETRIESLEFT)).isEqualTo(1); // assert that the variables sent in the response-message has been set // into the process final List<HistoricVariableInstance> variables = historyService.createHistoricVariableInstanceQuery().processInstanceId( processInstance.getId()).list(); assertThat(variables.size()).isEqualTo(2); final HashMap<String, Object> variablesAsMap = new HashMap<String, Object>(); for (final HistoricVariableInstance variable : variables) { variablesAsMap.put(variable.getName(), variable.getValue()); } assertThat(variablesAsMap.containsKey("var1")).isTrue(); assertThat(variablesAsMap.get("var1")).isEqualTo("foo"); assertThat(variablesAsMap.containsKey("var2")).isTrue(); assertThat(variablesAsMap.get("var2")).isEqualTo("bar"); // assert that process not ended final HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId( processInstance.getId()).singleResult(); assertThat(historicProcessInstance.getEndTime()).isNull(); // assert that incident raised assertThat(runtimeService.createIncidentQuery().count()).isEqualTo(incidentCount + 1); } }