/*
* Atricore IDBus
*
* Copyright (c) 2009, Atricore Inc.
*
* 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.atricore.idbus.kernel.planning.jbpm;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Element;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmException;
import org.jbpm.JbpmContext;
import org.jbpm.context.def.VariableAccess;
import org.jbpm.context.exe.ContextInstance;
import org.jbpm.graph.def.*;
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.jpdl.xml.JpdlXmlReader;
import org.jbpm.jpdl.xml.Parsable;
import org.jbpm.util.Clock;
/**
* @author <a href="mailto:gbrigandi@atricore.org">Gianluca Brigandi</a>
* @version $Rev: 278 $ $Date: 2008-12-31 19:42:22 -0200 (Wed, 31 Dec 2008) $
*/
public class ProcessFragmentState extends Node implements Parsable {
private static final long serialVersionUID = 1L;
protected final transient Log logger = LogFactory.getLog(getClass());
static ProcessFragmentResolver defaultProcessFragmentResolver;
static BPMSManager bpmsManager;
public static synchronized void setDefaultProcessFragmentResolver(ProcessFragmentResolver processFragmentResolver) {
defaultProcessFragmentResolver = processFragmentResolver;
}
public static ProcessFragmentResolver getDefaultProcessFragmentResolver() {
return defaultProcessFragmentResolver;
}
public static synchronized void setBpmsManager(BPMSManager m) {
bpmsManager = m;
}
protected Set<VariableAccess> variableAccesses = null;
protected String processFragmentName = null;
protected String processFragmentLifecycle = null;
protected String processFragmentPhase = null;
protected ProcessDefinition processFragmentDefinition = null;
public ProcessFragmentState() {
super();
}
// event types //////////////////////////////////////////////////////////////
public static final String[] supportedEventTypes = new String[]{Event.EVENTTYPE_SUBPROCESS_CREATED, Event.EVENTTYPE_SUBPROCESS_END,
Event.EVENTTYPE_NODE_ENTER, Event.EVENTTYPE_NODE_LEAVE, Event.EVENTTYPE_BEFORE_SIGNAL, Event.EVENTTYPE_AFTER_SIGNAL};
public String[] getSupportedEventTypes() {
return supportedEventTypes;
}
// xml //////////////////////////////////////////////////////////////////////
public void read(Element processStateElement, JpdlXmlReader jpdlReader) {
logger.debug("Start reading JPDL from XML ...");
ProcessFragment processFragment;
Element processFragmentElement = processStateElement.element("process-fragment");
if (processFragmentElement != null) {
processFragmentLifecycle = processFragmentElement.attributeValue("lifecycle");
processFragmentPhase = processFragmentElement.attributeValue("phase");
ProcessFragmentResolver processFragmentResolver = getProcessFragmentResolver();
try {
processFragment = processFragmentResolver.findProcessFragment(processFragmentElement);
if (processFragment != null) {
processFragmentName = processFragment.getName();
processFragmentDefinition = processFragment.getDefinition();
JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
if (jbpmContext != null) {
logger.debug("Contributions found for lifecycle [" + processFragmentLifecycle + "], " +
"phase [" + processFragmentPhase + "]"
);
logger.debug("Deploying Process Fragment Definition '" +
processFragmentDefinition.getName() + "'");
jbpmContext.deployProcessDefinition(processFragmentDefinition);
// Dump this process fragment for debugging purposes.
if (logger.isDebugEnabled()) {
logger.debug("Process Fragment ["+processFragmentName+"] Nodes:");
java.util.List<Node> nodes = processFragmentDefinition.getNodes();
for (Node node : nodes) {
StringBuffer sb = new StringBuffer();
sb.append("Node: ").append(node.getFullyQualifiedName());
sb.append(" from: [");
java.util.Set<Transition> froms = node.getArrivingTransitions();
if (froms != null) {
String prefix = "";
for (Transition t : froms) {
sb.append(prefix).append(t.getFrom().getFullyQualifiedName());
prefix = ",";
}
}
sb.append("]");
java.util.List<Transition> tos = node.getLeavingTransitions();
sb.append(" to: [");
if (tos != null) {
String prefix = "";
for (Transition t : tos) {
if (t.getTo() != null)
sb.append(prefix).append(t.getTo().getFullyQualifiedName());
else
sb.append(prefix).append("UNRESOLVED");
prefix = ",";
}
}
sb.append("]");
logger.debug(sb);
}
}
logger.debug("Deployed Process Fragment Definition " + processFragmentDefinition.getName());
}
} else {
logger.debug("No contributions found for lifecycle [" + processFragmentLifecycle + "], " +
"phase [" + processFragmentPhase + "]"
);
}
} catch (JbpmException e) {
jpdlReader.addWarning(e.getMessage());
}
}
if (processFragmentDefinition != null) {
logger.debug("processfragment for process-state '" + name + "' bound to '" +
processFragmentDefinition.getName() + "'");
}
// Fill variable accesses
this.variableAccesses = new HashSet<VariableAccess>();
java.util.List vas = jpdlReader.readVariableAccesses(processStateElement);
for (Object va : vas) {
this.variableAccesses.add((VariableAccess) va);
}
logger.debug("End reading JPDL from XML");
}
private ProcessFragmentResolver getProcessFragmentResolver() {
ProcessFragmentResolver processFragmentResolver = defaultProcessFragmentResolver;
/* TODO : Check why this restarts all spring setups!
if (JbpmConfiguration.Configs.hasObject("jbpm.process.fragment.resolver")) {
processFragmentResolver = (ProcessFragmentResolver) JbpmConfiguration.Configs.getObject("jbpm.process.fragment.resolver");
} */
return processFragmentResolver;
}
public void execute(ExecutionContext executionContext) {
ProcessFragment processFragment;
Token superProcessToken = executionContext.getToken();
ProcessDefinition usedProcessFragmentDefinition = processFragmentDefinition;
ProcessFragmentRegistry pfr;
String pdn;
if (processFragmentName != null) {
pfr = (ProcessFragmentRegistry) executionContext.getContextInstance().getTransientVariable(Constants.VAR_PFR);
if (pfr == null) {
throw new NullPointerException("No ProcessFragmentRegistry instance found as transient variable ["+ Constants.VAR_PFR +"] in fragment " + this.processFragmentName);
}
pdn = (String) executionContext.getContextInstance().getTransientVariable(Constants.VAR_PDN);
if (pdn == null) {
throw new NullPointerException("No Process Descriptor Name instance found as transient variable ["+ Constants.VAR_PDN +"] in fragment " + this.processFragmentName);
}
processFragment = pfr.lookupProcessFragment(processFragmentName);
ProcessDescriptor currentProcessDescriptor = pfr.lookupProcessDescriptor(pdn);
boolean isProcessFragmentActive = currentProcessDescriptor.isActive(processFragment.getName());
if (isProcessFragmentActive) {
logger.debug("Contributions found for lifecycle [" + processFragmentLifecycle + "], " +
"phase [" + processFragmentPhase + "]. Spawning enabled fragment " + processFragment.getName());
// create the processfragment
ProcessInstance processFragmentInstance = superProcessToken.createSubProcessInstance(usedProcessFragmentDefinition);
// fire the processfragment created event
fireEvent(Event.EVENTTYPE_SUBPROCESS_CREATED, executionContext);
// Copy all transient variables
ContextInstance superContextInstance = executionContext.getContextInstance();
ContextInstance subContextInstance = processFragmentInstance.getContextInstance();
for (Object var : superContextInstance.getTransientVariables().keySet()) {
logger.debug("Copying super process transient var '" + var + "'");
}
subContextInstance.setTransientVariables(superContextInstance.getTransientVariables());
// feed the readable variableInstances
if ((variableAccesses != null) && (!variableAccesses.isEmpty())) {
// loop over all the variable accesses
for (VariableAccess variableAccess : variableAccesses) {
// 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);
String mappedName = variableAccess.getMappedName() != null ?
variableAccess.getMappedName() :
variableAccess.getVariableName();
if (value != null) {
logger.debug("Copying super process var '" + variableName + "' to sub process var '" + mappedName + "': " + value);
subContextInstance.setVariable(mappedName, value);
} else {
logger.debug("Super process var '" + variableName + "' has no value, ignoring");
}
}
}
}
// TODO : If BPM enigne is removed from planning, this must go way
// Always add IN/OUT Artifacts as variables!
if (logger.isDebugEnabled())
logger.debug("Automatically Copying super process var '" + Constants.VAR_IN_IDENTITY_ARTIFACT
+ "' to sub process var '" + Constants.VAR_IN_IDENTITY_ARTIFACT + "': " +
superContextInstance.getVariable(Constants.VAR_IN_IDENTITY_ARTIFACT, superProcessToken));
subContextInstance.setVariable(Constants.VAR_IN_IDENTITY_ARTIFACT,
superContextInstance.getVariable(Constants.VAR_IN_IDENTITY_ARTIFACT, superProcessToken));
if (logger.isDebugEnabled())
logger.debug("Automatically Copying super process var '" + Constants.VAR_OUT_IDENTITY_ARTIFACT
+ "' to sub process var '" + Constants.VAR_OUT_IDENTITY_ARTIFACT + "': " +
superContextInstance.getVariable(Constants.VAR_OUT_IDENTITY_ARTIFACT, superProcessToken));
subContextInstance.setVariable(Constants.VAR_OUT_IDENTITY_ARTIFACT,
superContextInstance.getVariable(Constants.VAR_OUT_IDENTITY_ARTIFACT, superProcessToken));
processFragmentInstance.signal();
if (!processFragmentInstance.hasEnded()) {
logger.warn("Identity Plan process fragment '"+processFragmentName+"' [" + processFragmentInstance.getId() + "] has not ended! Check your process definition");
}
logger.debug("Spawned enabled Process Fragment " + processFragment.getName());
} else {
if (logger.isDebugEnabled()) {
StringBuffer actives = new StringBuffer();
Collection<String> activeFragments = currentProcessDescriptor.getActiveProcessFragments();
for (String name : activeFragments) {
actives.append(name).append(",");
}
logger.debug("Process Fragment " + processFragment.getName() + " not enabled. Skipping execution. " +
"Active fragments are ["+actives+"]");
}
leave(executionContext, getDefaultLeavingTransition());
}
} else {
logger.debug("No contributions found for lifecycle [" + processFragmentLifecycle + "], " +
"phase [" + processFragmentPhase + "]. Skipping execution..."
);
leave(executionContext, getDefaultLeavingTransition());
}
}
public void leave(ExecutionContext executionContext, Transition transition) {
ProcessInstance processFragmentInstance = executionContext.getSubProcessInstance();
if (processFragmentInstance != null) {
Token superProcessToken = processFragmentInstance.getSuperProcessToken();
// feed the readable variableInstances
if ((variableAccesses != null) && (!variableAccesses.isEmpty())) {
ContextInstance superContextInstance = executionContext.getContextInstance();
ContextInstance subContextInstance = processFragmentInstance.getContextInstance();
// loop over all the variable accesses
Iterator iter = variableAccesses.iterator();
while (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);
String variableName = variableAccess.getVariableName();
if (value != null) {
logger.debug("copying sub process var '" + mappedName + "' to super process var '" + variableName + "': " + value);
superContextInstance.setVariable(variableName, value, superProcessToken);
}
}
}
}
// fire the processfragment ended event
fireEvent(Event.EVENTTYPE_SUBPROCESS_END, executionContext);
// remove the processfragment reference
superProcessToken.setSubProcessInstance(null);
// We replaced the normal log generation of super.leave() by creating the log here
// and overriding the addNodeLog method with an empty version
superProcessToken.addLog(new ProcessStateLog(this, superProcessToken.getNodeEnter(), Clock.getCurrentTime(), processFragmentInstance));
}
// call the processFragmentEndAction
super.leave(executionContext, getDefaultLeavingTransition());
}
// We replaced the normal log generation of super.leave() by creating the log above in the leave method
// and overriding the addNodeLog method with an empty version
protected void addNodeLog(Token token) {
}
}