KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > workflow > impl > WfProcessImpl


1 /*
2  * $Id: WfProcessImpl.java 5462 2005-08-05 18:35:48Z jonesde $
3  *
4  * Copyright (c) 2001 The Open For Business Project - www.ofbiz.org
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  */

25 package org.ofbiz.workflow.impl;
26
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.Set JavaDoc;
34
35 import org.ofbiz.base.util.Debug;
36 import org.ofbiz.base.util.StringUtil;
37 import org.ofbiz.base.util.UtilDateTime;
38 import org.ofbiz.base.util.UtilMisc;
39 import org.ofbiz.entity.GenericDelegator;
40 import org.ofbiz.entity.GenericEntityException;
41 import org.ofbiz.entity.GenericValue;
42 import org.ofbiz.service.GenericResultWaiter;
43 import org.ofbiz.service.LocalDispatcher;
44 import org.ofbiz.service.job.Job;
45 import org.ofbiz.service.job.JobManager;
46 import org.ofbiz.service.job.JobManagerException;
47 import org.ofbiz.workflow.AlreadyRunning;
48 import org.ofbiz.workflow.CannotChangeRequester;
49 import org.ofbiz.workflow.CannotStart;
50 import org.ofbiz.workflow.CannotStop;
51 import org.ofbiz.workflow.InvalidData;
52 import org.ofbiz.workflow.InvalidPerformer;
53 import org.ofbiz.workflow.InvalidState;
54 import org.ofbiz.workflow.NotRunning;
55 import org.ofbiz.workflow.ResultNotAvailable;
56 import org.ofbiz.workflow.WfActivity;
57 import org.ofbiz.workflow.WfEventAudit;
58 import org.ofbiz.workflow.WfException;
59 import org.ofbiz.workflow.WfFactory;
60 import org.ofbiz.workflow.WfProcess;
61 import org.ofbiz.workflow.WfProcessMgr;
62 import org.ofbiz.workflow.WfRequester;
63 import org.ofbiz.workflow.WfUtil;
64 import org.ofbiz.workflow.client.StartActivityJob;
65
66 /**
67  * WfProcessImpl - Workflow Process Object implementation
68  *
69  * @author <a HREF="mailto:jaz@ofbiz.org">Andy Zeneski</a>
70  * @author David Ostrovsky (d.ostrovsky@gmx.de)
71  * @version $Rev: 5462 $
72  * @since 2.0
73  */

