diff --git a/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/CommonsAbstractFlowNodeBuilder.java b/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/CommonsAbstractFlowNodeBuilder.java new file mode 100644 index 0000000..843d819 --- /dev/null +++ b/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/CommonsAbstractFlowNodeBuilder.java @@ -0,0 +1,79 @@ +package org.camunda.bpm.model.bpmn.builder; +import AbstractFlowElementBuilder; +import org.camunda.bpm.model.bpmn.instance.paradigm.flows.FlowNode; +class CommonsAbstractFlowNodeBuilder extends AbstractFlowElementBuilder { + protected SequenceFlowBuilder currentSequenceFlowBuilder; + + protected boolean compensationStarted; + + /** + * Sets the Camunda AsyncBefore attribute for the build flow node. + * + * @param asyncBefore + * boolean value to set + * @return the builder object + */ + public B camundaAsyncBefore(boolean asyncBefore) { + element.setCamundaAsyncBefore(asyncBefore); + return myself; + } + + /** + * Sets the Camunda asyncBefore attribute to true. + * + * @return the builder object + */ + public B camundaAsyncBefore() { + element.setCamundaAsyncBefore(true); + return myself; + } + + /** + * Sets the Camunda asyncAfter attribute for the build flow node. + * + * @param asyncAfter + * boolean value to set + * @return the builder object + */ + public B camundaAsyncAfter(boolean asyncAfter) { + element.setCamundaAsyncAfter(asyncAfter); + return myself; + } + + /** + * Sets the Camunda asyncAfter attribute to true. + * + * @return the builder object + */ + public B camundaAsyncAfter() { + element.setCamundaAsyncAfter(true); + return myself; + } + + /** + * Sets the Camunda exclusive attribute to true. + * + * @return the builder object + */ + public B notCamundaExclusive() { + element.setCamundaExclusive(false); + return myself; + } + + /** + * Sets the camunda exclusive attribute for the build flow node. + * + * @param exclusive + * boolean value to set + * @return the builder object + */ + public B camundaExclusive(boolean exclusive) { + element.setCamundaExclusive(exclusive); + return myself; + } + + public B camundaJobPriority(String jobPriority) { + element.setCamundaJobPriority(jobPriority); + return myself; + } +} \ No newline at end of file diff --git a/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/DomainAbstractFlowNodeBuilder.java b/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/DomainAbstractFlowNodeBuilder.java new file mode 100644 index 0000000..66e6bd7 --- /dev/null +++ b/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/DomainAbstractFlowNodeBuilder.java @@ -0,0 +1,335 @@ +package org.camunda.bpm.model.bpmn.builder; +import ConditionExpression; +import TransactionBuilder; +import org.camunda.bpm.model.bpmn.BpmnModelException; +import org.camunda.bpm.model.bpmn.instance.bpmndi.BpmnShape; +import org.camunda.bpm.model.bpmn.instance.camunda.CamundaExecutionListener; +import org.camunda.bpm.model.bpmn.instance.camunda.CamundaFailedJobRetryTimeCycle; +import org.camunda.bpm.model.bpmn.instance.domain.events.advanced.BoundaryEvent; +import org.camunda.bpm.model.bpmn.instance.domain.events.advanced.CompensateEventDefinition; +import org.camunda.bpm.model.bpmn.instance.domain.events.advanced.IntermediateCatchEvent; +import org.camunda.bpm.model.bpmn.instance.domain.events.advanced.IntermediateThrowEvent; +import org.camunda.bpm.model.bpmn.instance.domain.humaninteraction.ManualTask; +import org.camunda.bpm.model.bpmn.instance.domain.humaninteraction.UserTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.Activity; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.BusinessRuleTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.CallActivity; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.ReceiveTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.ScriptTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.SendTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.ServiceTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.events.EndEvent; +import org.camunda.bpm.model.bpmn.instance.paradigm.events.EventDefinition; +import org.camunda.bpm.model.bpmn.instance.paradigm.flows.FlowNode; +import org.camunda.bpm.model.bpmn.instance.paradigm.flows.SequenceFlow; +import org.camunda.bpm.model.bpmn.instance.paradigm.gateways.EventBasedGateway; +import org.camunda.bpm.model.bpmn.instance.paradigm.gateways.ExclusiveGateway; +import org.camunda.bpm.model.bpmn.instance.paradigm.gateways.InclusiveGateway; +import org.camunda.bpm.model.bpmn.instance.paradigm.gateways.ParallelGateway; +import org.camunda.bpm.model.bpmn.instance.paradigm.subprocesses.SubProcess; +import org.camunda.bpm.model.bpmn.instance.paradigm.subprocesses.Transaction; +import org.camunda.bpm.model.xml.instance.ModelElementInstance; +import static org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder.createBpmnShape; +import static org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder.createInstance; +import static org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder.createSibling; +public abstract class DomainAbstractFlowNodeBuilder extends ParadigmAbstractFlowNodeBuilder { + protected BoundaryEvent compensateBoundaryEvent; + + protected SequenceFlowBuilder getCurrentSequenceFlowBuilder() { + if (this.currentSequenceFlowBuilder == null) { + SequenceFlow sequenceFlow = createSibling(SequenceFlow.class); + this.currentSequenceFlowBuilder = sequenceFlow.builder(); + } + return this.currentSequenceFlowBuilder; + } + + public B condition(String name, String condition) { + if (name != null) { + getCurrentSequenceFlowBuilder().name(name); + } + ConditionExpression conditionExpression = createInstance(ConditionExpression.class); + conditionExpression.setTextContent(condition); + getCurrentSequenceFlowBuilder().condition(conditionExpression); + return myself; + } + + protected void connectTarget(FlowNode target) { + // check if compensation was started + if (isBoundaryEventWithStartedCompensation()) { + // the target activity should be marked for compensation + if (target instanceof Activity) { + ((Activity) (target)).setForCompensation(true); + } + // connect the target via association instead of sequence flow + connectTargetWithAssociation(target); + } else if (isCompensationHandler()) { + // cannot connect to a compensation handler + throw new BpmnModelException("Only single compensation handler allowed. Call compensationDone() to continue main flow."); + } else { + // connect as sequence flow by default + connectTargetWithSequenceFlow(target); + } + } + + protected void connectTargetWithSequenceFlow(FlowNode target) { + getCurrentSequenceFlowBuilder().from(element).to(target); + SequenceFlow sequenceFlow = getCurrentSequenceFlowBuilder().getElement(); + createEdge(sequenceFlow); + this.currentSequenceFlowBuilder = null; + } + + public AbstractFlowNodeBuilder compensationDone() { + if (this.compensateBoundaryEvent != null) { + return this.compensateBoundaryEvent.getAttachedTo().builder(); + } else { + throw new BpmnModelException("No compensation in progress. Call compensationStart() first."); + } + } + + public B sequenceFlowId(String sequenceFlowId) { + getCurrentSequenceFlowBuilder().id(sequenceFlowId); + return myself; + } + + protected T createTarget(Class typeClass) { + return createTarget(typeClass, null); + } + + protected T createTarget(Class typeClass, String identifier) { + T target = createSibling(typeClass, identifier); + BpmnShape targetBpmnShape = createBpmnShape(target); + setCoordinates(targetBpmnShape); + connectTarget(target); + resizeSubProcess(targetBpmnShape); + return target; + } + + protected T createTargetBuilder(Class typeClass) { + return createTargetBuilder(typeClass, null); + } + + @SuppressWarnings("unchecked") + protected T createTargetBuilder(Class typeClass, String id) { + AbstractFlowNodeBuilder builder = createTarget(typeClass, id).builder(); + if (this.compensationStarted) { + // pass on current boundary event to return after compensationDone call + builder.compensateBoundaryEvent = this.compensateBoundaryEvent; + } + return ((T) (builder)); + } + + public ServiceTaskBuilder serviceTask() { + return createTargetBuilder(ServiceTask.class); + } + + public ServiceTaskBuilder serviceTask(String id) { + return createTargetBuilder(ServiceTask.class, id); + } + + public SendTaskBuilder sendTask() { + return createTargetBuilder(SendTask.class); + } + + public SendTaskBuilder sendTask(String id) { + return createTargetBuilder(SendTask.class, id); + } + + public UserTaskBuilder userTask() { + return createTargetBuilder(UserTask.class); + } + + public UserTaskBuilder userTask(String id) { + return createTargetBuilder(UserTask.class, id); + } + + public BusinessRuleTaskBuilder businessRuleTask() { + return createTargetBuilder(BusinessRuleTask.class); + } + + public BusinessRuleTaskBuilder businessRuleTask(String id) { + return createTargetBuilder(BusinessRuleTask.class, id); + } + + public ScriptTaskBuilder scriptTask() { + return createTargetBuilder(ScriptTask.class); + } + + public ScriptTaskBuilder scriptTask(String id) { + return createTargetBuilder(ScriptTask.class, id); + } + + public ReceiveTaskBuilder receiveTask() { + return createTargetBuilder(ReceiveTask.class); + } + + public ReceiveTaskBuilder receiveTask(String id) { + return createTargetBuilder(ReceiveTask.class, id); + } + + public ManualTaskBuilder manualTask() { + return createTargetBuilder(ManualTask.class); + } + + public ManualTaskBuilder manualTask(String id) { + return createTargetBuilder(ManualTask.class, id); + } + + public EndEventBuilder endEvent() { + return createTarget(EndEvent.class).builder(); + } + + public EndEventBuilder endEvent(String id) { + return createTarget(EndEvent.class, id).builder(); + } + + public ParallelGatewayBuilder parallelGateway() { + return createTarget(ParallelGateway.class).builder(); + } + + public ParallelGatewayBuilder parallelGateway(String id) { + return createTarget(ParallelGateway.class, id).builder(); + } + + public ExclusiveGatewayBuilder exclusiveGateway() { + return createTarget(ExclusiveGateway.class).builder(); + } + + public InclusiveGatewayBuilder inclusiveGateway() { + return createTarget(InclusiveGateway.class).builder(); + } + + public EventBasedGatewayBuilder eventBasedGateway() { + return createTarget(EventBasedGateway.class).builder(); + } + + public ExclusiveGatewayBuilder exclusiveGateway(String id) { + return createTarget(ExclusiveGateway.class, id).builder(); + } + + public InclusiveGatewayBuilder inclusiveGateway(String id) { + return createTarget(InclusiveGateway.class, id).builder(); + } + + public IntermediateCatchEventBuilder intermediateCatchEvent() { + return createTarget(IntermediateCatchEvent.class).builder(); + } + + public IntermediateCatchEventBuilder intermediateCatchEvent(String id) { + return createTarget(IntermediateCatchEvent.class, id).builder(); + } + + public IntermediateThrowEventBuilder intermediateThrowEvent() { + return createTarget(IntermediateThrowEvent.class).builder(); + } + + public IntermediateThrowEventBuilder intermediateThrowEvent(String id) { + return createTarget(IntermediateThrowEvent.class, id).builder(); + } + + public CallActivityBuilder callActivity() { + return createTarget(CallActivity.class).builder(); + } + + public CallActivityBuilder callActivity(String id) { + return createTarget(CallActivity.class, id).builder(); + } + + public SubProcessBuilder subProcess() { + return createTarget(SubProcess.class).builder(); + } + + public SubProcessBuilder subProcess(String id) { + return createTarget(SubProcess.class, id).builder(); + } + + public TransactionBuilder transaction() { + Transaction transaction = createTarget(Transaction.class); + return new TransactionBuilder(modelInstance, transaction); + } + + public TransactionBuilder transaction(String id) { + Transaction transaction = createTarget(Transaction.class, id); + return new TransactionBuilder(modelInstance, transaction); + } + + @SuppressWarnings("rawtypes") + public AbstractFlowNodeBuilder connectTo(String identifier) { + ModelElementInstance target = modelInstance.getModelElementById(identifier); + if (target == null) { + throw new BpmnModelException(((("Unable to connect " + element.getId()) + " to element ") + identifier) + " cause it not exists."); + } else if (!(target instanceof FlowNode)) { + throw new BpmnModelException(((("Unable to connect " + element.getId()) + " to element ") + identifier) + " cause its not a flow node."); + } else { + FlowNode targetNode = ((FlowNode) (target)); + connectTarget(targetNode); + return targetNode.builder(); + } + } + + /** + * Sets the camunda failedJobRetryTimeCycle attribute for the build flow node. + * + * @param retryTimeCycle + * the retry time cycle value to set + * @return the builder object + */ + public B camundaFailedJobRetryTimeCycle(String retryTimeCycle) { + CamundaFailedJobRetryTimeCycle failedJobRetryTimeCycle = createInstance(CamundaFailedJobRetryTimeCycle.class); + failedJobRetryTimeCycle.setTextContent(retryTimeCycle); + addExtensionElement(failedJobRetryTimeCycle); + return myself; + } + + @SuppressWarnings("rawtypes") + public B camundaExecutionListenerClass(String eventName, Class listenerClass) { + return camundaExecutionListenerClass(eventName, listenerClass.getName()); + } + + public B camundaExecutionListenerClass(String eventName, String fullQualifiedClassName) { + CamundaExecutionListener executionListener = createInstance(CamundaExecutionListener.class); + executionListener.setCamundaEvent(eventName); + executionListener.setCamundaClass(fullQualifiedClassName); + addExtensionElement(executionListener); + return myself; + } + + public B camundaExecutionListenerExpression(String eventName, String expression) { + CamundaExecutionListener executionListener = createInstance(CamundaExecutionListener.class); + executionListener.setCamundaEvent(eventName); + executionListener.setCamundaExpression(expression); + addExtensionElement(executionListener); + return myself; + } + + public B camundaExecutionListenerDelegateExpression(String eventName, String delegateExpression) { + CamundaExecutionListener executionListener = createInstance(CamundaExecutionListener.class); + executionListener.setCamundaEvent(eventName); + executionListener.setCamundaDelegateExpression(delegateExpression); + addExtensionElement(executionListener); + return myself; + } + + public B compensationStart() { + if (element instanceof BoundaryEvent) { + BoundaryEvent boundaryEvent = ((BoundaryEvent) (element)); + for (EventDefinition eventDefinition : boundaryEvent.getEventDefinitions()) { + if (eventDefinition instanceof CompensateEventDefinition) { + // if the boundary event contains a compensate event definition then + // save the boundary event to later return to it and start a compensation + this.compensateBoundaryEvent = boundaryEvent; + this.compensationStarted = true; + return myself; + } + } + } + throw new BpmnModelException("Compensation can only be started on a boundary event with a compensation event definition"); + } + + protected boolean isBoundaryEventWithStartedCompensation() { + return this.compensationStarted && (this.compensateBoundaryEvent != null); + } + + protected boolean isCompensationHandler() { + return (!this.compensationStarted) && (this.compensateBoundaryEvent != null); + } +} \ No newline at end of file diff --git a/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/ParadigmAbstractFlowNodeBuilder.java b/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/ParadigmAbstractFlowNodeBuilder.java new file mode 100644 index 0000000..1a82e3d --- /dev/null +++ b/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/ParadigmAbstractFlowNodeBuilder.java @@ -0,0 +1,57 @@ +package org.camunda.bpm.model.bpmn.builder; +import AssociationDirection.One; +import org.camunda.bpm.model.bpmn.BpmnModelException; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.Activity; +import org.camunda.bpm.model.bpmn.instance.paradigm.artifacts.Association; +import org.camunda.bpm.model.bpmn.instance.paradigm.flows.FlowNode; +import org.camunda.bpm.model.bpmn.instance.paradigm.gateways.Gateway; +import org.camunda.bpm.model.xml.instance.ModelElementInstance; +public abstract class ParadigmAbstractFlowNodeBuilder extends CommonsAbstractFlowNodeBuilder { + protected void connectTargetWithAssociation(FlowNode target) { + Association association = modelInstance.newInstance(Association.class); + association.setTarget(target); + association.setSource(element); + association.setAssociationDirection(One); + element.getParentElement().addChildElement(association); + createEdge(association); + } + + public Gateway findLastGateway() { + FlowNode lastGateway = element; + while (true) { + try { + lastGateway = lastGateway.getPreviousNodes().singleResult(); + if (lastGateway instanceof Gateway) { + return ((Gateway) (lastGateway)); + } + } catch (BpmnModelException e) { + throw new BpmnModelException("Unable to determine an unique previous gateway of " + lastGateway.getId(), e); + } + } + } + + @SuppressWarnings("rawtypes") + public AbstractGatewayBuilder moveToLastGateway() { + return findLastGateway().builder(); + } + + @SuppressWarnings("rawtypes") + public AbstractFlowNodeBuilder moveToNode(String identifier) { + ModelElementInstance instance = modelInstance.getModelElementById(identifier); + if ((instance != null) && (instance instanceof FlowNode)) { + return ((FlowNode) (instance)).builder(); + } else { + throw new BpmnModelException("Flow node not found for id " + identifier); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public T moveToActivity(String identifier) { + ModelElementInstance instance = modelInstance.getModelElementById(identifier); + if ((instance != null) && (instance instanceof Activity)) { + return ((T) (((Activity) (instance)).builder())); + } else { + throw new BpmnModelException("Activity not found for id " + identifier); + } + } +} \ No newline at end of file diff --git a/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/di/DiGeneratorForFlowNodesTest.java b/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/di/DiGeneratorForFlowNodesTest.java new file mode 100644 index 0000000..235f140 --- /dev/null +++ b/szenario16/after/src/main/java/org/camunda/bpm/model/bpmn/builder/di/DiGeneratorForFlowNodesTest.java @@ -0,0 +1,401 @@ +/* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH +under one or more contributor license agreements. See the NOTICE file +distributed with this work for additional information regarding copyright +ownership. Camunda licenses this file to you under the Apache License, +Version 2.0; 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. + */ +package org.camunda.bpm.model.bpmn.builder.di; +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.BOUNDARY_ID; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.CALL_ACTIVITY_ID; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.CATCH_ID; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.CONDITION_ID; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.END_EVENT_ID; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.SEND_TASK_ID; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.SERVICE_TASK_ID; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.START_EVENT_ID; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.SUB_PROCESS_ID; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.TASK_ID; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.TEST_CONDITION; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.TRANSACTION_ID; +import org.camunda.bpm.model.bpmn.BpmnTestConstants.USER_TASK_ID; +import org.junit.After; +import org.junit.Test; +import org.camunda.bpm.model.bpmn.Bpmn; +import org.camunda.bpm.model.bpmn.BpmnModelInstance; +import org.camunda.bpm.model.bpmn.builder.ProcessBuilder; +import org.camunda.bpm.model.bpmn.instance.bpmndi.BpmnDiagram; +import org.camunda.bpm.model.bpmn.instance.bpmndi.BpmnShape; +import static org.assertj.core.api.Assertions.assertThat; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.BOUNDARY_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.CALL_ACTIVITY_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.CATCH_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.CONDITION_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.END_EVENT_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.SEND_TASK_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.SERVICE_TASK_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.START_EVENT_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.SUB_PROCESS_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.TASK_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.TEST_CONDITION; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.TRANSACTION_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.USER_TASK_ID; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +public class DiGeneratorForFlowNodesTest { + private BpmnModelInstance instance; + + @After + public void validateModel() throws IOException { + if (instance != null) { + Bpmn.validateModel(instance); + } + } + + @Test + public void shouldGeneratePlaneForProcess() { + // when + instance = Bpmn.createExecutableProcess("process").done(); + // then + Collection bpmnDiagrams = instance.getModelElementsByType(BpmnDiagram.class); + assertEquals(1, bpmnDiagrams.size()); + BpmnDiagram diagram = bpmnDiagrams.iterator().next(); + assertNotNull(diagram.getId()); + assertNotNull(diagram.getBpmnPlane()); + assertEquals(diagram.getBpmnPlane().getBpmnElement(), instance.getModelElementById("process")); + } + + @Test + public void shouldGenerateShapeForStartEvent() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + assertEventShapeProperties(START_EVENT_ID); + } + + @Test + public void shouldGenerateShapeForUserTask() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).userTask(USER_TASK_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + assertTaskShapeProperties(USER_TASK_ID); + } + + @Test + public void shouldGenerateShapeForSendTask() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).sendTask(SEND_TASK_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + assertTaskShapeProperties(SEND_TASK_ID); + } + + @Test + public void shouldGenerateShapeForServiceTask() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).serviceTask(SERVICE_TASK_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + assertTaskShapeProperties(SERVICE_TASK_ID); + } + + @Test + public void shouldGenerateShapeForReceiveTask() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).receiveTask(TASK_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + assertTaskShapeProperties(TASK_ID); + } + + @Test + public void shouldGenerateShapeForManualTask() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).manualTask(TASK_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + assertTaskShapeProperties(TASK_ID); + } + + @Test + public void shouldGenerateShapeForBusinessRuleTask() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).businessRuleTask(TASK_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + assertTaskShapeProperties(TASK_ID); + } + + @Test + public void shouldGenerateShapeForScriptTask() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).scriptTask(TASK_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + assertTaskShapeProperties(TASK_ID); + } + + @Test + public void shouldGenerateShapeForCatchingIntermediateEvent() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).intermediateCatchEvent(CATCH_ID).endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + assertEventShapeProperties(CATCH_ID); + } + + @Test + public void shouldGenerateShapeForBoundaryIntermediateEvent() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).userTask(USER_TASK_ID).endEvent(END_EVENT_ID).moveToActivity(USER_TASK_ID).boundaryEvent(BOUNDARY_ID).conditionalEventDefinition(CONDITION_ID).condition(TEST_CONDITION).conditionalEventDefinitionDone().endEvent().done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(5, allShapes.size()); + assertEventShapeProperties(BOUNDARY_ID); + } + + @Test + public void shouldGenerateShapeForThrowingIntermediateEvent() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).intermediateThrowEvent("inter").endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + assertEventShapeProperties("inter"); + } + + @Test + public void shouldGenerateShapeForEndEvent() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + assertEventShapeProperties(END_EVENT_ID); + } + + @Test + public void shouldGenerateShapeForBlankSubProcess() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).subProcess(SUB_PROCESS_ID).endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + BpmnShape bpmnShapeSubProcess = findBpmnShape(SUB_PROCESS_ID); + assertNotNull(bpmnShapeSubProcess); + assertSubProcessSize(bpmnShapeSubProcess); + assertTrue(bpmnShapeSubProcess.isExpanded()); + } + + @Test + public void shouldGenerateShapesForNestedFlowNodes() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).subProcess(SUB_PROCESS_ID).embeddedSubProcess().startEvent("innerStartEvent").userTask("innerUserTask").endEvent("innerEndEvent").subProcessDone().endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(6, allShapes.size()); + assertEventShapeProperties("innerStartEvent"); + assertTaskShapeProperties("innerUserTask"); + assertEventShapeProperties("innerEndEvent"); + BpmnShape bpmnShapeSubProcess = findBpmnShape(SUB_PROCESS_ID); + assertNotNull(bpmnShapeSubProcess); + assertTrue(bpmnShapeSubProcess.isExpanded()); + } + + @Test + public void shouldGenerateShapeForEventSubProcess() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).endEvent(END_EVENT_ID).subProcess(SUB_PROCESS_ID).triggerByEvent().embeddedSubProcess().startEvent("innerStartEvent").endEvent("innerEndEvent").subProcessDone().done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(5, allShapes.size()); + assertEventShapeProperties("innerStartEvent"); + assertEventShapeProperties("innerEndEvent"); + BpmnShape bpmnShapeEventSubProcess = findBpmnShape(SUB_PROCESS_ID); + assertNotNull(bpmnShapeEventSubProcess); + assertTrue(bpmnShapeEventSubProcess.isExpanded()); + } + + @Test + public void shouldGenerateShapeForCallActivity() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).callActivity(CALL_ACTIVITY_ID).endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + assertTaskShapeProperties(CALL_ACTIVITY_ID); + } + + @Test + public void shouldGenerateShapeForTransaction() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).transaction(TRANSACTION_ID).embeddedSubProcess().startEvent("innerStartEvent").userTask("innerUserTask").endEvent("innerEndEvent").transactionDone().endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(6, allShapes.size()); + assertEventShapeProperties("innerStartEvent"); + assertTaskShapeProperties("innerUserTask"); + assertEventShapeProperties("innerEndEvent"); + BpmnShape bpmnShapeSubProcess = findBpmnShape(TRANSACTION_ID); + assertNotNull(bpmnShapeSubProcess); + assertTrue(bpmnShapeSubProcess.isExpanded()); + } + + @Test + public void shouldGenerateShapeForParallelGateway() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).parallelGateway("and").endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + assertGatewayShapeProperties("and"); + } + + @Test + public void shouldGenerateShapeForInclusiveGateway() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).inclusiveGateway("inclusive").endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + assertGatewayShapeProperties("inclusive"); + } + + @Test + public void shouldGenerateShapeForEventBasedGateway() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).eventBasedGateway().id("eventBased").endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + assertGatewayShapeProperties("eventBased"); + } + + @Test + public void shouldGenerateShapeForExclusiveGateway() { + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + // when + instance = processBuilder.startEvent(START_EVENT_ID).exclusiveGateway("or").endEvent(END_EVENT_ID).done(); + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + assertGatewayShapeProperties("or"); + BpmnShape bpmnShape = findBpmnShape("or"); + assertTrue(bpmnShape.isMarkerVisible()); + } + + protected void assertTaskShapeProperties(String id) { + BpmnShape bpmnShapeTask = findBpmnShape(id); + assertNotNull(bpmnShapeTask); + assertActivitySize(bpmnShapeTask); + } + + protected void assertEventShapeProperties(String id) { + BpmnShape bpmnShapeEvent = findBpmnShape(id); + assertNotNull(bpmnShapeEvent); + assertEventSize(bpmnShapeEvent); + } + + protected void assertGatewayShapeProperties(String id) { + BpmnShape bpmnShapeGateway = findBpmnShape(id); + assertNotNull(bpmnShapeGateway); + assertGatewaySize(bpmnShapeGateway); + } + + protected BpmnShape findBpmnShape(String id) { + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + Iterator iterator = allShapes.iterator(); + while (iterator.hasNext()) { + BpmnShape shape = iterator.next(); + if (shape.getBpmnElement().getId().equals(id)) { + return shape; + } + } + return null; + } + + protected void assertEventSize(BpmnShape shape) { + assertSize(shape, 36, 36); + } + + protected void assertGatewaySize(BpmnShape shape) { + assertSize(shape, 50, 50); + } + + protected void assertSubProcessSize(BpmnShape shape) { + assertSize(shape, 200, 350); + } + + protected void assertActivitySize(BpmnShape shape) { + assertSize(shape, 80, 100); + } + + protected void assertSize(BpmnShape shape, int height, int width) { + assertThat(shape.getBounds().getHeight()).isEqualTo(height); + assertThat(shape.getBounds().getWidth()).isEqualTo(width); + } +} \ No newline at end of file diff --git a/szenario16/before/src/AbstractFlowNodeBuilder.java b/szenario16/before/src/AbstractFlowNodeBuilder.java new file mode 100644 index 0000000..62078a8 --- /dev/null +++ b/szenario16/before/src/AbstractFlowNodeBuilder.java @@ -0,0 +1,505 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. Camunda licenses this file to you under the Apache License, + * Version 2.0; 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. + */ +package org.camunda.bpm.model.bpmn.builder; + +import org.camunda.bpm.model.bpmn.instance.domain.events.advanced.BoundaryEvent; +import org.camunda.bpm.model.bpmn.instance.domain.events.advanced.CompensateEventDefinition; +import org.camunda.bpm.model.bpmn.instance.domain.events.advanced.IntermediateCatchEvent; +import org.camunda.bpm.model.bpmn.instance.domain.events.advanced.IntermediateThrowEvent; +import org.camunda.bpm.model.bpmn.instance.paradigm.artifacts.AssociationDirection; +import org.camunda.bpm.model.bpmn.BpmnModelException; +import org.camunda.bpm.model.bpmn.BpmnModelInstance; +import org.camunda.bpm.model.bpmn.instance.*; +import org.camunda.bpm.model.bpmn.instance.bpmndi.BpmnShape; +import org.camunda.bpm.model.bpmn.instance.camunda.CamundaExecutionListener; +import org.camunda.bpm.model.bpmn.instance.camunda.CamundaFailedJobRetryTimeCycle; +import org.camunda.bpm.model.bpmn.instance.domain.humaninteraction.ManualTask; +import org.camunda.bpm.model.bpmn.instance.domain.humaninteraction.UserTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.Activity; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.BusinessRuleTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.CallActivity; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.ReceiveTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.ScriptTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.SendTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.activities.ServiceTask; +import org.camunda.bpm.model.bpmn.instance.paradigm.artifacts.Association; +import org.camunda.bpm.model.bpmn.instance.paradigm.events.EndEvent; +import org.camunda.bpm.model.bpmn.instance.paradigm.events.EventDefinition; +import org.camunda.bpm.model.bpmn.instance.paradigm.flows.FlowNode; +import org.camunda.bpm.model.bpmn.instance.paradigm.flows.SequenceFlow; +import org.camunda.bpm.model.bpmn.instance.paradigm.gateways.EventBasedGateway; +import org.camunda.bpm.model.bpmn.instance.paradigm.gateways.ExclusiveGateway; +import org.camunda.bpm.model.bpmn.instance.paradigm.gateways.Gateway; +import org.camunda.bpm.model.bpmn.instance.paradigm.gateways.InclusiveGateway; +import org.camunda.bpm.model.bpmn.instance.paradigm.gateways.ParallelGateway; +import org.camunda.bpm.model.bpmn.instance.paradigm.subprocesses.SubProcess; +import org.camunda.bpm.model.bpmn.instance.paradigm.subprocesses.Transaction; +import org.camunda.bpm.model.xml.instance.ModelElementInstance; + +/** + * @author Sebastian Menski + */ +public abstract class AbstractFlowNodeBuilder, E extends FlowNode> extends AbstractFlowElementBuilder { + + private SequenceFlowBuilder currentSequenceFlowBuilder; + + protected boolean compensationStarted; + protected BoundaryEvent compensateBoundaryEvent; + + protected AbstractFlowNodeBuilder(BpmnModelInstance modelInstance, E element, Class selfType) { + super(modelInstance, element, selfType); + } + + private SequenceFlowBuilder getCurrentSequenceFlowBuilder() { + if (currentSequenceFlowBuilder == null) { + SequenceFlow sequenceFlow = createSibling(SequenceFlow.class); + currentSequenceFlowBuilder = sequenceFlow.builder(); + } + return currentSequenceFlowBuilder; + } + + public B condition(String name, String condition) { + if (name != null) { + getCurrentSequenceFlowBuilder().name(name); + } + ConditionExpression conditionExpression = createInstance(ConditionExpression.class); + conditionExpression.setTextContent(condition); + getCurrentSequenceFlowBuilder().condition(conditionExpression); + return myself; + } + + protected void connectTarget(FlowNode target) { + // check if compensation was started + if (isBoundaryEventWithStartedCompensation()) { + // the target activity should be marked for compensation + if (target instanceof Activity) { + ((Activity) target).setForCompensation(true); + } + + // connect the target via association instead of sequence flow + connectTargetWithAssociation(target); + } + else if (isCompensationHandler()) { + // cannot connect to a compensation handler + throw new BpmnModelException("Only single compensation handler allowed. Call compensationDone() to continue main flow."); + } + else { + // connect as sequence flow by default + connectTargetWithSequenceFlow(target); + } + } + + protected void connectTargetWithSequenceFlow(FlowNode target) { + getCurrentSequenceFlowBuilder().from(element).to(target); + + SequenceFlow sequenceFlow = getCurrentSequenceFlowBuilder().getElement(); + createEdge(sequenceFlow); + currentSequenceFlowBuilder = null; + } + + protected void connectTargetWithAssociation(FlowNode target) { + Association association = modelInstance.newInstance(Association.class); + association.setTarget(target); + association.setSource(element); + association.setAssociationDirection(AssociationDirection.One); + element.getParentElement().addChildElement(association); + + createEdge(association); + } + + public AbstractFlowNodeBuilder compensationDone(){ + if (compensateBoundaryEvent != null) { + return compensateBoundaryEvent.getAttachedTo().builder(); + } + else { + throw new BpmnModelException("No compensation in progress. Call compensationStart() first."); + } + } + + public B sequenceFlowId(String sequenceFlowId) { + getCurrentSequenceFlowBuilder().id(sequenceFlowId); + return myself; + } + + private T createTarget(Class typeClass) { + return createTarget(typeClass, null); + } + + protected T createTarget(Class typeClass, String identifier) { + T target = createSibling(typeClass, identifier); + + BpmnShape targetBpmnShape = createBpmnShape(target); + setCoordinates(targetBpmnShape); + connectTarget(target); + resizeSubProcess(targetBpmnShape); + return target; + } + + protected T createTargetBuilder(Class typeClass) { + return createTargetBuilder(typeClass, null); + } + + @SuppressWarnings("unchecked") + protected T createTargetBuilder(Class typeClass, String id) { + AbstractFlowNodeBuilder builder = createTarget(typeClass, id).builder(); + + if (compensationStarted) { + // pass on current boundary event to return after compensationDone call + builder.compensateBoundaryEvent = compensateBoundaryEvent; + } + + return (T) builder; + + } + + public ServiceTaskBuilder serviceTask() { + return createTargetBuilder(ServiceTask.class); + } + + public ServiceTaskBuilder serviceTask(String id) { + return createTargetBuilder(ServiceTask.class, id); + } + + public SendTaskBuilder sendTask() { + return createTargetBuilder(SendTask.class); + } + + public SendTaskBuilder sendTask(String id) { + return createTargetBuilder(SendTask.class, id); + } + + public UserTaskBuilder userTask() { + return createTargetBuilder(UserTask.class); + } + + public UserTaskBuilder userTask(String id) { + return createTargetBuilder(UserTask.class, id); + } + + public BusinessRuleTaskBuilder businessRuleTask() { + return createTargetBuilder(BusinessRuleTask.class); + } + + public BusinessRuleTaskBuilder businessRuleTask(String id) { + return createTargetBuilder(BusinessRuleTask.class, id); + } + + public ScriptTaskBuilder scriptTask() { + return createTargetBuilder(ScriptTask.class); + } + + public ScriptTaskBuilder scriptTask(String id) { + return createTargetBuilder(ScriptTask.class, id); + } + + public ReceiveTaskBuilder receiveTask() { + return createTargetBuilder(ReceiveTask.class); + } + + public ReceiveTaskBuilder receiveTask(String id) { + return createTargetBuilder(ReceiveTask.class, id); + } + + public ManualTaskBuilder manualTask() { + return createTargetBuilder(ManualTask.class); + } + + public ManualTaskBuilder manualTask(String id) { + return createTargetBuilder(ManualTask.class, id); + } + + public EndEventBuilder endEvent() { + return createTarget(EndEvent.class).builder(); + } + + public EndEventBuilder endEvent(String id) { + return createTarget(EndEvent.class, id).builder(); + } + + public ParallelGatewayBuilder parallelGateway() { + return createTarget(ParallelGateway.class).builder(); + } + + public ParallelGatewayBuilder parallelGateway(String id) { + return createTarget(ParallelGateway.class, id).builder(); + } + + public ExclusiveGatewayBuilder exclusiveGateway() { + return createTarget(ExclusiveGateway.class).builder(); + } + + public InclusiveGatewayBuilder inclusiveGateway() { + return createTarget(InclusiveGateway.class).builder(); + } + + public EventBasedGatewayBuilder eventBasedGateway() { + return createTarget(EventBasedGateway.class).builder(); + } + + public ExclusiveGatewayBuilder exclusiveGateway(String id) { + return createTarget(ExclusiveGateway.class, id).builder(); + } + + public InclusiveGatewayBuilder inclusiveGateway(String id) { + return createTarget(InclusiveGateway.class, id).builder(); + } + + public IntermediateCatchEventBuilder intermediateCatchEvent() { + return createTarget(IntermediateCatchEvent.class).builder(); + } + + public IntermediateCatchEventBuilder intermediateCatchEvent(String id) { + return createTarget(IntermediateCatchEvent.class, id).builder(); + } + + public IntermediateThrowEventBuilder intermediateThrowEvent() { + return createTarget(IntermediateThrowEvent.class).builder(); + } + + public IntermediateThrowEventBuilder intermediateThrowEvent(String id) { + return createTarget(IntermediateThrowEvent.class, id).builder(); + } + + public CallActivityBuilder callActivity() { + return createTarget(CallActivity.class).builder(); + } + + public CallActivityBuilder callActivity(String id) { + return createTarget(CallActivity.class, id).builder(); + } + + public SubProcessBuilder subProcess() { + return createTarget(SubProcess.class).builder(); + } + + public SubProcessBuilder subProcess(String id) { + return createTarget(SubProcess.class, id).builder(); + } + + public TransactionBuilder transaction() { + Transaction transaction = createTarget(Transaction.class); + return new TransactionBuilder(modelInstance, transaction); + } + + public TransactionBuilder transaction(String id) { + Transaction transaction = createTarget(Transaction.class, id); + return new TransactionBuilder(modelInstance, transaction); + } + + public Gateway findLastGateway() { + FlowNode lastGateway = element; + while (true) { + try { + lastGateway = lastGateway.getPreviousNodes().singleResult(); + if (lastGateway instanceof Gateway) { + return (Gateway) lastGateway; + } + } catch (BpmnModelException e) { + throw new BpmnModelException("Unable to determine an unique previous gateway of " + lastGateway.getId(), e); + } + } + } + + @SuppressWarnings("rawtypes") + public AbstractGatewayBuilder moveToLastGateway() { + return findLastGateway().builder(); + } + + @SuppressWarnings("rawtypes") + public AbstractFlowNodeBuilder moveToNode(String identifier) { + ModelElementInstance instance = modelInstance.getModelElementById(identifier); + if (instance != null && instance instanceof FlowNode) { + return ((FlowNode) instance).builder(); + } else { + throw new BpmnModelException("Flow node not found for id " + identifier); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public T moveToActivity(String identifier) { + ModelElementInstance instance = modelInstance.getModelElementById(identifier); + if (instance != null && instance instanceof Activity) { + return (T) ((Activity) instance).builder(); + } else { + throw new BpmnModelException("Activity not found for id " + identifier); + } + } + + @SuppressWarnings("rawtypes") + public AbstractFlowNodeBuilder connectTo(String identifier) { + ModelElementInstance target = modelInstance.getModelElementById(identifier); + if (target == null) { + throw new BpmnModelException("Unable to connect " + element.getId() + " to element " + identifier + " cause it not exists."); + } else if (!(target instanceof FlowNode)) { + throw new BpmnModelException("Unable to connect " + element.getId() + " to element " + identifier + " cause its not a flow node."); + } else { + FlowNode targetNode = (FlowNode) target; + connectTarget(targetNode); + return targetNode.builder(); + } + } + + /** + * Sets the Camunda AsyncBefore attribute for the build flow node. + * + * @param asyncBefore + * boolean value to set + * @return the builder object + */ + public B camundaAsyncBefore(boolean asyncBefore) { + element.setCamundaAsyncBefore(asyncBefore); + return myself; + } + + /** + * Sets the Camunda asyncBefore attribute to true. + * + * @return the builder object + */ + public B camundaAsyncBefore() { + element.setCamundaAsyncBefore(true); + return myself; + } + + /** + * Sets the Camunda asyncAfter attribute for the build flow node. + * + * @param asyncAfter + * boolean value to set + * @return the builder object + */ + public B camundaAsyncAfter(boolean asyncAfter) { + element.setCamundaAsyncAfter(asyncAfter); + return myself; + } + + /** + * Sets the Camunda asyncAfter attribute to true. + * + * @return the builder object + */ + public B camundaAsyncAfter() { + element.setCamundaAsyncAfter(true); + return myself; + } + + /** + * Sets the Camunda exclusive attribute to true. + * + * @return the builder object + */ + public B notCamundaExclusive() { + element.setCamundaExclusive(false); + return myself; + } + + /** + * Sets the camunda exclusive attribute for the build flow node. + * + * @param exclusive + * boolean value to set + * @return the builder object + */ + public B camundaExclusive(boolean exclusive) { + element.setCamundaExclusive(exclusive); + return myself; + } + + public B camundaJobPriority(String jobPriority) { + element.setCamundaJobPriority(jobPriority); + return myself; + } + + /** + * Sets the camunda failedJobRetryTimeCycle attribute for the build flow node. + * + * @param retryTimeCycle + * the retry time cycle value to set + * @return the builder object + */ + public B camundaFailedJobRetryTimeCycle(String retryTimeCycle) { + CamundaFailedJobRetryTimeCycle failedJobRetryTimeCycle = createInstance(CamundaFailedJobRetryTimeCycle.class); + failedJobRetryTimeCycle.setTextContent(retryTimeCycle); + + addExtensionElement(failedJobRetryTimeCycle); + + return myself; + } + + @SuppressWarnings("rawtypes") + public B camundaExecutionListenerClass(String eventName, Class listenerClass) { + return camundaExecutionListenerClass(eventName, listenerClass.getName()); + } + + public B camundaExecutionListenerClass(String eventName, String fullQualifiedClassName) { + CamundaExecutionListener executionListener = createInstance(CamundaExecutionListener.class); + executionListener.setCamundaEvent(eventName); + executionListener.setCamundaClass(fullQualifiedClassName); + + addExtensionElement(executionListener); + + return myself; + } + + public B camundaExecutionListenerExpression(String eventName, String expression) { + CamundaExecutionListener executionListener = createInstance(CamundaExecutionListener.class); + executionListener.setCamundaEvent(eventName); + executionListener.setCamundaExpression(expression); + + addExtensionElement(executionListener); + + return myself; + } + + public B camundaExecutionListenerDelegateExpression(String eventName, String delegateExpression) { + CamundaExecutionListener executionListener = createInstance(CamundaExecutionListener.class); + executionListener.setCamundaEvent(eventName); + executionListener.setCamundaDelegateExpression(delegateExpression); + + addExtensionElement(executionListener); + + return myself; + } + + public B compensationStart() { + if (element instanceof BoundaryEvent) { + BoundaryEvent boundaryEvent = (BoundaryEvent) element; + for (EventDefinition eventDefinition : boundaryEvent.getEventDefinitions()) { + if(eventDefinition instanceof CompensateEventDefinition) { + // if the boundary event contains a compensate event definition then + // save the boundary event to later return to it and start a compensation + + compensateBoundaryEvent = boundaryEvent; + compensationStarted = true; + + return myself; + } + } + } + + throw new BpmnModelException("Compensation can only be started on a boundary event with a compensation event definition"); + } + + protected boolean isBoundaryEventWithStartedCompensation() { + return compensationStarted && compensateBoundaryEvent != null; + } + + protected boolean isCompensationHandler() { + return !compensationStarted && compensateBoundaryEvent != null; + } + +} diff --git a/szenario16/before/src/DiGeneratorForFlowNodesTest.java b/szenario16/before/src/DiGeneratorForFlowNodesTest.java new file mode 100644 index 0000000..95eacbc --- /dev/null +++ b/szenario16/before/src/DiGeneratorForFlowNodesTest.java @@ -0,0 +1,583 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. Camunda licenses this file to you under the Apache License, + * Version 2.0; 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. + */ +package org.camunda.bpm.model.bpmn.builder.di; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.BOUNDARY_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.CALL_ACTIVITY_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.CATCH_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.CONDITION_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.END_EVENT_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.SEND_TASK_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.SERVICE_TASK_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.START_EVENT_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.SUB_PROCESS_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.TASK_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.TEST_CONDITION; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.TRANSACTION_ID; +import static org.camunda.bpm.model.bpmn.BpmnTestConstants.USER_TASK_ID; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; + +import org.camunda.bpm.model.bpmn.Bpmn; +import org.camunda.bpm.model.bpmn.BpmnModelInstance; +import org.camunda.bpm.model.bpmn.builder.ProcessBuilder; +import org.camunda.bpm.model.bpmn.instance.bpmndi.BpmnDiagram; +import org.camunda.bpm.model.bpmn.instance.bpmndi.BpmnShape; +import org.junit.After; +import org.junit.Test; + +public class DiGeneratorForFlowNodesTest { + + private BpmnModelInstance instance; + + @After + public void validateModel() throws IOException { + if (instance != null) { + Bpmn.validateModel(instance); + } + } + + @Test + public void shouldGeneratePlaneForProcess() { + + // when + instance = Bpmn.createExecutableProcess("process").done(); + + // then + Collection bpmnDiagrams = instance.getModelElementsByType(BpmnDiagram.class); + assertEquals(1, bpmnDiagrams.size()); + + BpmnDiagram diagram = bpmnDiagrams.iterator().next(); + assertNotNull(diagram.getId()); + + assertNotNull(diagram.getBpmnPlane()); + assertEquals(diagram.getBpmnPlane().getBpmnElement(), instance.getModelElementById("process")); + } + + @Test + public void shouldGenerateShapeForStartEvent() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .endEvent(END_EVENT_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + + assertEventShapeProperties(START_EVENT_ID); + } + + @Test + public void shouldGenerateShapeForUserTask() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .userTask(USER_TASK_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + + assertTaskShapeProperties(USER_TASK_ID); + } + + @Test + public void shouldGenerateShapeForSendTask() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .sendTask(SEND_TASK_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + + assertTaskShapeProperties(SEND_TASK_ID); + } + + @Test + public void shouldGenerateShapeForServiceTask() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .serviceTask(SERVICE_TASK_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + + assertTaskShapeProperties(SERVICE_TASK_ID); + } + + @Test + public void shouldGenerateShapeForReceiveTask() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .receiveTask(TASK_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + + assertTaskShapeProperties(TASK_ID); + } + + @Test + public void shouldGenerateShapeForManualTask() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .manualTask(TASK_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + + assertTaskShapeProperties(TASK_ID); + } + + @Test + public void shouldGenerateShapeForBusinessRuleTask() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .businessRuleTask(TASK_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + + assertTaskShapeProperties(TASK_ID); + } + + @Test + public void shouldGenerateShapeForScriptTask() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .scriptTask(TASK_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + + assertTaskShapeProperties(TASK_ID); + } + + @Test + public void shouldGenerateShapeForCatchingIntermediateEvent() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .intermediateCatchEvent(CATCH_ID) + .endEvent(END_EVENT_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + + assertEventShapeProperties(CATCH_ID); + } + + @Test + public void shouldGenerateShapeForBoundaryIntermediateEvent() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .userTask(USER_TASK_ID) + .endEvent(END_EVENT_ID) + .moveToActivity(USER_TASK_ID) + .boundaryEvent(BOUNDARY_ID) + .conditionalEventDefinition(CONDITION_ID) + .condition(TEST_CONDITION) + .conditionalEventDefinitionDone() + .endEvent() + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(5, allShapes.size()); + + assertEventShapeProperties(BOUNDARY_ID); + } + + @Test + public void shouldGenerateShapeForThrowingIntermediateEvent() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .intermediateThrowEvent("inter") + .endEvent(END_EVENT_ID).done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + + assertEventShapeProperties("inter"); + } + + @Test + public void shouldGenerateShapeForEndEvent() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .endEvent(END_EVENT_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(2, allShapes.size()); + + assertEventShapeProperties(END_EVENT_ID); + } + + @Test + public void shouldGenerateShapeForBlankSubProcess() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .subProcess(SUB_PROCESS_ID) + .endEvent(END_EVENT_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + + BpmnShape bpmnShapeSubProcess = findBpmnShape(SUB_PROCESS_ID); + assertNotNull(bpmnShapeSubProcess); + assertSubProcessSize(bpmnShapeSubProcess); + assertTrue(bpmnShapeSubProcess.isExpanded()); + } + + @Test + public void shouldGenerateShapesForNestedFlowNodes() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .subProcess(SUB_PROCESS_ID) + .embeddedSubProcess() + .startEvent("innerStartEvent") + .userTask("innerUserTask") + .endEvent("innerEndEvent") + .subProcessDone() + .endEvent(END_EVENT_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(6, allShapes.size()); + + assertEventShapeProperties("innerStartEvent"); + assertTaskShapeProperties("innerUserTask"); + assertEventShapeProperties("innerEndEvent"); + + BpmnShape bpmnShapeSubProcess = findBpmnShape(SUB_PROCESS_ID); + assertNotNull(bpmnShapeSubProcess); + assertTrue(bpmnShapeSubProcess.isExpanded()); + } + + @Test + public void shouldGenerateShapeForEventSubProcess() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .endEvent(END_EVENT_ID) + .subProcess(SUB_PROCESS_ID) + .triggerByEvent() + .embeddedSubProcess() + .startEvent("innerStartEvent") + .endEvent("innerEndEvent") + .subProcessDone() + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(5, allShapes.size()); + + assertEventShapeProperties("innerStartEvent"); + assertEventShapeProperties("innerEndEvent"); + + BpmnShape bpmnShapeEventSubProcess = findBpmnShape(SUB_PROCESS_ID); + assertNotNull(bpmnShapeEventSubProcess); + assertTrue(bpmnShapeEventSubProcess.isExpanded()); + } + + @Test + public void shouldGenerateShapeForCallActivity() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .callActivity(CALL_ACTIVITY_ID) + .endEvent(END_EVENT_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + + assertTaskShapeProperties(CALL_ACTIVITY_ID); + } + + @Test + public void shouldGenerateShapeForTransaction() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .transaction(TRANSACTION_ID) + .embeddedSubProcess() + .startEvent("innerStartEvent") + .userTask("innerUserTask") + .endEvent("innerEndEvent") + .transactionDone() + .endEvent(END_EVENT_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(6, allShapes.size()); + + assertEventShapeProperties("innerStartEvent"); + assertTaskShapeProperties("innerUserTask"); + assertEventShapeProperties("innerEndEvent"); + + BpmnShape bpmnShapeSubProcess = findBpmnShape(TRANSACTION_ID); + assertNotNull(bpmnShapeSubProcess); + assertTrue(bpmnShapeSubProcess.isExpanded()); + } + + @Test + public void shouldGenerateShapeForParallelGateway() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .parallelGateway("and") + .endEvent(END_EVENT_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + + assertGatewayShapeProperties("and"); + } + + @Test + public void shouldGenerateShapeForInclusiveGateway() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .inclusiveGateway("inclusive") + .endEvent(END_EVENT_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + + assertGatewayShapeProperties("inclusive"); + } + + @Test + public void shouldGenerateShapeForEventBasedGateway() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .eventBasedGateway() + .id("eventBased") + .endEvent(END_EVENT_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + + assertGatewayShapeProperties("eventBased"); + } + + @Test + public void shouldGenerateShapeForExclusiveGateway() { + + // given + ProcessBuilder processBuilder = Bpmn.createExecutableProcess(); + + // when + instance = processBuilder + .startEvent(START_EVENT_ID) + .exclusiveGateway("or") + .endEvent(END_EVENT_ID) + .done(); + + // then + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + assertEquals(3, allShapes.size()); + + assertGatewayShapeProperties("or"); + BpmnShape bpmnShape = findBpmnShape("or"); + assertTrue(bpmnShape.isMarkerVisible()); + } + + protected void assertTaskShapeProperties(String id) { + BpmnShape bpmnShapeTask = findBpmnShape(id); + assertNotNull(bpmnShapeTask); + assertActivitySize(bpmnShapeTask); + } + + protected void assertEventShapeProperties(String id) { + BpmnShape bpmnShapeEvent = findBpmnShape(id); + assertNotNull(bpmnShapeEvent); + assertEventSize(bpmnShapeEvent); + } + + protected void assertGatewayShapeProperties(String id) { + BpmnShape bpmnShapeGateway = findBpmnShape(id); + assertNotNull(bpmnShapeGateway); + assertGatewaySize(bpmnShapeGateway); + } + + protected BpmnShape findBpmnShape(String id) { + Collection allShapes = instance.getModelElementsByType(BpmnShape.class); + + Iterator iterator = allShapes.iterator(); + while (iterator.hasNext()) { + BpmnShape shape = iterator.next(); + if (shape.getBpmnElement().getId().equals(id)) { + return shape; + } + } + return null; + } + + protected void assertEventSize(BpmnShape shape) { + assertSize(shape, 36, 36); + } + + protected void assertGatewaySize(BpmnShape shape) { + assertSize(shape, 50, 50); + } + + protected void assertSubProcessSize(BpmnShape shape) { + assertSize(shape, 200, 350); + } + + protected void assertActivitySize(BpmnShape shape) { + assertSize(shape, 80, 100); + } + + protected void assertSize(BpmnShape shape, int height, int width) { + assertThat(shape.getBounds().getHeight()).isEqualTo(height); + assertThat(shape.getBounds().getWidth()).isEqualTo(width); + } + +}