/*
* Copyright (c) NASK, NCSC
*
* This file is part of HoneySpider Network 2.1.
*
* This is a free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package pl.nask.hsn2.activiti;
import java.util.Arrays;
import java.util.List;
import org.activiti.engine.impl.pvm.PvmProcessDefinition;
import org.activiti.engine.impl.pvm.delegate.ActivityBehavior;
import pl.nask.hsn2.bus.operations.TaskErrorReasonType;
import pl.nask.hsn2.framework.workflow.builder.WorkflowBuilder;
import pl.nask.hsn2.framework.workflow.engine.ProcessDefinitionRegistry;
import pl.nask.hsn2.framework.workflow.hwl.ExecutionFlow;
import pl.nask.hsn2.framework.workflow.hwl.ExecutionPoint;
import pl.nask.hsn2.framework.workflow.hwl.Output;
import pl.nask.hsn2.framework.workflow.hwl.ProcessDefinition;
import pl.nask.hsn2.framework.workflow.hwl.ServiceParam;
import pl.nask.hsn2.framework.workflow.hwl.Workflow;
public final class ActivitiWorkflowBuilder implements WorkflowBuilder {
private ExtendedProcessDefinitionBuilder builder;
private ProcessDefinitionRegistry<PvmProcessDefinition> registry = new ProcessDefinitionRegistry<PvmProcessDefinition>();
private String nextActivityId;
private BehaviorFactory behaviorFactory;
private IdGen idgen = IdGen.getInstance();
public ActivitiWorkflowBuilder(BehaviorFactory behaviorFactory) {
this.behaviorFactory = behaviorFactory;
}
@Override
public void addConditional(String condition, ExecutionFlow onTrueFlow, ExecutionFlow onFalseFlow) {
String conditionStartActivityId = popCurrentId();
String endingActivityId = "condition-end-" + popCurrentId();
List<ExecutionFlow> flows = Arrays.asList(onTrueFlow, onFalseFlow);
String[] flowIds = splitExecution(conditionStartActivityId, behaviorFactory.decisionBehaviorInstance(condition), flows.size(), Arrays.asList("onTrue", "onFalse"));
addFlows(flowIds, flows, endingActivityId);
nextActivityId = endingActivityId;
}
@Override
public void buildWorkflow(String name, List<ProcessDefinition> processes) {
for (ProcessDefinition process: processes) {
process.transformToWorkflow(this);
}
}
public void buildWorkflow(Workflow workflow) {
buildWorkflow(workflow.getName(), workflow.getProcessDefinitions());
}
@Override
public void addProcess(String id, List<ExecutionPoint> executionPoints) {
builder = new ExtendedProcessDefinitionBuilder(id);
addStartState();
addFlow(executionPoints);
addProcessEndState();
ExtendedProcessDefinitionImpl definition = builder.buildProcessDefinition();
registry.add(definition.getId(), definition);
}
@Override
public void addParallel(List<ExecutionFlow> threads) {
if (threads.size() >= 2) {
String forkActivityId = popCurrentId();
String joinActivityId = "join-" + popCurrentId();
// split executions with fork
String[] flowIds = splitExecution(forkActivityId, behaviorFactory.forkBehaviorInstance(), threads.size(), null);
addFlows(flowIds, threads, joinActivityId);
// add join
builder
.createActivity(joinActivityId)
.behavior(behaviorFactory.joinBehaviorInstance())
.transition(genNextActivityId())
.endActivity();
} else if (threads.size() == 1) {
addFlow(threads.get(0));
}
}
@Override
public void addService(String name, String id, List<ServiceParam> parameters, List<Output> output,List<TaskErrorReasonType> errorsToIgnore) {
addActivity(behaviorFactory.serviceBehaviorInstance(name, id, parameters, output, registry, errorsToIgnore));
}
@Override
public void addWait(String expression) {
addActivity(behaviorFactory.waitBehavior(expression));
}
@Override
public void addScript(String scriptBody) {
addActivity(behaviorFactory.scriptBehavior(scriptBody));
}
private void addFlows(String[] flowIds, List<ExecutionFlow> flows, String endingActivityId) {
for (int i=0; i < flowIds.length; i++) {
nextActivityId = flowIds[i];
addFlow(flows.get(i));
// end the flow
builder.createActivity(nextActivityId)
.behavior(behaviorFactory.emptyBehaviorInstance())
.transition(endingActivityId)
.endActivity();
}
}
private String[] splitExecution(String startActivityId, ActivityBehavior behaviorInstance, int numberOftransitions, List<String> transitionIds) {
// add decision
String[] flowIds = new String[numberOftransitions];
builder
.createActivity(startActivityId)
.behavior(behaviorInstance);
for (int i=0; i < numberOftransitions; i++) {
flowIds[i] = "flow-start-" + popCurrentId();
if (transitionIds != null && i < transitionIds.size()) {
builder.transition(flowIds[i], transitionIds.get(i));
} else {
builder.transition(flowIds[i]);
}
}
builder.endActivity();
return flowIds;
}
private void addFlow(List<ExecutionPoint> executionPoints) {
if (executionPoints != null) {
for (ExecutionPoint ep: executionPoints) {
ep.transformToWorkflow(this);
}
}
}
private void addFlow(ExecutionFlow flow) {
if (!flow.isEmpty())
addFlow(flow.getExecutionPoints());
}
private void addActivity(ActivityBehavior behavior) {
builder.createActivity(popCurrentId())
.behavior(behavior)
.transition(nextActivityId)
.endActivity();
}
private void addStartState() {
builder
.createActivity("start")
.initial()
.behavior(behaviorFactory.startBehavior())
.transition(genNextActivityId())
.endActivity();
}
private void addProcessEndState() {
builder.createActivity(nextActivityId).behavior(behaviorFactory.waitBehavior(null)).endActivity();
}
private String popCurrentId() {
String currentActId = nextActivityId;
nextActivityId = idgen.nextId("act").getFormattedId();
return currentActId;
}
private String genNextActivityId() {
nextActivityId = idgen.nextId("act").getFormattedId();
return nextActivityId;
}
public ProcessDefinitionRegistry<PvmProcessDefinition> getRegistry() {
return registry;
}
}