diff --git a/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/GlobalHandler.java b/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/GlobalHandler.java index b91a7b931d3..6f66de03e55 100755 --- a/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/GlobalHandler.java +++ b/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/GlobalHandler.java @@ -18,13 +18,18 @@ */ package org.jbpm.bpmn2.xml; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import org.jbpm.compiler.xml.Handler; import org.jbpm.compiler.xml.Parser; import org.jbpm.compiler.xml.core.BaseAbstractHandler; +import org.jbpm.process.core.context.variable.Variable; +import org.jbpm.process.core.context.variable.VariableScope; +import org.jbpm.process.core.datatype.DataTypeResolver; import org.jbpm.workflow.core.impl.WorkflowProcessImpl; import org.kie.api.definition.process.Process; import org.xml.sax.Attributes; @@ -53,6 +58,7 @@ public Object start(final String uri, final String identifier = attrs.getValue("identifier"); final String type = attrs.getValue("type"); + process.addImports(Collections.singleton(type)); emptyAttributeCheck(localName, "identifier", identifier, parser); emptyAttributeCheck(localName, "type", type, parser); @@ -63,6 +69,21 @@ public Object start(final String uri, } map.put(identifier, type); + VariableScope variableScope = (VariableScope) process.getDefaultContext(VariableScope.VARIABLE_SCOPE); + List<Variable> variables = variableScope.getVariables(); + Variable variable = new Variable(); + variable.setId(identifier); + variable.setType(DataTypeResolver.fromType(type, parser.getClassLoader())); + // if name is given use it as variable name instead of id + if (identifier != null && identifier.length() > 0) { + variable.setName(identifier); + variable.setMetaData(identifier, variable.getName()); + } else { + variable.setName(identifier); + } + variable.setMetaData(identifier, variable.getName()); + variables.add(variable); + return null; } diff --git a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/AbstractNodeVisitor.java b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/AbstractNodeVisitor.java index c5cffb2e7b4..e3429dcdbb4 100644 --- a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/AbstractNodeVisitor.java +++ b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/AbstractNodeVisitor.java @@ -20,8 +20,11 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; import java.util.stream.Collectors; +import org.jbpm.process.builder.action.ActionCompilerRegistry; import org.jbpm.process.core.ContextContainer; import org.jbpm.process.core.context.variable.Mappable; import org.jbpm.process.core.context.variable.Variable; @@ -33,6 +36,8 @@ import org.jbpm.workflow.core.impl.ConnectionImpl; import org.jbpm.workflow.core.impl.DataAssociation; import org.jbpm.workflow.core.impl.DataDefinition; +import org.jbpm.workflow.core.impl.DroolsConsequenceAction; +import org.jbpm.workflow.core.impl.ExtendedNodeImpl; import org.jbpm.workflow.core.node.Assignment; import org.jbpm.workflow.core.node.HumanTaskNode; import org.jbpm.workflow.core.node.StartNode; @@ -69,6 +74,7 @@ import static org.jbpm.ruleflow.core.Metadata.HIDDEN; import static org.jbpm.ruleflow.core.factory.NodeFactory.METHOD_DONE; import static org.jbpm.ruleflow.core.factory.NodeFactory.METHOD_NAME; +import static org.kie.kogito.internal.utils.ConversionUtils.sanitizeString; public abstract class AbstractNodeVisitor<T extends Node> extends AbstractVisitor { @@ -79,6 +85,43 @@ public void visitNode(T node, BlockStmt body, VariableScope variableScope, Proce if (isAdHocNode(node) && !(node instanceof HumanTaskNode)) { metadata.addSignal(node.getName(), null); } + if (isExtendedNode(node)) { + ExtendedNodeImpl extendedNodeImpl = (ExtendedNodeImpl) node; + addScript(extendedNodeImpl, body, ON_ACTION_SCRIPT_METHOD, ExtendedNodeImpl.EVENT_NODE_ENTER); + addScript(extendedNodeImpl, body, ON_ACTION_SCRIPT_METHOD, ExtendedNodeImpl.EVENT_NODE_EXIT); + } + } + + private void addScript(ExtendedNodeImpl extendedNodeImpl, BlockStmt body, String factoryMethod, String actionType) { + if (!extendedNodeImpl.hasActions(actionType)) { + return; + } + List<DroolsConsequenceAction> scripts = extendedNodeImpl.getActions(actionType).stream() + .filter(Predicate.not(Objects::isNull)) + .filter(DroolsConsequenceAction.class::isInstance) + .map(DroolsConsequenceAction.class::cast) + .filter(e -> e.getConsequence() != null && !e.getConsequence().isBlank()) + .toList(); + + for (DroolsConsequenceAction script : scripts) { + body.addStatement(getFactoryMethod(getNodeId((T) extendedNodeImpl), factoryMethod, + new StringLiteralExpr(actionType), + new StringLiteralExpr(script.getDialect()), + new StringLiteralExpr(sanitizeString(script.getConsequence())), + buildDroolsConsequenceAction(extendedNodeImpl, script.getDialect(), script.getConsequence()))); + ; + } + } + + private Expression buildDroolsConsequenceAction(ExtendedNodeImpl extendedNodeImpl, String dialect, String script) { + if (script == null) { + return new NullLiteralExpr(); + } + return ActionCompilerRegistry.instance().find(dialect).buildAction(extendedNodeImpl, script); + } + + private boolean isExtendedNode(T node) { + return node instanceof ExtendedNodeImpl; } private boolean isAdHocNode(Node node) { diff --git a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/AbstractVisitor.java b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/AbstractVisitor.java index ce0168cfe0e..17dd3cd9d36 100644 --- a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/AbstractVisitor.java +++ b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/AbstractVisitor.java @@ -54,8 +54,9 @@ public abstract class AbstractVisitor { + protected static final String ON_ACTION_SCRIPT_METHOD = "onActionScript"; protected static final String FACTORY_FIELD_NAME = "factory"; - protected static final String KCONTEXT_VAR = "kcontext"; + public static final String KCONTEXT_VAR = "kcontext"; protected MethodCallExpr getWorkflowElementConstructor(WorkflowElementIdentifier identifier) { Type type = new ClassOrInterfaceType().setName(WorkflowElementIdentifierFactory.class.getName()); diff --git a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/ActionCompiler.java b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/ActionCompiler.java new file mode 100644 index 00000000000..16f476209a3 --- /dev/null +++ b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/ActionCompiler.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.jbpm.process.builder.action; + +import java.util.Arrays; + +import org.jbpm.workflow.core.impl.NodeImpl; + +import com.github.javaparser.ast.expr.Expression; + +public interface ActionCompiler { + + String[] dialects(); + + default boolean accept(String dialect) { + return Arrays.asList(dialect).contains(dialect); + } + + Expression buildAction(NodeImpl nodeImpl, String scrtip); +} diff --git a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/ActionCompilerRegistry.java b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/ActionCompilerRegistry.java new file mode 100644 index 00000000000..9edb3e5d774 --- /dev/null +++ b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/ActionCompilerRegistry.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.jbpm.process.builder.action; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +import org.jbpm.util.JbpmClassLoaderUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ActionCompilerRegistry { + + private static final Logger logger = LoggerFactory.getLogger(ActionCompilerRegistry.class); + + private static ActionCompilerRegistry INSTANCE; + + private List<ActionCompiler> registry; + + public static ActionCompilerRegistry instance() { + if (INSTANCE == null) { + INSTANCE = new ActionCompilerRegistry(); + } + return INSTANCE; + } + + protected ActionCompilerRegistry() { + this.registry = new ArrayList<>(); + ServiceLoader.load(ActionCompiler.class, JbpmClassLoaderUtil.findClassLoader()).forEach(registry::add); + } + + public void register(ActionCompiler actionCompiler) { + this.registry.add(actionCompiler); + logger.debug("Manual registration of scripting language {} with instance {}", List.of(actionCompiler.dialects()), actionCompiler); + } + + public ActionCompiler find(String language) { + for (ActionCompiler transformer : registry) { + if (transformer.accept(language)) { + return transformer; + } + } + throw new IllegalArgumentException("action compiler not support for dialect " + language); + } +} \ No newline at end of file diff --git a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/JavaActionCompiler.java b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/JavaActionCompiler.java new file mode 100644 index 00000000000..f2762126f28 --- /dev/null +++ b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/JavaActionCompiler.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.jbpm.process.builder.action; + +import java.util.HashSet; +import java.util.Set; + +import org.jbpm.compiler.canonical.AbstractNodeVisitor; +import org.jbpm.process.core.context.variable.Variable; +import org.jbpm.process.core.context.variable.VariableScope; +import org.jbpm.workflow.core.impl.NodeImpl; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.Parameter; +import com.github.javaparser.ast.expr.AssignExpr; +import com.github.javaparser.ast.expr.AssignExpr.Operator; +import com.github.javaparser.ast.expr.CastExpr; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.LambdaExpr; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.expr.NameExpr; +import com.github.javaparser.ast.expr.StringLiteralExpr; +import com.github.javaparser.ast.expr.VariableDeclarationExpr; +import com.github.javaparser.ast.stmt.BlockStmt; +import com.github.javaparser.ast.type.ClassOrInterfaceType; +import com.github.javaparser.ast.type.Type; + +public class JavaActionCompiler implements ActionCompiler { + + @Override + public String[] dialects() { + return new String[] { "java" }; + } + + @Override + public boolean accept(String dialect) { + return dialect.toLowerCase().contains("java"); + } + + @Override + public Expression buildAction(NodeImpl nodeImpl, String script) { + BlockStmt newDroolsConsequenceActionExpression = new BlockStmt(); + newDroolsConsequenceActionExpression = StaticJavaParser.parseBlock("{" + script + "}"); + Set<NameExpr> identifiers = new HashSet<>(newDroolsConsequenceActionExpression.findAll(NameExpr.class)); + for (NameExpr identifier : identifiers) { + VariableScope scope = (VariableScope) nodeImpl.resolveContext(VariableScope.VARIABLE_SCOPE, identifier.getNameAsString()); + if (scope == null) { + continue; + } + Variable var = scope.findVariable(identifier.getNameAsString()); + if (var == null) { + continue; + } + Type type = StaticJavaParser.parseType(var.getType().getStringType()); + VariableDeclarationExpr target = new VariableDeclarationExpr(type, var.getName()); + Expression source = new MethodCallExpr(new NameExpr(AbstractNodeVisitor.KCONTEXT_VAR), "getVariable", NodeList.nodeList(new StringLiteralExpr(var.getName()))); + source = new CastExpr(type, source); + AssignExpr assign = new AssignExpr(target, source, Operator.ASSIGN); + newDroolsConsequenceActionExpression.addStatement(0, assign); + } + ClassOrInterfaceType type = StaticJavaParser.parseClassOrInterfaceType(org.kie.kogito.internal.process.runtime.KogitoProcessContext.class.getName()); + return new LambdaExpr(NodeList.nodeList(new Parameter(type, AbstractNodeVisitor.KCONTEXT_VAR)), newDroolsConsequenceActionExpression, true); + } + +} diff --git a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/MVELActionCompiler.java b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/MVELActionCompiler.java new file mode 100644 index 00000000000..83124237aff --- /dev/null +++ b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/action/MVELActionCompiler.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.jbpm.process.builder.action; + +import org.jbpm.compiler.canonical.AbstractNodeVisitor; +import org.jbpm.workflow.core.impl.NodeImpl; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.Parameter; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.LambdaExpr; +import com.github.javaparser.ast.stmt.BlockStmt; +import com.github.javaparser.ast.type.ClassOrInterfaceType; + +import static org.kie.kogito.internal.utils.ConversionUtils.sanitizeString; + +public class MVELActionCompiler implements ActionCompiler { + + @Override + public String[] dialects() { + return new String[] { "mvel" }; + } + + @Override + public Expression buildAction(NodeImpl nodeImpl, String script) { + BlockStmt actionExpression = StaticJavaParser.parseBlock( + "{ org.mvel2.MVEL.eval(\"" + sanitizeString(script) + + "\", new org.jbpm.workflow.instance.impl.NodeInstanceResolverFactory((org.jbpm.workflow.instance.NodeInstance) kcontext.getNodeInstance())); }"); + ClassOrInterfaceType type = StaticJavaParser.parseClassOrInterfaceType(org.kie.kogito.internal.process.runtime.KogitoProcessContext.class.getName()); + return new LambdaExpr(NodeList.nodeList(new Parameter(type, AbstractNodeVisitor.KCONTEXT_VAR)), actionExpression, true); + } + +} diff --git a/jbpm/jbpm-flow-builder/src/main/resources/META-INF/services/org.jbpm.process.builder.action.ActionCompiler b/jbpm/jbpm-flow-builder/src/main/resources/META-INF/services/org.jbpm.process.builder.action.ActionCompiler new file mode 100644 index 00000000000..d9494beeea5 --- /dev/null +++ b/jbpm/jbpm-flow-builder/src/main/resources/META-INF/services/org.jbpm.process.builder.action.ActionCompiler @@ -0,0 +1,2 @@ +org.jbpm.process.builder.action.JavaActionCompiler +org.jbpm.process.builder.action.MVELActionCompiler \ No newline at end of file diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/AbstractCompositeNodeFactory.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/AbstractCompositeNodeFactory.java index 0befff9481e..329534bbedc 100644 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/AbstractCompositeNodeFactory.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/AbstractCompositeNodeFactory.java @@ -109,4 +109,5 @@ public P done() { } return super.done(); } + } diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/ExtendedNodeFactory.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/ExtendedNodeFactory.java index 0653d5f6df6..0a22a65261d 100755 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/ExtendedNodeFactory.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/ExtendedNodeFactory.java @@ -18,14 +18,9 @@ */ package org.jbpm.ruleflow.core.factory; -import java.util.ArrayList; -import java.util.List; - import org.jbpm.ruleflow.core.RuleFlowNodeContainerFactory; -import org.jbpm.workflow.core.DroolsAction; import org.jbpm.workflow.core.Node; import org.jbpm.workflow.core.NodeContainer; -import org.jbpm.workflow.core.impl.DroolsConsequenceAction; import org.jbpm.workflow.core.impl.ExtendedNodeImpl; import org.kie.api.definition.process.WorkflowElementIdentifier; @@ -39,25 +34,4 @@ protected ExtendedNodeImpl getExtendedNode() { return (ExtendedNodeImpl) getNode(); } - public T onEntryAction(String dialect, String action) { - if (getExtendedNode().getActions(dialect) != null) { - getExtendedNode().getActions(dialect).add(new DroolsConsequenceAction(dialect, action)); - } else { - List<DroolsAction> actions = new ArrayList<>(); - actions.add(new DroolsConsequenceAction(dialect, action)); - getExtendedNode().setActions(ExtendedNodeImpl.EVENT_NODE_ENTER, actions); - } - return (T) this; - } - - public T onExitAction(String dialect, String action) { - if (getExtendedNode().getActions(dialect) != null) { - getExtendedNode().getActions(dialect).add(new DroolsConsequenceAction(dialect, action)); - } else { - List<DroolsAction> actions = new ArrayList<>(); - actions.add(new DroolsConsequenceAction(dialect, action)); - getExtendedNode().setActions(ExtendedNodeImpl.EVENT_NODE_EXIT, actions); - } - return (T) this; - } } diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/NodeFactory.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/NodeFactory.java index bcbfb1f475f..ba22a6d53bc 100755 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/NodeFactory.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/NodeFactory.java @@ -18,10 +18,15 @@ */ package org.jbpm.ruleflow.core.factory; +import java.util.ArrayList; + import org.jbpm.process.core.context.variable.Mappable; +import org.jbpm.process.instance.impl.Action; import org.jbpm.ruleflow.core.RuleFlowNodeContainerFactory; import org.jbpm.workflow.core.Node; import org.jbpm.workflow.core.NodeContainer; +import org.jbpm.workflow.core.impl.DroolsConsequenceAction; +import org.jbpm.workflow.core.impl.ExtendedNodeImpl; import org.kie.api.definition.process.WorkflowElementIdentifier; public abstract class NodeFactory<T extends NodeFactory<T, P>, P extends RuleFlowNodeContainerFactory<P, ?>> implements MappableNodeFactory<T> { @@ -71,4 +76,27 @@ public Mappable getMappableNode() { return (Mappable) node; } + public T onEntryAction(String dialect, String action) { + return onActionScript(ExtendedNodeImpl.EVENT_NODE_ENTER, dialect, action, null); + } + + public T onExitAction(String dialect, String action) { + return onActionScript(ExtendedNodeImpl.EVENT_NODE_EXIT, dialect, action, null); + } + + public T onActionScript(String type, String dialect, String script, Action compiledScript) { + DroolsConsequenceAction action = new DroolsConsequenceAction(dialect, script); + if (compiledScript != null) { + action.setMetaData("Action", compiledScript); + } + if (getExtendedNode().getActions(type) == null) { + getExtendedNode().setActions(type, new ArrayList<>()); + } + getExtendedNode().getActions(type).add(action); + return (T) this; + } + + private ExtendedNodeImpl getExtendedNode() { + return (ExtendedNodeImpl) getNode(); + } } diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java index f1bb75950a4..5319367af5d 100755 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java @@ -45,12 +45,10 @@ import org.jbpm.ruleflow.core.Metadata; import org.jbpm.ruleflow.core.RuleFlowProcess; import org.jbpm.workflow.core.Constraint; -import org.jbpm.workflow.core.DroolsAction; import org.jbpm.workflow.core.Node; import org.jbpm.workflow.core.WorkflowProcess; import org.jbpm.workflow.core.impl.DataAssociation; import org.jbpm.workflow.core.impl.DroolsConsequenceAction; -import org.jbpm.workflow.core.impl.ExtendedNodeImpl; import org.jbpm.workflow.core.impl.NodeImpl; import org.jbpm.workflow.core.node.ActionNode; import org.jbpm.workflow.core.node.BoundaryEventNode; @@ -189,7 +187,6 @@ protected void validateNodes(org.kie.api.definition.process.Node[] nodes, errors); } else if (node instanceof RuleSetNode) { final RuleSetNode ruleSetNode = (RuleSetNode) node; - validateOnEntryOnExitScripts(ruleSetNode, errors, process); if (ruleSetNode.getFrom() == null && !acceptsNoIncomingConnections(node)) { addErrorMessage(process, node, @@ -318,7 +315,6 @@ protected void validateNodes(org.kie.api.definition.process.Node[] nodes, } } else if (node instanceof MilestoneNode) { final MilestoneNode milestone = (MilestoneNode) node; - validateOnEntryOnExitScripts(milestone, errors, process); if (milestone.getFrom() == null && !acceptsNoIncomingConnections(node)) { addErrorMessage(process, node, @@ -350,7 +346,6 @@ protected void validateNodes(org.kie.api.definition.process.Node[] nodes, } } else if (node instanceof SubProcessNode) { final SubProcessNode subProcess = (SubProcessNode) node; - validateOnEntryOnExitScripts(subProcess, errors, process); if (subProcess.getFrom() == null && !acceptsNoIncomingConnections(node)) { addErrorMessage(process, node, @@ -429,7 +424,6 @@ protected void validateNodes(org.kie.api.definition.process.Node[] nodes, } } else if (node instanceof WorkItemNode) { final WorkItemNode workItemNode = (WorkItemNode) node; - validateOnEntryOnExitScripts(workItemNode, errors, process); if (workItemNode.getFrom() == null && !acceptsNoIncomingConnections(node)) { addErrorMessage(process, node, @@ -469,7 +463,6 @@ protected void validateNodes(org.kie.api.definition.process.Node[] nodes, } } else if (node instanceof ForEachNode) { final ForEachNode forEachNode = (ForEachNode) node; - validateOnEntryOnExitScripts(forEachNode, errors, process); String variableName = forEachNode.getVariableName(); if (variableName == null || "".equals(variableName)) { addErrorMessage(process, @@ -513,7 +506,6 @@ protected void validateNodes(org.kie.api.definition.process.Node[] nodes, process); } else if (node instanceof DynamicNode) { final DynamicNode dynamicNode = (DynamicNode) node; - validateOnEntryOnExitScripts(dynamicNode, errors, process); if (dynamicNode.getDefaultIncomingConnections().isEmpty() && !acceptsNoIncomingConnections(dynamicNode)) { addErrorMessage(process, @@ -540,7 +532,6 @@ protected void validateNodes(org.kie.api.definition.process.Node[] nodes, process); } else if (node instanceof CompositeNode) { final CompositeNode compositeNode = (CompositeNode) node; - validateOnEntryOnExitScripts(compositeNode, errors, process); for (Map.Entry<String, NodeAndType> inType : compositeNode.getLinkedIncomingNodes().entrySet()) { if (compositeNode.getIncomingConnections(inType.getKey()).isEmpty() && !acceptsNoIncomingConnections(node)) { addErrorMessage(process, @@ -949,20 +940,6 @@ public ProcessValidationError[] validateProcess(Process process) { return validateProcess((RuleFlowProcess) process); } - //TODO To be removed once https://issues.redhat.com/browse/KOGITO-2067 is fixed - private void validateOnEntryOnExitScripts(Node node, List<ProcessValidationError> errors, RuleFlowProcess process) { - if (node instanceof ExtendedNodeImpl) { - List<DroolsAction> actions = ((ExtendedNodeImpl) node).getActions(ExtendedNodeImpl.EVENT_NODE_ENTER); - if (actions != null && !actions.isEmpty()) { - addErrorMessage(process, node, errors, "On Entry Action is not yet supported in Kogito"); - } - actions = ((ExtendedNodeImpl) node).getActions(ExtendedNodeImpl.EVENT_NODE_EXIT); - if (actions != null && !actions.isEmpty()) { - addErrorMessage(process, node, errors, "On Exit Action is not yet supported in Kogito"); - } - } - } - private void validateVariables(List<ProcessValidationError> errors, RuleFlowProcess process) { List<Variable> variables = process.getVariableScope().getVariables(); diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/ExtendedNodeImpl.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/ExtendedNodeImpl.java index 8a41d90e607..a0f4e215830 100755 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/ExtendedNodeImpl.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/ExtendedNodeImpl.java @@ -39,6 +39,10 @@ public void setActions(String type, List<DroolsAction> actions) { this.actions.put(type, actions); } + public boolean hasActions(String type) { + return this.actions.get(type) != null; + } + public List<DroolsAction> getActions(String type) { return this.actions.get(type); } diff --git a/jbpm/jbpm-flow/src/test/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidatorTest.java b/jbpm/jbpm-flow/src/test/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidatorTest.java index f6e52f25e7c..a2d99b7a714 100755 --- a/jbpm/jbpm-flow/src/test/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidatorTest.java +++ b/jbpm/jbpm-flow/src/test/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidatorTest.java @@ -26,26 +26,17 @@ import org.jbpm.process.core.validation.ProcessValidationError; import org.jbpm.ruleflow.core.RuleFlowProcess; import org.jbpm.ruleflow.core.WorkflowElementIdentifierFactory; -import org.jbpm.workflow.core.DroolsAction; import org.jbpm.workflow.core.Node; 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.CompositeNode; import org.jbpm.workflow.core.node.DynamicNode; import org.jbpm.workflow.core.node.EndNode; -import org.jbpm.workflow.core.node.ForEachNode; -import org.jbpm.workflow.core.node.MilestoneNode; -import org.jbpm.workflow.core.node.RuleSetNode; import org.jbpm.workflow.core.node.StartNode; -import org.jbpm.workflow.core.node.SubProcessNode; -import org.jbpm.workflow.core.node.WorkItemNode; -import org.jbpm.workflow.instance.rule.RuleType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.kie.api.definition.process.WorkflowElementIdentifier; -import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -211,32 +202,6 @@ void testCompositeNodeNoStart() { assertThat(errors[0].getMessage()).isEqualTo("Node 'CompositeNode' [3] Composite has no start node defined."); } - //TODO To be removed once https://issues.redhat.com/browse/KOGITO-2067 is fixed - @Test - void testOnEntryOnExitValidation() { - testNodeOnEntryOnExit(new MilestoneNode()); - RuleSetNode ruleSetNode = new RuleSetNode(); - ruleSetNode.setRuleType(mock(RuleType.class)); - testNodeOnEntryOnExit(ruleSetNode); - testNodeOnEntryOnExit(new SubProcessNode()); - testNodeOnEntryOnExit(new WorkItemNode()); - testNodeOnEntryOnExit(new ForEachNode(one)); - testNodeOnEntryOnExit(new DynamicNode()); - testNodeOnEntryOnExit(new CompositeNode()); - } - - private void testNodeOnEntryOnExit(ExtendedNodeImpl node) { - List<ProcessValidationError> errors = new ArrayList<>(); - node.setName("name"); - node.setId(node.getId()); - node.setActions(ExtendedNodeImpl.EVENT_NODE_ENTER, singletonList(new DroolsAction())); - node.setActions(ExtendedNodeImpl.EVENT_NODE_EXIT, singletonList(new DroolsAction())); - validator.validateNodes(new org.kie.api.definition.process.Node[] { node }, errors, process); - assertThat(errors).extracting("message").contains( - "Node 'name' [" + node.getId().toExternalFormat() + "] On Entry Action is not yet supported in Kogito", - "Node 'name' [" + node.getId().toExternalFormat() + "] On Exit Action is not yet supported in Kogito"); - } - @Test void testScriptTaskDialect() { StartNode startNode = new StartNode(); diff --git a/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/ActivityTest.java b/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/ActivityTest.java index 9af1206fb28..319595851d3 100755 --- a/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/ActivityTest.java +++ b/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/ActivityTest.java @@ -34,6 +34,8 @@ import org.jbpm.bpmn2.objects.HelloService; import org.jbpm.bpmn2.objects.Person; import org.jbpm.bpmn2.objects.TestWorkItemHandler; +import org.jbpm.bpmn2.subprocess.SubProcessWithEntryExitScriptsModel; +import org.jbpm.bpmn2.subprocess.SubProcessWithEntryExitScriptsProcess; import org.jbpm.bpmn2.test.RequirePersistence; import org.jbpm.process.builder.ActionBuilder; import org.jbpm.process.builder.AssignmentBuilder; @@ -49,6 +51,8 @@ import org.jbpm.process.instance.impl.demo.DoNothingWorkItemHandler; import org.jbpm.process.instance.impl.demo.SystemOutWorkItemHandler; import org.jbpm.test.util.ProcessCompletedCountDownProcessEventListener; +import org.jbpm.test.utils.EventTrackerProcessListener; +import org.jbpm.test.utils.ProcessTestHelper; import org.jbpm.workflow.core.impl.DataAssociation; import org.jbpm.workflow.core.impl.DataDefinition; import org.jbpm.workflow.core.node.ActionNode; @@ -76,6 +80,7 @@ import org.kie.api.event.rule.MatchCreatedEvent; import org.kie.api.runtime.process.DataTransformer; import org.kie.api.runtime.process.NodeInstance; +import org.kie.kogito.Application; import org.kie.kogito.internal.process.event.DefaultKogitoProcessEventListener; import org.kie.kogito.internal.process.runtime.KogitoNodeInstanceContainer; import org.kie.kogito.internal.process.runtime.KogitoProcessInstance; @@ -83,6 +88,7 @@ import org.kie.kogito.internal.process.runtime.KogitoWorkItem; import org.kie.kogito.internal.process.runtime.KogitoWorkItemManager; import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcessInstance; +import org.kie.kogito.process.ProcessInstance; import org.kie.kogito.process.workitems.InternalKogitoWorkItem; import static org.assertj.core.api.Assertions.assertThat; @@ -426,39 +432,27 @@ public void testCallActivityWithContantsAssignment() throws Exception { } @Test - @Disabled("On Exit not supported, see https://issues.redhat.com/browse/KOGITO-2067") public void testSubProcessWithEntryExitScripts() throws Exception { - kruntime = createKogitoProcessRuntime("subprocess/BPMN2-SubProcessWithEntryExitScripts.bpmn2"); - TestWorkItemHandler handler = new TestWorkItemHandler(); - kruntime.getKogitoWorkItemManager().registerWorkItemHandler("Human Task", handler); - - KogitoProcessInstance processInstance = kruntime.startProcess("com.sample.bpmn.hello"); - - assertNodeTriggered(processInstance.getStringId(), "Task1"); - Object var1 = getProcessVarValue(processInstance, "var1"); - assertThat(var1).isNotNull().hasToString("10"); - - assertNodeTriggered(processInstance.getStringId(), "Task2"); - Object var2 = getProcessVarValue(processInstance, "var2"); - assertThat(var2).isNotNull().hasToString("20"); - - assertNodeTriggered(processInstance.getStringId(), "Task3"); - Object var3 = getProcessVarValue(processInstance, "var3"); - assertThat(var3).isNotNull().hasToString("30"); - - assertNodeTriggered(processInstance.getStringId(), "SubProcess"); - Object var4 = getProcessVarValue(processInstance, "var4"); - assertThat(var4).isNotNull().hasToString("40"); - - Object var5 = getProcessVarValue(processInstance, "var5"); - assertThat(var5).isNotNull().hasToString("50"); - - org.kie.kogito.internal.process.runtime.KogitoWorkItem workItem = handler.getWorkItem(); - assertThat(workItem).isNotNull(); - - kruntime.getKogitoWorkItemManager().completeWorkItem(workItem.getStringId(), null); - - assertProcessInstanceCompleted(processInstance); + Application app = ProcessTestHelper.newApplication(); + EventTrackerProcessListener listener = new EventTrackerProcessListener(); + ProcessTestHelper.registerProcessEventListener(app, listener); + + org.kie.kogito.process.Process<SubProcessWithEntryExitScriptsModel> process = SubProcessWithEntryExitScriptsProcess.newProcess(app); + ProcessInstance<SubProcessWithEntryExitScriptsModel> processInstance = process.createInstance(process.createModel()); + processInstance.start(); + + assertThat(listener.tracked()).anyMatch(ProcessTestHelper.triggered("Task1")); + assertThat(processInstance.variables().getVar1()).isNotNull().hasToString("10"); + assertThat(listener.tracked()).anyMatch(ProcessTestHelper.triggered("Task2")); + assertThat(processInstance.variables().getVar2()).isNotNull().hasToString("20"); + assertThat(listener.tracked()).anyMatch(ProcessTestHelper.triggered("Task3")); + assertThat(processInstance.variables().getVar3()).isNotNull().hasToString("30"); + assertThat(listener.tracked()).anyMatch(ProcessTestHelper.triggered("SubProcess")); + assertThat(processInstance.variables().getVar4()).isNotNull().hasToString("40"); + assertThat(processInstance.variables().getVar5()).isNotNull().hasToString("50"); + + ProcessTestHelper.completeWorkItem(processInstance, "john", Collections.emptyMap()); + assertThat(processInstance).extracting(ProcessInstance::status).isEqualTo(ProcessInstance.STATE_COMPLETED); } @Test diff --git a/jbpm/jbpm-tests/src/test/java/org/jbpm/test/utils/EventTrackerProcessListener.java b/jbpm/jbpm-tests/src/test/java/org/jbpm/test/utils/EventTrackerProcessListener.java new file mode 100644 index 00000000000..e88492b1253 --- /dev/null +++ b/jbpm/jbpm-tests/src/test/java/org/jbpm/test/utils/EventTrackerProcessListener.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.jbpm.test.utils; + +import java.util.ArrayList; +import java.util.List; + +import org.kie.api.event.process.ProcessNodeEvent; +import org.kie.api.event.process.ProcessNodeLeftEvent; +import org.kie.api.event.process.ProcessNodeTriggeredEvent; +import org.kie.kogito.internal.process.event.DefaultKogitoProcessEventListener; + +public class EventTrackerProcessListener extends DefaultKogitoProcessEventListener { + + List<ProcessNodeEvent> nodeEvents; + + public EventTrackerProcessListener() { + this.nodeEvents = new ArrayList<>(); + } + + @Override + public void afterNodeTriggered(ProcessNodeTriggeredEvent event) { + nodeEvents.add(event); + } + + @Override + public void afterNodeLeft(ProcessNodeLeftEvent event) { + nodeEvents.add(event); + } + + public List<ProcessNodeEvent> tracked() { + return nodeEvents; + } +} diff --git a/jbpm/jbpm-tests/src/test/java/org/jbpm/test/utils/ProcessTestHelper.java b/jbpm/jbpm-tests/src/test/java/org/jbpm/test/utils/ProcessTestHelper.java index d191f061931..6c1d8ac54a0 100644 --- a/jbpm/jbpm-tests/src/test/java/org/jbpm/test/utils/ProcessTestHelper.java +++ b/jbpm/jbpm-tests/src/test/java/org/jbpm/test/utils/ProcessTestHelper.java @@ -21,7 +21,10 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.function.Predicate; +import org.kie.api.event.process.ProcessNodeEvent; +import org.kie.api.event.process.ProcessNodeTriggeredEvent; import org.kie.kogito.Application; import org.kie.kogito.Model; import org.kie.kogito.StaticApplication; @@ -76,4 +79,10 @@ public static WorkItem findWorkItem(ProcessInstance<? extends Model> processInst return workItems.stream().findFirst().get(); } + public static <T extends ProcessNodeEvent> Predicate<T> triggered(String nodeName) { + return e -> { + return e instanceof ProcessNodeTriggeredEvent && nodeName.equals(((ProcessNodeTriggeredEvent) e).getNodeInstance().getNodeName()); + }; + } + } diff --git a/jbpm/jbpm-tools/jbpm-tools-maven-plugin/src/test/resources/unit/project/src/main/bpmn/BPMN2-SubProcessWithEntryExitScripts.bpmn2 b/jbpm/jbpm-tools/jbpm-tools-maven-plugin/src/test/resources/unit/project/src/main/bpmn/BPMN2-SubProcessWithEntryExitScripts.bpmn2 new file mode 100755 index 00000000000..3dce9dac8c6 --- /dev/null +++ b/jbpm/jbpm-tools/jbpm-tools-maven-plugin/src/test/resources/unit/project/src/main/bpmn/BPMN2-SubProcessWithEntryExitScripts.bpmn2 @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="UTF-8"?> +<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.jboss.org/drools" xmlns="http://www.jboss.org/drools" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd http://www.jboss.org/drools drools.xsd http://www.bpsim.org/schemas/1.0 bpsim.xsd" id="Definition" expressionLanguage="http://www.mvel.org/2.0" targetNamespace="http://www.jboss.org/drools" typeLanguage="http://www.java.com/javaTypes"> + <bpmn2:itemDefinition id="_String" structureRef="String"/> + <bpmn2:itemDefinition id="_Integer" structureRef="Integer"/> + <bpmn2:process id="SubProcessWithEntryExitScripts" tns:version="1" tns:packageName="org.jbpm.bpmn2.subprocess" name="Hello World" isExecutable="true" processType="Private"> + <bpmn2:extensionElements/> + <bpmn2:property id="var2" itemSubjectRef="_Integer"/> + <bpmn2:property id="var3" itemSubjectRef="_Integer"/> + <bpmn2:property id="var1" itemSubjectRef="_Integer"/> + <bpmn2:property id="var4" itemSubjectRef="_Integer"/> + <bpmn2:property id="var5" itemSubjectRef="_Integer"/> + <bpmn2:scriptTask id="_2" name="Task1" scriptFormat="http://www.java.com/java"> + <bpmn2:incoming>_1-_2</bpmn2:incoming> + <bpmn2:outgoing>SequenceFlow_11</bpmn2:outgoing> + <bpmn2:script>System.err.println("Task 1, var1 = 10"); +kcontext.setVariable("var1", new Integer(10)); +</bpmn2:script> + </bpmn2:scriptTask> + <bpmn2:startEvent id="_1" name=""> + <bpmn2:outgoing>_1-_2</bpmn2:outgoing> + </bpmn2:startEvent> + <bpmn2:sequenceFlow id="_1-_2" tns:priority="1" sourceRef="_1" targetRef="_2"/> + <bpmn2:scriptTask id="ScriptTask_2" name="Task3"> + <bpmn2:incoming>SequenceFlow_12</bpmn2:incoming> + <bpmn2:outgoing>SequenceFlow_7</bpmn2:outgoing> + <bpmn2:script>System.err.println("Task 3, var3 = 30"); +kcontext.setVariable("var3",30); +</bpmn2:script> + </bpmn2:scriptTask> + <bpmn2:sequenceFlow id="SequenceFlow_7" tns:priority="1" name="" sourceRef="ScriptTask_2" targetRef="UserTask_2"/> + <bpmn2:endEvent id="_3" name=""> + <bpmn2:incoming>SequenceFlow_8</bpmn2:incoming> + <bpmn2:terminateEventDefinition id="TerminateEventDefinition_1"/> + </bpmn2:endEvent> + <bpmn2:userTask id="UserTask_2" name="User Task 2"> + <bpmn2:incoming>SequenceFlow_7</bpmn2:incoming> + <bpmn2:outgoing>SequenceFlow_8</bpmn2:outgoing> + <bpmn2:ioSpecification id="_InputOutputSpecification_24"> + <bpmn2:dataInput id="_DataInput_150" name="TaskName"/> + <bpmn2:dataInput id="_DataInput_151" name="Priority"/> + <bpmn2:dataInput id="_DataInput_152" name="Comment"/> + <bpmn2:dataInput id="_DataInput_153" name="GroupId"/> + <bpmn2:dataInput id="_DataInput_154" name="Skippable"/> + <bpmn2:dataInput id="_DataInput_155" name="Content"/> + <bpmn2:dataInput id="_DataInput_156" name="Locale"/> + <bpmn2:inputSet id="_InputSet_24" name="New Input Set"> + <bpmn2:dataInputRefs>_DataInput_150</bpmn2:dataInputRefs> + <bpmn2:dataInputRefs>_DataInput_151</bpmn2:dataInputRefs> + <bpmn2:dataInputRefs>_DataInput_152</bpmn2:dataInputRefs> + <bpmn2:dataInputRefs>_DataInput_153</bpmn2:dataInputRefs> + <bpmn2:dataInputRefs>_DataInput_154</bpmn2:dataInputRefs> + <bpmn2:dataInputRefs>_DataInput_155</bpmn2:dataInputRefs> + <bpmn2:dataInputRefs>_DataInput_156</bpmn2:dataInputRefs> + </bpmn2:inputSet> + <bpmn2:outputSet id="_OutputSet_21" name="Output Set"/> + </bpmn2:ioSpecification> + <bpmn2:dataInputAssociation id="_DataInputAssociation_150"> + <bpmn2:targetRef>_DataInput_150</bpmn2:targetRef> + </bpmn2:dataInputAssociation> + <bpmn2:dataInputAssociation id="_DataInputAssociation_151"> + <bpmn2:targetRef>_DataInput_151</bpmn2:targetRef> + </bpmn2:dataInputAssociation> + <bpmn2:dataInputAssociation id="_DataInputAssociation_152"> + <bpmn2:targetRef>_DataInput_152</bpmn2:targetRef> + </bpmn2:dataInputAssociation> + <bpmn2:dataInputAssociation id="_DataInputAssociation_153"> + <bpmn2:targetRef>_DataInput_153</bpmn2:targetRef> + </bpmn2:dataInputAssociation> + <bpmn2:dataInputAssociation id="_DataInputAssociation_154"> + <bpmn2:targetRef>_DataInput_154</bpmn2:targetRef> + </bpmn2:dataInputAssociation> + <bpmn2:dataInputAssociation id="_DataInputAssociation_155"> + <bpmn2:targetRef>_DataInput_155</bpmn2:targetRef> + </bpmn2:dataInputAssociation> + <bpmn2:dataInputAssociation id="_DataInputAssociation_156"> + <bpmn2:targetRef>_DataInput_156</bpmn2:targetRef> + </bpmn2:dataInputAssociation> + </bpmn2:userTask> + <bpmn2:sequenceFlow id="SequenceFlow_8" tns:priority="1" name="" sourceRef="UserTask_2" targetRef="_3"/> + <bpmn2:subProcess id="SubProcess_1" name="SubProcess"> + <bpmn2:extensionElements> + <tns:onEntry-script scriptFormat="http://www.java.com/java"> + <tns:script>System.err.println("SubProcess, var4 = 40"); +kcontext.setVariable("var4",40); +</tns:script> + </tns:onEntry-script> + <tns:onExit-script scriptFormat="http://www.java.com/java"> + <tns:script>System.err.println("SubProcess, var5 = 50"); +kcontext.setVariable("var5",50); +</tns:script> + </tns:onExit-script> + </bpmn2:extensionElements> + <bpmn2:incoming>SequenceFlow_11</bpmn2:incoming> + <bpmn2:outgoing>SequenceFlow_12</bpmn2:outgoing> + <bpmn2:startEvent id="StartEvent_1" name=""> + <bpmn2:outgoing>SequenceFlow_10</bpmn2:outgoing> + </bpmn2:startEvent> + <bpmn2:sequenceFlow id="SequenceFlow_10" tns:priority="1" name="" sourceRef="StartEvent_1" targetRef="ScriptTask_1"/> + <bpmn2:scriptTask id="ScriptTask_1" name="Task2" scriptFormat="http://www.java.com/java"> + <bpmn2:incoming>SequenceFlow_10</bpmn2:incoming> + <bpmn2:outgoing>SequenceFlow_9</bpmn2:outgoing> + <bpmn2:script>System.err.println("Task 2, var2 = 20"); +kcontext.setVariable("var2",20); +</bpmn2:script> + </bpmn2:scriptTask> + <bpmn2:sequenceFlow id="SequenceFlow_9" tns:priority="1" sourceRef="ScriptTask_1" targetRef="EndEvent_1"/> + <bpmn2:endEvent id="EndEvent_1" name=""> + <bpmn2:incoming>SequenceFlow_9</bpmn2:incoming> + </bpmn2:endEvent> + </bpmn2:subProcess> + <bpmn2:sequenceFlow id="SequenceFlow_11" tns:priority="1" name="" sourceRef="_2" targetRef="SubProcess_1"/> + <bpmn2:sequenceFlow id="SequenceFlow_12" tns:priority="1" name="" sourceRef="SubProcess_1" targetRef="ScriptTask_2"/> + </bpmn2:process> + <bpmndi:BPMNDiagram id="BPMNDiagram_1"> + <bpmndi:BPMNPlane id="BPMNPlane_Process_1" bpmnElement="com.sample.bpmn.hello"> + <bpmndi:BPMNShape id="BPMNShape_SubProcess_1" bpmnElement="SubProcess_1" isExpanded="true"> + <dc:Bounds height="111.0" width="274.0" x="290.0" y="6.0"/> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="BPMNShape_StartEvent_1" bpmnElement="_1"> + <dc:Bounds height="36.0" width="36.0" x="40.0" y="43.0"/> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="BPMNShape_EndEvent_1" bpmnElement="_3"> + <dc:Bounds height="36.0" width="36.0" x="990.0" y="43.0"/> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="BPMNShape_ScriptTask_1" bpmnElement="_2"> + <dc:Bounds height="48.0" width="80.0" x="120.0" y="39.0"/> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="BPMNShape_ScriptTask_3" bpmnElement="ScriptTask_2"> + <dc:Bounds height="50.0" width="110.0" x="630.0" y="35.0"/> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="BPMNShape_UserTask_2" bpmnElement="UserTask_2"> + <dc:Bounds height="50.0" width="110.0" x="820.0" y="35.0"/> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="BPMNShape_EndEvent_2" bpmnElement="EndEvent_1"> + <dc:Bounds height="36.0" width="36.0" x="508.0" y="44.0"/> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"> + <dc:Bounds height="36.0" width="36.0" x="310.0" y="44.0"/> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="BPMNShape_ScriptTask_2" bpmnElement="ScriptTask_1"> + <dc:Bounds height="50.0" width="110.0" x="373.0" y="37.0"/> + </bpmndi:BPMNShape> + <bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_1" bpmnElement="_1-_2"> + <di:waypoint xsi:type="dc:Point" x="76.0" y="61.0"/> + <di:waypoint xsi:type="dc:Point" x="120.0" y="63.0"/> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_8" bpmnElement="SequenceFlow_7" sourceElement="BPMNShape_ScriptTask_3" targetElement="BPMNShape_UserTask_2"> + <di:waypoint xsi:type="dc:Point" x="740.0" y="60.0"/> + <di:waypoint xsi:type="dc:Point" x="820.0" y="60.0"/> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_9" bpmnElement="SequenceFlow_8" sourceElement="BPMNShape_UserTask_2" targetElement="BPMNShape_EndEvent_1"> + <di:waypoint xsi:type="dc:Point" x="930.0" y="60.0"/> + <di:waypoint xsi:type="dc:Point" x="990.0" y="61.0"/> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_10" bpmnElement="SequenceFlow_9" sourceElement="BPMNShape_ScriptTask_2" targetElement="BPMNShape_EndEvent_2"> + <di:waypoint xsi:type="dc:Point" x="483.0" y="62.0"/> + <di:waypoint xsi:type="dc:Point" x="494.0" y="62.0"/> + <di:waypoint xsi:type="dc:Point" x="494.0" y="62.0"/> + <di:waypoint xsi:type="dc:Point" x="508.0" y="62.0"/> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_11" bpmnElement="SequenceFlow_10" sourceElement="BPMNShape_StartEvent_2" targetElement="BPMNShape_ScriptTask_2"> + <di:waypoint xsi:type="dc:Point" x="346.0" y="62.0"/> + <di:waypoint xsi:type="dc:Point" x="358.0" y="62.0"/> + <di:waypoint xsi:type="dc:Point" x="358.0" y="62.0"/> + <di:waypoint xsi:type="dc:Point" x="373.0" y="62.0"/> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_12" bpmnElement="SequenceFlow_11" sourceElement="BPMNShape_ScriptTask_1" targetElement="BPMNShape_SubProcess_1"> + <di:waypoint xsi:type="dc:Point" x="200.0" y="63.0"/> + <di:waypoint xsi:type="dc:Point" x="290.0" y="61.0"/> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_13" bpmnElement="SequenceFlow_12" sourceElement="BPMNShape_SubProcess_1" targetElement="BPMNShape_ScriptTask_3"> + <di:waypoint xsi:type="dc:Point" x="564.0" y="61.0"/> + <di:waypoint xsi:type="dc:Point" x="630.0" y="60.0"/> + </bpmndi:BPMNEdge> + </bpmndi:BPMNPlane> + </bpmndi:BPMNDiagram> +</bpmn2:definitions> \ No newline at end of file