blob: e0b4155cdb6950c960c1ca786f8df3aa39bd2c1d [file] [log] [blame]
/**
*
* Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation
*
*/
package org.eclipse.osbp.vaaclipse.addons.softwarefactory.bpmImpl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.drools.definition.process.Connection;
import org.drools.definition.process.Node;
import org.drools.definition.process.Process;
import org.drools.process.core.datatype.DataType;
import org.drools.process.core.datatype.impl.type.ObjectDataType;
import org.drools.process.core.datatype.impl.type.StringDataType;
import org.drools.process.core.impl.WorkImpl;
import org.eclipse.osbp.bpm.api.AbstractBPMServiceTask;
import org.eclipse.osbp.bpm.api.BPMCallActivity;
import org.eclipse.osbp.bpm.api.BPMEndEvent;
import org.eclipse.osbp.bpm.api.BPMScriptTask;
import org.eclipse.osbp.bpm.api.BPMSplitGateway;
import org.eclipse.osbp.bpm.api.IBlipBPMConstants;
import org.eclipse.osbp.bpm.api.IBlipBPMItem;
import org.eclipse.osbp.bpm.api.IBlipBPMOutgoing;
import org.eclipse.osbp.bpm.api.IBlipBPMUserTask;
import org.eclipse.osbp.ui.api.useraccess.IBlipProcessPermissions;
import org.eclipse.osbp.xtext.blip.common.BlipBPMStartInfo;
import org.eclipse.osbp.xtext.blip.common.BlipHelper;
import org.jbpm.compiler.IProcessEnhancer;
import org.jbpm.process.core.context.variable.Variable;
import org.jbpm.ruleflow.core.RuleFlowProcess;
import org.jbpm.workflow.core.Constraint;
import org.jbpm.workflow.core.DroolsAction;
import org.jbpm.workflow.core.impl.ConstraintImpl;
import org.jbpm.workflow.core.impl.DroolsConsequenceAction;
import org.jbpm.workflow.core.impl.ExtendedNodeImpl;
import org.jbpm.workflow.core.node.ActionNode;
import org.jbpm.workflow.core.node.EndNode;
import org.jbpm.workflow.core.node.HumanTaskNode;
import org.jbpm.workflow.core.node.Join;
import org.jbpm.workflow.core.node.Split;
import org.jbpm.workflow.core.node.StartNode;
import org.jbpm.workflow.core.node.SubProcessNode;
import org.jbpm.workflow.core.node.WorkItemNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.drools.process.core.ParameterDefinition;
import org.drools.process.core.impl.ParameterDefinitionImpl;
public class BPMProcessEnhancer implements IProcessEnhancer {
private static Logger log = LoggerFactory.getLogger(BPMProcessEnhancer.class);
private final BlipBPMStartInfo startInfo;
private final IBlipProcessPermissions blipProcessPermissions;
private ObjectDataType protocolDataType;
private StringDataType stringDataType;
private ObjectDataType workloadDtoDataType;
public BPMProcessEnhancer(BlipBPMStartInfo startInfo, IBlipProcessPermissions blipProcessPermissions) {
this.startInfo = startInfo;
if (blipProcessPermissions == null) {
this.blipProcessPermissions = DefaultBlipProcessPermissions.INSTANCE;
}
else {
this.blipProcessPermissions = blipProcessPermissions;
}
}
@Override
public void enhanceProcess(Process basicProcess) {
if (basicProcess instanceof RuleFlowProcess) {
RuleFlowProcess process = (RuleFlowProcess) basicProcess;
String workloadDtoFqn = startInfo.getWorkloadDtoFqn();
String functionGroupFqn = startInfo.getFunctionGroupFqn();
if (functionGroupFqn == null) {
functionGroupFqn = "";
}
String[] tokens = functionGroupFqn.split("\\.");
String functionGroupCls = tokens[tokens.length-1];
enhanceProcessImports(process, workloadDtoFqn, functionGroupFqn);
enhanceProcessFunctionImports(process, functionGroupFqn);
enhanceProcessVariables(process, workloadDtoFqn);
// enhance nodes
for (Node node : process.getNodes()) {
if (node instanceof HumanTaskNode) {
enhanceHumanTaskNode(process, (HumanTaskNode) node, functionGroupCls);
}
else if (node instanceof ActionNode) {
enhanceActionNode(process, (ActionNode) node, functionGroupCls);
}
else if (node instanceof Split) {
enhanceSplitGateway(process, functionGroupCls, (Split) node);
}
else if (node instanceof Join) {
log.warn("Node "+node.getName()+" "+node.getClass().getCanonicalName()+" does not be enhanced RIGHT NOW! Maybe later on?");
}
else if (node instanceof SubProcessNode) {
enhanceSubProcessNode(process, functionGroupCls, (SubProcessNode) node);
}
else if (node instanceof StartNode) {
log.debug("Node "+node.getName()+" "+node.getClass().getCanonicalName()+" does not need to be enhanced");
}
else if (node instanceof EndNode) {
enhanceEndNode(process, (EndNode) node);
}
else if (node instanceof WorkItemNode) {
enhanceWorkItemNode(process, (WorkItemNode) node, functionGroupCls);
}
else {
log.warn("Node "+node.getName()+" "+node.getClass().getCanonicalName()+" will not be enhanced! IS ITR NECESSARY?");
}
}
}
}
private void enhanceSplitGateway(RuleFlowProcess process, String functionGroupCls, Split split) {
String name = BlipHelper.getBpmItemRecommendedName(split.getName());
IBlipBPMItem bpmItem = startInfo.getBpmItemForBpmId(name);
if (bpmItem instanceof BPMSplitGateway) {
for (IBlipBPMOutgoing outgoing : ((BPMSplitGateway) bpmItem).getOutgoings()) {
List<Connection> connections = split.getOutgoingConnections(org.jbpm.workflow.core.Node.CONNECTION_DEFAULT_TYPE);
for (Connection connection : connections) {
if (connection.getMetaData("UniqueId").equals(outgoing.getBpmId())) {
// enhance sequence flow constraints
boolean modify = true;
Constraint constraint = split.getConstraint(connection);
if (constraint == null) {
constraint = new ConstraintImpl();
modify = false;
}
constraint.setName(outgoing.getBlipId());
constraint.setType("code");
constraint.setDialect("mvel");
constraint.setDefault(false);
constraint.setPriority(outgoing.getPriority());
constraint.setConstraint("return "+functionGroupCls+"."+outgoing.getTestFunction()+"(kcontext);");
if (modify) {
log.debug(process.getId()+" "+constraint.getName()+": modify constraint "+constraint.getPriority()+" "+constraint.getDialect()+" "+constraint.getConstraint());
}
else {
split.setConstraint(connection, constraint);
log.debug(process.getId()+" "+constraint.getName()+": add constraint "+constraint.getPriority()+" "+constraint.getDialect()+" "+constraint.getConstraint());
}
}
}
}
}
}
private void enhanceProcessImports(RuleFlowProcess process, String workloadDtoFqn, String functionGroupFqn) {
boolean modified = false;
List<String> imports = process.getImports();
if (imports == null) {
imports = new ArrayList<String>();
}
modified |= addItem(process, "imports", imports, workloadDtoFqn, "");
modified |= addItem(process, "imports", imports, functionGroupFqn, "");
if (modified) {
process.setImports(imports);
}
}
private void enhanceProcessFunctionImports(RuleFlowProcess process, String functionGroupFqn) {
boolean modified;
modified = false;
List<String> functions = process.getFunctionImports();
if (functions == null) {
functions = new ArrayList<String>();
}
modified |= addItem(process, "function imports", functions, functionGroupFqn, ".*");
if (modified) {
process.setFunctionImports(functions);
}
}
private void enhanceProcessVariables(RuleFlowProcess process, String workloadDtoFqn) {
boolean modified;
protocolDataType = new ObjectDataType(org.eclipse.osbp.bpm.api.IBlipBPMProcessProtocol.class.getCanonicalName());
stringDataType = new StringDataType();
workloadDtoDataType = null;
if ((workloadDtoFqn != null) && !workloadDtoFqn.isEmpty()) {
workloadDtoDataType = new ObjectDataType(workloadDtoFqn);
}
// ObjectDataType functionGroupDataType = new ObjectDataType(functionGroupFqn);
modified = false;
List<Variable> variables = process.getVariableScope().getVariables();
modified |= addVariable (process, variables, IBlipBPMConstants.VARIABLE_PROCESS_PROTOCOL, protocolDataType);
if (workloadDtoFqn != null) {
modified |= addVariable (process, variables, IBlipBPMConstants.VARIABLE_PROCESS_WORKLOAD_DTO_FQN, stringDataType);
modified |= addVariable (process, variables, IBlipBPMConstants.VARIABLE_PROCESS_WORKLOAD_DTO, workloadDtoDataType);
}
if (modified) {
process.getVariableScope().setVariables(variables);
}
}
private static final String[] inOutMappings = new String[] {
IBlipBPMConstants.VARIABLE_PROCESS_PROTOCOL,
IBlipBPMConstants.VARIABLE_PROCESS_WORKLOAD_DTO_FQN,
IBlipBPMConstants.VARIABLE_PROCESS_WORKLOAD_DTO,
};
private void enhanceHumanTaskNode(RuleFlowProcess process, HumanTaskNode humantask, String functionGroupCls) {
String processName = BlipHelper.getBpmItemRecommendedName(process.getId());
String humanTaskName = BlipHelper.getBpmItemRecommendedName(humantask.getName());
IBlipBPMUserTask bpmUserTask = startInfo.getBpmHumanTaskForBpmId(humanTaskName);
// enhance authorization roles and locale
List<String> roles = blipProcessPermissions.getHumanTaskExecutableRoles(processName, humanTaskName);
modifyWorkParameter(process, humantask, "GroupId", String.join(",", roles));
modifyWorkParameter(process, humantask, "Locale", "en-US");
// enhance in-/out-mapping of process local variables
for (String variable : inOutMappings) {
if (!humantask.getInMappings().containsKey(variable)) {
humantask.addInMapping(variable, variable);
log.debug(process.getId()+" "+humantask.getName()+": add in-mapping "+variable);
}
if (!humantask.getOutMappings().containsKey(variable)) {
humantask.addOutMapping(variable, variable);
log.debug(process.getId()+" "+humantask.getName()+": add out-mapping "+variable);
}
}
if (bpmUserTask != null) {
String function;
List<DroolsAction> actions;
// enhance on entry
function = bpmUserTask.getOnEntryFunction();
if (function != null) {
function = functionGroupCls+"."+function+"(kcontext);";
actions = humantask.getActions(ExtendedNodeImpl.EVENT_NODE_ENTER);
if (actions == null) {
actions = new ArrayList<DroolsAction>();
}
else {
actions.clear();
}
actions.add(new DroolsConsequenceAction("java", function));
}
else {
actions = null;
}
humantask.setActions(ExtendedNodeImpl.EVENT_NODE_ENTER, actions);
log.debug(process.getId()+" "+humantask.getName()+": modify on entry "+function);
// enhance on exit
function = bpmUserTask.getOnExitFunction();
if (function != null) {
function = functionGroupCls+"."+function+"(kcontext);";
actions = humantask.getActions(ExtendedNodeImpl.EVENT_NODE_EXIT);
if (actions == null) {
actions = new ArrayList<DroolsAction>();
}
else {
actions.clear();
}
actions.add(new DroolsConsequenceAction("java", function));
}
else {
actions = null;
}
humantask.setActions(ExtendedNodeImpl.EVENT_NODE_EXIT, actions);
log.debug(process.getId()+" "+humantask.getName()+": modify on exit "+function);
}
}
private void enhanceSubProcessNode(RuleFlowProcess process, String functionGroupCls, SubProcessNode subProcess) {
String processName = BlipHelper.getBpmItemRecommendedName(process.getId());
String subProcessName = BlipHelper.getBpmItemRecommendedName(subProcess.getName());
IBlipBPMItem bpmItem = startInfo.getBpmItemForBpmId(subProcessName);
BPMCallActivity bpmCallActivity = (bpmItem instanceof BPMCallActivity) ? (BPMCallActivity)bpmItem : null;
// enhance authorization roles and locale
// List<String> roles = blipProcessPermissions.getHumanTaskExecutableRoles(processName, humanTaskName);
// modifyWorkParameter(process, humantask, "GroupId", String.join(",", roles));
// modifyWorkParameter(process, humantask, "Locale", "en-US");
// enhance in-/out-mapping of process local variables
for (String variable : inOutMappings) {
if (!subProcess.getInMappings().containsKey(variable)) {
subProcess.addInMapping(variable, variable);
log.debug(process.getId()+" "+subProcess.getName()+": add in-mapping "+variable);
}
if (!subProcess.getOutMappings().containsKey(variable)) {
subProcess.addOutMapping(variable, variable);
log.debug(process.getId()+" "+subProcess.getName()+": add out-mapping "+variable);
}
}
if (bpmCallActivity != null) {
String function;
List<DroolsAction> actions;
// enhance on entry
function = bpmCallActivity.getOnEntryFunction();
if (function != null) {
function = functionGroupCls+"."+function+"(kcontext);";
actions = subProcess.getActions(ExtendedNodeImpl.EVENT_NODE_ENTER);
if (actions == null) {
actions = new ArrayList<DroolsAction>();
}
else {
actions.clear();
}
actions.add(new DroolsConsequenceAction("java", function));
}
else {
actions = null;
}
subProcess.setActions(ExtendedNodeImpl.EVENT_NODE_ENTER, actions);
log.debug(process.getId()+" "+subProcess.getName()+": modify on entry "+function);
// enhance on exit
function = bpmCallActivity.getOnExitFunction();
if (function != null) {
function = functionGroupCls+"."+function+"(kcontext);";
actions = subProcess.getActions(ExtendedNodeImpl.EVENT_NODE_EXIT);
if (actions == null) {
actions = new ArrayList<DroolsAction>();
}
else {
actions.clear();
}
actions.add(new DroolsConsequenceAction("java", function));
}
else {
actions = null;
}
subProcess.setActions(ExtendedNodeImpl.EVENT_NODE_EXIT, actions);
log.debug(process.getId()+" "+subProcess.getName()+": modify on exit "+function);
}
}
private void enhanceWorkItemNode(RuleFlowProcess process, WorkItemNode workItem, String functionGroupCls) {
String processName = BlipHelper.getBpmItemRecommendedName(process.getId());
String workItemName = BlipHelper.getBpmItemRecommendedName(workItem.getName());
IBlipBPMItem bpmWorkItem = startInfo.getBpmItemForBpmId(workItemName);
// enhance authorization roles and locale
// enhance in-/out-mapping of process local variables
for (String variable : inOutMappings) {
if (!workItem.getInMappings().containsKey(variable)) {
workItem.addInMapping(variable, variable);
log.debug(process.getId()+" "+workItem.getName()+": add in-mapping "+variable);
}
if (!workItem.getOutMappings().containsKey(variable)) {
workItem.addOutMapping(variable, variable);
log.debug(process.getId()+" "+workItem.getName()+": add out-mapping "+variable);
}
}
Set<ParameterDefinition> parameterDefinitions = new HashSet<>();
Map<String, Object> parameters = new HashMap<>();
parameterDefinitions.add(new ParameterDefinitionImpl(IBlipBPMConstants.VARIABLE_PROCESS_PROTOCOL, protocolDataType));
parameters.put(IBlipBPMConstants.VARIABLE_PROCESS_PROTOCOL, null);
parameterDefinitions.add(new ParameterDefinitionImpl(IBlipBPMConstants.VARIABLE_PROCESS_WORKLOAD_DTO_FQN, stringDataType));
parameters.put(IBlipBPMConstants.VARIABLE_PROCESS_WORKLOAD_DTO_FQN, null);
if (workloadDtoDataType != null) {
parameterDefinitions.add(new ParameterDefinitionImpl(IBlipBPMConstants.VARIABLE_PROCESS_WORKLOAD_DTO, workloadDtoDataType));
parameters.put(IBlipBPMConstants.VARIABLE_PROCESS_WORKLOAD_DTO, null);
}
workItem.getWork().setParameterDefinitions(parameterDefinitions);
workItem.getWork().setParameters(parameters);
if (bpmWorkItem instanceof AbstractBPMServiceTask) {
String function;
List<DroolsAction> actions;
// enhance on entry
function = ((AbstractBPMServiceTask)bpmWorkItem).getOnEntryFunction();
if (function != null) {
function = functionGroupCls+"."+function+"(kcontext);";
actions = workItem.getActions(ExtendedNodeImpl.EVENT_NODE_ENTER);
if (actions == null) {
actions = new ArrayList<DroolsAction>();
}
else {
actions.clear();
}
actions.add(new DroolsConsequenceAction("java", function));
}
else {
actions = null;
}
workItem.setActions(ExtendedNodeImpl.EVENT_NODE_ENTER, actions);
log.debug(process.getId()+" "+workItem.getName()+": modify on entry "+function);
// enhance on exit
function = ((AbstractBPMServiceTask)bpmWorkItem).getOnExitFunction();
if (function != null) {
function = functionGroupCls+"."+function+"(kcontext);";
actions = workItem.getActions(ExtendedNodeImpl.EVENT_NODE_EXIT);
if (actions == null) {
actions = new ArrayList<DroolsAction>();
}
else {
actions.clear();
}
actions.add(new DroolsConsequenceAction("java", function));
}
else {
actions = null;
}
workItem.setActions(ExtendedNodeImpl.EVENT_NODE_EXIT, actions);
log.debug(process.getId()+" "+workItem.getName()+": modify on exit "+function);
}
}
private void enhanceActionNode(RuleFlowProcess process, ActionNode action, String functionGroupCls) {
String name = BlipHelper.getBpmItemRecommendedName(action.getName());
IBlipBPMItem bpmItem = startInfo.getBpmItemForBpmId(name);
if (bpmItem instanceof BPMScriptTask) {
BPMScriptTask script = (BPMScriptTask) bpmItem;
String function = script.getFunction();
if (function == null) {
function = "";
}
action.setAction(new DroolsConsequenceAction("java",
functionGroupCls+"."+function+"(kcontext);"
));
log.debug(process.getId()+" "+action.getName()+": modify action "+function);
}
}
private void enhanceEndNode(RuleFlowProcess process, EndNode endNode) {
String name = BlipHelper.getBpmItemRecommendedName(endNode.getName());
IBlipBPMItem bpmItem = startInfo.getBpmItemForBpmId(name);
if (bpmItem instanceof BPMEndEvent) {
boolean terminateProcess = ((BPMEndEvent)bpmItem).isTerminatesProcess();
endNode.setTerminate(terminateProcess);
log.debug(process.getId()+" "+endNode.getName()+": modify terminate process="+terminateProcess);
}
}
private void modifyWorkParameter(RuleFlowProcess process, HumanTaskNode humantask, String parameter, String value) {
WorkImpl work = (WorkImpl) humantask.getWork();
if (!value.equals(work.getParameter(parameter))) {
work.setParameter(parameter, value);
log.debug(process.getId()+" "+humantask.getName()+": set "+parameter+"="+value);
}
}
private Variable findVariable(List<Variable> variables, String name) {
for (Variable variable : variables) {
if (variable.getName().equals(name)) {
return variable;
}
}
return null;
}
private boolean addVariable(RuleFlowProcess process, List<Variable> variables, String name, DataType dataType) {
if ((dataType != null) && (findVariable(variables, name) == null)) {
Variable variable = new Variable();
variable.setName(name);
variable.setType(dataType);
variables.add(variable);
log.debug(process.getId()+" variables: add "+name);
return true;
}
return false;
}
private boolean addItem(RuleFlowProcess process, String listname, List<String> items, String item, String postfix) {
if ((item != null) && !item.isEmpty() && !items.contains(item)) {
items.add(item+postfix);
log.debug(process.getId()+" "+listname+": add "+item+postfix);
return true;
}
return false;
}
}