View Javadoc

1   package org.apache.maven.werkz;
2   
3   /* ====================================================================
4    *   Licensed to the Apache Software Foundation (ASF) under one or more
5    *   contributor license agreements.  See the NOTICE file distributed with
6    *   this work for additional information regarding copyright ownership.
7    *   The ASF licenses this file to You under the Apache License, Version 2.0
8    *   (the "License"); you may not use this file except in compliance with
9    *   the License.  You may obtain a copy of the License at
10   *
11   *       http://www.apache.org/licenses/LICENSE-2.0
12   *
13   *   Unless required by applicable law or agreed to in writing, software
14   *   distributed under the License is distributed on an "AS IS" BASIS,
15   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   *   See the License for the specific language governing permissions and
17   *   limitations under the License.
18   * ====================================================================
19   */
20  
21  /*
22   $Id: Goal.java 528706 2007-04-13 23:46:00Z aheritier $
23  
24   Copyright 2002 (C) The Werken Company. All Rights Reserved.
25   
26   Redistribution and use of this software and associated documentation
27   ("Software"), with or without modification, are permitted provided
28   that the following conditions are met:
29  
30   1. Redistributions of source code must retain copyright
31   statements and notices.  Redistributions must also contain a
32   copy of this document.
33   
34   2. Redistributions in binary form must reproduce the
35   above copyright notice, this list of conditions and the
36   following disclaimer in the documentation and/or other
37   materials provided with the distribution.
38   
39   3. The name "werkz" must not be used to endorse or promote
40   products derived from this Software without prior written
41   permission of The Werken Company.  For written permission,
42   please contact bob@werken.com.
43   
44   4. Products derived from this Software may not be called "werkz"
45   nor may "werkz" appear in their names without prior written
46   permission of The Werken Company. "werkz" is a registered
47   trademark of The Werken Company.
48   
49   5. Due credit should be given to "the werkz project"
50   ( http://werkz.werken.com/ ).
51   
52   THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS
53   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
54   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
55   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
56   THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
57   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
58   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
59   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
61   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
62   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
63   OF THE POSSIBILITY OF SUCH DAMAGE.
64   
65   */
66  
67  import java.util.ArrayList;
68  import java.util.Collections;
69  import java.util.HashSet;
70  import java.util.Iterator;
71  import java.util.List;
72  import java.util.Set;
73  
74  /**
75   * Abstract Goal to attain.
76   * 
77   * <p>
78   * A <code>Goal</code> embodies both an action and the precursors for that action. A <code>Goal</code>'s precursors
79   * will be satisfied before attempting to perform the target <code>Goal</code>'s action. There may be a case that
80   * once precursors have been satisfied there is no further action required to be perform for a particular
81   * <code>Goal</code>.
82   * </p>
83   * 
84   * <p>
85   * A <code>Goal</code>'s postcursors are also tracked so that if a <code>Goal</code>'s state has been changed and
86   * the <code>Goal</code>s ahead of it in the hierarchy need to be notified, it can <code>percolate</code> forward
87   * and have it's postcursors satisfied.
88   * </p>
89   * 
90   * @see WerkzProject
91   * @see Action
92   * 
93   * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
94   */
95  public class Goal
96  {
97      // ------------------------------------------------------------
98      // Constants
99      // ------------------------------------------------------------
100 
101     /** Empty <code>Goal</code> array. */
102     public static final Goal[] EMPTY_ARRAY = new Goal[0];
103 
104     // ------------------------------------------------------------
105     // Class members
106     // ------------------------------------------------------------
107 
108     /** Unique name. */
109     private String name;
110 
111     /** Ordered list of precursor <code>Goal</code>s. */
112     private List precursors;
113 
114     /** Ordered list of postcursor <code>Goal</code>s. */
115     private List postcursors;
116 
117     /** Action to perform. */
118     private Action action;
119 
120     /** Pre-goal callbacks. */
121     private List preGoalCallbacks;
122 
123     /** Post-goal callbacks. */
124     private List postGoalCallbacks;
125 
126     /** Pre-action callbacks. */
127     private List preActionCallbacks;
128 
129     /** Post-action callbacks. */
130     private List postActionCallbacks;
131 
132     /** Description of the goal (for auto-documenting). */
133     private String description;
134 
135     // ------------------------------------------------------------
136     // Constructors
137     // ------------------------------------------------------------
138 
139     /**
140      * Construct a new <code>Goal</code> with the specified name.
141      * 
142      * @param name
143      *            The name of the <code>Goal</code>.
144      */
145     public Goal( String name )
146     {
147         this.name = name;
148         this.precursors = Collections.EMPTY_LIST;
149         this.postcursors = Collections.EMPTY_LIST;
150 
151         this.preGoalCallbacks = Collections.EMPTY_LIST;
152         this.postGoalCallbacks = Collections.EMPTY_LIST;
153         this.preActionCallbacks = Collections.EMPTY_LIST;
154         this.postActionCallbacks = Collections.EMPTY_LIST;
155     }
156 
157     /**
158      * Construct a new <code>Goal</code> with the specified name and <code>Action</code>.
159      * 
160      * @param name
161      *            The name of the <code>Goal</code>.
162      * @param action
163      *            The <code>Action</code> for this <code>Goal</code>.
164      */
165     public Goal( String name, Action action )
166     {
167         this( name );
168 
169         setAction( action );
170     }
171 
172     // ------------------------------------------------------------
173     // Instance methods
174     // ------------------------------------------------------------
175 
176     /**
177      * Retrieve the name of this <code>Goal</code>.
178      * 
179      * @return This <code>Goal</code>'s name.
180      */
181     public String getName()
182     {
183         return this.name;
184     }
185 
186     /**
187      * Retrieve the description of this <code>Goal</code>.
188      * 
189      * @return The description of this goal.
190      */
191     public String getDescription()
192     {
193         return this.description;
194     }
195 
196     /**
197      * Set the description for this <code>Goal</code>.
198      * 
199      * @param description
200      *            The description of this goal.
201      */
202     public void setDescription( String description )
203     {
204         this.description = description;
205     }
206 
207     /**
208      * Retrieve the <code>Action</code> of this <code>Goal</code>.
209      * 
210      * @return The <code>Action</code> of this <code>Goal</code>.
211      */
212     public Action getAction()
213     {
214         return this.action;
215     }
216 
217     /**
218      * Set the <code>Action</code> of this <code>Goal</code>.
219      * 
220      * @param action
221      *            The <code>Action</code> of this <code>Goal</code>.
222      */
223     public void setAction( Action action )
224     {
225         this.action = action;
226     }
227 
228     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
229     // PRE GOAL
230     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
231 
232     /**
233      * Add a <b>pre-goal</b> callback to this <code>Goal</code>.
234      * 
235      * @param callback
236      *            The callback to add.
237      */
238     public void addPreGoalCallback( PreGoalCallback callback )
239     {
240         if ( Collections.EMPTY_LIST.equals( this.preGoalCallbacks ) )
241         {
242             this.preGoalCallbacks = Collections.synchronizedList( new ArrayList( 3 ) );
243         }
244 
245         this.preGoalCallbacks.add( callback );
246     }
247 
248     /**
249      * Remove all occurences of a <b>pre-goal</b> callback from this <code>Goal</code>.
250      * 
251      * @param callback
252      *            The callback to remove.
253      */
254     public void removePreGoalCallback( PreGoalCallback callback )
255     {
256         while ( this.preGoalCallbacks.remove( callback ) )
257         {
258             // intentionally left blank;
259         }
260     }
261 
262     /**
263      * Retrieve an unmodifiable list of the <b>pre-goal</b> callbacks.
264      * 
265      * @return An unmodifiable <code>List</code> of <code>PreGoalCallback</code>s.
266      */
267     public List getPreGoalCallbacks()
268     {
269         return Collections.unmodifiableList( this.preGoalCallbacks );
270     }
271 
272     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
273     // POST GOAL
274     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
275 
276     /**
277      * Add a <b>post-goal</b> callback to this <code>Goal</code>.
278      * 
279      * @param callback
280      *            The callback to add.
281      */
282     public void addPostGoalCallback( PostGoalCallback callback )
283     {
284         if ( Collections.EMPTY_LIST.equals( this.postGoalCallbacks ) )
285         {
286             this.postGoalCallbacks = Collections.synchronizedList( new ArrayList( 3 ) );
287         }
288 
289         this.postGoalCallbacks.add( callback );
290     }
291 
292     /**
293      * Remove all occurences of a <b>post-goal</b> callback from this <code>Goal</code>.
294      * 
295      * @param callback
296      *            The callback to remove.
297      */
298     public void removePostGoalCallback( PostGoalCallback callback )
299     {
300         while ( this.postGoalCallbacks.remove( callback ) )
301         {
302             // intentionally left blank;
303         }
304     }
305 
306     /**
307      * Retrieve an unmodifiable list of the <b>post-goal</b> callbacks.
308      * 
309      * @return An unmodifiable <code>List</code> of <code>PostGoalCallback</code>s.
310      */
311     public List getPostGoalCallbacks()
312     {
313         return Collections.unmodifiableList( this.postGoalCallbacks );
314     }
315 
316     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
317     // PRE ACTION
318     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
319 
320     /**
321      * Add a <b>pre-action</b> callback to this <code>Goal</code>.
322      * 
323      * @param callback
324      *            The callback to add.
325      */
326     public void addPreActionCallback( PreActionCallback callback )
327     {
328         if ( this.preActionCallbacks.equals( Collections.EMPTY_LIST ) )
329         {
330             this.preActionCallbacks = Collections.synchronizedList( new ArrayList( 3 ) );
331         }
332 
333         this.preActionCallbacks.add( callback );
334     }
335 
336     /**
337      * Remove all occurences of a <b>pre-action</b> callback from this <code>Goal</code>.
338      * 
339      * @param callback
340      *            The callback to remove.
341      */
342     public void removePreActionCallback( PreActionCallback callback )
343     {
344         while ( this.preActionCallbacks.remove( callback ) )
345         {
346             // intentionally left blank;
347         }
348     }
349 
350     /**
351      * Retrieve an unmodifiable list of the <b>pre-action</b> callbacks.
352      * 
353      * @return An unmodifiable <code>List</code> of <code>PreActionCallback</code>s.
354      */
355     public List getPreActionCallbacks()
356     {
357         return Collections.unmodifiableList( this.preActionCallbacks );
358     }
359 
360     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
361     // POST ACTION
362     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
363 
364     /**
365      * Add a <b>post-action</b> callback to this <code>Goal</code>.
366      * 
367      * @param callback
368      *            The callback to add.
369      */
370     public void addPostActionCallback( PostActionCallback callback )
371     {
372         if ( this.postActionCallbacks.equals( Collections.EMPTY_LIST ) )
373         {
374             this.postActionCallbacks = Collections.synchronizedList( new ArrayList( 3 ) );
375         }
376 
377         this.postActionCallbacks.add( callback );
378     }
379 
380     /**
381      * Remove all occurences of a <b>post-action</b> callback from this <code>Goal</code>.
382      * 
383      * @param callback
384      *            The callback to remove.
385      */
386     public void removePostActionCallback( PostActionCallback callback )
387     {
388         while ( this.postActionCallbacks.remove( callback ) )
389         {
390             // intentionally left blank;
391         }
392     }
393 
394     /**
395      * Retrieve an unmodifiable list of the <b>post-action</b> callbacks.
396      * 
397      * @return An unmodifiable <code>List</code> of <code>PostActionCallback</code>s.
398      */
399     public List getPostActionCallbacks()
400     {
401         return Collections.unmodifiableList( this.postActionCallbacks );
402     }
403 
404     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
405     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
406 
407     /**
408      * Determine if this <code>Goal</code> has been satisfied for the specified <code>Session</code>.
409      * 
410      * @param session
411      *            The <code>Session</code> context in which to test for goal satisfaction.
412      * 
413      * @return <code>true</code> if this <code>Goal</code> has been satisfied within the <code>Session</code>,
414      *         otherwise <code>false</code>.
415      */
416     public boolean isSatisfied( Session session )
417     {
418         return session.isGoalSatisfied( this );
419     }
420 
421     /**
422      * Add a precursor <code>Goal</code> to this <code>Goal</code>.
423      * 
424      * @param precursor
425      *            The precursor <code>Goal</code> that must be satisfied before performing this <code>Goal</code>.
426      * 
427      * @throws CyclicGoalChainException
428      *             if adding the precursor would result in a cyclic dependency.
429      */
430     public void addPrecursor( Goal precursor ) throws CyclicGoalChainException
431     {
432         if ( Collections.EMPTY_LIST.equals( this.precursors ) )
433         {
434             this.precursors = Collections.synchronizedList( new ArrayList() );
435         }
436 
437         this.precursors.add( precursor );
438 
439         try
440         {
441             checkForCycles();
442 
443             precursor.addInternalPostcursor( this );
444         }
445         catch ( CyclicGoalChainException e )
446         {
447             e.fillInStackTrace();
448             this.precursors.remove( precursor );
449             throw e;
450         }
451     }
452 
453     /**
454      * Add a postcursor <code>Goal</code> to this <code>Goal</code>.
455      * 
456      * @param postcursor
457      *            The postcursor <code>Goal</code> that depends on this <code>Goal</code>.
458      * 
459      * @throws CyclicGoalChainException
460      *             if adding the postcursor would result in a cyclic dependency.
461      */
462     public void addPostcursor( Goal postcursor ) throws CyclicGoalChainException
463     {
464         postcursor.addPrecursor( this );
465     }
466 
467     /**
468      * Adds a postcursor <code>Goal</code> without looping back to the <code>postcursor.addPrecursor</code> and
469      * entering an infinite loop.
470      * 
471      * @param postcursor
472      *            The postcursor <code>Goal</code> that is already tracking this
473      *            <code>Goal</goal> as a precursor and is letting this 
474      *      <code>Goal</code> know that it is a postcursor.
475      */
476     private void addInternalPostcursor( Goal postcursor )
477     {
478         if ( Collections.EMPTY_LIST.equals( this.postcursors ) )
479         {
480             this.postcursors = Collections.synchronizedList( new ArrayList() );
481         }
482 
483         this.postcursors.add( postcursor );
484     }
485 
486     /**
487      * Retrieve an unmodifiable <code>List</code> of this <code>Goal</code>'s precursor <code>Goal</code>s.
488      * 
489      * @return The <code>List<code> of precursor <code>Goal</code>s.
490      */
491     public List getPrecursors()
492     {
493         return Collections.unmodifiableList( this.precursors );
494     }
495 
496     /**
497      * Retrive an unmodifiable <code>List</code> of this <code>Goal</code>'s postcursor <code>Goal</code>s.
498      * 
499      * @return The <code>List</code> of postcursor <code>Goal</code>s.
500      */
501     public List getPostcursors()
502     {
503         return Collections.unmodifiableList( this.postcursors );
504     }
505 
506     /**
507      * Attempt to attain this <code>Goal</code>'s precursor <code>Goal</code>s.
508      * 
509      * @param session
510      *            The context in which to attain goals.
511      * 
512      * @throws UnattainableGoalException
513      *             if unable to satisfy a precursor.
514      * @throws NoActionDefinitionException
515      *             if this goal contains no action definition.
516      */
517     void attainPrecursors( Session session ) throws UnattainableGoalException, NoActionDefinitionException
518     {
519         Iterator precursorIter = getPrecursors().iterator();
520         Goal eachPrereq = null;
521 
522         while ( precursorIter.hasNext() )
523         {
524             eachPrereq = (Goal) precursorIter.next();
525 
526             eachPrereq.attain( session );
527         }
528     }
529 
530     /**
531      * Attempt to attain this <code>Goal</code>'s postcursor <code>Goal</code>s.
532      * 
533      * @param session
534      *            The context in which to attain goals.
535      * 
536      * @throws UnattainableGoalException
537      *             if unable to satisfy a postcursor.
538      * @throws NoActionDefinitionException
539      *             if this goal contains no action definition.
540      */
541     void percolatePostcursors( Session session ) throws UnattainableGoalException, NoActionDefinitionException
542     {
543         Iterator postreqIter = getPostcursors().iterator();
544         Goal eachPostreq = null;
545 
546         while ( postreqIter.hasNext() )
547         {
548             eachPostreq = (Goal) postreqIter.next();
549 
550             eachPostreq.percolate( session );
551         }
552     }
553 
554     /**
555      * Perform a cyclic dependency check.
556      * 
557      * @throws CyclicGoalChainException
558      *             if a dependency cycle is discovered.
559      */
560     void checkForCycles() throws CyclicGoalChainException
561     {
562         Set visited = new HashSet();
563 
564         checkForCycles( this, visited );
565     }
566 
567     /**
568      * Perform a cyclic dependency check.
569      * 
570      * @param initialGoal
571      *            The root <code>Goal</code> initiating the cycle check.
572      * @param visited
573      *            The <code>Set</code> of all <code>Goal</code>s visited between the root <code>initialGoal</code>
574      *            and this <code>Goal</code>.
575      * 
576      * @throws CyclicGoalChainException
577      *             if a cyclic dependency is detected.
578      */
579     void checkForCycles( Goal initialGoal, Set visited ) throws CyclicGoalChainException
580     {
581         if ( visited.contains( this ) )
582         {
583             throw new CyclicGoalChainException( initialGoal );
584         }
585 
586         visited.add( this );
587 
588         Iterator precursorIter = getPrecursors().iterator();
589         Goal eachPrereq = null;
590         while ( precursorIter.hasNext() )
591         {
592             eachPrereq = (Goal) precursorIter.next();
593 
594             eachPrereq.checkForCycles( initialGoal, visited );
595             visited.remove( eachPrereq );
596         }
597     }
598 
599     /**
600      * Attempt to attain this <code>Goal</code>.
601      * 
602      * @param session
603      *            The context in which to attain goals.
604      * 
605      * @throws UnattainableGoalException
606      *             if unable to attain this <code>Goal</code> or one of its precursor <code>Goal</code>s.
607      * @throws NoActionDefinitionException
608      *             if this goal contains no action definition.
609      */
610     public final void attain( Session session ) throws UnattainableGoalException, NoActionDefinitionException
611     {
612         if ( session.isGoalSatisfied( this ) )
613         {
614             // session.info( getName() + ":" );
615             // session.info( "" );
616             return;
617         }
618 
619         // session.info( getName() + ": checking precursors." );
620 
621         attainPrecursors( session );
622 
623         fire( session );
624     }
625 
626     /**
627      * Attempt to percolate this <code>Goal</code> through to its Postcursors.
628      * 
629      * @param session
630      *            The context in which to percolate goals.
631      * 
632      * @throws UnattainableGoalException
633      *             if unable to attain this <code>Goal</code> or one of its precursor <code>Goal</code>s.
634      * @throws NoActionDefinitionException
635      *             if this goal contains no action definition.
636      */
637     public final void percolate( Session session ) throws UnattainableGoalException, NoActionDefinitionException
638     {
639         if ( session.isGoalSatisfied( this ) )
640         {
641             return;
642         }
643 
644         fire( session );
645 
646         percolatePostcursors( session );
647     }
648 
649     /**
650      * Fires pre-goal callbacks, the <code>Goal</code>'s action, if need be, and the post-goal callbacks.
651      * 
652      * @param session
653      *            The context in which to fire the goal.
654      * 
655      * @throws UnattainableGoalException
656      *             if unable to attain this <code>Goal</code> or one of its precursor <code>Goal</code>s.
657      * @throws NoActionDefinitionException
658      *             if this goal contains no action definition.
659      */
660     private final void fire( Session session ) throws UnattainableGoalException, NoActionDefinitionException
661     {
662         session.info( getName() + ":" );
663 
664         // log.info( "begin goal" );
665 
666         try
667         {
668             // session.info( getName() + ": firing pre-goal callbacks." );
669             firePreGoalCallbacks();
670         }
671         catch ( Exception e )
672         {
673             throw new UnattainableGoalException( getName(), e );
674         }
675 
676         Action action = getAction();
677 
678         if ( action == null )
679         {
680             throw new NoActionDefinitionException( this );
681         }
682 
683         if ( action.requiresAction() )
684         {
685             try
686             {
687                 // session.info( getName() + ": firing pre-action callbacks." );
688                 // log.info( "begin action" );
689                 firePreActionCallbacks();
690                 // session.info( getName() + ": firing goal action." );
691                 getAction().performAction( session );
692                 // session.info( getName() + ": firing post-action callbacks." );
693                 firePostActionCallbacks();
694                 // log.info( "end action" );
695             }
696             catch ( Exception e )
697             {
698                 throw new UnattainableGoalException( getName(), e );
699             }
700         }
701 
702         try
703         {
704             // session.info( getName() + ": firing post-goal callbacks." );
705             firePostGoalCallbacks();
706         }
707         catch ( Exception e )
708         {
709             throw new UnattainableGoalException( getName(), e );
710         }
711 
712         // log.info( "end goal" );
713 
714         // session.info( getName() + ": checking done." );
715 
716         session.addSatisfiedGoal( this );
717 
718         session.info( "" );
719     }
720 
721     /**
722      * Fire the pre-goal callbacks.
723      * 
724      * @throws Exception
725      *             if an error occurs while firing a callback.
726      */
727     void firePreGoalCallbacks() throws Exception
728     {
729         Iterator callbackIter = this.preGoalCallbacks.iterator();
730         PreGoalCallback eachCallback = null;
731 
732         if ( !callbackIter.hasNext() )
733         {
734             return;
735         }
736 
737         // log.info( "begin pre-goal callbacks" );
738 
739         while ( callbackIter.hasNext() )
740         {
741             eachCallback = (PreGoalCallback) callbackIter.next();
742             eachCallback.firePreGoal( this );
743         }
744 
745         // log.info( "end pre-goal callbacks" );
746     }
747 
748     /**
749      * Fire the post-goal callbacks.
750      * 
751      * @throws Exception
752      *             if an error occurs while firing a callback.
753      */
754     void firePostGoalCallbacks() throws Exception
755     {
756         Iterator callbackIter = this.postGoalCallbacks.iterator();
757         PostGoalCallback eachCallback = null;
758 
759         while ( callbackIter.hasNext() )
760         {
761             eachCallback = (PostGoalCallback) callbackIter.next();
762 
763             eachCallback.firePostGoal( this );
764         }
765     }
766 
767     /**
768      * Fire the pre-action callbacks.
769      * 
770      * @throws Exception
771      *             if an error occurs while firing a callback.
772      */
773     void firePreActionCallbacks() throws Exception
774     {
775         Iterator callbackIter = this.preActionCallbacks.iterator();
776         PreActionCallback eachCallback = null;
777 
778         while ( callbackIter.hasNext() )
779         {
780             eachCallback = (PreActionCallback) callbackIter.next();
781 
782             eachCallback.firePreAction( this );
783         }
784     }
785 
786     /**
787      * Fire the post-action callbacks.
788      * 
789      * @throws Exception
790      *             if an error occurs while firing a callback.
791      */
792     void firePostActionCallbacks() throws Exception
793     {
794         Iterator callbackIter = this.postActionCallbacks.iterator();
795         PostActionCallback eachCallback = null;
796 
797         while ( callbackIter.hasNext() )
798         {
799             eachCallback = (PostActionCallback) callbackIter.next();
800 
801             eachCallback.firePostAction( this );
802         }
803     }
804 
805     // ------------------------------------------------------------
806     // java.lang.Object implementation
807     // ------------------------------------------------------------
808 
809     /**
810      * Retrieve the hash-code of this object.
811      * 
812      * <p>
813      * The hash-code is derrived <b>only</b> from the name of the <code>Goal</code> as returned by {@link #getName}.
814      * </p>
815      * 
816      * @return The hash-code of this object.
817      */
818     public int hashCode()
819     {
820         return getName().hashCode();
821     }
822 
823     /**
824      * Determine if two <code>Goal</code> objects are equivelant.
825      * 
826      * <p>
827      * Equivelancy is determined <b>only</b> from the names of the <code>Goal</code>s as return by {@link #getName}.
828      * </p>
829      * 
830      * @param thatObj
831      *            The object to compare to this object.
832      * 
833      * @return The hash-code of this object.
834      */
835     public boolean equals( Object thatObj )
836     {
837         Goal that = (Goal) thatObj;
838 
839         return this.getName().equals( that.getName() );
840     }
841 
842     /**
843      * Produce a textual representation suitable for debugging.
844      * 
845      * @return A textual representation suitable for debugging.
846      */
847     public String toString()
848     {
849         return "[Goal: name=\"" + getName() + "\"]" + "; precursor=" + getPrecursors() + "]";
850     }
851 
852 }