001: /*
002: * JBoss, Home of Professional Open Source
003: * Copyright 2005, JBoss Inc., and individual contributors as indicated
004: * by the @authors tag. See the copyright.txt in the distribution for a
005: * full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jbpm.graph.node;
023:
024: import java.io.IOException;
025: import java.io.Writer;
026: import java.util.ArrayList;
027: import java.util.Date;
028: import java.util.HashSet;
029: import java.util.Iterator;
030: import java.util.List;
031: import java.util.Map;
032: import java.util.Set;
033:
034: import org.apache.commons.logging.Log;
035: import org.apache.commons.logging.LogFactory;
036: import org.dom4j.Attribute;
037: import org.dom4j.Branch;
038: import org.dom4j.CDATA;
039: import org.dom4j.Comment;
040: import org.dom4j.Document;
041: import org.dom4j.Element;
042: import org.dom4j.Entity;
043: import org.dom4j.InvalidXPathException;
044: import org.dom4j.Namespace;
045: import org.dom4j.ProcessingInstruction;
046: import org.dom4j.QName;
047: import org.dom4j.Text;
048: import org.dom4j.Visitor;
049: import org.dom4j.XPath;
050: import org.dom4j.tree.DefaultElement;
051: import org.dom4j.tree.FlyweightAttribute;
052: import org.jbpm.JbpmConfiguration;
053: import org.jbpm.JbpmException;
054: import org.jbpm.context.def.VariableAccess;
055: import org.jbpm.context.exe.ContextInstance;
056: import org.jbpm.graph.def.Event;
057: import org.jbpm.graph.def.Node;
058: import org.jbpm.graph.def.ProcessDefinition;
059: import org.jbpm.graph.def.Transition;
060: import org.jbpm.graph.exe.ExecutionContext;
061: import org.jbpm.graph.exe.ProcessInstance;
062: import org.jbpm.graph.exe.Token;
063: import org.jbpm.graph.log.NodeLog;
064: import org.jbpm.graph.log.ProcessStateLog;
065: import org.jbpm.jpdl.el.impl.JbpmExpressionEvaluator;
066: import org.jbpm.jpdl.xml.JpdlXmlReader;
067: import org.jbpm.jpdl.xml.Parsable;
068: import org.jbpm.util.Clock;
069:
070: public class ProcessState extends Node implements Parsable {
071:
072: private static final long serialVersionUID = 1L;
073:
074: static SubProcessResolver defaultSubProcessResolver = new DbSubProcessResolver();
075:
076: public static void setDefaultSubProcessResolver(
077: SubProcessResolver subProcessResolver) {
078: defaultSubProcessResolver = subProcessResolver;
079: }
080:
081: protected ProcessDefinition subProcessDefinition = null;
082: protected Set variableAccesses = null;
083: protected String subProcessName = null;
084:
085: // event types //////////////////////////////////////////////////////////////
086:
087: public static final String[] supportedEventTypes = new String[] {
088: Event.EVENTTYPE_SUBPROCESS_CREATED,
089: Event.EVENTTYPE_SUBPROCESS_END, Event.EVENTTYPE_NODE_ENTER,
090: Event.EVENTTYPE_NODE_LEAVE, Event.EVENTTYPE_BEFORE_SIGNAL,
091: Event.EVENTTYPE_AFTER_SIGNAL };
092:
093: public String[] getSupportedEventTypes() {
094: return supportedEventTypes;
095: }
096:
097: // xml //////////////////////////////////////////////////////////////////////
098:
099: public void read(Element processStateElement,
100: JpdlXmlReader jpdlReader) {
101: Element subProcessElement = processStateElement
102: .element("sub-process");
103:
104: if (subProcessElement != null) {
105:
106: String binding = subProcessElement
107: .attributeValue("binding");
108: if ("late".equalsIgnoreCase(binding)) {
109: subProcessName = subProcessElement
110: .attributeValue("name");
111: } else {
112:
113: SubProcessResolver subProcessResolver = getSubProcessResolver();
114:
115: try {
116: subProcessDefinition = subProcessResolver
117: .findSubProcess(subProcessElement);
118: } catch (JbpmException e) {
119: jpdlReader.addWarning(e.getMessage());
120: }
121:
122: // in case this is a self-recursive process invocation...
123: if (subProcessDefinition == null) {
124: String subProcessName = subProcessElement
125: .attributeValue("name");
126: if (subProcessName.equals(processDefinition
127: .getName())) {
128: subProcessDefinition = processDefinition;
129: }
130: }
131: }
132: }
133:
134: if (subProcessDefinition != null) {
135: log.debug("subprocess for process-state '" + name
136: + "' bound to " + subProcessDefinition);
137: } else if (subProcessName != null) {
138: log.debug("subprocess for process-state '" + name
139: + "' will be late bound to " + subProcessName);
140: } else {
141: log.debug("subprocess for process-state '" + name
142: + "' not yet bound");
143: }
144:
145: this .variableAccesses = new HashSet(jpdlReader
146: .readVariableAccesses(processStateElement));
147: }
148:
149: private SubProcessResolver getSubProcessResolver() {
150: SubProcessResolver subProcessResolver = defaultSubProcessResolver;
151: if (JbpmConfiguration.Configs
152: .hasObject("jbpm.sub.process.resolver")) {
153: subProcessResolver = (SubProcessResolver) JbpmConfiguration.Configs
154: .getObject("jbpm.sub.process.resolver");
155: }
156: return subProcessResolver;
157: }
158:
159: public void execute(ExecutionContext executionContext) {
160: Token super ProcessToken = executionContext.getToken();
161:
162: ProcessDefinition usedSubProcessDefinition = subProcessDefinition;
163: // if this process has late binding
164: if ((subProcessDefinition == null) && (subProcessName != null)) {
165: SubProcessResolver subProcessResolver = getSubProcessResolver();
166: List attributes = new ArrayList();
167: String subProcessNameResolved = (String) JbpmExpressionEvaluator
168: .evaluate(subProcessName, executionContext);
169: if (log.isDebugEnabled()) {
170: log.debug("SubProcessName after eval: "
171: + subProcessNameResolved);
172: }
173: attributes.add(new FlyweightAttribute("name",
174: subProcessNameResolved));
175: Element subProcessElement = new DefaultElement(
176: "sub-process");
177: subProcessElement.setAttributes(attributes);
178:
179: usedSubProcessDefinition = subProcessResolver
180: .findSubProcess(subProcessElement);
181: }
182:
183: // create the subprocess
184: ProcessInstance subProcessInstance = super ProcessToken
185: .createSubProcessInstance(usedSubProcessDefinition);
186:
187: // fire the subprocess created event
188: fireEvent(Event.EVENTTYPE_SUBPROCESS_CREATED, executionContext);
189:
190: // feed the readable variableInstances
191: if ((variableAccesses != null) && (!variableAccesses.isEmpty())) {
192:
193: ContextInstance super ContextInstance = executionContext
194: .getContextInstance();
195: ContextInstance subContextInstance = subProcessInstance
196: .getContextInstance();
197: subContextInstance
198: .setTransientVariables(super ContextInstance
199: .getTransientVariables());
200:
201: // loop over all the variable accesses
202: Iterator iter = variableAccesses.iterator();
203: while (iter.hasNext()) {
204: VariableAccess variableAccess = (VariableAccess) iter
205: .next();
206: // if this variable access is readable
207: if (variableAccess.isReadable()) {
208: // the variable is copied from the super process variable name
209: // to the sub process mapped name
210: String variableName = variableAccess
211: .getVariableName();
212: Object value = super ContextInstance.getVariable(
213: variableName, super ProcessToken);
214: String mappedName = variableAccess.getMappedName();
215: log.debug("copying super process var '"
216: + variableName + "' to sub process var '"
217: + mappedName + "': " + value);
218: if (value != null) {
219: subContextInstance.setVariable(mappedName,
220: value);
221: }
222: }
223: }
224: }
225:
226: // send the signal to start the subprocess
227: subProcessInstance.signal();
228: }
229:
230: public void leave(ExecutionContext executionContext,
231: Transition transition) {
232: ProcessInstance subProcessInstance = executionContext
233: .getSubProcessInstance();
234:
235: Token super ProcessToken = subProcessInstance
236: .getSuperProcessToken();
237:
238: // feed the readable variableInstances
239: if ((variableAccesses != null) && (!variableAccesses.isEmpty())) {
240:
241: ContextInstance super ContextInstance = executionContext
242: .getContextInstance();
243: ContextInstance subContextInstance = subProcessInstance
244: .getContextInstance();
245:
246: // loop over all the variable accesses
247: Iterator iter = variableAccesses.iterator();
248: while (iter.hasNext()) {
249: VariableAccess variableAccess = (VariableAccess) iter
250: .next();
251: // if this variable access is writable
252: if (variableAccess.isWritable()) {
253: // the variable is copied from the sub process mapped name
254: // to the super process variable name
255: String mappedName = variableAccess.getMappedName();
256: Object value = subContextInstance
257: .getVariable(mappedName);
258: String variableName = variableAccess
259: .getVariableName();
260: log.debug("copying sub process var '" + mappedName
261: + "' to super process var '" + variableName
262: + "': " + value);
263: if (value != null) {
264: super ContextInstance.setVariable(variableName,
265: value, super ProcessToken);
266: }
267: }
268: }
269: }
270:
271: // fire the subprocess ended event
272: fireEvent(Event.EVENTTYPE_SUBPROCESS_END, executionContext);
273:
274: // remove the subprocess reference
275: super ProcessToken.setSubProcessInstance(null);
276:
277: // We replaced the normal log generation of super.leave() by creating the log here
278: // and overriding the addNodeLog method with an empty version
279: super ProcessToken.addLog(new ProcessStateLog(this ,
280: super ProcessToken.getNodeEnter(), Clock
281: .getCurrentTime(), subProcessInstance));
282:
283: // call the subProcessEndAction
284: super .leave(executionContext, getDefaultLeavingTransition());
285: }
286:
287: // We replaced the normal log generation of super.leave() by creating the log above in the leave method
288: // and overriding the addNodeLog method with an empty version
289: protected void addNodeLog(Token token) {
290: }
291:
292: public ProcessDefinition getSubProcessDefinition() {
293: return subProcessDefinition;
294: }
295:
296: public void setSubProcessDefinition(
297: ProcessDefinition subProcessDefinition) {
298: this .subProcessDefinition = subProcessDefinition;
299: }
300:
301: private static Log log = LogFactory.getLog(ProcessState.class);
302: }
|