74 public class WfProcessImpl extends WfExecutionObjectImpl implements WfProcess {
75
76     public static final String JavaDoc module = WfProcessImpl.class.getName();
77
78     protected WfRequester requester = null;
79     protected WfProcessMgr manager = null;
80    
81     public WfProcessImpl(GenericValue valueObject, WfProcessMgr manager) throws WfException {
82         super(valueObject, null);
83         this.manager = manager;
84         this.requester = null;
85         init();
86     }
87         
88     /**
89      * @see org.ofbiz.workflow.impl.WfExecutionObjectImpl#WfExecutionObjectImpl(org.ofbiz.entity.GenericDelegator, java.lang.String)
90      */

91     public WfProcessImpl(GenericDelegator delegator, String JavaDoc workEffortId) throws WfException {
92         super(delegator, workEffortId);
93         if (activityId != null && activityId.length() > 0)
94             throw new WfException("Execution object is not of type WfProcess.");
95         this.manager = WfFactory.getWfProcessMgr(delegator, packageId, packageVersion, processId, processVersion);
96         this.requester = null;
97     }
98     
99     private void init() throws WfException {
100         // since we are a process we don't have a context yet
101
// get the context to use with parsing descriptions from the manager
102
Map JavaDoc context = manager.getInitialContext();
103         this.parseDescriptions(context);
104     }
105
106     /**
107      * @see org.ofbiz.workflow.WfProcess#setRequester(org.ofbiz.workflow.WfRequester)
108      */

109     public void setRequester(WfRequester newValue) throws WfException, CannotChangeRequester {
110         if (requester != null)
111             throw new CannotChangeRequester();
112         requester = newValue;
113     }
114
115     /**
116      * @see org.ofbiz.workflow.WfProcess#getSequenceStep(int)
117      */

118     public List JavaDoc getSequenceStep(int maxNumber) throws WfException {
119         if (maxNumber > 0)
120             return new ArrayList JavaDoc(activeSteps().subList(0, maxNumber - 1));
121         return activeSteps();
122     }
123     
124     /**
125      * @see org.ofbiz.workflow.WfExecutionObject#abort()
126      */

127     public void abort() throws WfException, CannotStop, NotRunning {
128         super.abort();
129         
130         // cancel the active activities
131
Iterator JavaDoc activities = this.activeSteps().iterator();
132         while (activities.hasNext()) {
133             WfActivity activity = (WfActivity) activities.next();
134             activity.abort();
135         }
136     }
137   
138     /**
139      * @see org.ofbiz.workflow.WfProcess#start()
140      */

141     public void start() throws WfException, CannotStart, AlreadyRunning {
142         start(null);
143     }
144      
145     /**
146      * @see org.ofbiz.workflow.WfProcess#start()
147      */

148     public void start(String JavaDoc activityId) throws WfException, CannotStart, AlreadyRunning {
149         if (state().equals("open.running"))
150             throw new AlreadyRunning("Process is already running");
151
152         if (activityId == null && getDefinitionObject().get("defaultStartActivityId") == null)
153             throw new CannotStart("Initial activity is not defined.");
154
155         changeState("open.running");
156
157         // start the first activity (using the defaultStartActivityId)
158
GenericValue start = null;
159
160         try {
161             if (activityId != null) {
162                 GenericValue processDef = getDefinitionObject();
163                 Map JavaDoc fields = UtilMisc.toMap("packageId", processDef.getString("packageId"), "packageVersion",
164                         processDef.getString("packageVersion"), "processId", processDef.getString("processId"),
165                         "processVersion", processDef.getString("processVersion"), "activityId", activityId);
166                 start = getDelegator().findByPrimaryKey("WorkflowActivity", fields);
167                 
168                 // here we must check and make sure this activity is defined to as a starting activity
169
if (!start.getBoolean("canStart").booleanValue())
170                     throw new CannotStart("The specified activity cannot initiate the workflow process");
171             } else {
172                 // this is either the first activity defined or specified as an ExtendedAttribute
173
// since this is defined in XPDL, we don't care if canStart is set.
174
start = getDefinitionObject().getRelatedOne("DefaultStartWorkflowActivity");
175             }
176         } catch (GenericEntityException e) {
177             throw new WfException(e.getMessage(), e.getNested());
178         }
179         if (start == null)
180             throw new CannotStart("No initial activity available");
181
182         if (Debug.verboseOn())
183             Debug.logVerbose("[WfProcess.start] : Started the workflow process.", module);
184             
185         // set the actualStartDate
186
try {
187             GenericValue v = getRuntimeObject();
188             v.set("actualStartDate", UtilDateTime.nowTimestamp());
189             v.store();
190         } catch (GenericEntityException e) {
191             Debug.logWarning("Could not set 'actualStartDate'.", module);
192             e.printStackTrace();
193         }
194         startActivity(start);
195     }
196   
197     /**
198      * @see org.ofbiz.workflow.WfProcess#manager()
199      */

200     public WfProcessMgr manager() throws WfException {
201         return manager;
202     }
203     
204     /**
205      * @see org.ofbiz.workflow.WfProcess#requester()
206      */

207     public WfRequester requester() throws WfException {
208         return requester;
209     }
210    
211     /**
212      * @see org.ofbiz.workflow.WfProcess#getIteratorStep()
213      */

214     public Iterator JavaDoc getIteratorStep() throws WfException {
215         return activeSteps().iterator();
216     }
217    
218     /**
219      * @see org.ofbiz.workflow.WfProcess#isMemberOfStep(org.ofbiz.workflow.WfActivity)
220      */

221     public boolean isMemberOfStep(WfActivity member) throws WfException {
222         return activeSteps().contains(member);
223     }
224     
225     /**
226      * @see org.ofbiz.workflow.WfProcess#getActivitiesInState(java.lang.String)
227      */

228     public Iterator JavaDoc getActivitiesInState(String JavaDoc state) throws WfException, InvalidState {
229         ArrayList JavaDoc res = new ArrayList JavaDoc();
230         Iterator JavaDoc i = getIteratorStep();
231
232         while (i.hasNext()) {
233             WfActivity a = (WfActivity) i.next();
234
235             if (a.state().equals(state))
236                 res.add(a);
237         }
238         return res.iterator();
239     }
240   
241     /**
242      * @see org.ofbiz.workflow.WfProcess#result()
243      */

244     public Map JavaDoc result() throws WfException, ResultNotAvailable {
245         Map JavaDoc resultSig = manager().resultSignature();
246         Map JavaDoc results = new HashMap JavaDoc();
247         Map JavaDoc context = processContext();
248
249         if (resultSig != null) {
250             Set JavaDoc resultKeys = resultSig.keySet();
251             Iterator JavaDoc i = resultKeys.iterator();
252
253             while (i.hasNext()) {
254                 Object JavaDoc key = i.next();
255
256                 if (context.containsKey(key))
257                     results.put(key, context.get(key));
258             }
259         }
260         return results;
261     }
262    
263     /**
264      * @see org.ofbiz.workflow.WfProcess#howManyStep()
265      */

266     public int howManyStep() throws WfException {
267         return activeSteps().size();
268     }
269   
270     /**
271      * @see org.ofbiz.workflow.WfProcess#receiveResults(org.ofbiz.workflow.WfActivity, java.util.Map)
272      */

273     public synchronized void receiveResults(WfActivity activity, Map JavaDoc results) throws WfException, InvalidData {
274         Map JavaDoc context = processContext();
275         context.putAll(results);
276         setSerializedData(context);
277     }
278     
279     /**
280      * @see org.ofbiz.workflow.WfProcess#activityComplete(org.ofbiz.workflow.WfActivity)
281      */

282     public synchronized void activityComplete(WfActivity activity) throws WfException {
283         if (!activity.state().equals("closed.completed"))
284             throw new WfException("Activity state is not completed");
285         if (Debug.verboseOn()) Debug.logVerbose("[WfProcess.activityComplete] : Activity (" + activity.name() + ") is complete", module);
286         queueNext(activity);
287     }
288
289     /**
290      * @see org.ofbiz.workflow.impl.WfExecutionObjectImpl#executionObjectType()
291      */

292     public String JavaDoc executionObjectType() {
293         return "WfProcess";
294     }
295
296     // Queues the next activities for processing
297
private void queueNext(WfActivity fromActivity) throws WfException {
298         List JavaDoc nextTrans = getTransFrom(fromActivity);
299
300         if (nextTrans.size() > 0) {
301             Iterator JavaDoc i = nextTrans.iterator();
302
303             while (i.hasNext()) {
304                 GenericValue trans = (GenericValue) i.next();
305
306                 // Get the activity definition
307
GenericValue toActivity = null;
308
309                 try {
310                     toActivity = trans.getRelatedOne("ToWorkflowActivity");
311                 } catch (GenericEntityException e) {
312                     throw new WfException(e.getMessage(), e);
313                 }
314
315                 // check for a join
316
String JavaDoc join = "WJT_AND"; // default join is AND
317

318                 if (toActivity.get("joinTypeEnumId") != null)
319                     join = toActivity.getString("joinTypeEnumId");
320
321                 if (Debug.verboseOn()) Debug.logVerbose("[WfProcess.queueNext] : " + join + " join.", module);
322
323                 // activate if XOR or test the join transition(s)
324
if (join.equals("WJT_XOR"))
325                     startActivity(toActivity);
326                 else
327                     joinTransition(toActivity, trans);
328             }
329         } else {
330             if (Debug.verboseOn())
331                 Debug.logVerbose("[WfProcess.queueNext] : No transitions left to follow.", module);
332             this.finishProcess();
333         }
334     }
335
336     // Follows the and-join transition
337
private void joinTransition(GenericValue toActivity,
338         GenericValue transition) throws WfException {
339         // get all TO transitions to this activity
340
GenericValue dataObject = getRuntimeObject();
341         Collection JavaDoc toTrans = null;
342
343         try {
344             toTrans = toActivity.getRelated("ToWorkflowTransition");
345         } catch (GenericEntityException e) {
346             throw new WfException(e.getMessage(), e);
347         }
348
349         // get a list of followed transition to this activity
350
Collection JavaDoc followed = null;
351
352         try {
353             Map JavaDoc fields = new HashMap JavaDoc();
354             fields.put("processWorkEffortId", dataObject.getString("workEffortId"));
355             fields.put("toActivityId", toActivity.getString("activityId"));
356             followed = getDelegator().findByAnd("WorkEffortTransBox", fields);
357         } catch (GenericEntityException e) {
358             throw new WfException(e.getMessage(), e);
359         }
360
361         if (Debug.verboseOn()) Debug.logVerbose("[WfProcess.joinTransition] : toTrans (" + toTrans.size() + ") followed (" +
362                 (followed.size() + 1) + ")", module);
363
364         // check to see if all transition requirements are met
365
if (toTrans.size() == (followed.size() + 1)) {
366             Debug.logVerbose("[WfProcess.joinTransition] : All transitions have followed.", module);
367             startActivity(toActivity);
368             try {
369                 Map JavaDoc fields = new HashMap JavaDoc();
370                 fields.put("processWorkEffortId", dataObject.getString("workEffortId"));
371                 fields.put("toActivityId", toActivity.getString("activityId"));
372                 getDelegator().removeByAnd("WorkEffortTransBox", fields);
373             } catch (GenericEntityException e) {
374                 throw new WfException(e.getMessage(), e);
375             }
376         } else {
377             Debug.logVerbose("[WfProcess.joinTransition] : Waiting for transitions to finish.", module);
378             try {
379                 Map JavaDoc fields = new HashMap JavaDoc();
380                 fields.put("processWorkEffortId", dataObject.getString("workEffortId"));
381                 fields.put("toActivityId", toActivity.getString("activityId"));
382                 fields.put("transitionId", transition.getString("transitionId"));
383                 GenericValue obj = getDelegator().makeValue("WorkEffortTransBox", fields);
384
385                 getDelegator().create(obj);
386             } catch (GenericEntityException e) {
387                 throw new WfException(e.getMessage(), e);
388             }
389         }
390     }
391
392     // Activates an activity object
393
private void startActivity(GenericValue value) throws WfException {
394         WfActivity activity = WfFactory.getWfActivity(value, workEffortId);
395         GenericResultWaiter req = new GenericResultWaiter();
396
397         if (Debug.verboseOn()) Debug.logVerbose("[WfProcess.startActivity] : Attempting to start activity (" + activity.name() + ")", module);
398         
399         // locate the dispatcher to use
400
LocalDispatcher dispatcher = this.getDispatcher();
401         
402         // get the job manager
403
JobManager jm = dispatcher.getJobManager();
404         if (jm == null) {
405             throw new WfException("No job manager found on the service dispatcher; cannot start activity");
406         }
407           
408         // using the StartActivityJob class to run the activity within its own thread
409
try {
410             Job activityJob = new StartActivityJob(activity, req);
411             jm.runJob(activityJob);
412         } catch (JobManagerException e) {
413             throw new WfException("JobManager error", e);
414         }
415          
416         // the GenericRequester object will hold any exceptions; and report the job as failed
417
if (req.status() == GenericResultWaiter.SERVICE_FAILED) {
418             Throwable JavaDoc reqt = req.getThrowable();
419             if (reqt instanceof CannotStart)
420                 Debug.logVerbose("[WfProcess.startActivity] : Cannot start activity. Waiting for manual start.", module);
421             else if (reqt instanceof AlreadyRunning)
422                 throw new WfException("Activity already running", reqt);
423             else
424                 throw new WfException("Activity error", reqt);
425         }
426     }
427
428     // Determine the next activity or activities
429
private List JavaDoc getTransFrom(WfActivity fromActivity) throws WfException {
430         List JavaDoc transList = new ArrayList JavaDoc();
431         // get the from transitions
432
Collection JavaDoc fromCol = null;
433
434         try {
435             fromCol = fromActivity.getDefinitionObject().getRelated("FromWorkflowTransition");
436         } catch (GenericEntityException e) {
437             throw new WfException(e.getMessage(), e);
438         }
439
440         // check for a split
441
String JavaDoc split = "WST_AND"; // default split is AND
442

443         if (fromActivity.getDefinitionObject().get("splitTypeEnumId") != null)
444             split = fromActivity.getDefinitionObject().getString("splitTypeEnumId");
445
446         // the default value is TRUE, so if no expression is supplied we evaluate as true
447
boolean transitionOk = true;
448         
449         // the otherwise condition (only used by XOR splits)
450
GenericValue otherwise = null;
451
452         // iterate through the possible transitions
453
Iterator JavaDoc fromIt = fromCol.iterator();
454         while (fromIt.hasNext()) {
455             GenericValue transition = (GenericValue) fromIt.next();
456
457             // if this transition is OTHERWISE store it for later and continue on
458
if (transition.get("conditionTypeEnumId") != null && transition.getString("conditionTypeEnumId").equals("WTC_OTHERWISE")) {
459                 // there should be only one of these, if there is more then one we will use the last one defined
460
otherwise = transition;
461                 continue;
462             }
463             
464             // get the condition body from the condition tag
465
String JavaDoc conditionBody = transition.getString("conditionExpr");
466             
467             // get the extended attributes for the transition
468
Map JavaDoc extendedAttr = StringUtil.strToMap(transition.getString("extendedAttributes"));
469             
470             // check for a conditionClassName attribute if exists use it
471
if (extendedAttr != null && extendedAttr.get("conditionClassName") != null) {
472                 String JavaDoc conditionClassName = (String JavaDoc) extendedAttr.get("conditionClassName");
473                 transitionOk = this.evalConditionClass(conditionClassName, conditionBody, this.processContext(), extendedAttr);
474             } else {
475                 // since no condition class is supplied, evaluate the expression using bsh
476
if (conditionBody != null) {
477                     transitionOk = this.evalBshCondition(conditionBody, this.processContext());
478                 }
479             }
480                                    
481             if (transitionOk) {
482                 transList.add(transition);
483                 if (split.equals("WST_XOR"))
484                     break;
485             }
486         }
487
488         // we only use the otherwise transition for XOR splits
489
if (split.equals("WST_XOR") && transList.size() == 0 && otherwise != null) {
490             transList.add(otherwise);
491             Debug.logVerbose("Used OTHERWISE Transition.", module);
492         }
493
494         if (Debug.verboseOn()) Debug.logVerbose("[WfProcess.getTransFrom] : Transitions: " + transList.size(), module);
495         return transList;
496     }
497
498     // Gets a specific activity by its key
499
private WfActivity getActivity(String JavaDoc key) throws WfException {
500         Iterator JavaDoc i = getIteratorStep();
501
502         while (i.hasNext()) {
503             WfActivity a = (WfActivity) i.next();
504             if (a.key().equals(key))
505                 return a;
506         }
507         throw new WfException("Activity not an active member of this process");
508     }
509
510     // Complete this workflow
511
private void finishProcess() throws WfException {
512         changeState("closed.completed");
513         Debug.logVerbose("[WfProcess.finishProcess] : Workflow Complete. Calling back to requester.", module);
514         if (requester != null) {
515             WfEventAudit audit = WfFactory.getWfEventAudit(this, null); // this will need to be updated
516

517             try {
518                 requester.receiveEvent(audit);
519             } catch (InvalidPerformer e) {
520                 throw new WfException(e.getMessage(), e);
521             }
522         }
523     }
524
525     // Get the active process activities
526
private List JavaDoc activeSteps() throws WfException {
527         List JavaDoc steps = new ArrayList JavaDoc();
528         Collection JavaDoc c = null;
529
530         try {
531             c = getDelegator().findByAnd("WorkEffort", UtilMisc.toMap("workEffortParentId", runtimeKey()));
532         } catch (GenericEntityException e) {
533             throw new WfException(e.getMessage(), e);
534         }
535         if (c == null)
536             return steps;
537         Iterator JavaDoc i = c.iterator();
538
539         while (i.hasNext()) {
540             GenericValue v = (GenericValue) i.next();
541
542             if (v.get("currentStatusId") != null &&
543                 WfUtil.getOMGStatus(v.getString("currentStatusId")).startsWith("open."))
544                 steps.add(WfFactory.getWfActivity(getDelegator(), v.getString("workEffortId")));
545         }
546         return steps;
547     }
548 }
549
550
Popular Tags