Java tutorial
/** * Copyright (C) 2015 BonitaSoft S.A. * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation * version 2.1 of the License. * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301, USA. **/ package org.bonitasoft.engine.test; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.BPMRemoteTests; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.connector.ConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.flownode.impl.internal.FlowElementContainerDefinitionImpl; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.connector.Connector; import org.bonitasoft.engine.connectors.ConnectorExecutionIT; import org.bonitasoft.engine.connectors.TestConnector; import org.bonitasoft.engine.connectors.TestConnector3; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.service.TenantServiceAccessor; import org.bonitasoft.engine.service.TenantServiceSingleton; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.test.annotation.Cover; import org.bonitasoft.engine.test.annotation.Cover.BPMNConcept; import org.junit.Test; /** * @author Baptiste Mesta * @author Celine Souchet * @author Elias Ricken de Medeiros */ @SuppressWarnings("javadoc") public class ConnectorExecutionsLocalIT extends ConnectorExecutionIT { protected TenantServiceAccessor getTenantAccessor() { try { final SessionAccessor sessionAccessor = ServiceAccessorFactory.getInstance().createSessionAccessor(); final long tenantId = sessionAccessor.getTenantId(); return TenantServiceSingleton.getInstance(tenantId); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "On finish", "Data input", "Automatic activity" }, story = "Test connector on finish of an automatic activity with data input.", jira = "") @Test public void executeConnectorOnFinishOfAnAutomaticActivityWithDataAsInput() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String dataName = "myData1"; final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnFinishOfAnAutomaticActivityWithDataAsInput", "1.0"); designProcessDefinition.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addAutomaticTask("step1") .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createDataExpression(dataName, String.class.getName())); designProcessDefinition.addUserTask("step2", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step2"); waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector, Several, On start, User task" }, jira = "ENGINE-472", story = "Test of several connectors on start of an user task.") @Test public void executeSeveralConnectorsOnUserTaskOnStart() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String valueOfInput2 = "valueOfInput2"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeSeveralConnectorsOnUserTaskOnStart", "1.0"); processBuilder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder taskBuilder = new UserTaskDefinitionBuilder(processBuilder, (FlowElementContainerDefinitionImpl) processBuilder.getProcess().getProcessContainer(), "step1", ACTOR_NAME); taskBuilder.addConnector("myConnector1", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER).addInput(TestConnector3.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); taskBuilder.addConnector("myConnector2", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER).addInput(TestConnector3.INPUT2, new ExpressionBuilder().createConstantStringExpression(valueOfInput2)); processBuilder.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); assignAndExecuteStep(step1Id, user); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector, Several, On start, Automatic task" }, jira = "ENGINE-472", story = "Test of several connectors on start of an automatic task.") @Test public void executeSeveralConnectorsOnAutomaticTaskOnStart() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String valueOfInput2 = "valueOfInput2"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeSeveralConnectorsOnAutomaticTaskOnStart", "1.0"); processBuilder.addActor(ACTOR_NAME); final AutomaticTaskDefinitionBuilder taskBuilder = new AutomaticTaskDefinitionBuilder(processBuilder, (FlowElementContainerDefinitionImpl) processBuilder.getProcess().getProcessContainer(), "step1"); taskBuilder.addConnector("myConnector1", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER).addInput(TestConnector3.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); taskBuilder.addConnector("myConnector2", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER).addInput(TestConnector3.INPUT2, new ExpressionBuilder().createConstantStringExpression(valueOfInput2)); processBuilder.addAutomaticTask("step2").addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector, Several, On finish, User task" }, jira = "ENGINE-472", story = "Test of several connectors on finish of an user task.") @Test public void executeSeveralConnectorsOnUserTaskOnFinish() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String valueOfInput2 = "valueOfInput2"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeSeveralConnectorsOnUserTaskOnFinish", "1.0"); processBuilder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder taskBuilder = processBuilder.addUserTask("step1", ACTOR_NAME); taskBuilder.addConnector("myConnector1", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); taskBuilder.addConnector("myConnector2", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT2, new ExpressionBuilder().createConstantStringExpression(valueOfInput2)); processBuilder.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); final long step2Id = waitForUserTask(processInstance, "step2"); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); assignAndExecuteStep(step2Id, userId); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector, Several, On finish, Automatic task" }, jira = "ENGINE-472", story = "Test of several connectors on finish of an automatic task.") @Test public void executeSeveralConnectorsOnAutomaticTaskOnFinish() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String valueOfInput2 = "valueOfInput2"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeSeveralConnectorsOnAutomaticTaskOnFinish", "1.0"); processBuilder.addActor(ACTOR_NAME); final AutomaticTaskDefinitionBuilder taskBuilder = processBuilder.addAutomaticTask("step1"); taskBuilder .addConnector("myConnector1", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH) .addInput("input1", new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); taskBuilder .addConnector("myConnector2", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH) .addInput("input2", new ExpressionBuilder().createConstantStringExpression(valueOfInput2)); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector, Several, On start, On finish" }, jira = "ENGINE-472", story = "Test of several connectors on start and finish of an user task.") @Test public void executeSeveralConnectorsOnStartAndOnFinishWithDataInput() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String valueOfInput2 = "valueOfInput2"; final String valueOfInput3 = "valueOfInput3"; final String valueOfInput4 = "valueOfInput4"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeSeveralConnectorsOnStartAndOnFinish", "1.0"); processBuilder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder taskBuilder = processBuilder.addUserTask("step1", ACTOR_NAME); taskBuilder.addConnector("myConnector1", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER).addInput(TestConnector3.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); taskBuilder.addConnector("myConnector2", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER).addInput(TestConnector3.INPUT2, new ExpressionBuilder().createConstantStringExpression(valueOfInput2)); taskBuilder.addConnector("myConnector3", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT3, new ExpressionBuilder().createConstantStringExpression(valueOfInput3)); taskBuilder.addConnector("myConnector4", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT4, new ExpressionBuilder().createConstantStringExpression(valueOfInput4)); processBuilder.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); assignAndExecuteStep(step1Id, userId); final long step2Id = waitForUserTask(processInstance, "step2"); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); assignAndExecuteStep(step2Id, userId); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "On finish", "Automatic activity" }, story = "Test connector on finish of an automatic activity.", jira = "") @Test public void executeConnectorOnFinishOfAnAutomaticActivity() throws Exception { final String valueOfInput1 = "valueOfInput1"; final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addAutomaticTask("step1") .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); waitForProcessToFinish(startProcess); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "On enter", "User task" }, story = "Test connector on start of an user task.", jira = "") @Test public void executeConnectorOnEnterOfAnUserTask() throws Exception { final String valueOfInput1 = "valueOfInput1"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnStartOfAnUserActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("step1", ACTOR_NAME) .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "Connector deletion", "On enter", "User task", }, story = "Test connectors are deleted when the task is completed.", jira = "") @Test public void connectorsAreDeletedAfterTaskCompletion() throws Exception { // deploy process final String taskName = "step1"; final ProcessDefinition processDefinition = deployProcessWithConnectorOnUserTask(user, taskName); // start the process final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // wait for step containing the connector and execute it final long step1Id = waitForUserTaskAndExecuteIt(processInstance, taskName, user); waitForUserTask(processInstance, "step2"); // check that there are no more connector instances final SearchResult<ConnectorInstance> searchResult = searchConnectors(step1Id, ConnectorInstance.FLOWNODE_TYPE, 10); assertEquals(0, searchResult.getCount()); // clean up disableAndDeleteProcess(processDefinition); } private SearchResult<ConnectorInstance> searchConnectors(final long containerId, final String containerType, final int maxResults) throws SearchException { final SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, maxResults); optionsBuilder.filter(ConnectorInstancesSearchDescriptor.CONTAINER_ID, containerId); optionsBuilder.filter(ConnectorInstancesSearchDescriptor.CONTAINER_TYPE, containerType); return getProcessAPI().searchConnectorInstances(optionsBuilder.done()); } private ProcessDefinition deployProcessWithConnectorOnUserTask(final User user, final String taskName) throws BonitaException, IOException { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask(taskName, ACTOR_NAME) .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_ENTER) .addInput("input1", new ExpressionBuilder().createConstantStringExpression("valueOfInput1")); processBuilder.addUserTask("step2", ACTOR_NAME).addTransition(taskName, "step2"); return deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); } @Cover(classes = { Connector.class, HumanTaskInstance.class }, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "On finish", "User task", "Starting State" }, story = "Test connector on finish on starting state of an user task.", jira = "ENGINE-604") @Test public void executeConnectorOnFinishOfAnUserTask() throws Exception { final String valueOfInput1 = "valueOfInput1"; // Configure process and human tasks final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnFinishOfAnUserTask", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("step1", ACTOR_NAME) .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("step1", "step2"); // Deploy and start process final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // Assign human task with connector final long step1Id = waitForUserTaskAndAssigneIt(processInstance, "step1", user).getId(); // Check that the "input1" variable has no value for "valueOfInput1" final WaitUntil waitUntil = waitForVariableStorage(50, 800, TestConnector.INPUT1, valueOfInput1); assertFalse(waitUntil.waitUntil()); // Run Started state of the human task getProcessAPI().executeFlowNode(step1Id); waitForArchivedActivity(step1Id, TestStates.NORMAL_FINAL); // Check that the "input1" variable has value for "valueOfInput1", in Started state of human task waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); // Remove all for the end disableAndDeleteProcess(processDefinition); } @Cover(classes = { Connector.class, HumanTaskInstance.class }, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "On finish", "User task", "Boundary event", "Timer event", "Starting State" }, story = "Test connector on finish on starting state of an user task, with a boundary timer evnet.", jira = "ENGINE-604") @Test public void executeConnectorOnFinishStateOfAnUserTaskWithTimerEvent() throws Exception { final String valueOfInput1 = "valueOfInput1"; // Configure process and human tasks final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnFinishStateOfAnUserTaskWithTimerEvent", "1.0"); processBuilder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processBuilder.addUserTask("step1", ACTOR_NAME); userTaskDefinitionBuilder.addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH).addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); processBuilder.addStartEvent("start"); userTaskDefinitionBuilder.addBoundaryEvent("timer", true).addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createConstantLongExpression(3000)); userTaskDefinitionBuilder.addUserTask("exceptionStep", ACTOR_NAME); processBuilder.addEndEvent("end"); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("step1", "step2"); processBuilder.addTransition("timer", "exceptionStep"); // Deploy and start process final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // Assign human task with connector final ActivityInstance step1 = waitForUserTaskAndAssigneIt(processInstance, "step1", user); // Check that the "input1" variable has no value for "valueOfInput1", in Ready state of human task final WaitUntil waitUntil = waitForVariableStorage(50, 800, TestConnector.INPUT1, valueOfInput1); assertFalse(waitUntil.waitUntil()); // Run Started state of the human task getProcessAPI().executeFlowNode(step1.getId()); waitForUserTask(processInstance, "step2"); // Check that the "input1" variable has value for "valueOfInput1", in Started state of human task waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); // Remove all for the end disableAndDeleteProcess(processDefinition); } @SuppressWarnings("deprecation") private WaitUntil waitForVariableStorage(final int repeatEach, final int timeout, final String inputName, final String valueOfInput) { final WaitUntil waitUntil = new WaitUntil(repeatEach, timeout, false) { @Override protected boolean check() { return VariableStorage.getInstance().getVariableValue(inputName).equals(valueOfInput); } }; return waitUntil; } @SuppressWarnings("deprecation") private void waitForVariableStorage(final String inputName, final String valueOfInput) throws Exception { final WaitUntil waitUntil = waitForVariableStorage(50, 5000, inputName, valueOfInput); assertTrue(waitUntil.waitUntil()); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "On enter", "Process" }, story = "Test connector on start of a process.", jira = "") @Test public void executeConnectorOnEnterOfProcess() throws Exception { final String valueOfInput1 = "valueOfInput1"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_ENTER).addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); processBuilder.addAutomaticTask("step1"); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); waitForUserTask(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "On finish", "Process" }, story = "Test connector on finish of a process.", jira = "") @Test public void executeConnectorOnFinishOfAProcess() throws Exception { final String valueOfInput = "valueOfInput1"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnStartOfAnAutomaticActivity", PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder.addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH).addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput)); processBuilder.addUserTask("step1", ACTOR_NAME); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final WaitUntil waitUntil = waitForVariableStorage(50, 800, TestConnector.INPUT1, valueOfInput); assertFalse(waitUntil.waitUntil()); assignAndExecuteStep(step1Id, userId); waitForVariableStorage(TestConnector.INPUT1, valueOfInput); // Clean up waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "Connector deletion", "On finish", "Process" }, story = "Test connectors attached to a process are deleted when the process completes.", jira = "") @Test public void connectorsAreDeletedAfterProcessCompletion() throws Exception { // deploy the a process with a connector final String taskName = "step1"; final ProcessDefinition processDefinition = deployProcessWithConnectorOnFinish(user, taskName); // execute the process final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, taskName, user); waitForProcessToFinish(processInstance); // check there are no connector instances final SearchResult<ConnectorInstance> searchResult = searchConnectors(processInstance.getId(), ConnectorInstance.PROCESS_TYPE, 10); assertEquals(0, searchResult.getCount()); // clean up disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployProcessWithConnectorOnFinish(final User user, final String taskName) throws BonitaException, IOException { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput("input1", new ExpressionBuilder().createConstantStringExpression("valueOfInput1")); processBuilder.addUserTask(taskName, ACTOR_NAME); return deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "On enter", "Automatic activity" }, story = "Test connector on start of an automatic activity.", jira = "") @Test public void executeConnectorOnEnterOfAnAutomaticActivity() throws Exception { final String valueOfInput1 = "valueOfInput1"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addAutomaticTask("step1") .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); waitForUserTask(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "Missing implementation", "Process instance" }, story = "Execute connector with missing implementation on process instance.", jira = "") @Test public void executeMissingImplConnectorOnProcessInstance() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnActivityInstance", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME) .addConnector("UnkownConnector", "unkownConnectorId", "1.0.0", ConnectorEvent.ON_ENTER); final ProcessDefinition processDefinition = getProcessAPI().deploy(new BusinessArchiveBuilder() .createNewBusinessArchive().setProcessDefinition(designProcessDefinition.done()).done()); getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, userId); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState()); final List<Problem> processResolutionProblems = getProcessAPI() .getProcessResolutionProblems(processDefinition.getId()); assertThat(processResolutionProblems).extracting("resource").contains("connector"); deleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Connector", "Missing class connector", "Process instance" }, story = "Execute connector with missing class on process instance.", jira = "") @Test public void executeMissingClassConnectorOnProcessInstance() throws Exception { final PrintStream stdout = System.out; final ByteArrayOutputStream myOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(myOut)); try { final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnActivityInstance", "1.0"); processDefBuilder.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME).addConnector( "UnkownClassConnector", "unkownClassConnectorDef", "1.0.0", ConnectorEvent.ON_ENTER); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder() .createNewBusinessArchive().setProcessDefinition(processDefBuilder.done()); businessArchiveBuilder.addConnectorImplementation( new BarResource("UnknownClassConnector.impl", IOUtils.toByteArray(BPMRemoteTests.class .getResourceAsStream("/org/bonitasoft/engine/connectors/UnknownClassConnector.impl")))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor( businessArchiveBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForFlowNodeInFailedState(processInstance, "step1"); disableAndDeleteProcess(processDefinition); } finally { System.setOut(stdout); } final String logs = myOut.toString(); System.out.println(logs); assertTrue("should have written in logs an exception", logs.contains("SConnectorException")); assertTrue("should have written in logs an exception", logs.contains("org.unknown.MyUnknownClass")); } @Cover(classes = Connector.class, concept = BPMNConcept.OTHERS, keywords = { "Connector", "Classpath" }, jira = "ENGINE-773") @Test public void executeConnectorWithCustomOutputTypeOnActivity() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("testConnectorWithExecutionTooLong", "1.0"); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addShortTextData("value", null); designProcessDefinition.addAutomaticTask("step1") .addConnector("myConnector1", "connectorWithCustomType", "1.0.0", ConnectorEvent.ON_ENTER) .addOutput(new OperationBuilder().createNewInstance().setLeftOperand("value", false) .setType(OperatorType.ASSIGNMENT) .setRightOperand(new ExpressionBuilder().createGroovyScriptExpression("script", "output.getValue()", String.class.getName(), new ExpressionBuilder() .createInputExpression("output", "org.connector.custom.CustomType"))) .done()); designProcessDefinition.addUserTask("step2", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); final List<BarResource> resources = new ArrayList<BarResource>(); addResource(resources, "/org/bonitasoft/engine/connectors/TestConnectorWithCustomType.impl", "TestConnectorWithCustomType.impl"); addResource(resources, "/org/bonitasoft/engine/connectors/connector-with-custom-type.bak", "connector-with-custom-type.jar"); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder() .createNewBusinessArchive(); businessArchiveBuilder.addConnectorImplementation(resources.get(0)); businessArchiveBuilder.addClasspathResource(resources.get(1)); businessArchiveBuilder.setProcessDefinition(designProcessDefinition.done()); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, user); final ProcessInstance process = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(process, "step2"); assertEquals("value", getProcessAPI().getProcessDataInstance("value", process.getId()).getValue()); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.OTHERS, keywords = { "Connector", "Classpath" }, jira = "ENGINE-773") @Test public void executeConnectorWithCustomOutputTypeOnProcess() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("testConnectorWithExecutionTooLong", "1.0"); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addShortTextData("value", null); designProcessDefinition .addConnector("myConnector1", "connectorWithCustomType", "1.0.0", ConnectorEvent.ON_ENTER) .addOutput(new OperationBuilder().createNewInstance().setLeftOperand("value", false) .setType(OperatorType.ASSIGNMENT) .setRightOperand(new ExpressionBuilder().createGroovyScriptExpression("script", "output.getValue()", String.class.getName(), new ExpressionBuilder() .createInputExpression("output", "org.connector.custom.CustomType"))) .done()); designProcessDefinition.addAutomaticTask("step1"); designProcessDefinition.addUserTask("step2", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); final List<BarResource> resources = new ArrayList<BarResource>(); addResource(resources, "/org/bonitasoft/engine/connectors/TestConnectorWithCustomType.impl", "TestConnectorWithCustomType.impl"); addResource(resources, "/org/bonitasoft/engine/connectors/connector-with-custom-type.bak", "connector-with-custom-type.jar"); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder() .createNewBusinessArchive(); businessArchiveBuilder.addConnectorImplementation(resources.get(0)); businessArchiveBuilder.addClasspathResource(resources.get(1)); businessArchiveBuilder.setProcessDefinition(designProcessDefinition.done()); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, user); final ProcessInstance process = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(process, "step2"); assertEquals("value", getProcessAPI().getProcessDataInstance("value", process.getId()).getValue()); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, jira = "ENGINE-783", keywords = { "Connector", "No connector implementation", "cache" }, story = "get connector implementation still work avec cache is cleared.") @Test public void getConnectorImplementationWorksAfterCacheCleared() throws Exception { final String valueOfInput1 = "valueOfInput1"; // Configure process and human tasks final Expression input1Expression = new ExpressionBuilder().createConstantStringExpression(valueOfInput1); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder .addUserTask("step1", ACTOR_NAME).addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput(TestConnector.INPUT1, input1Expression); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("step1", "step2"); // Deploy and start process final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); assertTrue(getProcessAPI().getProcessResolutionProblems(processDefinition.getId()).isEmpty()); final SessionAccessor sessionAccessor = ServiceAccessorFactory.getInstance().createSessionAccessor(); sessionAccessor.setSessionInfo(getSession().getId(), getSession().getTenantId()); final CacheService cacheservice = getTenantAccessor().getCacheService(); cacheservice.clearAll(); sessionAccessor.deleteSessionId(); assertTrue(getProcessAPI().getProcessResolutionProblems(processDefinition.getId()).isEmpty()); disableAndDeleteProcess(processDefinition); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Failed", "Database", "Connector", "On enter", "Automatic activity" }, jira = "ENGINE-936") @Test public void executeFailedNonSerializableOutputConnectorOnEnterOfAnAutomaticActivity() throws Exception { executeFailedNonSerializableOutputConnectorOfAnAutomaticActivity(ConnectorEvent.ON_ENTER); } @Cover(classes = Connector.class, concept = BPMNConcept.CONNECTOR, keywords = { "Failed", "Database", "Connector", "On finish", "Automatic activity" }, jira = "ENGINE-936") @Test public void executeFailedNonSerializableOutputConnectorOnFinishOfAnAutomaticActivity() throws Exception { executeFailedNonSerializableOutputConnectorOfAnAutomaticActivity(ConnectorEvent.ON_FINISH); } protected void executeFailedNonSerializableOutputConnectorOfAnAutomaticActivity( final ConnectorEvent connectorEvent) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithNonSerializableOutputConnector", "1.0"); processBuilder.addStartEvent("start").addEndEvent("end"); processBuilder.addActor(ACTOR_NAME); processBuilder.addData("resultProcess", Object.class.getName(), new ExpressionBuilder().createConstantLongExpression(0L)); final AutomaticTaskDefinitionBuilder autoTask = processBuilder.addAutomaticTask("Step1"); processBuilder.addTransition("start", "end"); autoTask.addConnector("nonSerializableFailedConnector", "org.bonitasoft.connector.testConnectorWithNotSerializableOutput", "1.0", connectorEvent) .addOutput(new OperationBuilder().createNewInstance().setLeftOperand("resultProcess", false) .setType(OperatorType.ASSIGNMENT) .setRightOperand( new ExpressionBuilder().createInputExpression("output1", Object.class.getName())) .done()); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithNotSerializableOutput( processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForFlowNodeInFailedState(processInstance, "Step1"); // Clean-up Thread.sleep(1000); disableAndDeleteProcess(processDefinition); } @Cover(classes = Expression.class, concept = BPMNConcept.EXPRESSIONS, keywords = { "Failed", "Database", "Connector", "On finish", "Automatic activity" }, jira = "ENGINE-1814", story = "execute expression on process that is in initializing, e.g. process execute a connector on on enter, the result must be no exception and be able to retrive data") @Test public void executeExpressionAtProcessInstantiationOnProcessInInitializing() throws Exception { // will block the connector final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithBlockingConnector", "1.0"); processBuilder.addConnector("myConnector", "blocking-connector", "1.0", ConnectorEvent.ON_ENTER); processBuilder.addData("a", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("avalue")); processBuilder.addData("b", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("bvalue")); processBuilder.addData("c", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("cvalue")); final AutomaticTaskDefinitionBuilder addAutomaticTask = processBuilder.addAutomaticTask("step1"); addAutomaticTask.addOperation(new OperationBuilder().createSetDataOperation("a", new ExpressionBuilder().createConstantStringExpression("changed"))); addAutomaticTask.addOperation(new OperationBuilder().createSetDataOperation("b", new ExpressionBuilder().createConstantStringExpression("changed"))); addAutomaticTask.addOperation(new OperationBuilder().createSetDataOperation("c", new ExpressionBuilder().createConstantStringExpression("changed"))); // Add user task for confirmation form evaluation: processBuilder.addActor(ACTOR_NAME); final String userTaskName = "userTaskWithOnFinishConnector"; final UserTaskDefinitionBuilder userTaskDef = processBuilder.addUserTask(userTaskName, ACTOR_NAME); userTaskDef.addConnector("myConnector2", "blocking-connector", "1.0", ConnectorEvent.ON_FINISH); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processBuilder.done()) .addConnectorImplementation( new BarResource("blocking-connector.impl", BuildTestUtil.buildConnectorImplementationFile("blocking-connector", "1.0", "blocking-connector-impl", "1.0", BlockingConnector.class.getName()))) .done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); BlockingConnector.semaphore.acquire(); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final Map<Expression, Map<String, Serializable>> expressions = new HashMap<Expression, Map<String, Serializable>>( 2); expressions.put( new ExpressionBuilder().createGroovyScriptExpression("ascripte", "a+b+c", String.class.getName(), new ExpressionBuilder().createDataExpression("a", String.class.getName()), new ExpressionBuilder().createDataExpression("b", String.class.getName()), new ExpressionBuilder().createDataExpression("c", String.class.getName())), Collections.<String, Serializable>emptyMap()); expressions.put(new ExpressionBuilder().createDataExpression("a", String.class.getName()), Collections.<String, Serializable>emptyMap()); final Map<String, Serializable> evaluateExpressionsAtProcessInstantiation = getProcessAPI() .evaluateExpressionsAtProcessInstanciation(processInstance.getId(), expressions); assertEquals("avaluebvaluecvalue", evaluateExpressionsAtProcessInstantiation.get("ascripte")); assertEquals("avalue", evaluateExpressionsAtProcessInstantiation.get("a")); BlockingConnector.semaphore.release(); final long userTaskId = waitForUserTask(processInstance.getId(), userTaskName); BlockingConnector.semaphore.acquire(); assignAndExecuteStep(userTaskId, userId); // Try to evaluate expression on non-completed activity: final Expression engineConstantExpr = new ExpressionBuilder() .createEngineConstant(ExpressionConstants.PROCESS_INSTANCE_ID); final Map<Expression, Map<String, Serializable>> exprToEvaluate = new HashMap<Expression, Map<String, Serializable>>( 1); exprToEvaluate.put(engineConstantExpr, Collections.<String, Serializable>emptyMap()); final Map<String, Serializable> evaluatedExpressions = getProcessAPI() .evaluateExpressionsOnCompletedActivityInstance(userTaskId, exprToEvaluate); assertEquals(processInstance.getId(), ((Long) evaluatedExpressions.get("processInstanceId")).longValue()); // Release the connector for the user task to complete: BlockingConnector.semaphore.release(); waitForProcessToFinish(processInstance.getId()); getProcessAPI().evaluateExpressionsAtProcessInstanciation(processInstance.getId(), expressions); assertEquals("avaluebvaluecvalue", evaluateExpressionsAtProcessInstantiation.get("ascripte")); assertEquals("avalue", evaluateExpressionsAtProcessInstantiation.get("a")); disableAndDeleteProcess(processDefinition); } @Override public ProcessDefinition deployProcessWithActorAndTestConnector( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, null, "TestConnector.impl", TestConnector.class, "TestConnector.jar"); } @Override public ProcessDefinition deployProcessWithActorAndTestConnector3( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, null, "TestConnector3.impl", TestConnector3.class, "TestConnector3.jar"); } }