/* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.activiti.workflow.simple.converter.listener; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.activiti.bpmn.model.EndEvent; import org.activiti.bpmn.model.FlowElement; import org.activiti.bpmn.model.FlowNode; import org.activiti.bpmn.model.Process; import org.activiti.bpmn.model.SequenceFlow; import org.activiti.bpmn.model.StartEvent; import org.activiti.workflow.simple.converter.ConversionConstants; import org.activiti.workflow.simple.converter.WorkflowDefinitionConversion; import org.activiti.workflow.simple.converter.WorkflowDefinitionConversionFactory; import org.activiti.workflow.simple.definition.WorkflowDefinition; /** * Default listener for {@link WorkflowDefinitionConversion} lifecycle events. * * When added to a {@link WorkflowDefinitionConversionFactory}, this class will * make sure a start event and end event is created for the {@link Process}. * Further, it will generate a correct incoming and outgoing sequence flow list * for each {@link Process} element, as required by some toolings (eg Modeler). * * @author Joram Barrez */ public class DefaultWorkflowDefinitionConversionListener implements WorkflowDefinitionConversionListener { private static final long serialVersionUID = -145978383684020118L; private static final String START_EVENT_ID = "start"; private static final String END_EVENT_ID = "end"; public void beforeStepsConversion(WorkflowDefinitionConversion conversion) { initializeProcess(conversion); } protected void initializeProcess(WorkflowDefinitionConversion conversion) { WorkflowDefinition workflowDefinition = conversion.getWorkflowDefinition(); // Create new process Process process = conversion.getProcess(); process.setId(generateProcessId(workflowDefinition)); process.setName(workflowDefinition.getName()); process.setDocumentation(workflowDefinition.getDescription()); if (workflowDefinition.getCategory() != null) { conversion.getBpmnModel().setTargetNamespace(workflowDefinition.getCategory()); } conversion.setProcess(process); // Add start-event StartEvent startEvent = new StartEvent(); startEvent.setId(START_EVENT_ID); if(workflowDefinition.getStartFormDefinition() != null && workflowDefinition.getStartFormDefinition().getFormKey() != null) { startEvent.setFormKey(workflowDefinition.getStartFormDefinition().getFormKey()); } process.addFlowElement(startEvent); conversion.setLastActivityId(startEvent.getId()); } /** * @param workflowDefinition * @return process definition id that is randomized, to avoid name clashes * (eg. amongst differnt tenants) */ protected String generateProcessId(WorkflowDefinition workflowDefinition) { String processId = null; if(workflowDefinition.getId() != null) { processId = workflowDefinition.getId(); } else { // Revert to using the name of the process if(workflowDefinition.getName() != null) { processId = workflowDefinition.getName().replace(" ", "_"); } } return processId; } public void afterStepsConversion(WorkflowDefinitionConversion conversion) { // Add end-event to process Process process = conversion.getProcess(); EndEvent endEvent = new EndEvent(); endEvent.setId(END_EVENT_ID); process.addFlowElement(endEvent); // Sequence flow from last created activity to end SequenceFlow sequenceFlow = new SequenceFlow(); sequenceFlow.setId(conversion.getUniqueNumberedId(ConversionConstants.DEFAULT_SEQUENCEFLOW_PREFIX)); sequenceFlow.setSourceRef(conversion.getLastActivityId()); sequenceFlow.setTargetRef(END_EVENT_ID); process.addFlowElement(sequenceFlow); // To make the generated workflow compatible with some tools (eg the // Modeler, but also others), // We must add the ingoing and outgoing sequence flow to each of the flow // nodes SequenceFlowMapping sequenceFlowMapping = generateSequenceflowMappings(process); for (FlowNode flowNode : process.findFlowElementsOfType(FlowNode.class)) { List<SequenceFlow> incomingSequenceFlow = sequenceFlowMapping.getIncomingSequenceFlowMapping().get(flowNode.getId()); if (incomingSequenceFlow != null) { flowNode.setIncomingFlows(incomingSequenceFlow); } List<SequenceFlow> outgoingSequenceFlow = sequenceFlowMapping.getOutgoingSequenceFlowMapping().get(flowNode.getId()); if (outgoingSequenceFlow != null) { flowNode.setOutgoingFlows(outgoingSequenceFlow); } } } protected SequenceFlowMapping generateSequenceflowMappings(Process process) { HashMap<String, List<SequenceFlow>> incomingSequenceFlowMapping = new HashMap<String, List<SequenceFlow>>(); HashMap<String, List<SequenceFlow>> outgoingSequenceFlowMapping = new HashMap<String, List<SequenceFlow>>(); for (FlowElement flowElement : process.findFlowElementsOfType(SequenceFlow.class)) { SequenceFlow sequenceFlow = (SequenceFlow) flowElement; String srcId = sequenceFlow.getSourceRef(); String targetId = sequenceFlow.getTargetRef(); if (outgoingSequenceFlowMapping.get(srcId) == null) { outgoingSequenceFlowMapping.put(srcId, new ArrayList<SequenceFlow>()); } outgoingSequenceFlowMapping.get(srcId).add(sequenceFlow); if (incomingSequenceFlowMapping.get(targetId) == null) { incomingSequenceFlowMapping.put(targetId, new ArrayList<SequenceFlow>()); } incomingSequenceFlowMapping.get(targetId).add(sequenceFlow); } SequenceFlowMapping mapping = new SequenceFlowMapping(); mapping.setIncomingSequenceFlowMapping(incomingSequenceFlowMapping); mapping.setOutgoingSequenceFlowMapping(outgoingSequenceFlowMapping); return mapping; } static class SequenceFlowMapping { protected HashMap<String, List<SequenceFlow>> incomingSequenceFlowMapping; protected HashMap<String, List<SequenceFlow>> outgoingSequenceFlowMapping; public HashMap<String, List<SequenceFlow>> getIncomingSequenceFlowMapping() { return incomingSequenceFlowMapping; } public void setIncomingSequenceFlowMapping(HashMap<String, List<SequenceFlow>> incomingSequenceFlowMapping) { if (incomingSequenceFlowMapping != null) { this.incomingSequenceFlowMapping = incomingSequenceFlowMapping; } else { this.incomingSequenceFlowMapping = new HashMap<String, List<SequenceFlow>>(); } } public HashMap<String, List<SequenceFlow>> getOutgoingSequenceFlowMapping() { return outgoingSequenceFlowMapping; } public void setOutgoingSequenceFlowMapping(HashMap<String, List<SequenceFlow>> outgoingSequenceFlowMapping) { if (outgoingSequenceFlowMapping != null) { this.outgoingSequenceFlowMapping = outgoingSequenceFlowMapping; } else { this.outgoingSequenceFlowMapping = new HashMap<String, List<SequenceFlow>>(); } } } }