001: /*
002: * $Id: WorkflowEngine.java,v 1.3 2003/08/28 19:06:14 ajzeneski Exp $
003: *
004: * Copyright (c) 2001, 2002 The Open For Business Project - www.ofbiz.org
005: *
006: * Permission is hereby granted, free of charge, to any person obtaining a
007: * copy of this software and associated documentation files (the "Software"),
008: * to deal in the Software without restriction, including without limitation
009: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
010: * and/or sell copies of the Software, and to permit persons to whom the
011: * Software is furnished to do so, subject to the following conditions:
012: *
013: * The above copyright notice and this permission notice shall be included
014: * in all copies or substantial portions of the Software.
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
017: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
018: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
019: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
020: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
021: * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
022: * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023: *
024: */
025: package org.ofbiz.workflow;
026:
027: import java.util.Date;
028: import java.util.List;
029: import java.util.Locale;
030: import java.util.Map;
031:
032: import javax.transaction.InvalidTransactionException;
033: import javax.transaction.SystemException;
034: import javax.transaction.Transaction;
035: import javax.transaction.TransactionManager;
036:
037: import org.ofbiz.base.util.Debug;
038: import org.ofbiz.base.util.StringUtil;
039: import org.ofbiz.base.util.UtilDateTime;
040: import org.ofbiz.base.util.UtilMisc;
041: import org.ofbiz.entity.GenericEntityException;
042: import org.ofbiz.entity.GenericValue;
043: import org.ofbiz.entity.transaction.GenericTransactionException;
044: import org.ofbiz.entity.transaction.TransactionFactory;
045: import org.ofbiz.entity.transaction.TransactionUtil;
046: import org.ofbiz.service.GenericRequester;
047: import org.ofbiz.service.GenericResultWaiter;
048: import org.ofbiz.service.GenericServiceException;
049: import org.ofbiz.service.ModelService;
050: import org.ofbiz.service.ServiceDispatcher;
051: import org.ofbiz.service.engine.GenericEngine;
052: import org.ofbiz.service.job.AbstractJob;
053: import org.ofbiz.service.job.Job;
054: import org.ofbiz.service.job.JobManagerException;
055:
056: /**
057: * WorkflowEngine - Workflow Service Engine
058: *
059: * @author <a href="mailto:jaz@ofbiz.org">Andy Zeneski</a>
060: * @version $Revision: 1.3 $
061: * @since 2.0
062: */
063: public class WorkflowEngine implements GenericEngine {
064:
065: public static final String module = WorkflowEngine.class.getName();
066:
067: protected ServiceDispatcher dispatcher;
068:
069: public WorkflowEngine(ServiceDispatcher dispatcher) {
070: this .dispatcher = dispatcher;
071: }
072:
073: /**
074: * @see org.ofbiz.service.engine.GenericEngine#runSync(java.lang.String, org.ofbiz.service.ModelService, java.util.Map)
075: */
076: public Map runSync(String localName, ModelService modelService,
077: Map context) throws GenericServiceException {
078: GenericResultWaiter waiter = new GenericResultWaiter();
079: runAsync(localName, modelService, context, waiter, false);
080: return waiter.waitForResult();
081: }
082:
083: /**
084: * @see org.ofbiz.service.engine.GenericEngine#runSyncIgnore(java.lang.String, org.ofbiz.service.ModelService, java.util.Map)
085: */
086: public void runSyncIgnore(String localName,
087: ModelService modelService, Map context)
088: throws GenericServiceException {
089: runAsync(localName, modelService, context, null, false);
090: }
091:
092: /**
093: * @see org.ofbiz.service.engine.GenericEngine#runAsync(java.lang.String, org.ofbiz.service.ModelService, java.util.Map, boolean)
094: */
095: public void runAsync(String localName, ModelService modelService,
096: Map context, boolean persist)
097: throws GenericServiceException {
098: runAsync(localName, modelService, context, null, persist);
099: }
100:
101: /**
102: * @see org.ofbiz.service.engine.GenericEngine#runAsync(java.lang.String, org.ofbiz.service.ModelService, java.util.Map, org.ofbiz.service.GenericRequester, boolean)
103: */
104: public void runAsync(String localName, ModelService modelService,
105: Map context, GenericRequester requester, boolean persist)
106: throws GenericServiceException {
107: // Suspend the current transaction
108: TransactionManager tm = TransactionFactory
109: .getTransactionManager();
110: if (tm == null) {
111: throw new GenericServiceException(
112: "Cannot get the transaction manager; cannot run persisted services.");
113: }
114:
115: Transaction parentTrans = null;
116: boolean beganTransaction = false;
117: try {
118: try {
119: parentTrans = tm.suspend();
120: beganTransaction = TransactionUtil.begin();
121: //Debug.logInfo("Suspended transaction; began new: " + beganTransaction, module);
122: } catch (SystemException se) {
123: Debug.logError(se, "Cannot suspend transaction: "
124: + se.getMessage(), module);
125: } catch (GenericTransactionException e) {
126: Debug.logError(e, "Cannot begin nested transaction: "
127: + e.getMessage(), module);
128: }
129:
130: // Build the requester
131: WfRequester req = null;
132: try {
133: req = WfFactory.getWfRequester();
134: } catch (WfException e) {
135: try {
136: TransactionUtil.rollback(beganTransaction);
137: } catch (GenericTransactionException gte) {
138: Debug.logError(gte,
139: "Unable to rollback nested exception.",
140: module);
141: }
142: throw new GenericServiceException(e.getMessage(), e);
143: }
144:
145: // Get the package and process ID::VERSION
146: String packageId = this .getSplitPosition(
147: modelService.location, 0);
148: String packageVersion = this .getSplitPosition(
149: modelService.location, 1);
150: String processId = this .getSplitPosition(
151: modelService.invoke, 0);
152: String processVersion = this .getSplitPosition(
153: modelService.invoke, 1);
154:
155: // Build the process manager
156: WfProcessMgr mgr = null;
157: try {
158: mgr = WfFactory.getWfProcessMgr(dispatcher
159: .getDelegator(), packageId, packageVersion,
160: processId, processVersion);
161: } catch (WfException e) {
162: Debug.logError(e, "Process manager error", module);
163: try {
164: TransactionUtil.rollback(beganTransaction);
165: } catch (GenericTransactionException gte) {
166: Debug.logError(gte,
167: "Unable to rollback nested exception.",
168: module);
169: }
170: throw new GenericServiceException(e.getMessage(), e);
171: } catch (Exception e) {
172: Debug.logError(e, "Un-handled process manager error",
173: module);
174: throw new GenericServiceException(e.getMessage(), e);
175: }
176:
177: // Create the process
178: WfProcess process = null;
179: try {
180: process = mgr.createProcess(req);
181: } catch (NotEnabled ne) {
182: try {
183: TransactionUtil.rollback(beganTransaction);
184: } catch (GenericTransactionException gte) {
185: Debug.logError(gte,
186: "Unable to rollback nested exception.",
187: module);
188: }
189: throw new GenericServiceException(ne.getMessage(), ne);
190: } catch (InvalidRequester ir) {
191: try {
192: TransactionUtil.rollback(beganTransaction);
193: } catch (GenericTransactionException gte) {
194: Debug.logError(gte,
195: "Unable to rollback nested exception.",
196: module);
197: }
198: throw new GenericServiceException(ir.getMessage(), ir);
199: } catch (RequesterRequired rr) {
200: try {
201: TransactionUtil.rollback(beganTransaction);
202: } catch (GenericTransactionException gte) {
203: Debug.logError(gte,
204: "Unable to rollback nested exception.",
205: module);
206: }
207: throw new GenericServiceException(rr.getMessage(), rr);
208: } catch (WfException wfe) {
209: try {
210: TransactionUtil.rollback(beganTransaction);
211: } catch (GenericTransactionException gte) {
212: Debug.logError(gte,
213: "Unable to rollback nested exception.",
214: module);
215: }
216: throw new GenericServiceException(wfe.getMessage(), wfe);
217: } catch (Exception e) {
218: Debug.logError(e, "Un-handled process exception",
219: module);
220: throw new GenericServiceException(e.getMessage(), e);
221: }
222:
223: // Assign the owner of the process
224: GenericValue userLogin = null;
225: if (context.containsKey("userLogin")) {
226: userLogin = (GenericValue) context.remove("userLogin");
227: try {
228: Map fields = UtilMisc.toMap("partyId", userLogin
229: .getString("partyId"), "roleTypeId",
230: "WF_OWNER", "workEffortId", process
231: .runtimeKey(), "fromDate",
232: UtilDateTime.nowTimestamp());
233:
234: try {
235: GenericValue wepa = dispatcher.getDelegator()
236: .makeValue("WorkEffortPartyAssignment",
237: fields);
238: dispatcher.getDelegator().create(wepa);
239: } catch (GenericEntityException e) {
240: try {
241: TransactionUtil.rollback(beganTransaction);
242: } catch (GenericTransactionException gte) {
243: Debug
244: .logError(
245: gte,
246: "Unable to rollback nested exception.",
247: module);
248: }
249: throw new GenericServiceException(
250: "Cannot set ownership of workflow", e);
251: }
252: } catch (WfException we) {
253: try {
254: TransactionUtil.rollback(beganTransaction);
255: } catch (GenericTransactionException gte) {
256: Debug.logError(gte,
257: "Unable to rollback nested exception.",
258: module);
259: }
260: throw new GenericServiceException(
261: "Cannot get the workflow process runtime key");
262: }
263: }
264:
265: // Grab the locale from the context
266: Locale locale = (Locale) context.remove("locale");
267:
268: // Grab the starting activityId from the context
269: String startActivityId = (String) context
270: .remove("startWithActivityId");
271:
272: // Register the process and set the workflow owner
273: try {
274: req.registerProcess(process, context, requester);
275: if (userLogin != null) {
276: Map pContext = process.processContext();
277: pContext.put("workflowOwnerId", userLogin
278: .getString("userLoginId"));
279: process.setProcessContext(pContext);
280: }
281: } catch (WfException wfe) {
282: try {
283: TransactionUtil.rollback(beganTransaction);
284: } catch (GenericTransactionException gte) {
285: Debug.logError(gte,
286: "Unable to rollback nested exception.",
287: module);
288: }
289: throw new GenericServiceException(wfe.getMessage(), wfe);
290: }
291:
292: // Set the initial locale - (in context)
293: if (locale != null) {
294: try {
295: Map pContext = process.processContext();
296: pContext.put("initialLocale", locale);
297: process.setProcessContext(pContext);
298: } catch (WfException wfe) {
299: try {
300: TransactionUtil.rollback(beganTransaction);
301: } catch (GenericTransactionException gte) {
302: Debug.logError(gte,
303: "Unable to rollback nested exception.",
304: module);
305: }
306: throw new GenericServiceException(wfe.getMessage(),
307: wfe);
308: }
309: }
310:
311: // Use the WorkflowRunner to start the workflow in a new thread
312: try {
313: Job job = new WorkflowRunner(process, requester,
314: startActivityId);
315: if (Debug.verboseOn())
316: Debug.logVerbose("Created WorkflowRunner: " + job,
317: module);
318: dispatcher.getJobManager().runJob(job);
319: } catch (JobManagerException je) {
320: try {
321: TransactionUtil.rollback(beganTransaction);
322: } catch (GenericTransactionException gte) {
323: Debug.logError(gte,
324: "Unable to rollback nested exception.",
325: module);
326: }
327: throw new GenericServiceException(je.getMessage(), je);
328: }
329:
330: try {
331: TransactionUtil.commit(beganTransaction);
332: } catch (GenericTransactionException e) {
333: Debug.logError(e, "Cannot commit nested transaction: "
334: + e.getMessage(), module);
335: }
336: } finally {
337: // Resume the parent transaction
338: if (parentTrans != null) {
339: try {
340: tm.resume(parentTrans);
341: //Debug.logInfo("Resumed the parent transaction.", module);
342: } catch (InvalidTransactionException ite) {
343: throw new GenericServiceException(
344: "Cannot resume transaction", ite);
345: } catch (SystemException se) {
346: throw new GenericServiceException(
347: "Unexpected transaction error", se);
348: }
349: }
350: }
351: }
352:
353: private String getSplitPosition(String splitString, int position) {
354: if (splitString.indexOf("::") == -1) {
355: if (position == 0)
356: return splitString;
357: if (position == 1)
358: return null;
359: }
360: List splitList = StringUtil.split(splitString, "::");
361: return (String) splitList.get(position);
362: }
363: }
364:
365: /** Workflow Runner class runs inside its own thread using the Scheduler API */
366: class WorkflowRunner extends AbstractJob {
367:
368: GenericRequester requester;
369: WfProcess process;
370: String startActivityId;
371:
372: WorkflowRunner(WfProcess process, GenericRequester requester,
373: String startActivityId) {
374: super (process.toString());
375: this .process = process;
376: this .requester = requester;
377: this .startActivityId = startActivityId;
378: runtime = new Date().getTime();
379: }
380:
381: protected void finish() {
382: runtime = -1;
383: }
384:
385: public void exec() {
386: try {
387: if (startActivityId != null)
388: process.start(startActivityId);
389: else
390: process.start();
391: } catch (Exception e) {
392: Debug.logError(e, module);
393: if (requester != null)
394: requester.receiveResult(null);
395: }
396: finish();
397: }
398: }
|