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.taskmgmt.exe;
023:
024: import java.lang.reflect.Field;
025: import java.util.ArrayList;
026: import java.util.Collection;
027: import java.util.HashMap;
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: import java.util.StringTokenizer;
034:
035: import org.apache.commons.logging.Log;
036: import org.apache.commons.logging.LogFactory;
037: import org.jbpm.JbpmConfiguration;
038: import org.jbpm.JbpmException;
039: import org.jbpm.graph.def.DelegationException;
040: import org.jbpm.graph.def.GraphElement;
041: import org.jbpm.graph.exe.ExecutionContext;
042: import org.jbpm.graph.exe.Token;
043: import org.jbpm.instantiation.Delegation;
044: import org.jbpm.instantiation.UserCodeInterceptorConfig;
045: import org.jbpm.jpdl.el.impl.JbpmExpressionEvaluator;
046: import org.jbpm.module.exe.ModuleInstance;
047: import org.jbpm.security.SecurityHelper;
048: import org.jbpm.svc.Services;
049: import org.jbpm.taskmgmt.TaskInstanceFactory;
050: import org.jbpm.taskmgmt.def.AssignmentHandler;
051: import org.jbpm.taskmgmt.def.Swimlane;
052: import org.jbpm.taskmgmt.def.Task;
053: import org.jbpm.taskmgmt.def.TaskMgmtDefinition;
054: import org.jbpm.taskmgmt.log.TaskCreateLog;
055:
056: /**
057: * process instance extension for managing tasks on a process instance.
058: */
059: public class TaskMgmtInstance extends ModuleInstance {
060:
061: private static final long serialVersionUID = 1L;
062:
063: TaskMgmtDefinition taskMgmtDefinition = null;
064: Map swimlaneInstances = null;
065: Set taskInstances = null;
066: /** non persistent collection that stores all the task instances that have
067: * variable updates*/
068: Collection taskInstanceVariableUpdates = null;
069:
070: public TaskMgmtInstance() {
071: }
072:
073: public TaskMgmtInstance(TaskMgmtDefinition taskMgmtDefinition) {
074: this .taskMgmtDefinition = taskMgmtDefinition;
075: }
076:
077: // task instances ///////////////////////////////////////////////////////////
078:
079: public TaskInstance createTaskInstance() {
080: return createTaskInstance(null, (ExecutionContext) null);
081: }
082:
083: public TaskInstance createTaskInstance(Task task) {
084: return createTaskInstance(task, (ExecutionContext) null);
085: }
086:
087: public TaskInstance createTaskInstance(Token token) {
088: return createTaskInstance(null, new ExecutionContext(token));
089: }
090:
091: /**
092: * creates a new task instance on the given token, for the given task.
093: */
094: public TaskInstance createTaskInstance(Task task, Token token) {
095: ExecutionContext executionContext = new ExecutionContext(token);
096: executionContext.setTask(task);
097: return createTaskInstance(task, executionContext);
098: }
099:
100: /**
101: * creates a new task instance on the given task, in the given execution context.
102: */
103: public TaskInstance createTaskInstance(Task task,
104: ExecutionContext executionContext) {
105: // instantiate the new task instance
106: TaskInstance taskInstance = instantiateNewTaskInstance(executionContext);
107:
108: // bind the task instance to the TaskMgmtInstance
109: addTaskInstance(taskInstance);
110: // ... add a reference to the process instance
111: taskInstance.setProcessInstance(executionContext
112: .getProcessInstance());
113:
114: // initialize the task instance
115: if (task != null)
116: taskInstance.setTask(task);
117:
118: // assign an id to the task instance
119: Services.assignId(taskInstance);
120:
121: // copy the task properties
122: String description = null;
123: if (task != null) {
124: description = task.getDescription();
125: taskInstance.setDescription(description);
126: taskInstance.setBlocking(task.isBlocking());
127: taskInstance.setSignalling(task.isSignalling());
128: }
129:
130: if (executionContext != null) {
131: Token token = executionContext.getToken();
132: taskInstance.setToken(token);
133:
134: taskInstance.initializeVariables();
135:
136: try {
137: // update the executionContext
138: executionContext.setTask(task);
139: executionContext.setTaskInstance(taskInstance);
140: executionContext.setEventSource(task);
141:
142: // evaluate the description
143: if ((description != null)
144: && (description.indexOf("#{") != -1)) {
145: Object result = JbpmExpressionEvaluator.evaluate(
146: description, executionContext);
147: if (result != null) {
148: taskInstance.setDescription(result.toString());
149: }
150: }
151:
152: // create the task instance
153: taskInstance.create(executionContext);
154:
155: // if this task instance is created for a task, perform assignment
156: if (task != null) {
157: taskInstance.assign(executionContext);
158: }
159:
160: } finally {
161: // clean the executionContext
162: executionContext.setTask(null);
163: executionContext.setTaskInstance(null);
164: executionContext.setEventSource(null);
165: }
166:
167: // log this creation
168: // WARNING: The events create and assign are fired in the right order, but
169: // the logs are still not ordered properly.
170: token.addLog(new TaskCreateLog(taskInstance, taskInstance
171: .getActorId()));
172:
173: } else {
174: taskInstance.create();
175: }
176:
177: return taskInstance;
178: }
179:
180: public SwimlaneInstance getInitializedSwimlaneInstance(
181: ExecutionContext executionContext, Swimlane swimlane) {
182: // initialize the swimlane
183: if (swimlaneInstances == null)
184: swimlaneInstances = new HashMap();
185: SwimlaneInstance swimlaneInstance = (SwimlaneInstance) swimlaneInstances
186: .get(swimlane.getName());
187: if (swimlaneInstance == null) {
188: swimlaneInstance = new SwimlaneInstance(swimlane);
189: addSwimlaneInstance(swimlaneInstance);
190: // assign the swimlaneInstance
191: performAssignment(swimlane.getAssignmentDelegation(),
192: swimlane.getActorIdExpression(), swimlane
193: .getPooledActorsExpression(),
194: swimlaneInstance, executionContext);
195: }
196:
197: return swimlaneInstance;
198: }
199:
200: public void performAssignment(Delegation assignmentDelegation,
201: String actorIdExpression, String pooledActorsExpression,
202: Assignable assignable, ExecutionContext executionContext) {
203: try {
204: if (assignmentDelegation != null) {
205: performAssignmentDelegation(assignmentDelegation,
206: assignable, executionContext);
207: } else {
208: if (actorIdExpression != null) {
209: performAssignmentActorIdExpr(actorIdExpression,
210: assignable, executionContext);
211: }
212: if (pooledActorsExpression != null) {
213: performAssignmentPooledActorsExpr(
214: pooledActorsExpression, assignable,
215: executionContext);
216: }
217: }
218:
219: } catch (Exception exception) {
220: GraphElement graphElement = executionContext
221: .getEventSource();
222: if (graphElement != null) {
223: graphElement
224: .raiseException(exception, executionContext);
225: } else {
226: throw new DelegationException(exception,
227: executionContext);
228: }
229: }
230: }
231:
232: void performAssignmentDelegation(Delegation assignmentDelegation,
233: Assignable assignable, ExecutionContext executionContext)
234: throws Exception {
235: // instantiate the assignment handler
236: AssignmentHandler assignmentHandler = (AssignmentHandler) assignmentDelegation
237: .instantiate();
238: // invoke the assignment handler
239: if (UserCodeInterceptorConfig.userCodeInterceptor != null) {
240: UserCodeInterceptorConfig.userCodeInterceptor
241: .executeAssignment(assignmentHandler, assignable,
242: executionContext);
243: } else {
244: assignmentHandler.assign(assignable, executionContext);
245: }
246: }
247:
248: void performAssignmentActorIdExpr(String actorIdExpression,
249: Assignable assignable, ExecutionContext executionContext) {
250: Object result = null;
251: String actorId = null;
252: try {
253: result = JbpmExpressionEvaluator.evaluate(
254: actorIdExpression, executionContext);
255: if (result == null) {
256: throw new JbpmException("actor-id expression '"
257: + actorIdExpression + "' returned null");
258: }
259: actorId = (String) result;
260: } catch (ClassCastException e) {
261: throw new JbpmException("actor-id expression '"
262: + actorIdExpression
263: + "' didn't resolve to a java.lang.String: '"
264: + result + "' (" + result.getClass().getName()
265: + ")");
266: }
267: assignable.setActorId(actorId);
268: }
269:
270: void performAssignmentPooledActorsExpr(
271: String pooledActorsExpression, Assignable assignable,
272: ExecutionContext executionContext) {
273: String[] pooledActors = null;
274: Object result = JbpmExpressionEvaluator.evaluate(
275: pooledActorsExpression, executionContext);
276: if (result == null) {
277: throw new JbpmException("pooled-actors expression '"
278: + pooledActorsExpression + "' returned null");
279: }
280:
281: if (result instanceof String[]) {
282: pooledActors = (String[]) result;
283:
284: } else if (result instanceof Collection) {
285: Collection collection = (Collection) result;
286: pooledActors = (String[]) collection
287: .toArray(new String[collection.size()]);
288:
289: } else if (result instanceof String) {
290: List pooledActorList = new ArrayList();
291: StringTokenizer tokenizer = new StringTokenizer(
292: (String) result, ",");
293: while (tokenizer.hasMoreTokens()) {
294: pooledActorList.add(tokenizer.nextToken().trim());
295: }
296: pooledActors = (String[]) pooledActorList
297: .toArray(new String[pooledActorList.size()]);
298: } else {
299: throw new JbpmException(
300: "pooled-actors expression '"
301: + pooledActorsExpression
302: + "' didn't resolve to a comma separated String, a Collection or a String[]: '"
303: + result + "' ("
304: + result.getClass().getName() + ")");
305: }
306:
307: assignable.setPooledActors(pooledActors);
308: }
309:
310: /**
311: * creates a task instance on the rootToken, and assigns it
312: * to the currently authenticated user.
313: */
314: public TaskInstance createStartTaskInstance() {
315: TaskInstance taskInstance = null;
316: Task startTask = taskMgmtDefinition.getStartTask();
317: if (startTask != null) {
318: Token rootToken = processInstance.getRootToken();
319: ExecutionContext executionContext = new ExecutionContext(
320: rootToken);
321: taskInstance = createTaskInstance(startTask,
322: executionContext);
323: taskInstance.setActorId(SecurityHelper
324: .getAuthenticatedActorId());
325: }
326: return taskInstance;
327: }
328:
329: TaskInstance instantiateNewTaskInstance(
330: ExecutionContext executionContext) {
331: TaskInstance newTaskInstance = null;
332: TaskInstanceFactory taskInstanceFactory = (TaskInstanceFactory) JbpmConfiguration.Configs
333: .getObject("jbpm.task.instance.factory");
334: try {
335: newTaskInstance = taskInstanceFactory
336: .createTaskInstance(executionContext);
337: } catch (NullPointerException e) {
338: throw new JbpmException(
339: "jbpm.task.instance.factory was not configured in jbpm.cfg.xml",
340: e);
341: } catch (Exception e) {
342: e.printStackTrace();
343: throw new JbpmException(
344: "couldn't instantiate task instance with task instance factory '"
345: + taskInstanceFactory + "'", e);
346: }
347: return newTaskInstance;
348: }
349:
350: /**
351: * is true if the given token has task instances that keep the
352: * token from leaving the current node.
353: */
354: public boolean hasBlockingTaskInstances(Token token) {
355: boolean hasBlockingTasks = false;
356: if (taskInstances != null) {
357: Iterator iter = taskInstances.iterator();
358: while ((iter.hasNext()) && (!hasBlockingTasks)) {
359: TaskInstance taskInstance = (TaskInstance) iter.next();
360: if ((!taskInstance.hasEnded())
361: && (taskInstance.isBlocking())
362: && (token != null)
363: && (token.equals(taskInstance.getToken()))) {
364: hasBlockingTasks = true;
365: }
366: }
367: }
368: return hasBlockingTasks;
369: }
370:
371: /**
372: * is true if the given token has task instances that are not yet ended.
373: */
374: public boolean hasUnfinishedTasks(Token token) {
375: return (getUnfinishedTasks(token).size() > 0);
376: }
377:
378: /**
379: * is the collection of {@link TaskInstance}s on the given token that are not ended.
380: */
381: public Collection getUnfinishedTasks(Token token) {
382: Collection unfinishedTasks = new ArrayList();
383: if (taskInstances != null) {
384: Iterator iter = taskInstances.iterator();
385: while (iter.hasNext()) {
386: TaskInstance task = (TaskInstance) iter.next();
387: if ((!task.hasEnded()) && (token != null)
388: && (token.equals(task.getToken()))) {
389: unfinishedTasks.add(task);
390: }
391: }
392: }
393: return unfinishedTasks;
394: }
395:
396: /**
397: * is true if there are {@link TaskInstance}s on the given token that can trigger
398: * the token to continue.
399: */
400: public boolean hasSignallingTasks(ExecutionContext executionContext) {
401: return (getSignallingTasks(executionContext).size() > 0);
402: }
403:
404: /**
405: * is the collection of {@link TaskInstance}s for the given token that can trigger
406: * the token to continue.
407: */
408: public Collection getSignallingTasks(
409: ExecutionContext executionContext) {
410: Collection signallingTasks = new ArrayList();
411: if (taskInstances != null) {
412: Iterator iter = taskInstances.iterator();
413: while (iter.hasNext()) {
414: TaskInstance taskInstance = (TaskInstance) iter.next();
415: if (taskInstance.isSignalling()
416: && (executionContext.getToken()
417: .equals(taskInstance.getToken()))) {
418: signallingTasks.add(taskInstance);
419: }
420: }
421: }
422: return signallingTasks;
423: }
424:
425: /**
426: * returns all the taskInstances for the this process instance. This
427: * includes task instances that have been completed previously.
428: */
429: public Collection getTaskInstances() {
430: return taskInstances;
431: }
432:
433: public void addTaskInstance(TaskInstance taskInstance) {
434: if (taskInstances == null)
435: taskInstances = new HashSet();
436: taskInstances.add(taskInstance);
437: taskInstance.setTaskMgmtInstance(this );
438: }
439:
440: public void removeTaskInstance(TaskInstance taskInstance) {
441: if (taskInstances != null) {
442: taskInstances.remove(taskInstance);
443: }
444: }
445:
446: // swimlane instances ///////////////////////////////////////////////////////
447:
448: public Map getSwimlaneInstances() {
449: return swimlaneInstances;
450: }
451:
452: public void addSwimlaneInstance(SwimlaneInstance swimlaneInstance) {
453: if (swimlaneInstances == null)
454: swimlaneInstances = new HashMap();
455: swimlaneInstances.put(swimlaneInstance.getName(),
456: swimlaneInstance);
457: swimlaneInstance.setTaskMgmtInstance(this );
458: }
459:
460: public SwimlaneInstance getSwimlaneInstance(String swimlaneName) {
461: return (SwimlaneInstance) (swimlaneInstances != null ? swimlaneInstances
462: .get(swimlaneName)
463: : null);
464: }
465:
466: public SwimlaneInstance createSwimlaneInstance(String swimlaneName) {
467: Swimlane swimlane = (taskMgmtDefinition != null ? taskMgmtDefinition
468: .getSwimlane(swimlaneName)
469: : null);
470: if (swimlane != null) {
471: return createSwimlaneInstance(swimlane);
472: }
473: throw new JbpmException(
474: "couldn't create swimlane instance for non-existing swimlane "
475: + swimlaneName);
476: }
477:
478: public SwimlaneInstance createSwimlaneInstance(Swimlane swimlane) {
479: if (swimlaneInstances == null)
480: swimlaneInstances = new HashMap();
481: SwimlaneInstance swimlaneInstance = new SwimlaneInstance(
482: swimlane);
483: try {
484: swimlaneInstance.setTaskMgmtInstance(this );
485: Class persistentMapClass = swimlaneInstances.getClass();
486: Field mapField = persistentMapClass.getDeclaredField("map");
487: mapField.setAccessible(true);
488: // TODO remove the size when we switch to hibernate 3.2.1 (it's a workaround for a bug)
489: swimlaneInstances.size();
490: swimlaneInstances.put(swimlaneInstance.getName(),
491: swimlaneInstance);
492: } catch (Exception e) {
493: e.printStackTrace();
494: }
495: return swimlaneInstance;
496: }
497:
498: // getters and setters //////////////////////////////////////////////////////
499:
500: public TaskMgmtDefinition getTaskMgmtDefinition() {
501: return taskMgmtDefinition;
502: }
503:
504: /**
505: * suspends all task instances for this process instance.
506: */
507: public void suspend(Token token) {
508: if (token == null) {
509: throw new JbpmException(
510: "can't suspend task instances for token null");
511: }
512: if (taskInstances != null) {
513: Iterator iter = taskInstances.iterator();
514: while (iter.hasNext()) {
515: TaskInstance taskInstance = (TaskInstance) iter.next();
516: if ((token.equals(taskInstance.getToken()))
517: && (taskInstance.isOpen())) {
518: taskInstance.suspend();
519: }
520: }
521: }
522: }
523:
524: /**
525: * resumes all task instances for this process instance.
526: */
527: public void resume(Token token) {
528: if (token == null) {
529: throw new JbpmException(
530: "can't suspend task instances for token null");
531: }
532: if (taskInstances != null) {
533: Iterator iter = taskInstances.iterator();
534: while (iter.hasNext()) {
535: TaskInstance taskInstance = (TaskInstance) iter.next();
536: if ((token.equals(taskInstance.getToken()))
537: && (taskInstance.isOpen())) {
538: taskInstance.resume();
539: }
540: }
541: }
542: }
543:
544: void notifyVariableUpdate(TaskInstance taskInstance) {
545: if (taskInstanceVariableUpdates == null) {
546: taskInstanceVariableUpdates = new HashSet();
547: }
548: taskInstanceVariableUpdates.add(taskInstance);
549: }
550:
551: /**
552: * returns the collection of task instance with variable updates.
553: */
554: public Collection getTaskInstancesWithVariableUpdates() {
555: return taskInstanceVariableUpdates;
556: }
557:
558: /**
559: * convenience method to end all tasks related to a given process instance.
560: */
561: public void endAll() {
562: if (taskInstances != null) {
563: Iterator iter = taskInstances.iterator();
564: while (iter.hasNext()) {
565: TaskInstance taskInstance = (TaskInstance) iter.next();
566: if (!taskInstance.hasEnded()) {
567: taskInstance.end();
568: }
569: }
570: }
571: }
572:
573: /**
574: * removes signalling capabilities from all task instances related to the given token.
575: */
576: public void removeSignalling(Token token) {
577: if (taskInstances != null) {
578: Iterator iter = taskInstances.iterator();
579: while (iter.hasNext()) {
580: TaskInstance taskInstance = (TaskInstance) iter.next();
581: if ((token != null)
582: && (token.equals(taskInstance.getToken()))) {
583: taskInstance.setSignalling(false);
584: }
585: }
586: }
587: }
588:
589: private static final Log log = LogFactory
590: .getLog(TaskMgmtInstance.class);
591: }
|