org.jbpm.graph.node.ProcessState Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jbpm.graph.node;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Element;
import org.dom4j.tree.DefaultElement;
import org.jbpm.JbpmConfiguration.Configs;
import org.jbpm.JbpmContext;
import org.jbpm.context.def.VariableAccess;
import org.jbpm.context.exe.ContextInstance;
import org.jbpm.graph.def.Event;
import org.jbpm.graph.def.Node;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;
import org.jbpm.graph.log.ProcessStateLog;
import org.jbpm.job.SignalTokenJob;
import org.jbpm.jpdl.JpdlException;
import org.jbpm.jpdl.el.impl.JbpmExpressionEvaluator;
import org.jbpm.jpdl.xml.JpdlXmlReader;
import org.jbpm.msg.MessageService;
import org.jbpm.util.Clock;
public class ProcessState extends Node {
private static final long serialVersionUID = 1L;
private static SubProcessResolver defaultSubProcessResolver;
/** @deprecated set configuration entry jbpm.sub.process.resolver
instead */
public static void setDefaultSubProcessResolver(SubProcessResolver subProcessResolver) {
defaultSubProcessResolver = subProcessResolver;
}
public static SubProcessResolver getSubProcessResolver() {
return defaultSubProcessResolver != null ? defaultSubProcessResolver
: (SubProcessResolver) Configs.getObject("jbpm.sub.process.resolver");
}
protected ProcessDefinition subProcessDefinition;
protected Set variableAccesses;
protected String subProcessName;
public ProcessState() {
}
public ProcessState(String name) {
super(name);
}
// event types //////////////////////////////////////////////////////////////
private static final String[] EVENT_TYPES = {
Event.EVENTTYPE_SUBPROCESS_CREATED,
Event.EVENTTYPE_SUBPROCESS_END,
Event.EVENTTYPE_NODE_ENTER,
Event.EVENTTYPE_NODE_LEAVE,
Event.EVENTTYPE_BEFORE_SIGNAL,
Event.EVENTTYPE_AFTER_SIGNAL
};
/**
* @deprecated arrays are mutable and thus vulnerable to external manipulation. use
* {@link #getSupportedEventTypes()} instead
*/
public static final String[] supportedEventTypes = (String[]) EVENT_TYPES.clone();
public String[] getSupportedEventTypes() {
return (String[]) EVENT_TYPES.clone();
}
// xml //////////////////////////////////////////////////////////////////////
public void read(Element processStateElement, JpdlXmlReader jpdlReader) {
Element subProcessElement = processStateElement.element("sub-process");
if (subProcessElement != null) {
String binding = subProcessElement.attributeValue("binding");
if ("late".equalsIgnoreCase(binding)) {
subProcessName = subProcessElement.attributeValue("name");
if (log.isDebugEnabled()) {
log.debug(this + " will be late bound to process definition: " + subProcessName);
}
}
else {
subProcessDefinition = resolveSubProcess(subProcessElement, jpdlReader);
}
}
variableAccesses = new HashSet(jpdlReader.readVariableAccesses(processStateElement));
}
private ProcessDefinition resolveSubProcess(Element subProcessElement,
JpdlXmlReader jpdlReader) {
SubProcessResolver subProcessResolver = getSubProcessResolver();
try {
ProcessDefinition subProcess = subProcessResolver.findSubProcess(subProcessElement);
if (subProcess != null) {
if (log.isDebugEnabled()) log.debug("bound " + this + " to " + subProcess);
return subProcess;
}
}
catch (JpdlException e) {
jpdlReader.addError(e.getMessage());
}
// check whether this is a recursive process invocation
String subProcessName = subProcessElement.attributeValue("name");
if (subProcessName != null && subProcessName.equals(processDefinition.getName())) {
if (log.isDebugEnabled()) {
log.debug("bound " + this + " to its own " + processDefinition);
}
return processDefinition;
}
return null;
}
public void execute(ExecutionContext executionContext) {
Token superProcessToken = executionContext.getToken();
ProcessDefinition usedSubProcessDefinition = subProcessDefinition;
// if this process has late binding
if (subProcessDefinition == null && subProcessName != null) {
Element subProcessElement = new DefaultElement("sub-process");
subProcessElement.addAttribute("name", (String) JbpmExpressionEvaluator
.evaluate(subProcessName, executionContext, String.class));
SubProcessResolver subProcessResolver = getSubProcessResolver();
usedSubProcessDefinition = subProcessResolver.findSubProcess(subProcessElement);
}
// create the subprocess
ProcessInstance subProcessInstance = superProcessToken
.createSubProcessInstance(usedSubProcessDefinition);
// fire the subprocess created event
fireEvent(Event.EVENTTYPE_SUBPROCESS_CREATED, executionContext);
// feed the readable variableInstances
if (variableAccesses != null && !variableAccesses.isEmpty()) {
ContextInstance superContextInstance = executionContext.getContextInstance();
ContextInstance subContextInstance = subProcessInstance.getContextInstance();
subContextInstance.setTransientVariables(superContextInstance.getTransientVariables());
// loop over all the variable accesses
for (Iterator iter = variableAccesses.iterator(); iter.hasNext();) {
VariableAccess variableAccess = (VariableAccess) iter.next();
// if this variable access is readable
if (variableAccess.isReadable()) {
// the variable is copied from the super process variable name
// to the sub process mapped name
String variableName = variableAccess.getVariableName();
Object value = superContextInstance.getVariable(variableName, superProcessToken);
if (value != null) {
String mappedName = variableAccess.getMappedName();
if (log.isDebugEnabled()) {
log.debug(superProcessToken + " reads '" + variableName + "' into '" + mappedName
+ '\'');
}
subContextInstance.setVariable(mappedName, value);
}
}
}
}
// send the signal to start the subprocess
JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
MessageService messageService;
if (jbpmContext != null && Configs.getBoolean("jbpm.sub.process.async")
&& (messageService = jbpmContext.getServices().getMessageService()) != null) {
// signal sub-process token asynchronously to clearly denote transactional boundaries
// https://jira.jboss.org/browse/JBPM-2948
SignalTokenJob job = new SignalTokenJob(subProcessInstance.getRootToken());
job.setDueDate(new Date());
messageService.send(job);
}
else {
// message service unavailable, signal sub-process synchronously
subProcessInstance.signal();
}
}
public void leave(ExecutionContext executionContext, Transition transition) {
ProcessInstance subProcessInstance = executionContext.getSubProcessInstance();
Token superProcessToken = subProcessInstance.getSuperProcessToken();
// feed the readable variableInstances
if (variableAccesses != null && !variableAccesses.isEmpty()) {
ContextInstance superContextInstance = executionContext.getContextInstance();
ContextInstance subContextInstance = subProcessInstance.getContextInstance();
// loop over all the variable accesses
for (Iterator iter = variableAccesses.iterator(); iter.hasNext();) {
VariableAccess variableAccess = (VariableAccess) iter.next();
// if this variable access is writable
if (variableAccess.isWritable()) {
// the variable is copied from the sub process mapped name
// to the super process variable name
String mappedName = variableAccess.getMappedName();
Object value = subContextInstance.getVariable(mappedName);
if (value != null) {
String variableName = variableAccess.getVariableName();
if (log.isDebugEnabled()) {
log.debug(superProcessToken + " writes '" + variableName + "' from '"
+ mappedName + '\'');
}
superContextInstance.setVariable(variableName, value, superProcessToken);
}
}
}
}
// fire the subprocess ended event
fireEvent(Event.EVENTTYPE_SUBPROCESS_END, executionContext);
// remove the subprocess reference
superProcessToken.setSubProcessInstance(null);
// override the normal log generation in super.leave() by creating the log here
// and replacing addNodeLog() with an empty version
superProcessToken.addLog(new ProcessStateLog(this, superProcessToken.getNodeEnter(),
Clock.getCurrentTime(), subProcessInstance));
// call the subProcessEndAction
super.leave(executionContext, getDefaultLeavingTransition());
}
protected void addNodeLog(Token token) {
// override the normal log generation in super.leave() by creating the log in this.leave()
// and replacing this method with an empty version
}
public ProcessDefinition getSubProcessDefinition() {
return subProcessDefinition;
}
public void setSubProcessDefinition(ProcessDefinition subProcessDefinition) {
this.subProcessDefinition = subProcessDefinition;
}
private static final Log log = LogFactory.getLog(ProcessState.class);
}