org.jbpm.workflow.instance.node.DynamicNodeInstance Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates.
*
* 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.jbpm.workflow.instance.node;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.drools.core.common.InternalAgenda;
import org.drools.core.definitions.rule.impl.RuleImpl;
import org.drools.core.spi.Activation;
import org.drools.mvel.MVELSafeHelper;
import org.jbpm.workflow.core.impl.ExtendedNodeImpl;
import org.jbpm.workflow.core.impl.NodeImpl;
import org.jbpm.workflow.core.node.DynamicNode;
import org.jbpm.workflow.instance.impl.NodeInstanceResolverFactory;
import org.kie.api.definition.process.Node;
import org.kie.api.event.rule.AfterMatchFiredEvent;
import org.kie.api.event.rule.AgendaEventListener;
import org.kie.api.event.rule.AgendaGroupPoppedEvent;
import org.kie.api.event.rule.AgendaGroupPushedEvent;
import org.kie.api.event.rule.BeforeMatchFiredEvent;
import org.kie.api.event.rule.MatchCancelledEvent;
import org.kie.api.event.rule.MatchCreatedEvent;
import org.kie.api.event.rule.RuleFlowGroupActivatedEvent;
import org.kie.api.event.rule.RuleFlowGroupDeactivatedEvent;
import org.kie.api.runtime.process.NodeInstance;
import org.kie.api.runtime.rule.Match;
public class DynamicNodeInstance extends CompositeContextNodeInstance implements AgendaEventListener {
private static final long serialVersionUID = 510l;
private String getRuleFlowGroupName() {
return getNodeName();
}
protected DynamicNode getDynamicNode() {
return (DynamicNode) getNode();
}
@Override
public String getNodeName() {
return resolveVariable(super.getNodeName());
}
@Override
public void internalTrigger(NodeInstance from, String type) {
triggerEvent(ExtendedNodeImpl.EVENT_NODE_ENTER);
this.registerBoundaryEvents();
// if node instance was cancelled, abort
if (getNodeInstanceContainer().getNodeInstance(getId()) == null) {
return;
}
InternalAgenda agenda = (InternalAgenda) getProcessInstance().getKnowledgeRuntime().getAgenda();
String ruleFlowGroup = getRuleFlowGroupName();
if (ruleFlowGroup != null && !agenda.getRuleFlowGroup(ruleFlowGroup).isActive()) {
agenda.getRuleFlowGroup(ruleFlowGroup).setAutoDeactivate(false);
agenda.activateRuleFlowGroup(ruleFlowGroup, getProcessInstance().getId(), getUniqueId());
}
// if (getDynamicNode().isAutoComplete() && getNodeInstances(false).isEmpty()) {
// triggerCompleted(NodeImpl.CONNECTION_DEFAULT_TYPE);
// }
String rule = "RuleFlow-AdHocComplete-" + getProcessInstance().getProcessId() + "-" + getDynamicNode().getUniqueId();
boolean isActive = ((InternalAgenda) getProcessInstance().getKnowledgeRuntime().getAgenda())
.isRuleActiveInRuleFlowGroup(getRuleFlowGroupName(), rule, getProcessInstance().getId());
if (isActive) {
triggerCompleted();
} else {
addActivationListener();
}
// activate ad hoc fragments if they are marked as such
List autoStartNodes = getDynamicNode().getAutoStartNodes();
autoStartNodes
.forEach(austoStartNode -> triggerSelectedNode(austoStartNode, null));
}
public void addEventListeners() {
super.addEventListeners();
addActivationListener();
}
public void removeEventListeners() {
super.removeEventListeners();
getProcessInstance().getKnowledgeRuntime().removeEventListener(this);
getProcessInstance().removeEventListener(getActivationEventType(), this, true);
}
private void addActivationListener() {
getProcessInstance().getKnowledgeRuntime().addEventListener(this);
getProcessInstance().addEventListener(getActivationEventType(), this, true);
}
private String getActivationEventType() {
return "RuleFlow-AdHocComplete-" + getProcessInstance().getProcessId()
+ "-" + getDynamicNode().getUniqueId();
}
public void nodeInstanceCompleted(org.jbpm.workflow.instance.NodeInstance nodeInstance, String outType) {
Node nodeInstanceNode = nodeInstance.getNode();
if( nodeInstanceNode != null ) {
Object compensationBoolObj = nodeInstanceNode.getMetaData().get("isForCompensation");
boolean isForCompensation = compensationBoolObj == null ? false : ((Boolean) compensationBoolObj);
if( isForCompensation ) {
return;
}
}
String completionCondition = getDynamicNode().getCompletionExpression();
// TODO what if we reach the end of one branch but others might still need to be created ?
// TODO are we sure there will always be node instances left if we are not done yet?
if (isTerminated(nodeInstance)) {
triggerCompleted(NodeImpl.CONNECTION_DEFAULT_TYPE);
} else if (getDynamicNode().isAutoComplete() && getNodeInstances(false).isEmpty()) {
triggerCompleted(NodeImpl.CONNECTION_DEFAULT_TYPE);
} else if (completionCondition != null && "mvel".equals(getDynamicNode().getLanguage())) {
Object value = MVELSafeHelper.getEvaluator().eval(completionCondition, new NodeInstanceResolverFactory(this));
if ( !(value instanceof Boolean) ) {
throw new RuntimeException( "Completion condition expression must return boolean values: " + value
+ " for expression " + completionCondition);
}
if (((Boolean) value).booleanValue()) {
triggerCompleted(NodeImpl.CONNECTION_DEFAULT_TYPE);
}
}
}
public void triggerCompleted(String outType) {
((InternalAgenda) getProcessInstance().getKnowledgeRuntime().getAgenda())
.deactivateRuleFlowGroup(getRuleFlowGroupName());
super.triggerCompleted(outType);
}
@Override
public void signalEvent(String type, Object event) {
if (type.startsWith("RuleFlow-AdHocActivate")) {
if (event instanceof MatchCreatedEvent) {
Match match = ((MatchCreatedEvent) event).getMatch();
match.getDeclarationIds().forEach(s -> this.setVariable(s.replaceFirst("\\$", ""), match.getDeclarationValue(s)));
}
trigger(null, org.jbpm.workflow.core.Node.CONNECTION_DEFAULT_TYPE);
} else if (getActivationEventType().equals(type)) {
if (event instanceof MatchCreatedEvent) {
matchCreated((MatchCreatedEvent) event);
}
} else {
super.signalEvent(type, event);
for (Node node: getCompositeNode().getNodes()) {
if (type.equals(resolveVariable(node.getName())) && node.getIncomingConnections().isEmpty()) {
triggerSelectedNode(node, event);
}
}
}
}
protected boolean isTerminated(NodeInstance from) {
if (from instanceof EndNodeInstance) {
return ((EndNodeInstance) from).getEndNode().isTerminate();
}
return false;
}
@SuppressWarnings("unchecked")
protected void triggerSelectedNode(Node node, Object event) {
NodeInstance nodeInstance = getNodeInstance(node);
if (event != null) {
Map dynamicParams = new HashMap<>();
if (event instanceof Map) {
dynamicParams.putAll((Map) event);
} else {
dynamicParams.put("Data", event);
}
((org.jbpm.workflow.instance.NodeInstance) nodeInstance).setDynamicParameters(dynamicParams);
}
((org.jbpm.workflow.instance.NodeInstance) nodeInstance).trigger(null, NodeImpl.CONNECTION_DEFAULT_TYPE);
}
public void matchCreated(MatchCreatedEvent event) {
// check whether this activation is from the DROOLS_SYSTEM agenda group
String ruleFlowGroup = ((RuleImpl) event.getMatch().getRule()).getRuleFlowGroup();
if ("DROOLS_SYSTEM".equals(ruleFlowGroup)) {
// new activations of the rule associate with a milestone node
// trigger node instances of that milestone node
String ruleName = event.getMatch().getRule().getName();
String milestoneName = "RuleFlow-AdHocComplete-" + getProcessInstance().getProcessId() + "-" + getNodeId();
if (milestoneName.equals(ruleName) && checkProcessInstance((Activation) event.getMatch()) && checkDeclarationMatch(event.getMatch(), (String) getVariable("MatchVariable"))) {
synchronized(getProcessInstance()) {
DynamicNodeInstance.this.removeEventListeners();
DynamicNodeInstance.this.triggerCompleted(NodeImpl.CONNECTION_DEFAULT_TYPE);
}
}
}
}
public void matchCancelled(MatchCancelledEvent event) {
// Do nothing
}
public void afterMatchFired(AfterMatchFiredEvent event) {
// Do nothing
}
public void agendaGroupPopped(AgendaGroupPoppedEvent event) {
// Do nothing
}
public void agendaGroupPushed(AgendaGroupPushedEvent event) {
// Do nothing
}
public void beforeMatchFired(BeforeMatchFiredEvent event) {
// Do nothing
}
public void afterRuleFlowGroupActivated(RuleFlowGroupActivatedEvent event) {
}
public void afterRuleFlowGroupDeactivated(RuleFlowGroupDeactivatedEvent event) {
}
public void beforeRuleFlowGroupActivated(RuleFlowGroupActivatedEvent event) {
}
public void beforeRuleFlowGroupDeactivated(RuleFlowGroupDeactivatedEvent event) {
}
}