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.util.*;
025:
026: import org.dom4j.Element;
027: import org.jbpm.graph.def.*;
028: import org.jbpm.graph.exe.*;
029: import org.jbpm.jpdl.el.impl.JbpmExpressionEvaluator;
030: import org.jbpm.jpdl.xml.*;
031: import org.jbpm.taskmgmt.def.*;
032: import org.jbpm.taskmgmt.exe.*;
033:
034: /**
035: * is a node that relates to one or more tasks.
036: * Property <code>signal</code> specifies how task completion
037: * triggers continuation of execution.
038: */
039: public class TaskNode extends Node implements Parsable {
040:
041: private static final long serialVersionUID = 1L;
042:
043: /**
044: * execution always continues, regardless wether tasks are created or still unfinished.
045: */
046: public static final int SIGNAL_UNSYNCHRONIZED = 0;
047: /**
048: * execution never continues, regardless wether tasks are created or still unfinished.
049: */
050: public static final int SIGNAL_NEVER = 1;
051: /**
052: * proceeds execution when the first task instance is completed.
053: * when no tasks are created on entrance of this node, execution is continued.
054: */
055: public static final int SIGNAL_FIRST = 2;
056: /**
057: * proceeds execution when the first task instance is completed.
058: * when no tasks are created on entrance of this node, execution is continued.
059: */
060: public static final int SIGNAL_FIRST_WAIT = 3;
061: /**
062: * proceeds execution when the last task instance is completed.
063: * when no tasks are created on entrance of this node, execution waits in the task node till tasks are created.
064: */
065: public static final int SIGNAL_LAST = 4;
066: /**
067: * proceeds execution when the last task instance is completed.
068: * when no tasks are created on entrance of this node, execution waits in the task node till tasks are created.
069: */
070: public static final int SIGNAL_LAST_WAIT = 5;
071:
072: public static int parseSignal(String text) {
073: if ("unsynchronized".equalsIgnoreCase(text)) {
074: return SIGNAL_UNSYNCHRONIZED;
075: } else if ("never".equalsIgnoreCase(text)) {
076: return SIGNAL_NEVER;
077: } else if ("first".equalsIgnoreCase(text)) {
078: return SIGNAL_FIRST;
079: } else if ("first-wait".equalsIgnoreCase(text)) {
080: return SIGNAL_FIRST_WAIT;
081: } else if ("last-wait".equalsIgnoreCase(text)) {
082: return SIGNAL_LAST_WAIT;
083: } else { // return default
084: return SIGNAL_LAST;
085: }
086: }
087:
088: public static String signalToString(int signal) {
089: if (signal == SIGNAL_UNSYNCHRONIZED) {
090: return "unsynchronized";
091: } else if (signal == SIGNAL_NEVER) {
092: return "never";
093: } else if (signal == SIGNAL_FIRST) {
094: return "first";
095: } else if (signal == SIGNAL_FIRST_WAIT) {
096: return "first-wait";
097: } else if (signal == SIGNAL_LAST) {
098: return "last";
099: } else if (signal == SIGNAL_LAST_WAIT) {
100: return "last-wait";
101: } else {
102: return null;
103: }
104: }
105:
106: Set tasks = null;
107: int signal = SIGNAL_LAST;
108: boolean createTasks = true;
109: boolean endTasks = false;
110:
111: public TaskNode() {
112: }
113:
114: public TaskNode(String name) {
115: super (name);
116: }
117:
118: public void read(Element element, JpdlXmlReader jpdlReader) {
119: // get the signal
120: String signalText = element.attributeValue("signal");
121: if (signalText != null) {
122: signal = parseSignal(signalText);
123: }
124:
125: // create tasks
126: String createTasksText = element.attributeValue("create-tasks");
127: if (createTasksText != null) {
128: if (("no".equalsIgnoreCase(createTasksText))
129: || ("false".equalsIgnoreCase(createTasksText))) {
130: createTasks = false;
131: }
132: }
133:
134: // create tasks
135: String removeTasksText = element.attributeValue("end-tasks");
136: if (removeTasksText != null) {
137: if (("yes".equalsIgnoreCase(removeTasksText))
138: || ("true".equalsIgnoreCase(removeTasksText))) {
139: endTasks = true;
140: }
141: }
142:
143: // parse the tasks
144: jpdlReader.readTasks(element, this );
145: }
146:
147: public void addTask(Task task) {
148: if (tasks == null)
149: tasks = new HashSet();
150: tasks.add(task);
151: task.setTaskNode(this );
152: }
153:
154: // node behaviour methods
155: /////////////////////////////////////////////////////////////////////////////
156:
157: public void execute(ExecutionContext executionContext) {
158:
159: TaskMgmtInstance tmi = getTaskMgmtInstance(executionContext
160: .getToken());
161:
162: // if this tasknode should create instances
163: if ((createTasks) && (tasks != null)) {
164: Iterator iter = tasks.iterator();
165: while (iter.hasNext()) {
166: Task task = (Task) iter.next();
167: executionContext.setTask(task);
168: if (evaluateTaskCondition(task.getCondition(),
169: executionContext)) {
170: tmi.createTaskInstance(task, executionContext);
171: }
172: }
173: }
174:
175: // check if we should continue execution
176: boolean continueExecution = false;
177: switch (signal) {
178: case SIGNAL_UNSYNCHRONIZED:
179: continueExecution = true;
180: break;
181: case SIGNAL_FIRST_WAIT:
182: case SIGNAL_LAST_WAIT:
183: case SIGNAL_NEVER:
184: continueExecution = false;
185: break;
186: case SIGNAL_FIRST:
187: case SIGNAL_LAST:
188: continueExecution = tmi
189: .getSignallingTasks(executionContext).isEmpty();
190: }
191:
192: if (continueExecution) {
193: leave(executionContext);
194: }
195: }
196:
197: boolean evaluateTaskCondition(String condition,
198: ExecutionContext executionContext) {
199: if (condition == null)
200: return true;
201: Object result = JbpmExpressionEvaluator.evaluate(condition,
202: executionContext);
203: if (Boolean.TRUE.equals(result)) {
204: return true;
205: }
206: return false;
207: }
208:
209: public void leave(ExecutionContext executionContext,
210: Transition transition) {
211: TaskMgmtInstance tmi = getTaskMgmtInstance(executionContext
212: .getToken());
213: if (tmi.hasBlockingTaskInstances(executionContext.getToken())) {
214: throw new IllegalStateException("task-node '" + name
215: + "' still has blocking tasks");
216: }
217: removeTaskInstanceSynchronization(executionContext.getToken());
218: super .leave(executionContext, transition);
219: }
220:
221: // task behaviour methods
222: /////////////////////////////////////////////////////////////////////////////
223:
224: public boolean completionTriggersSignal(TaskInstance taskInstance) {
225: boolean completionTriggersSignal = false;
226: if ((signal == SIGNAL_FIRST) || (signal == SIGNAL_FIRST_WAIT)) {
227: completionTriggersSignal = true;
228: } else if (((signal == SIGNAL_LAST) || (signal == SIGNAL_LAST_WAIT))
229: && (isLastToComplete(taskInstance))) {
230: completionTriggersSignal = true;
231: }
232: return completionTriggersSignal;
233: }
234:
235: boolean isLastToComplete(TaskInstance taskInstance) {
236: Token token = taskInstance.getToken();
237: TaskMgmtInstance tmi = getTaskMgmtInstance(token);
238:
239: boolean isLastToComplete = true;
240: Iterator iter = tmi.getTaskInstances().iterator();
241: while (iter.hasNext() && (isLastToComplete)) {
242: TaskInstance other = (TaskInstance) iter.next();
243: if ((token != null) && (token.equals(other.getToken()))
244: && (!other.equals(taskInstance))
245: && (other.isSignalling()) && (!other.hasEnded())) {
246: isLastToComplete = false;
247: }
248: }
249:
250: return isLastToComplete;
251: }
252:
253: public void removeTaskInstanceSynchronization(Token token) {
254: TaskMgmtInstance tmi = getTaskMgmtInstance(token);
255: Collection taskInstances = tmi.getTaskInstances();
256: if (taskInstances != null) {
257: Iterator iter = taskInstances.iterator();
258: while (iter.hasNext()) {
259: TaskInstance taskInstance = (TaskInstance) iter.next();
260: if (token.equals(taskInstance.getToken())) {
261: // remove signalling
262: if (taskInstance.isSignalling()) {
263: taskInstance.setSignalling(false);
264: }
265: // remove blocking
266: if (taskInstance.isBlocking()) {
267: taskInstance.setBlocking(false);
268: }
269: // if this is a non-finished task and all those
270: // tasks should be finished
271: if ((!taskInstance.hasEnded()) && (endTasks)) {
272: if (tasks.contains(taskInstance.getTask())) {
273: // end this task
274: taskInstance.end();
275: }
276: }
277: }
278: }
279: }
280: }
281:
282: TaskMgmtInstance getTaskMgmtInstance(Token token) {
283: return (TaskMgmtInstance) token.getProcessInstance()
284: .getInstance(TaskMgmtInstance.class);
285: }
286:
287: // getters and setters
288: /////////////////////////////////////////////////////////////////////////////
289:
290: /**
291: * is a Map with the tasks, keyed by task-name or an empty map in case
292: * no tasks are present in this task-node.
293: */
294: public Map getTasksMap() {
295: Map tasksMap = new HashMap();
296: if (tasks != null) {
297: Iterator iter = tasks.iterator();
298: while (iter.hasNext()) {
299: Task task = (Task) iter.next();
300: tasksMap.put(task.getName(), task);
301: }
302: }
303: return tasksMap;
304: }
305:
306: /**
307: * is the task in this task-node with the given name or null if the given task
308: * does not exist in this node.
309: */
310: public Task getTask(String taskName) {
311: return (Task) getTasksMap().get(taskName);
312: }
313:
314: public Set getTasks() {
315: return tasks;
316: }
317:
318: public int getSignal() {
319: return signal;
320: }
321:
322: public boolean getCreateTasks() {
323: return createTasks;
324: }
325:
326: public boolean isEndTasks() {
327: return endTasks;
328: }
329:
330: public void setCreateTasks(boolean createTasks) {
331: this .createTasks = createTasks;
332: }
333:
334: public void setEndTasks(boolean endTasks) {
335: this .endTasks = endTasks;
336: }
337:
338: public void setSignal(int signal) {
339: this .signal = signal;
340: }
341:
342: public void setTasks(Set tasks) {
343: this.tasks = tasks;
344: }
345: }
|