提交 afade337 编写于 作者: J Joram Barrez

ACT-1518: first cut of generic bits for kickstart

上级 6d5adfa8
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>Activiti - Simple Workflow</name>
<artifactId>activiti-simple-workflow</artifactId>
<parent>
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.12-SNAPSHOT</version>
</parent>
<dependencies>
<!-- BPMN model conversion -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
</dependency>
<!-- Json parsing -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>distro</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
<!-- This repo declaration is here for as this will be part of the distro -->
<repository>
<id>activiti</id>
<name>Activiti</name>
<url>https://maven.alfresco.com/nexus/content/repositories/activiti/</url>
</repository>
<repository>
<id>Alfresco thirdparty</id>
<url>https://maven.alfresco.com/nexus/content/repositories/thirdparty/</url>
</repository>
</repositories>
</project>
/* 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;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.workflow.simple.definition.StepDefinition;
/**
* Base class that can be used for {@link StepDefinitionConverter}, contains
* utility-methods.
*
* @author Frederik Heremans
* @author Joram Barrez
*/
public abstract class BaseStepDefinitionConverter<U extends StepDefinition, T> implements StepDefinitionConverter {
@SuppressWarnings("unchecked")
public void convertStepDefinition(StepDefinition stepDefinition, WorkflowDefinitionConversion conversion) {
U typedStepDefinition = (U) stepDefinition;
T processArtifact = createProcessArtifact(typedStepDefinition, conversion);
createAdditionalArtifacts(processArtifact);
}
/**
* Subclasses must implement this method and create the BPMN 2.0 process artifact(s) for the provided step.
*/
protected abstract T createProcessArtifact(U stepDefinition, WorkflowDefinitionConversion conversion);
/**
* Subclasses should override this method if they want to create additional artifacts
* for this specific step. The default generated process artifact is passed as parameter.
*/
protected void createAdditionalArtifacts(T defaultGeneratedArtifact) {
}
protected void addFlowElement(WorkflowDefinitionConversion conversion, FlowElement flowElement) {
addFlowElement(conversion, flowElement, true);
}
protected void addFlowElement(WorkflowDefinitionConversion conversion, FlowElement flowElement, boolean addSequenceFlow) {
if (addSequenceFlow) {
addSequenceFlow(conversion, conversion.getLastActivityId(), flowElement.getId());
}
conversion.getProcess().addFlowElement(flowElement);
conversion.setLastActivityId(flowElement.getId());
}
/**
* Add a sequence-flow to the current process from source to target.
* Sequence-flow name is set to a user-friendly name, containing an
* incrementing number.
*
* @param sourceActivityId
* @param targetActivityId
*/
public void addSequenceFlow(WorkflowDefinitionConversion conversion, String sourceActivityId, String targetActivityId) {
SequenceFlow sequenceFlow = new SequenceFlow();
sequenceFlow.setId(conversion.getUniqueNumberedId(getSequenceFlowPrefix()));
sequenceFlow.setSourceRef(sourceActivityId);
sequenceFlow.setTargetRef(targetActivityId);
conversion.getProcess().addFlowElement(sequenceFlow);
}
// Subclasses can overwrite this if they want a different sequence flow prefix
protected String getSequenceFlowPrefix() {
return ConversionConstants.DEFAULT_SEQUENCEFLOW_PREFIX;
}
}
/* 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;
public interface ConversionConstants {
String DEFAULT_SEQUENCEFLOW_PREFIX = "sequenceFlow";
}
/* 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;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.StartEvent;
import org.activiti.workflow.simple.definition.WorkflowDefinition;
/**
* @author Joram Barrez
*/
public class DefaultWorkflowDefinitionConversionListener implements WorkflowDefinitionConversionListener
{
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());
conversion.setProcess(process);
// Add start-event
StartEvent startEvent = new StartEvent();
startEvent.setId(START_EVENT_ID);
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)
{
return workflowDefinition.getName().replace(" ", "_");
}
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);
}
}
/* 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;
import org.activiti.bpmn.model.UserTask;
import org.activiti.workflow.simple.definition.HumanStepDefinition;
import org.activiti.workflow.simple.definition.StepDefinition;
/**
* @author Frederik Heremans
* @author Joram Barrez
*/
public class HumanStepDefinitionConverter extends BaseStepDefinitionConverter<HumanStepDefinition, UserTask> {
private static final String USER_TASK_PREFIX = "userTask";
private static final String INITIATOR_ASSIGNEE_EXPRESSION = "${initiator.properties.userName}";
public Class< ? extends StepDefinition> getHandledClass() {
return HumanStepDefinition.class;
}
protected UserTask createProcessArtifact(HumanStepDefinition stepDefinition, WorkflowDefinitionConversion conversion) {
UserTask userTask = createUserTask(stepDefinition, conversion);
addFlowElement(conversion, userTask);
return userTask;
}
protected UserTask createUserTask(HumanStepDefinition humanStepDefinition, WorkflowDefinitionConversion conversion) {
// TODO: validate and throw exception on missing properties
UserTask userTask = new UserTask();
userTask.setId(conversion.getUniqueNumberedId(USER_TASK_PREFIX));
userTask.setName(humanStepDefinition.getName());
userTask.setDocumentation(humanStepDefinition.getDescription());
if (humanStepDefinition.isAssigneeInitiator()) {
userTask.setAssignee(INITIATOR_ASSIGNEE_EXPRESSION);
} else if (humanStepDefinition.getAssignee() != null) {
userTask.setAssignee(humanStepDefinition.getAssignee());
}
if (humanStepDefinition.getCandidateUsers() != null && humanStepDefinition.getCandidateUsers().size() > 0) {
userTask.setCandidateUsers(humanStepDefinition.getCandidateUsers());
}
if (humanStepDefinition.getCandidateGroups() != null && humanStepDefinition.getCandidateGroups().size() > 0) {
userTask.setCandidateGroups(humanStepDefinition.getCandidateGroups());
}
return userTask;
}
}
/* 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;
import org.activiti.workflow.simple.definition.StepDefinition;
/**
* <p>
* A class that is responsible for converting a single {@link StepDefinition} to
* all required artifacts needed. This includes process-elements, content-types
* and forms.
* </p>
*
* <p>
* Please note that {@link StepDefinitionConverter} instances are reused and
* should be state-less.
* </p>
*
* @author Frederik Heremans
*/
public interface StepDefinitionConverter {
/**
* @return class that this converter is capable of handling.
*/
Class< ? extends StepDefinition> getHandledClass();
/**
* Convert given {@link StepDefinition} to correct artifacts and adds them to
* process, models and forms.
*
* @param stepDefinition
* the {@link StepDefinition}
* @param conversion
* context to add artifacts to
*/
void convertStepDefinition(StepDefinition stepDefinition, WorkflowDefinitionConversion conversion);
}
/* 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;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.ActivitiException;
import org.activiti.workflow.simple.definition.StepDefinition;
import org.activiti.workflow.simple.definition.WorkflowDefinition;
import org.activiti.workflow.simple.diagram.WorkflowDIGenerator;
/**
* Context that holds all artifacts and meta-data required when converting
* {@link WorkflowDefinition}s into required artifacts for deployment.
*
* @see StepDefinitionConverter
*
* @author Frederik Heremans
* @author Joram Barrez
*/
public class WorkflowDefinitionConversion {
// Input
protected WorkflowDefinition workflowDefinition;
// Artifacts of the conversion
protected BpmnModel bpmnModel;
protected Process process;
protected Map<String, Object> additionalArtifacts;
// Helper members
protected WorkflowDefinitionConversionFactory conversionFactory;
protected String lastActivityId;
protected HashMap<String, Integer> incrementalIdMapping;
/* package */WorkflowDefinitionConversion(WorkflowDefinitionConversionFactory factory) {
this.conversionFactory = factory;
}
/* package */
public WorkflowDefinitionConversion(WorkflowDefinitionConversionFactory factory, WorkflowDefinition workflowDefinition) {
this(factory);
this.workflowDefinition = workflowDefinition;
}
public void convert() {
if (workflowDefinition == null) {
throw new ActivitiException("Cannot start conversion: need to set a WorkflowDefinition first!");
}
this.incrementalIdMapping = new HashMap<String, Integer>();
this.additionalArtifacts = new HashMap<String, Object>();
// Create new process
bpmnModel = new BpmnModel();
process = new Process();
bpmnModel.addProcess(process);
// Let conversion listeners know initialization is finished
for (WorkflowDefinitionConversionListener conversionListener : conversionFactory.getWorkflowDefinitionConversionListeners()) {
conversionListener.beforeStepsConversion(this);
}
// Convert each step
for (StepDefinition step : workflowDefinition.getSteps()) {
conversionFactory.getStepConverterFor(step).convertStepDefinition(step, this);
}
// Let conversion listeners know step conversion is done
for (WorkflowDefinitionConversionListener conversionListener : conversionFactory.getWorkflowDefinitionConversionListeners()) {
conversionListener.afterStepsConversion(this);
}
// Add DI information to bpmn model
WorkflowDIGenerator workflowDIGenerator = new WorkflowDIGenerator(workflowDefinition, bpmnModel);
workflowDIGenerator.generateDI();
}
/**
* @param baseName
* base name of the unique identifier
* @return a string that can be used as a unique id. Eg. if a baseName with
* value "userTask" is passed, the first time "userTask1" will be
* returned. When called agian with the same baseName, "userTask2" is
* returned. Counts are incremented for each baseName independently
* withing this context instance.
*/
public String getUniqueNumberedId(String baseName) {
Integer index = incrementalIdMapping.get(baseName);
if (index == null) {
index = 1;
incrementalIdMapping.put(baseName, index);
} else {
index = index + 1;
incrementalIdMapping.put(baseName, index);
}
return baseName + index;
}
/**
* @return id of the activity that is at the end of the current process. Used
* to add additional steps and sequence-flows to the process.
*/
public String getLastActivityId() {
return this.lastActivityId;
}
/**
* @param lastActivityId
* id of the activity that is at the end of the current process. Used
* to add additional steps and sequence-flows to the process.
*/
public void setLastActivityId(String lastActivityId) {
this.lastActivityId = lastActivityId;
}
public BpmnModel getBpmnModel() {
return bpmnModel;
}
public void setBpmnModel(BpmnModel bpmnModel) {
this.bpmnModel = bpmnModel;
}
public Process getProcess() {
return process;
}
public void setProcess(Process process) {
this.process = process;
}
public Object getArtifact(String artifactKey) {
return additionalArtifacts.get(artifactKey);
}
public void setArtifact(String artifactKey, Object artifact) {
additionalArtifacts.put(artifactKey, artifact);
}
public WorkflowDefinition getWorkflowDefinition() {
return workflowDefinition;
}
public void setWorkflowDefinition(WorkflowDefinition workflowDefinition) {
this.workflowDefinition = workflowDefinition;
}
public String getbpm20Xml() {
BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
return new String(bpmnXMLConverter.convertToXML(bpmnModel));
}
public InputStream getWorkflowDiagramImage() {
WorkflowDIGenerator workflowDIGenerator = new WorkflowDIGenerator(workflowDefinition, bpmnModel);
return workflowDIGenerator.generateDiagram();
}
}
/* 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;
import java.util.HashMap;
import java.util.List;
import org.activiti.workflow.simple.definition.StepDefinition;
import org.activiti.workflow.simple.definition.WorkflowDefinition;
/**
* Factory that is capable of creating {@link WorkflowDefinitionConversion}
* objects.
*
* @author Frederik Heremans
* @author Joram Barrez
*/
public class WorkflowDefinitionConversionFactory {
protected HashMap<Class< ? >, StepDefinitionConverter> stepConverters;
protected List<WorkflowDefinitionConversionListener> workflowDefinitionConversionListeners;
/**
* @return a new, empty conversion to be used to store all converted
* artifacts.
*/
public WorkflowDefinitionConversion createWorkflowDefinitionConversion() {
return new WorkflowDefinitionConversion(this);
}
public WorkflowDefinitionConversion createWorkflowDefinitionConversion(WorkflowDefinition workflowDefinition) {
return new WorkflowDefinitionConversion(this, workflowDefinition);
}
/**
* @param stepConverters
* converter to register with this factory
*/
public void setStepDefinitionConverters(List<StepDefinitionConverter> stepConverters) {
this.stepConverters = new HashMap<Class< ? >, StepDefinitionConverter>();
for (StepDefinitionConverter converter : stepConverters) {
this.stepConverters.put(converter.getHandledClass(), converter);
}
}
public List<WorkflowDefinitionConversionListener> getWorkflowDefinitionConversionListeners() {
return workflowDefinitionConversionListeners;
}
public void setWorkflowDefinitionConversionListeners(List<WorkflowDefinitionConversionListener> workflowDefinitionConversionListeners) {
this.workflowDefinitionConversionListeners = workflowDefinitionConversionListeners;
}
/**
* @param definition
* step definition to get converter for.
* @return Converter that can be used on the given definition.
* @throws IllegalArgumentException
* when there is no converter known for the given definition.
*/
public StepDefinitionConverter getStepConverterFor(StepDefinition definition) {
final StepDefinitionConverter converter = stepConverters.get(definition.getClass());
if (converter == null) {
// TODO: i18n and error-handling
throw new IllegalArgumentException("No converter found for step: " + definition.getClass());
}
return converter;
}
}
/* 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;
/**
* @author Joram Barrez
*/
public interface WorkflowDefinitionConversionListener {
void beforeStepsConversion(WorkflowDefinitionConversion conversion);
void afterStepsConversion(WorkflowDefinitionConversion conversion);
}
/* 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.definition;
import java.util.ArrayList;
import java.util.List;
/**
* @author Joram Barrez
*/
public class FormDefinition {
protected String formKey;
protected List<FormPropertyDefinition> formProperties = new ArrayList<FormPropertyDefinition>();
public List<FormPropertyDefinition> getFormProperties() {
return formProperties;
}
public void setFormProperties(List<FormPropertyDefinition> formProperties) {
this.formProperties = formProperties;
}
public void addFormProperty(FormPropertyDefinition formProperty) {
formProperties.add(formProperty);
}
public String getFormKey() {
return formKey;
}
public void setFormKey(String formKey) {
this.formKey = formKey;
}
}
/* 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.definition;
/**
* @author Joram Barrez
*/
public class FormPropertyDefinition {
protected String propertyName;
protected String type;
protected boolean required;
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
}
/* 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.definition;
import java.util.List;
/**
* @author Joram Barrez
*/
public class HumanStepDefinition extends StepDefinition {
protected String assignee;
protected boolean isAssigneeInitiator = false;
protected List<String> candidateUsers;
protected List<String> candidateGroups;
protected FormDefinition form;
public boolean isAssigneeInitiator() {
return isAssigneeInitiator;
}
public void setAssigneeInitiator(boolean isAssigneeInitiator) {
this.isAssigneeInitiator = isAssigneeInitiator;
}
public String getAssignee() {
return assignee;
}
public void setAssignee(String assignee) {
this.assignee = assignee;
}
public List<String> getCandidateUsers() {
return candidateUsers;
}
public void setCandidateUsers(List<String> candidateUsers) {
this.candidateUsers = candidateUsers;
}
public List<String> getCandidateGroups() {
return candidateGroups;
}
public void setCandidateGroups(List<String> candidateGroups) {
this.candidateGroups = candidateGroups;
}
public FormDefinition getForm() {
return form;
}
public void setForm(FormDefinition form) {
this.form = form;
}
}
/* 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.definition;
/**
* @author Joram Barrez
*/
public class StepDefinition {
protected String name;
protected String description;
boolean isStartWithPrevious;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isStartWithPrevious() {
return isStartWithPrevious;
}
public void setStartWithPrevious(boolean isStartWithPrevious) {
this.isStartWithPrevious = isStartWithPrevious;
}
}
/* 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.definition;
/**
* @author Joram Barrez
*/
public interface StepDefinitionContainer {
void addStep(StepDefinition stepDefinition);
StepDefinitionContainer addHumanStep(String name, String assignee);
StepDefinitionContainer addHumanStepForWorkflowInitiator(String name);
}
\ No newline at end of file
/* 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.definition;
import java.util.ArrayList;
import java.util.List;
/**
* @author Joram Barrez
*/
public class WorkflowDefinition implements StepDefinitionContainer {
protected String name;
protected String description;
protected List<StepDefinition> steps = new ArrayList<StepDefinition>();
protected ParallelBlock currentParallelBlock;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public WorkflowDefinition name(String name) {
setName(name);
return this;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public WorkflowDefinition description(String description) {
setDescription(description);
return this;
}
public void addStep(StepDefinition stepDefinition) {
steps.add(stepDefinition);
}
public List<StepDefinition> getSteps() {
return steps;
}
public WorkflowDefinition addHumanStep(String name, String assignee) {
return addHumanStep(name, assignee, false);
}
public WorkflowDefinition addHumanStepForWorkflowInitiator(String name) {
return addHumanStep(name, null, true);
}
protected WorkflowDefinition addHumanStep(String name, String assignee, boolean initiator) {
HumanStepDefinition humanStepDefinition = new HumanStepDefinition();
if (name != null) {
humanStepDefinition.setName(name);
}
if (assignee != null) {
humanStepDefinition.setAssignee(assignee);
}
humanStepDefinition.setAssigneeInitiator(initiator);
humanStepDefinition.setStartWithPrevious(currentParallelBlock != null);
addStep(humanStepDefinition);
return this;
}
public ParallelBlock inParallel() {
currentParallelBlock = new ParallelBlock(this);
return currentParallelBlock;
}
public WorkflowDefinition endParallel() {
currentParallelBlock = null;
return this;
}
// Helper classes
public static class ParallelBlock implements StepDefinitionContainer {
protected WorkflowDefinition workflowDefinition;
public ParallelBlock(WorkflowDefinition workflowDefinition) {
this.workflowDefinition = workflowDefinition;
}
public void addStep(StepDefinition stepDefinition) {
workflowDefinition.addStep(stepDefinition);
}
public ParallelBlock addHumanStep(String name, String assignee) {
workflowDefinition.addHumanStep(name, assignee);
return this;
}
public ParallelBlock addHumanStepForWorkflowInitiator(String name) {
workflowDefinition.addHumanStepForWorkflowInitiator(name);
return this;
}
public WorkflowDefinition endParallel() {
workflowDefinition.endParallel();
return workflowDefinition;
}
}
}
/* 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.diagram;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.GraphicInfo;
import org.activiti.bpmn.model.ParallelGateway;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.ScriptTask;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.ServiceTask;
import org.activiti.bpmn.model.StartEvent;
import org.activiti.bpmn.model.Task;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.impl.bpmn.diagram.ProcessDiagramCanvas;
import org.activiti.workflow.simple.definition.StepDefinition;
import org.activiti.workflow.simple.definition.WorkflowDefinition;
/**
* @author Joram Barrez
*/
public class WorkflowDIGenerator {
// Constants
protected static final int SEQUENCE_FLOW_WITHOUT_ARROW_WIDTH = 45;
protected static final int ARROW_WIDTH = 5;
protected static final int SEQUENCE_FLOW_WIDTH = SEQUENCE_FLOW_WITHOUT_ARROW_WIDTH + ARROW_WIDTH;
protected static final int LONG_SEQUENCE_FLOW_WITHOUT_ARROW_WIDTH = SEQUENCE_FLOW_WITHOUT_ARROW_WIDTH * 2;
protected static final int LONG_SEQUENCE_FLOW_WIDTH = LONG_SEQUENCE_FLOW_WITHOUT_ARROW_WIDTH + ARROW_WIDTH;
protected static final int TASK_WIDTH = 130;
protected static final int TASK_HEIGHT = 60;
protected static final int TASK_HEIGHT_SPACING = 10;
protected static final int EVENT_WIDTH = 20;
protected static final int GATEWAY_WIDTH = 40;
protected static final int GATEWAY_HEIGHT = 40;
protected int TASK_BLOCK_WIDTH = GATEWAY_WIDTH + TASK_WIDTH + SEQUENCE_FLOW_WIDTH + LONG_SEQUENCE_FLOW_WITHOUT_ARROW_WIDTH;
// Input
protected WorkflowDefinition workflowDefinition;
protected BpmnModel bpmnModel;
protected Process process;
// Will be set during image generation
protected int startX;
protected int startY;
protected int currentWidth;
protected ProcessDiagramCanvas processDiagramCanvas;
protected List<BlockOfSteps> allStepBlocks;
protected Map<String, List<SequenceFlow>> outgoingSequenceFlowMapping;
protected Map<String, List<SequenceFlow>> incomingSequenceFlowMapping;
protected Set<String> handledElements;
public WorkflowDIGenerator(WorkflowDefinition workflowDefinition, BpmnModel bpmnModel) {
this.workflowDefinition = workflowDefinition;
this.bpmnModel = bpmnModel;
}
public void generateDI() {
generateDI(false);
}
protected void generateDI(boolean generateImage) {
// Reset any previous DI information
bpmnModel.getLocationMap().clear();
bpmnModel.getFlowLocationMap().clear();
bpmnModel.getLocationMap().clear();
process = bpmnModel.getProcesses().get(0); // will always contain just one
// Create task blocks (used for image generation and canvas size calculation)
generateTaskBlocks();
// Calulcate image height and width
this.startX = 0;
this.startY = calculateMaximumHeight() / 2 + 10;
this.currentWidth = 0;
if (generateImage) {
int width = calculateMaximumWidth() + 50;
int height = calculateMaximumHeight() + 50;
processDiagramCanvas = new ProcessDiagramCanvas(width, height);
}
Collection<FlowElement> flowElements = process.getFlowElements();
generateSequenceflowMappings(flowElements);
this.handledElements = new HashSet<String>();
// Enough preparation, actually draw some stuff
for (FlowElement flowElement : flowElements) {
if (!handledElements.contains(flowElement.getId())) {
if (flowElement instanceof StartEvent) {
drawStartEvent(flowElement, startX, startY, EVENT_WIDTH, EVENT_WIDTH, generateImage);
} else if (flowElement instanceof EndEvent) {
drawSequenceFlow(incomingSequenceFlowMapping.get(flowElement.getId()).get(0),
generateImage,
currentWidth, startY + EVENT_WIDTH / 2, currentWidth
+ SEQUENCE_FLOW_WIDTH, startY + EVENT_WIDTH / 2);
drawEndEvent(flowElement, currentWidth, startY, EVENT_WIDTH, EVENT_WIDTH, generateImage);
} else if (flowElement instanceof ParallelGateway
&& outgoingSequenceFlowMapping.get(flowElement.getId()).size() > 1) { // fork
ParallelGateway parallelGateway = (ParallelGateway) flowElement;
drawSequenceFlow(incomingSequenceFlowMapping.get(flowElement.getId()).get(0),
generateImage,
currentWidth, startY + EVENT_WIDTH / 2, currentWidth
+ SEQUENCE_FLOW_WIDTH, startY + EVENT_WIDTH / 2);
drawParallelBlock(currentWidth, startY - EVENT_WIDTH / 2, parallelGateway, generateImage);
} else if (flowElement instanceof Task) {
drawSequenceFlow(incomingSequenceFlowMapping.get(flowElement.getId()).get(0),
generateImage,
currentWidth, startY + EVENT_WIDTH / 2, currentWidth
+ SEQUENCE_FLOW_WIDTH, startY + EVENT_WIDTH / 2);
drawTask(flowElement, currentWidth, startY - ((TASK_HEIGHT - EVENT_WIDTH) / 2),
TASK_WIDTH, TASK_HEIGHT, generateImage);
}
}
}
}
public InputStream generateDiagram() {
generateDI(true); // Generates DI and also the canvas which can export the image
return processDiagramCanvas.generateImage("png");
}
protected void generateTaskBlocks() {
allStepBlocks = new ArrayList<BlockOfSteps>();
List<StepDefinition> workflowSteps = workflowDefinition.getSteps();
for (int i=0; i<workflowSteps.size(); i++) {
StepDefinition stepDefinition = workflowSteps.get(i);
// Parallel tasks are grouped in the same task block
if (stepDefinition.isStartWithPrevious() && (i != 0)) {
allStepBlocks.get(allStepBlocks.size() - 1).addStep(stepDefinition);
} else {
BlockOfSteps blockOfSteps = new BlockOfSteps();
blockOfSteps.addStep(stepDefinition);
allStepBlocks.add(blockOfSteps);
}
}
}
protected void generateSequenceflowMappings(Collection<FlowElement> flowElements) {
this.outgoingSequenceFlowMapping = new HashMap<String, List<SequenceFlow>>();
this.incomingSequenceFlowMapping = new HashMap<String, List<SequenceFlow>>();
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof SequenceFlow) {
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);
}
}
}
protected int calculateMaximumWidth() {
int width = 0;
for (BlockOfSteps blockOfSteps : allStepBlocks) {
if (blockOfSteps.getNrOfSteps() == 1) {
width += TASK_WIDTH + SEQUENCE_FLOW_WIDTH;
} else {
width += TASK_BLOCK_WIDTH + SEQUENCE_FLOW_WIDTH;
}
}
width += SEQUENCE_FLOW_WIDTH + 2 * EVENT_WIDTH;
return width;
}
protected int calculateMaximumHeight() {
int maxNrOfTasksInOneBlock = 0;
for (BlockOfSteps blockOfSteps : allStepBlocks) {
if (blockOfSteps.getNrOfSteps() > maxNrOfTasksInOneBlock) {
maxNrOfTasksInOneBlock = blockOfSteps.getNrOfSteps();
}
}
int extra = 0;
if (maxNrOfTasksInOneBlock % 2 == 0) { // If there is an even nr of tasks -> evenly spread, but no task in the middle
extra = 2 * TASK_HEIGHT;
}
return (maxNrOfTasksInOneBlock * (TASK_HEIGHT + TASK_HEIGHT_SPACING)) + extra;
}
protected void drawParallelBlock(int x, int y, ParallelGateway parallelGateway, boolean generateImage) {
int originalCurrentWidth = currentWidth;
List<SequenceFlow> sequenceFlows = outgoingSequenceFlowMapping.get(parallelGateway.getId());
int nrOfTasks = sequenceFlows.size();
// First parallel gateway
drawParallelGateway(parallelGateway, x, y, GATEWAY_WIDTH, GATEWAY_HEIGHT, generateImage);
handledElements.add(parallelGateway.getId());
// Sequence flow up and down
int centerOfRhombus = x + GATEWAY_WIDTH / 2;
int maxHeight = (nrOfTasks / 2) * (TASK_HEIGHT + TASK_HEIGHT_SPACING);
int currentHeight = y - maxHeight;
// first half
for (int i = 0; i < nrOfTasks / 2; i++) {
SequenceFlow sequenceFlow1 = sequenceFlows.get(i);
drawSequenceFlow(sequenceFlow1, generateImage, centerOfRhombus, y, centerOfRhombus, currentHeight,
centerOfRhombus + SEQUENCE_FLOW_WIDTH, currentHeight);
String targetFlowElementId = sequenceFlow1.getTargetRef();
FlowElement userTask = process.getFlowElement(targetFlowElementId);
drawTask(userTask, centerOfRhombus + SEQUENCE_FLOW_WIDTH,
currentHeight - ((TASK_HEIGHT + TASK_HEIGHT_SPACING) / 2), TASK_WIDTH, TASK_HEIGHT, generateImage);
handledElements.add(userTask.getId());
int seqFlowX = centerOfRhombus + SEQUENCE_FLOW_WIDTH + TASK_WIDTH;
SequenceFlow sequenceFlow2 = outgoingSequenceFlowMapping.get(userTask.getId()).get(0);
drawSequenceFlow(sequenceFlow2, generateImage,
seqFlowX, currentHeight,
seqFlowX + LONG_SEQUENCE_FLOW_WITHOUT_ARROW_WIDTH, currentHeight,
seqFlowX + LONG_SEQUENCE_FLOW_WITHOUT_ARROW_WIDTH, y);
currentHeight += TASK_HEIGHT + TASK_HEIGHT_SPACING;
}
// middle task
if (nrOfTasks % 2 != 0) {
SequenceFlow sequenceFlow1 = sequenceFlows.get(nrOfTasks / 2);
drawSequenceFlow(sequenceFlow1, generateImage,
centerOfRhombus + GATEWAY_WIDTH / 2,
startY + EVENT_WIDTH / 2, centerOfRhombus + SEQUENCE_FLOW_WIDTH,
startY + EVENT_WIDTH / 2);
String targetFlowElementId = sequenceFlow1.getTargetRef();
FlowElement userTask = process.getFlowElement(targetFlowElementId);
drawTask(userTask, centerOfRhombus + SEQUENCE_FLOW_WIDTH,
startY - ((TASK_HEIGHT - GATEWAY_HEIGHT)), TASK_WIDTH, TASK_HEIGHT, generateImage);
handledElements.add(userTask.getId());
int seqflowX = centerOfRhombus + GATEWAY_WIDTH / 2 + (SEQUENCE_FLOW_WIDTH - GATEWAY_WIDTH / 2) + TASK_WIDTH;
SequenceFlow sequenceFlow2 = outgoingSequenceFlowMapping.get(userTask.getId()).get(0);
drawSequenceFlow(sequenceFlow2, generateImage,
seqflowX, startY + EVENT_WIDTH / 2,
seqflowX + LONG_SEQUENCE_FLOW_WIDTH - GATEWAY_WIDTH / 2 - ARROW_WIDTH, startY
+ EVENT_WIDTH / 2);
}
currentHeight = y + GATEWAY_HEIGHT + TASK_HEIGHT + TASK_HEIGHT_SPACING;
// second half
int startIndex = nrOfTasks % 2 == 0 ? nrOfTasks / 2 : (nrOfTasks / 2) + 1;
for (int i = startIndex; i < nrOfTasks; i++) {
SequenceFlow sequenceFlow1 = sequenceFlows.get(i);
drawSequenceFlow(sequenceFlow1, generateImage,
centerOfRhombus, y + GATEWAY_HEIGHT, centerOfRhombus,
currentHeight, centerOfRhombus + SEQUENCE_FLOW_WIDTH, currentHeight);
String targetFlowElementId = sequenceFlow1.getTargetRef();
FlowElement userTask = process.getFlowElement(targetFlowElementId);
drawTask(userTask, centerOfRhombus + SEQUENCE_FLOW_WIDTH,
currentHeight - ((TASK_HEIGHT + TASK_HEIGHT_SPACING) / 2), TASK_WIDTH,
TASK_HEIGHT, generateImage);
int seqFlowX = centerOfRhombus + SEQUENCE_FLOW_WIDTH + TASK_WIDTH;
SequenceFlow sequenceFlow2 = outgoingSequenceFlowMapping.get(userTask.getId()).get(0);
drawSequenceFlow(sequenceFlow2, generateImage,
seqFlowX, currentHeight,
seqFlowX + LONG_SEQUENCE_FLOW_WITHOUT_ARROW_WIDTH, currentHeight, seqFlowX
+ LONG_SEQUENCE_FLOW_WITHOUT_ARROW_WIDTH, y + GATEWAY_HEIGHT);
handledElements.add(userTask.getId());
currentHeight += TASK_HEIGHT + TASK_HEIGHT_SPACING;
}
// Second parallel gateway
String someTaskId = sequenceFlows.get(0).getTargetRef();
FlowElement join = process.getFlowElement(outgoingSequenceFlowMapping.get(someTaskId).get(0).getTargetRef());
centerOfRhombus = centerOfRhombus + SEQUENCE_FLOW_WIDTH + TASK_WIDTH + LONG_SEQUENCE_FLOW_WITHOUT_ARROW_WIDTH;
drawParallelGateway(join, centerOfRhombus - GATEWAY_WIDTH / 2, y, GATEWAY_WIDTH, GATEWAY_HEIGHT, generateImage);
handledElements.add(join.getId());
currentWidth = originalCurrentWidth + TASK_BLOCK_WIDTH;
}
protected void drawStartEvent(FlowElement flowElement, int x, int y, int width, int height, boolean generateImage) {
if (generateImage) {
processDiagramCanvas.drawNoneStartEvent(x, y, width, height);
}
createDiagramInterchangeInformation(flowElement, x, y, width, height);
currentWidth += EVENT_WIDTH;
}
protected void drawEndEvent(FlowElement flowElement, int x, int y, int width, int height, boolean generateImage) {
if (generateImage) {
processDiagramCanvas.drawNoneEndEvent(x, y, width, height);
}
createDiagramInterchangeInformation(flowElement, x, y, width, height);
currentWidth += EVENT_WIDTH;
}
protected void drawParallelGateway(FlowElement flowElement, int x, int y, int width, int height, boolean generateImage) {
if (generateImage) {
processDiagramCanvas.drawParallelGateway(x, y, width, height);
}
createDiagramInterchangeInformation(flowElement, x, y, width, height);
currentWidth += GATEWAY_WIDTH;
}
protected void drawTask(FlowElement flowElement, int x, int y, int width, int height, boolean generateImage) {
if (generateImage) {
if (flowElement instanceof UserTask) {
processDiagramCanvas.drawUserTask(flowElement.getName(), x, y, width, height);
} else if (flowElement instanceof ServiceTask) {
processDiagramCanvas.drawServiceTask(flowElement.getName(), x, y, width, height);
} else if (flowElement instanceof ScriptTask) {
processDiagramCanvas.drawScriptTask(flowElement.getName(), x, y, width, height);
}
}
createDiagramInterchangeInformation(flowElement, x, y, width, height);
currentWidth += TASK_WIDTH;
}
protected void drawSequenceFlow(SequenceFlow sequenceFlow, boolean generateImage, int... waypoints) {
// Draw on diagram canvas
int minX = Integer.MAX_VALUE;
int maxX = 0;
for (int i = 2; i < waypoints.length; i += 2) { // waypoints.size()
if (generateImage) {
// minimally 4: x1, y1, x2, y2
if (i < waypoints.length - 2) {
processDiagramCanvas.drawSequenceflowWithoutArrow(waypoints[i - 2],
waypoints[i - 1], waypoints[i], waypoints[i + 1], false);
} else {
processDiagramCanvas.drawSequenceflow(waypoints[i - 2],
waypoints[i - 1], waypoints[i], waypoints[i + 1], false);
}
}
if (waypoints[i - 2] < minX || waypoints[i] < minX) {
minX = Math.min(waypoints[i - 2], waypoints[i]);
}
if (waypoints[i - 2] > maxX || waypoints[i] > maxX) {
maxX = Math.max(waypoints[i - 2], waypoints[i]);
}
}
currentWidth += maxX - minX;
// Generate DI information
List<GraphicInfo> graphicInfoForWaypoints = new ArrayList<GraphicInfo>();
for (int i = 0; i < waypoints.length; i += 2) {
GraphicInfo graphicInfo = new GraphicInfo();
graphicInfo.element = sequenceFlow;
graphicInfo.x = waypoints[i];
graphicInfo.y = waypoints[i + 1];
graphicInfoForWaypoints.add(graphicInfo);
}
bpmnModel.addFlowGraphicInfoList(sequenceFlow.getId(), graphicInfoForWaypoints);
}
protected void createDiagramInterchangeInformation(FlowElement flowElement,
int x, int y, int width, int height) {
GraphicInfo graphicInfo = new GraphicInfo();
graphicInfo.x = x;
graphicInfo.y = y;
graphicInfo.width = width;
graphicInfo.height = height;
graphicInfo.element = flowElement;
bpmnModel.addGraphicInfo(flowElement.getId(), graphicInfo);
}
// Helper class ------------------------------------------------------------------------
static class BlockOfSteps {
protected List<StepDefinition> steps;
public BlockOfSteps() {
this.steps = new ArrayList<StepDefinition>();
}
public List<StepDefinition> getSteps() {
return steps;
}
public void addStep(StepDefinition step) {
steps.add(step);
}
public StepDefinition get(int index) {
return steps.get(index);
}
public int getNrOfSteps() {
return steps.size();
}
}
}
/* 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.test.ActivitiRule;
import org.activiti.workflow.simple.converter.DefaultWorkflowDefinitionConversionListener;
import org.activiti.workflow.simple.converter.HumanStepDefinitionConverter;
import org.activiti.workflow.simple.converter.StepDefinitionConverter;
import org.activiti.workflow.simple.converter.WorkflowDefinitionConversion;
import org.activiti.workflow.simple.converter.WorkflowDefinitionConversionFactory;
import org.activiti.workflow.simple.converter.WorkflowDefinitionConversionListener;
import org.activiti.workflow.simple.definition.WorkflowDefinition;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Joram Barrez
*/
public class WorkflowConversionTest {
private static Logger log = LoggerFactory.getLogger(WorkflowConversionTest.class);
@Rule
public ActivitiRule activitiRule = new ActivitiRule();
protected WorkflowDefinitionConversionFactory conversionFactory;
@Before
public void initialiseTest() {
// Alternatively, the following setup could be done using a dependency injection container
conversionFactory = new WorkflowDefinitionConversionFactory();
// Steps
List<StepDefinitionConverter> stepConverters = new ArrayList<StepDefinitionConverter>();
stepConverters.add(new HumanStepDefinitionConverter());
conversionFactory.setStepDefinitionConverters(stepConverters);
// Listeners
List<WorkflowDefinitionConversionListener> conversionListeners = new ArrayList<WorkflowDefinitionConversionListener>();
conversionListeners.add(new DefaultWorkflowDefinitionConversionListener());
conversionFactory.setWorkflowDefinitionConversionListeners(conversionListeners);
}
@After
public void cleanup() {
for (Deployment deployment : activitiRule.getRepositoryService().createDeploymentQuery().list()) {
activitiRule.getRepositoryService().deleteDeployment(deployment.getId(), true);
}
}
@Test
public void testSimplestProcess() {
WorkflowDefinition workflowDefinition = new WorkflowDefinition()
.name("testWorkflow")
.description("This is a test workflow");
// Validate
ProcessInstance processInstance = activitiRule.getRuntimeService().startProcessInstanceByKey(convertAndDeploy(workflowDefinition));
assertTrue(processInstance.isEnded());
}
@Test
public void testUserTasksWithOnlyAssignees() {
String[] assignees = new String[] {"kermit", "gonzo", "mispiggy"};
WorkflowDefinition workflowDefinition = new WorkflowDefinition()
.name("testWorkflow")
.description("This is a test workflow")
.addHumanStep("first task", assignees[0])
.addHumanStep("second step", assignees[1])
.addHumanStep("third step", assignees[2]);
// Validate
activitiRule.getRuntimeService().startProcessInstanceByKey(convertAndDeploy(workflowDefinition));
for (String assignee : assignees) {
Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
assertEquals(assignee, task.getAssignee());
activitiRule.getTaskService().complete(task.getId());
}
assertEquals(0, activitiRule.getRuntimeService().createProcessInstanceQuery().count());
}
// @Test
// public void testTwoUserTasksInParallel() {
// WorkflowDefinition workflowDefinition = new WorkflowDefinition()
// .name("testWorkflow")
// .description("This is a test workflow")
// .inParallel()
// .addHumanStep("first task", "kermit")
// .addHumanStep("second step", "gonzo")
// .endParallel();
//
// // Validate
// activitiRule.getRuntimeService().startProcessInstanceByKey(convertAndDeploy(workflowDefinition));
// assertEquals(1, activitiRule.getTaskService().createTaskQuery().taskAssignee("kermit").count());
// assertEquals(1, activitiRule.getTaskService().createTaskQuery().taskAssignee("gonzo").count());
// }
// Helper methods -----------------------------------------------------------------------------
protected String convertAndDeploy(WorkflowDefinition workflowDefinition) {
// Convert
WorkflowDefinitionConversion conversion = conversionFactory.createWorkflowDefinitionConversion(workflowDefinition);
conversion.convert();
log.info("Converted process : " + conversion.getbpm20Xml());
// InputStream is = conversion.getWorkflowDiagramImage();
// try {
// flow(is, new FileOutputStream("temp.png"), new byte[1024]);
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// } catch (IOException e) {
// e.printStackTrace();
// }
// Deploy
deployProcessDefinition(conversion);
return getDeployedProcessKey();
}
public static void flow( InputStream is, OutputStream os, byte[] buf )
throws IOException {
int numRead;
while ( (numRead = is.read(buf) ) >= 0) {
os.write(buf, 0, numRead);
}
}
protected String getDeployedProcessKey() {
ProcessDefinition processDefinition = activitiRule.getRepositoryService().createProcessDefinitionQuery().singleResult();
assertNotNull(processDefinition);
return processDefinition.getKey();
}
protected void deployProcessDefinition(WorkflowDefinitionConversion conversion) {
long nrOfDeployments = countNrOfDeployments();
activitiRule.getRepositoryService().createDeployment()
.addString(conversion.getProcess().getId() + ".bpmn20.xml", conversion.getbpm20Xml())
.deploy();
assertEquals(nrOfDeployments + 1, countNrOfDeployments());
}
protected long countNrOfDeployments() {
return activitiRule.getRepositoryService().createDeploymentQuery().count();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="sa" />
<property name="jdbcPassword" value="" />
<!-- Database configurations -->
<property name="databaseSchemaUpdate" value="true" />
<!-- job executor configurations -->
<property name="jobExecutorActivate" value="false" />
<property name="createDiagramOnDeploy" value="false" />
</bean>
</beans>
log4j.rootLogger=INFO, CA
# ConsoleAppender
log4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern= %d{hh:MM:ss,SSS} [%t] %-5p %c %x - %m%n
log4j.logger.org.apache.ibatis.level=INFO
log4j.logger.javax.activation.level=INFO
......@@ -752,6 +752,7 @@
<module>modules/activiti-bpmn-model</module>
<module>modules/activiti-bpmn-converter</module>
<module>modules/activiti-json-converter</module>
<module>modules/activiti-simple-workflow</module>
<module>modules/activiti-modeler</module>
<module>modules/activiti-explorer</module>
<module>modules/activiti-rest</module>
......
......@@ -251,7 +251,7 @@ repositoryService.createDeployment()
<para>
If, for some reason, it is not necessary or wanted to generate a diagram during deployment the
<literal>isCreateDiagramOnDeploy</literal> property can be set on the process engine configuration:
<programlisting>&lt;property name=&quot;isCreateDiagramOnDeploy&quot; value=&quot;false&quot; /&gt;</programlisting>
<programlisting>&lt;property name=&quot;createDiagramOnDeploy&quot; value=&quot;false&quot; /&gt;</programlisting>
No diagram will be generated now.
</para>
</section>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册