001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
004: * All rights reserved.
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * $Id: AbstractExecutionObject.java,v 1.11.2.1 2007/11/02 16:00:33 drmlipp Exp $
021: *
022: * $Log: AbstractExecutionObject.java,v $
023: * Revision 1.11.2.1 2007/11/02 16:00:33 drmlipp
024: * Merged bug fixes from HEAD.
025: *
026: * Revision 1.13 2007/09/21 07:57:12 drmlipp
027: * Removed superfluous import.
028: *
029: * Revision 1.12 2007/09/21 06:19:35 mlipp
030: * Fixed problem with NamingException during process deletion.
031: *
032: * Revision 1.11 2007/05/03 21:58:19 mlipp
033: * Internal refactoring for making better use of local EJBs.
034: *
035: */
036:
037: package de.danet.an.workflow.domain;
038:
039: import java.rmi.RemoteException;
040: import java.util.ArrayList;
041: import java.util.Collection;
042: import java.util.Date;
043: import java.util.Iterator;
044: import java.util.List;
045: import java.util.Map;
046:
047: import de.danet.an.workflow.api.ProcessDefinition;
048: import de.danet.an.workflow.api.Activity.ClosedCompletedState;
049: import de.danet.an.workflow.apix.ExtActivity;
050: import de.danet.an.workflow.apix.ExtProcess;
051: import de.danet.an.workflow.internalapi.ExtExecutionObjectLocal;
052: import de.danet.an.workflow.localcoreapi.WfExecutionObjectLocal;
053: import de.danet.an.workflow.omgcore.AlreadySuspendedException;
054: import de.danet.an.workflow.omgcore.CannotResumeException;
055: import de.danet.an.workflow.omgcore.CannotStopException;
056: import de.danet.an.workflow.omgcore.CannotSuspendException;
057: import de.danet.an.workflow.omgcore.HistoryNotAvailableException;
058: import de.danet.an.workflow.omgcore.InvalidControlOperationException;
059: import de.danet.an.workflow.omgcore.InvalidDataException;
060: import de.danet.an.workflow.omgcore.InvalidPriorityException;
061: import de.danet.an.workflow.omgcore.InvalidStateException;
062: import de.danet.an.workflow.omgcore.NotRunningException;
063: import de.danet.an.workflow.omgcore.NotSuspendedException;
064: import de.danet.an.workflow.omgcore.ProcessData;
065: import de.danet.an.workflow.omgcore.ResultNotAvailableException;
066: import de.danet.an.workflow.omgcore.SourceNotAvailableException;
067: import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
068: import de.danet.an.workflow.omgcore.UpdateNotAllowedException;
069: import de.danet.an.workflow.omgcore.WfAuditEvent;
070: import de.danet.an.workflow.omgcore.WfExecutionObject;
071: import de.danet.an.workflow.omgcore.WfStateAuditEvent;
072: import de.danet.an.workflow.omgcore.WfExecutionObject.ClosedState;
073: import de.danet.an.workflow.omgcore.WfExecutionObject.NotRunningState;
074: import de.danet.an.workflow.omgcore.WfExecutionObject.OpenState;
075: import de.danet.an.workflow.omgcore.WfExecutionObject.State;
076:
077: /**
078: * <code>AbstractExecutionObject</code> represents a base
079: * implementation of the interface {@link
080: * de.danet.an.workflow.api.ExecutionObject
081: * <code>ExecutionObject</code>}.<P>
082: *
083: * With logger level <code>DEBUG</code>, event handling information
084: * will be logged.
085: */
086: public abstract class AbstractExecutionObject implements
087: WfExecutionObjectLocal, ExtExecutionObjectLocal {
088:
089: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
090: .getLog(AbstractExecutionObject.class);
091:
092: // Make sure that loading this class loads the new states
093: static {
094: State s = RunningState.RUNNING;
095: s = DebugState.ABORTING;
096: s = SuspendedState.SUSPENDED;
097: s = ClosedCompletedState.NORMAL;
098: }
099:
100: //
101: // Persistent attribute accessors and associated methods
102: //
103:
104: /**
105: * The getter method for the persistent attribute <code>key</code>.
106: *
107: * @return the value of key.
108: */
109: protected abstract String getPaKey();
110:
111: /**
112: * The getter method for the persistent attribute <code>name</code>.
113: *
114: * @return the value of name.
115: * @see #setPaName
116: */
117: protected abstract String getPaName();
118:
119: /**
120: * The setter method for the persistent attribute <code>name</code>.
121: *
122: * @param newName the new value of name.
123: * @see #getPaName
124: */
125: protected abstract void setPaName(String newName);
126:
127: /**
128: * The getter method for the persistent attribute <code>description</code>.
129: *
130: * @return the value of description.
131: * @see #setPaDescription
132: */
133: protected abstract String getPaDescription();
134:
135: /**
136: * The setter method for the persistent attribute <code>description</code>.
137: *
138: * @param newDescription the new value of description.
139: * @see #getPaDescription
140: */
141: protected abstract void setPaDescription(String newDescription);
142:
143: /**
144: * The getter method for the persistent attribute <code>priority</code>.
145: *
146: * @return the value of priority.
147: * @see #setPaPriority
148: */
149: protected abstract Priority getPaPriority();
150:
151: /**
152: * The setter method for the persistent attribute <code>priority</code>.
153: *
154: * @param newPriority the new value of priority.
155: * @see #getPaPriority
156: */
157: protected abstract void setPaPriority(Priority newPriority);
158:
159: /**
160: * The getter method for the persistent attribute
161: * <code>lastStateTime</code>. This attribute holds the time when
162: * the state was last changed. This may happen from an explicit
163: * action like the <code>complete()</code> method or via a state
164: * change propagation from another WfmExecutionObject.
165: *
166: * @return the value of lastStateTime.
167: * @see #setPaLastStateTime
168: */
169: protected abstract Date getPaLastStateTime();
170:
171: /**
172: * The setter method for the persistent attribute
173: * <code>lastStateTime</code>.
174: *
175: * @param newLastStateTime the new value of lastStateTime.
176: * @see #getPaLastStateTime
177: */
178: protected abstract void setPaLastStateTime(Date newLastStateTime);
179:
180: /**
181: * The getter method for the persistent attribute
182: * <code>typedState</code>.
183: *
184: * @return the value of typedState.
185: * @see #setPaTypedState
186: */
187: protected abstract State getPaTypedState();
188:
189: /**
190: * The setter method for the persistent attribute
191: * <code>typedState</code>.
192: *
193: * @param newTypedState the new value of typedState.
194: * @see #getPaTypedState
195: */
196: protected abstract void setPaTypedState(State newTypedState);
197:
198: /**
199: * The getter method for the persistent attribute <code>debug</code>.
200: *
201: * @return the value of debug.
202: * @see #setPaDebug
203: */
204: protected abstract boolean getPaDebug();
205:
206: /**
207: * The setter method for the persistent attribute <code>debug</code>.
208: *
209: * @param newDebug the new value of debug.
210: * @see #getPaDebug
211: */
212: protected abstract void setPaDebug(boolean newDebug);
213:
214: /**
215: * The getter method for the persistent attribute
216: * <code>auditEventSelection</code>.
217: *
218: * @return the value of auditEventSelection.
219: * @see #setPaAuditEventSelection
220: */
221: protected abstract int getPaAuditEventSelection();
222:
223: /**
224: * The setter method for the persistent attribute
225: * <code>auditEventSelection</code>.
226: *
227: * @param newAuditEventSelection the new value of
228: * auditEventSelection.
229: * @see #getPaAuditEventSelection
230: */
231: protected abstract void setPaAuditEventSelection(
232: int newAuditEventSelection);
233:
234: /**
235: * The getter method for the persistent attribute
236: * <code>storeAuditEvents</code>.
237: *
238: * @return the value of storeAuditEvents.
239: * @see #setPaStoreAuditEvents
240: */
241: protected abstract boolean getPaStoreAuditEvents();
242:
243: /**
244: * The setter method for the persistent attribute
245: * <code>storeAuditEvents</code>.
246: *
247: * @param newStoreAuditEvents the new value of storeAuditEvents.
248: * @see #getPaStoreAuditEvents
249: */
250: protected abstract void setPaStoreAuditEvents(
251: boolean newStoreAuditEvents);
252:
253: /**
254: * Initializes the class, i.e. resets all attributes to default
255: * values. Note that
256: * {@link #refresh <code>refresh</code>} will be called subsequently.
257: *
258: * @see #dispose
259: */
260: protected void init() {
261: setPaLastStateTime(new Date());
262: setPaPriority(Priority.NORMAL);
263: setPaDebug(false);
264: }
265:
266: /**
267: * Called after change of persistent attributes. May be used to
268: * synchronise state derived from persistent attributes with
269: * the new values.
270: *
271: * @see #init
272: */
273: protected void refresh() {
274: }
275:
276: /**
277: * Releases all allocated resources. The object will be in an
278: * unusable state until resources are reallocated by calling
279: * {@link #init <code>init</code>} and
280: * {@link #refresh <code>refresh</code>}.
281: */
282: protected void dispose() {
283: }
284:
285: //
286: // Domain methods
287: //
288:
289: /**
290: * Returns a hash code value for this object.<P>
291: *
292: * Note that stubs do not in general implement
293: * <code>hashCode</code> correctly, so even if two execution
294: * objects are equal (<code>this.equals(obj)</code> is
295: * <code>true</code>) <code>this.hashCode()</code> need not be
296: * equal to <code>obj.hashCode()</code>.
297: *
298: * @return the hash code value.
299: */
300: public int hashCode() {
301: try {
302: return getPaKey().hashCode();
303: } catch (NullPointerException e) {
304: // workaround for jboss bug #634362
305: return 1;
306: }
307: }
308:
309: /**
310: * Returns the name of the execution object.
311: * @return name of the execution object
312: */
313: public String name() {
314: return getPaName();
315: }
316:
317: /**
318: * Set a new name of the execution object.
319: * @param newValue new name.
320: * @see #name
321: */
322: public void setName(String newValue) {
323: setPaName(newValue);
324: }
325:
326: /**
327: * Returns the key of the execution object.
328: * @return key of the execution object
329: */
330: public String key() {
331: return getPaKey();
332: }
333:
334: /**
335: * Returns the description of the execution object.
336: * @return description of the execution object
337: */
338: public String description() {
339: return getPaDescription();
340: }
341:
342: /**
343: * Set a new description of the execution object.
344: * @param newValue new description
345: * @see #description
346: */
347: public void setDescription(String newValue) {
348: setPaDescription(newValue);
349: }
350:
351: /**
352: * Updates process context of the execution object.
353: * @param newValues the name-value pairs to be set.
354: * @throws InvalidDataException If a name or value type does not match
355: * the signature of this process.
356: * @throws UpdateNotAllowedException If the update is not allowed.
357: */
358: public void setProcessContext(ProcessData newValues)
359: throws InvalidDataException, UpdateNotAllowedException {
360: throw new UpdateNotAllowedException("Not implemented.");
361: }
362:
363: /**
364: * Returns the priority of the execution object.
365: * @return priority of the execution object
366: */
367: public int priority() {
368: return getPaPriority().toInt();
369: }
370:
371: /**
372: * For the first iteration throws an
373: * <code>UpdateNotAllowedException</code>.
374: * @param newValue new priority
375: * @throws InvalidPriorityException when the specified priority is out of
376: * range.
377: * @throws UpdateNotAllowedException when the priority cannot be updated.
378: */
379: public void setPriority(int newValue)
380: throws InvalidPriorityException, UpdateNotAllowedException {
381: setPaPriority(Priority.fromInt(newValue));
382: }
383:
384: /**
385: * Enable or disable debugging of the execution object.
386: * @param debug if the execution object is to be debugged
387: * @throws InvalidStateException if changing debug mode is not
388: * allowed
389: */
390: public void setDebugEnabled(boolean debug)
391: throws InvalidStateException {
392: setPaDebug(debug);
393: }
394:
395: /**
396: * Checks if the execution object is in debugging mode.
397: *
398: * @return <code>true</code> if the execution object is in
399: * debugging mode
400: */
401: public boolean debugEnabled() {
402: return getPaDebug();
403: }
404:
405: //
406: // state handling
407: //
408:
409: /**
410: * Returns the last state time of the execution object.
411: * @return last state time of the execution object
412: */
413: public Date lastStateTime() {
414: return getPaLastStateTime();
415: }
416:
417: /**
418: * Returns a list of all the valid states (as <code>State</code> objects)
419: * that can be reached
420: * from the current state. If there is no valid state, the collection
421: * will be empty.
422: * @return list of all the valid states
423: */
424: protected Collection validTypedStates() {
425: Collection c = (Collection) getStateTransitionMap().get(
426: getPaTypedState());
427: if (c == null) {
428: return new ArrayList(0);
429: }
430: return c;
431: }
432:
433: /**
434: * Returns a list of all the valid states as strings that can be reached
435: * from the current state. If there is no valid state, the collection
436: * will be empty.
437: * @return list of all the valid states
438: */
439: public Collection validStates() {
440: Collection c = (Collection) getStateTransitionMap().get(
441: getPaTypedState());
442: if (c == null) {
443: return new ArrayList(0);
444: }
445: Collection cs = new ArrayList();
446: for (Iterator i = c.iterator(); i.hasNext();) {
447: cs.add(((State) i.next()).toString());
448: }
449: return cs;
450: }
451:
452: /**
453: * Returns the {@link java.util.Map <code>Map</code>} that maps
454: * the execution object states to a {@link java.util.Map
455: * <code>List</code>} of reachable process states. <P>
456: *
457: * The map returns the state transitions allowed as parameters of
458: * {@link
459: * de.danet.an.workflow.localcoreapi.WfExecutionObjectLocal#changeState
460: * <code>changeState</code>} only. I.e. the map does not reflect
461: * all possible transitions, there may be more, but those are only
462: * accessible to the workflow engine itself.
463: * @return the resulting map.
464: */
465: protected abstract Map getStateTransitionMap();
466:
467: /**
468: * Updates the current state of the execution object. As a result
469: * the state of execution objects associated with this execution
470: * object might be updated, too. This implementation effectively
471: * calls {@link ExecutionObjectLocal#changeState
472: * <code>changeState(State)</code>} after converting the argument
473: * to <code>State</code>. Derived classes therefore only need to
474: * override <code>changeState(State)</code>.
475: *
476: * @param newState State to change to.
477: * @throws InvalidStateException If <code>newState</code> is an invalid
478: * state for the execution object.
479: * @throws TransitionNotAllowedException If the transition from the current
480: * state to <code>newState</code> is not allowed.
481: */
482: public void changeState(String newState)
483: throws InvalidStateException, TransitionNotAllowedException {
484: changeState(State.fromString(newState));
485: }
486:
487: /**
488: * Returns the internal state, i.e. the state represented as
489: * <code>State</code> object.
490: * @return internal state.
491: * @see #updateState
492: */
493: public State typedState() {
494: return getPaTypedState();
495: }
496:
497: /**
498: * Updates the current state of the execution object. As a result
499: * the state of execution objects associated with this execution object
500: * may be updated, too. This method uses
501: * the <code>State</code> class to represent states.
502: *
503: * @param newState the new state.
504: * @throws InvalidStateException If <code>newState</code> is an invalid
505: * state for the execution object.
506: * @throws TransitionNotAllowedException If the transition from the current
507: * state to <code>newState</code> is not allowed.
508: */
509: public void changeState(State newState)
510: throws InvalidStateException, TransitionNotAllowedException {
511: try {
512: if (newState.isSameOrSubState(NotRunningState.SUSPENDED)) {
513: suspend();
514: return;
515: }
516: if (newState == OpenState.RUNNING) {
517: resume();
518: return;
519: }
520: if (newState == ClosedState.TERMINATED) {
521: terminate();
522: return;
523: }
524: if (newState == ClosedState.ABORTED) {
525: abort();
526: return;
527: }
528: } catch (NotRunningException e) {
529: throw new TransitionNotAllowedException(e.getClass()
530: .getName()
531: + ": " + e.getMessage());
532: } catch (InvalidControlOperationException e) {
533: throw new TransitionNotAllowedException(e.getClass()
534: .getName()
535: + ": " + e.getMessage());
536: }
537: throw new TransitionNotAllowedException(toString() + " from "
538: + state() + " to " + newState.toString());
539: }
540:
541: /**
542: * Sets the state of this object. The method updates the last
543: * state time and fires a state change event.
544: *
545: * @param newState new state.
546: * @see #typedState
547: */
548: protected void updateState(State newState) {
549: State oldState = getPaTypedState();
550: setPaLastStateTime(new Date());
551: setPaTypedState(newState);
552: int auditSel = getPaAuditEventSelection();
553: // base event information
554: WfAuditEvent event = auditEventBase((this instanceof AbstractProcess) ? WfAuditEvent.PROCESS_STATE_CHANGED
555: : WfAuditEvent.ACTIVITY_STATE_CHANGED);
556: // Now add general or specific information to the event. Note that we
557: // must always fire a state audit event if a process closes (see below),
558: // but we have to add result information only if the event is to be
559: // published.
560: if ((this instanceof AbstractProcess)
561: && newState.isSameOrSubState(State.CLOSED)
562: && (auditSel == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS
563: || auditSel == ProcessDefinition.AUDIT_SELECTION_STATE_EVENTS_ONLY || auditSel == ProcessDefinition.AUDIT_SELECTION_PROCESS_CLOSED_EVENTS_ONLY)) {
564: try {
565: event = new DefaultProcessClosedAuditEvent(event,
566: oldState.toString(), newState.toString(),
567: ((AbstractProcess) this ).result());
568: } catch (ResultNotAvailableException e) {
569: // Cannot happen, see implementation
570: logger.debug("Unexpected exception: " + e.getMessage(),
571: e);
572: }
573: } else {
574: event = new DefaultStateAuditEvent(event, oldState
575: .toString(), newState.toString());
576: }
577: // Note that process closed events
578: // (case AUDIT_SELECTION_PROCESS_CLOSED_EVENTS_ONLY) are fired
579: // because they are handled by the process
580: if (auditSel == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS
581: || auditSel == ProcessDefinition.AUDIT_SELECTION_STATE_EVENTS_ONLY
582: || AbstractActivity.isHandled(event)
583: || AbstractProcess.isHandled(event)) {
584: fireAuditEvent(event);
585: }
586: }
587:
588: /**
589: * Like {@link #updateState <code>updateState</code>} this method
590: * sets the state of this object and fires a state change
591: * event. The state change event will, however, not be fed back to
592: * the engine, i.e. it will not lead to follow up activities and
593: * the last state time will not be updated.<P>
594: *
595: * The method is intended to be used for an interim state change,
596: * i.e. the caller knows that the state will change again before
597: * the transaction is completed.
598: *
599: * @param newState new state.
600: * @see #typedState
601: */
602: protected void updateInterim(State newState) {
603: doUpdateNoFeedback(newState, false);
604: }
605:
606: /**
607: * Like {@link #updateState <code>updateState</code>} this method
608: * sets the state of this object and fires a state change
609: * event. The state change event will, however, not be fed back to
610: * the engine, i.e. it will not lead to follow up activities.<P>
611: *
612: * The method is intended to be used in cases where the engine triggers
613: * a state change itself and also performs all actions that are usually
614: * done in the event handling method.
615: *
616: * @param newState new state.
617: * @see #typedState
618: */
619: protected void updateImmediate(State newState) {
620: doUpdateNoFeedback(newState, true);
621: }
622:
623: private void doUpdateNoFeedback(State newState,
624: boolean updateStateTime) {
625: State oldState = getPaTypedState();
626: setPaTypedState(newState);
627: if (updateStateTime) {
628: setPaLastStateTime(new Date());
629: }
630: int auditSel = getPaAuditEventSelection();
631: if (auditSel == ProcessDefinition.AUDIT_SELECTION_ALL_EVENTS
632: || auditSel == ProcessDefinition.AUDIT_SELECTION_STATE_EVENTS_ONLY) {
633: fireAuditEvent(new DefaultStateAuditEvent(
634: auditEventBase((this instanceof AbstractProcess) ? WfAuditEvent.PROCESS_STATE_CHANGED
635: : WfAuditEvent.ACTIVITY_STATE_CHANGED),
636: oldState.toString(), newState.toString(), true));
637: }
638: }
639:
640: /**
641: * Returns the workflow state.
642: * @return the workflow state.
643: */
644: public State workflowState() {
645: return getPaTypedState().workflowState();
646: }
647:
648: /**
649: * Returns the workflow substate for open execution objects.
650: * @return the workflow substate.
651: */
652: public State whileOpen() {
653: return getPaTypedState().whileOpenState();
654: }
655:
656: /**
657: * Returns the workflow substate for open, not running
658: * execution objects.
659: * @return the workflow substate.
660: */
661: public State whyNotRunning() {
662: return getPaTypedState().whyNotRunningState();
663: }
664:
665: /**
666: * Returns the workflow substate for closed
667: * execution objects.
668: * @return the workflow substate.
669: */
670: public State howClosed() {
671: return getPaTypedState().howClosedState();
672: }
673:
674: /**
675: * Returns the string representation of the state.
676: * @return string representation of the state.
677: */
678: public String state() {
679: return getPaTypedState().toString();
680: }
681:
682: /* Comment copied from Interface. */
683: public void suspend() throws CannotSuspendException,
684: NotRunningException, AlreadySuspendedException {
685: if (typedState().isSameOrSubState(NotRunningState.SUSPENDED)) {
686: throw new AlreadySuspendedException(toString());
687: }
688: if (!typedState().isSameOrSubState(OpenState.RUNNING)) {
689: throw new NotRunningException(toString() + " is " + state());
690: }
691: if (!validTypedStates().contains(NotRunningState.SUSPENDED)) {
692: throw new CannotSuspendException(toString() + " is "
693: + state());
694: }
695: updateState(SuspendedState.SUSPENDED);
696: }
697:
698: /* Comment copied from Interface. */
699: public void resume() throws CannotResumeException,
700: NotRunningException, NotSuspendedException {
701: if (!typedState().isSameOrSubState(OpenState.RUNNING)
702: && !typedState().isSameOrSubState(
703: SuspendedState.SUSPENDED)) {
704: throw new NotRunningException(toString() + " is " + state());
705: }
706: if (!typedState().isSameOrSubState(NotRunningState.SUSPENDED)) {
707: throw new NotSuspendedException(toString());
708: }
709: if (!validTypedStates().contains(OpenState.RUNNING)) {
710: throw new CannotResumeException(toString() + " is "
711: + state());
712: }
713: updateState(RunningState.RUNNING);
714: }
715:
716: /* Comment copied from Interface. */
717: public void terminate() throws CannotStopException,
718: NotRunningException {
719: throw new CannotStopException(toString()
720: + " cannot be terminated.");
721: }
722:
723: /* Comment copied from Interface. */
724: public void abort() throws CannotStopException, NotRunningException {
725: throw new CannotStopException(toString()
726: + " cannot be aborted.");
727: }
728:
729: /* Comment copied from Interface. */
730: public abstract Collection history()
731: throws HistoryNotAvailableException;
732:
733: //
734: // Audit handling
735: //
736:
737: /**
738: * Returns a <code>WfAuditEvent</code> containing information about the
739: * execution object.
740: * @param eventType event type
741: * @return the event containing the required information.
742: */
743: protected abstract WfAuditEvent auditEventBase(String eventType);
744:
745: private static ThreadLocal queuingEvents = new ThreadLocal() {
746: protected Object initialValue() {
747: return Boolean.FALSE;
748: }
749: };
750: private static ThreadLocal eventQueue = new ThreadLocal() {
751: protected Object initialValue() {
752: return new ArrayList();
753: }
754: };
755:
756: /**
757: * Process newly generated event. This fall-back implementation
758: * logs the event with level <code>DEBUG</code> and calls handlers
759: * for state change events. If the event source is an
760: * <code>AbstractProcess</code> its <code>handleAuditEvent</code>
761: * is called. If the event source is an
762: * <code>AbstractActivity</code> both the activity's and the
763: * process' handler are called.<P>
764: *
765: * This implementation is mainly intended for unit tests on the
766: * domain level. It must be overridden by the derived classes.
767: *
768: * @param event Event
769: */
770: protected void fireAuditEvent(WfAuditEvent event) {
771: logger.debug(event.toString());
772: List eq = (List) eventQueue.get();
773: eq.add(event);
774: if (((Boolean) queuingEvents.get()).booleanValue()) {
775: return;
776: }
777: queuingEvents.set(Boolean.TRUE);
778: while (eq.size() > 0) {
779: event = (WfAuditEvent) eq.remove(0);
780: if ((event instanceof WfStateAuditEvent)
781: || (event instanceof ImplCompleteAuditEvent)) {
782: try {
783: WfExecutionObject eo = event.source();
784: if (eo instanceof ExtProcess) {
785: ((ExtProcess) eo).handleAuditEvent(event);
786: } else if (eo instanceof ExtActivity) {
787: ExtActivity aAct = (ExtActivity) eo;
788: aAct.handleAuditEvent(event);
789: if (!(event instanceof ImplCompleteAuditEvent)) {
790: ((ExtProcess) aAct.container())
791: .handleAuditEvent(event);
792: }
793: }
794: } catch (SourceNotAvailableException e) {
795: // can't do anything about this
796: logger.error("Event discarded: " + e.getMessage(),
797: e);
798: } catch (RemoteException e) {
799: // shouldn't really happen on domain level
800: logger.error(e.getMessage(), e);
801: }
802: }
803: }
804: queuingEvents.set(Boolean.FALSE);
805: }
806:
807: /**
808: * Handles the given audit event. The fall back implementation
809: * simply calls
810: * <code>AbstractExecutionObject.handleStateAuditEvent</code> if
811: * the event source is this object and the event is a
812: * <code>WfStateAuditEvent</code> or a
813: * <code>ToolCompletedAuditEvent</code> .
814: * @param event the event.
815: * @ejb.interface-method view-type="remote"
816: */
817: public void handleAuditEvent(WfAuditEvent event) {
818: if (logger.isDebugEnabled()) {
819: logger.debug("Got event: " + event.toString());
820: }
821: // Is this the target?
822: if (event.activityKey() == null) {
823: if (!(this instanceof AbstractProcess)
824: || !event.processKey().equals(getPaKey())) {
825: return;
826: }
827: } else {
828: if (!(this instanceof AbstractActivity)
829: || !event.activityKey().equals(getPaKey())) {
830: return;
831: }
832: }
833: if (event instanceof ToolInvocationFailedAuditEvent) {
834: handleToolInvocationFailedAuditEvent((ToolInvocationFailedAuditEvent) event);
835: return;
836: }
837: if ((event instanceof WfStateAuditEvent)
838: || (event instanceof ImplCompleteAuditEvent)) {
839: handleStateAuditEvent(event);
840: }
841: }
842:
843: /**
844: * Handles a tool invocation failed audit event. The default
845: * implementation does nothing.
846: * @param event the event.
847: */
848: protected void handleToolInvocationFailedAuditEvent(
849: ToolInvocationFailedAuditEvent event) {
850: }
851:
852: /**
853: * Handles a state audit event, this includes
854: * <code>ImplCompleteAuditEvent</code>s. This implementation
855: * simply calls the appropriate handler method
856: * <code>handle<i>Transition</i>Event</code>.
857: * @param event the event.
858: */
859: protected void handleStateAuditEvent(WfAuditEvent event) {
860: try {
861: if (event instanceof ImplCompleteAuditEvent) {
862: handleImplCompletedEvent((ImplCompleteAuditEvent) event);
863: return;
864: }
865: WfStateAuditEvent evt = (WfStateAuditEvent) event;
866: State oldState = State.fromString(evt.oldState());
867: State newState = State.fromString(evt.newState());
868: if (oldState.isSameOrSubState(NotRunningState.NOT_STARTED)) {
869: if (newState == RunningState.RUNNING) {
870: handleStartedEvent(evt);
871: return;
872: }
873: if (newState == ClosedState.TERMINATED) {
874: handleTerminatedEvent(evt);
875: return;
876: }
877: } else if (oldState
878: .isSameOrSubState(NotRunningState.SUSPENDED)) {
879: if (newState == RunningState.RUNNING) {
880: handleResumedEvent(evt);
881: return;
882: }
883: if (newState == ClosedState.ABORTED) {
884: handleAbortedEvent(evt);
885: return;
886: }
887: } else if (oldState.isSameOrSubState(OpenState.RUNNING)) {
888: if (newState == SuspendedState.SUSPENDED) {
889: handleSuspendedEvent(evt);
890: return;
891: }
892: if (newState == ClosedCompletedState.NORMAL) {
893: handleCompletedEvent(evt);
894: return;
895: }
896: if (newState == ClosedState.TERMINATED) {
897: handleTerminatedEvent(evt);
898: return;
899: }
900: }
901: logger
902: .warn("Cannot handle undefined state transition from "
903: + oldState.toString()
904: + " to "
905: + newState.toString() + " for " + this );
906: } catch (InvalidStateException e) {
907: // can't do much about this, shouldn't happen
908: logger.error(e.getMessage(), e);
909: }
910: }
911:
912: /**
913: * Handles a tool completed audit event. The default
914: * implementation does nothing.
915: * @param event the event.
916: */
917: protected void handleImplCompletedEvent(ImplCompleteAuditEvent event) {
918: }
919:
920: /**
921: * Handles a started audit event. The default
922: * implementation does nothing.
923: * @param event the event.
924: */
925: protected void handleStartedEvent(WfStateAuditEvent event) {
926: }
927:
928: /**
929: * Handles a terminated audit event. The default
930: * implementation does nothing.
931: * @param event the event.
932: */
933: protected void handleTerminatedEvent(WfStateAuditEvent event) {
934: }
935:
936: /**
937: * Handles a resumed audit event. The default
938: * implementation does nothing.
939: * @param event the event.
940: */
941: protected void handleResumedEvent(WfStateAuditEvent event) {
942: }
943:
944: /**
945: * Handles a aborting audit event. The default
946: * implementation does nothing.
947: * @param event the event.
948: */
949: protected void handleAbortedEvent(WfStateAuditEvent event) {
950: }
951:
952: /**
953: * Handles a suspended audit event. The default
954: * implementation does nothing.
955: * @param event the event.
956: */
957: protected void handleSuspendedEvent(WfStateAuditEvent event) {
958: }
959:
960: /**
961: * Handles a completed audit event. The default
962: * implementation does nothing.
963: * @param event the event.
964: */
965: protected void handleCompletedEvent(WfStateAuditEvent event) {
966: }
967:
968: }
|