1 /*********************************************************************
2 Copyright 2012, Ralph Ronnquist.
4 This file is part of GORITE.
6 GORITE is free software: you can redistribute it and/or modify it
7 under the terms of the Lesser GNU General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GORITE is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the Lesser GNU General Public
17 License along with GORITE. If not, see <http://www.gnu.org/licenses/>.
18 **********************************************************************/
20 package com.intendico.gorite;
22 import com.intendico.data.Query;
23 import com.intendico.data.Ref;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.Vector;
30 * The Goal class is base class for a goal oriented process modelling
31 * through a hierarchy that reflects the decomposition of a goal as an
32 * abstraction of sub goals. A process model is a hierarchy with a
33 * top level goal that is sucessively decomposed as particular
34 * combinations of sub goals. The top level goal is achieved by means
35 * of processing its sub goals according to the goal type.
37 * <p> A goal is processed by instantiating it via its {@link
38 * #instantiate} method, which creates the {@link Instance} object
39 * that represents an actual intention to achieve the goal. The
40 * intention is then pursued via the {@link Instance#perform} method,
41 * which eventually results in invoking one of the particular {@link
42 * Instance#action} methods that implement the particular execution
45 * <p> The base class is also used for task goals, where the {@link
46 * #execute} method is overridden to perform the intended task. In
47 * many cases, the task execution is a matter of processing one or
48 * more input data elements to proved one or more result data
49 * elements, as illustrated by the following example:
51 * public States execute(Data data) {
52 * data.setValue( "result", process( data.getValue( "input" ) ) );
57 * Of course, the data element names need to be designed and defined
58 * in the full application context, since all goal executions of the
59 * same intention use the same {@link Data} object. Typically you
60 * would employ some data naming scheme to ensure appropriate name
63 * <p> Note that an Goal may be augmented with a reimplemented {@link
64 * Goal#instantiate(String,Data)} method as way of defining data
65 * values, a reimplemented {@link Goal#execute(Data,Goal.Instance)}
66 * method as way of defining post processing, and a reimplemented
67 * {@link Goal#cancelled(Goal.Instance)} method as way of restoring
68 * state if the goal execution is cancelled.
70 * In particular, if the implementation requires a dynamic lexical
71 * environment for each execution, then the {@link
72 * Goal#instantiate(String,Data)} needs to create a vacuous Goal
73 * extension with that environment and ultimately containing the
74 * associated {@link Goal#execute(Data,Goal.Instance)} method. The
75 * following is an illustration example of this.
77 * public Goal.Instance instantiate(String head,Data data) {
78 * return new Goal( "achieving " + getGoalName() ) {
80 * public States execute(Data data) {
81 * // Sees a dynamically instantiated x
82 * while ( x++ < 10 ) {
87 * }.instantiate( head, goal );
91 * Using that approach, a vacuous Goal object, with the "int x"
92 * member, is created for each instantation of the surrounding goal,
93 * and then that goal is instantiated for providing the actual
99 * Interface for intrusive tracing of goal execution.
102 public interface Tracer {
105 * Invoked before performing an instance for given data. This
106 * method is invoked by the gorite executor thread prior to
107 * entering or re-entering the goal instance execution. A
108 * Tracer implementation may, by hogging the thread, hold back
109 * the execution while inspecting the model state. The Data is
110 * the instance data to be; this data object becomes the
111 * {@link Instance#data} upon the first entry (after the call
114 public void enterPerform(Instance instance,Data d);
117 * Invoked after performing instance returns. This method is
118 * invoked when the execution thread stops performing the
119 * instance, which is either because it is finished, or
120 * because it is blocked or stopped. The given {@link States}
121 * is the execution return value, which in many cases is also
122 * cached in {@link Instance#state}.
124 public void exitPerform(Instance instance,States s);
127 * Invoked after performing instance throws exception. This
128 * method is invoked when the execution thread stops
129 * performing the instance due to it throwing an exception or
130 * ({@link Throwable}). If it happens to be a {@link
131 * LoopEndException} or {@link ParallelEndException}, then the
132 * exception is passed in on the call, otherwise null is
133 * passed in. Note that this invocation happens in the
134 * "finally block" for the pending exception propagation,
135 * which thus gets propagated when the method returns.
137 public void exceptionPerform(Instance instance,Exception e);
142 * The global intrusive goal execution tracer, if any. This may be
143 * set via the command line property <tt>-Dgorite.tracer=XX</tt>
144 * where <tt>XX</tt> is the class name for the Tracer
145 * implementation to instantiate. It may also be set in code.
147 public static Tracer tracer = null;
150 * These are the return values of the {@link #execute(Data)}
151 * method, to report on the state of a goal instance execution.
153 public /*enum*/ static class States {
156 * A "name" of a States object.
158 protected String name;
161 * The PASSED state is returned to indicate that the goal
162 * execution has completed successfully.
164 public final static States PASSED =
165 new States() {{ name = "PASSED"; }};
168 * The FAILED state is returned to indicate that the goal
169 * execution has completed without success.
171 public final static States FAILED =
172 new States() {{ name = "FAILED"; }};
175 * The CANCEL state is not returned by goal instance
176 * execution, but is used to mark an {@link Instance} when the
177 * execution has been cancelled.
179 public final static States CANCEL =
180 new States() {{ name = "CANCEL"; }};
183 * The BLOCKED state is returned to indicate that the goal
184 * execution has not completed, and that it cannot progress
185 * until a blocking condition has been removed. This return
186 * code is further a promise that something will notify the
187 * execution system via {@link Data#setFlag} call when the
188 * blocking condition possibly has changed. Having returned
189 * this code, the goal instance execution may be re-entered
190 * whenever the execution system so wishes, regardless of the
191 * blocking condition, and the execution method needs to
192 * confirm the condition or return BLOCKED again.
193 * @see Data#setTrigger(Observable)
194 * @see Data#setTrigger(Query)
196 public final static States BLOCKED =
197 new States() {{ name = "BLOCKED"; }};
200 * The STOPPED state is returned to indicate that the goal
201 * execution has not completed, but is interrupted for some
202 * reason other than a blocking condition. The goal instance
203 * execution is thus expecting re-entry as soon as possible.
205 public final static States STOPPED =
206 new States() {{ name = "STOPPED"; }};
211 public String toString() {
217 * The property name for tracing goal execution.
219 public static final String TRACE = "gorite.goal.trace";
222 * The name of the data element that identifies the executing
225 public static final String PERFORMER = "this performer";
228 * The name of this goal. There are no restrictions on which names
229 * goals have, but it is the name that identifies the goal when
230 * resolving a {@link BDIGoal} or a {@link TeamGoal} into some
231 * plan to achieve it. Conceptually it is the name that *is* the
232 * goal, wheras a Goal object rather is a node in a goal hierarchy
233 * to represent the idea of achieving the named goal.
235 * <p> An actual attempt to achieve the named goal is also
236 * separate both from the goal (name) itself, and from the Goal
237 * object (the idea of achieving it). Such an attempt is
238 * understood as an "intention", which in GORITE gets represented
239 * by a {@link Goal.Instance}.
241 private String name = null;
244 * The control data, if any. This is only used by certain kinds of
245 * goals, to carry some certain definition detail separate from
248 * <p> It is for instance used by {@link ParallelGoal} and {@link
249 * RepeatGoal} goals to name the data element that identifies each
251 * @see Data#fork(String,int,String)
252 * @see Data#join(String,HashSet,String)
254 private String control = null;
257 * The execution group to be used for pursuing the goal. If set,
258 * it is the name of the {@link Performer.TodoGroup} to use for
259 * goal instance execution. Goals of the same group are then
260 * managed by the execution management associated with the {@link
261 * Performer.TodoGroup}.
263 private String group = null;
266 * The sub goals of this goal.
268 private Goal [] subgoals;
271 * Where is this created?
273 final public StackTraceElement created;
278 public Goal(String n,Goal [] sg) {
281 StackTraceElement [] where = new Error().getStackTrace();
282 for ( int i = 0; i < where.length; i++ ) {
283 String c = where[i].getClassName();
284 if ( ! c.startsWith( "com.intendico.gorite." ) ) {
293 * Convenience constructor without sub goals. The sub goals may be
294 * set explicitly as value to the {@link #subgoals} member.
296 public Goal(String n) {
301 * This flag captures whether or not the {@link #TRACE} property
302 * set (to any value) upon class initialisation, and it may be set
303 * or reset subsequently for controlling the amount of goal
304 * execution logging to generate.
306 * <p> Ordinarily one will turn on standard goal execution tracing
307 * by using the command line argument:
309 * -Dgorite.goal.trace=yes
312 public static boolean tracing = System.getProperty( TRACE, null ) != null;
314 // Set up a tracer if declared
316 String c = System.getProperty( "gorite.tracer", null );
319 tracer = (Tracer) Class.forName( c ).newInstance();
320 } catch (Exception e) {
326 * This is a utility method to discover whether tracing has been
327 * requested by defining the {@link #TRACE} property, or not.
329 public static boolean isTracing() {
338 * This is the primary execution method for a goal. It is invoked
339 * from the {@link Instance} object for a goal so as to achieve
340 * the goal, and it is told about both the calling goal {@link
341 * Instance} and {@link Data}. This base implementation invokes
342 * {@link Instance#action(String,Data)} for actual progress of the
345 * <p> This method may be overridden by a user method when
346 * defining a task goal that needs access to the {@link Instance}
347 * object. See {@link #execute(Data)} for details.
349 public States execute(Data d,Instance i)
350 throws LoopEndException, ParallelEndException {
351 return i.action( i.head, d );
355 * This is the simplified execution method for task goal. It is
356 * invoked from the {@link Instance} object for a task goal so as
357 * to achieve the goal by means of Java code, and it is told only
358 * about the intention {@link Data}. This base implementation
359 * merely returns {@link States#PASSED}, and it should be
360 * overridden by a user method when defining an actual task goal.
362 * <p> The method returns either {@link States#PASSED} or {@link
363 * States#FAILED} to indicate that the execution has terminated
364 * and the goal is achieved or not, or it returns {@link
365 * States#STOPPED} or {@link States#BLOCKED} to indicate that the
366 * attempt of achieving the goal is still going on, but execution
367 * control is relinquished temporarily (so as to allow other
368 * intentions to progress). By returning {@link States#BLOCKED},
369 * it also indicates that this code need not be invoked unless
370 * some other intention has progressed, but the {@link Executor}
371 * may choose to go idle. This is typically combined with the
372 * setting of an execution trigger, which is done via {@link
373 * Data#setTrigger(Query)} or {@link Data#setTrigger(Observable)},
374 * to ensure that the {@link Executor} wakes up when something
375 * interesting has happened.
377 public States execute(Data d)
378 throws InterruptedException, LoopEndException, ParallelEndException {
379 return States.PASSED;
383 * Utility method for instantiating a goal according to its
384 * type. Each goal type has its own instance class extension,
385 * which implement the ways in which a goal is achieved through
388 public Instance instantiate(String head,Data d) {
389 return new Instance( head );
393 * Control callback whereby the goal gets notified that an (its)
394 * intention is cancelled.
396 public void cancelled(Instance which) {
400 * Base class for goal instance execution procedures.
402 public class Instance {
405 * The trace name of the execution point. This defines where
406 * in the abstract goal execution tree this instance appears.
407 * The head is a kind of cursor into the abstract tree, set up
408 * as a sequence of numbers that tell which branch to follow
409 * so as to eventually arrive at this node. Branches are
410 * numbered from 0 and up, i.e. branch #3 is the fourth
411 * branch; for some goal types the branch number is also an
412 * index into the {@link #subgoals} array for the goal the
413 * execution is an instantiation of.
414 * The numbers are separated by characters that mark which
415 * kind of expansion the abstract tree has at the point.
417 * <li> ".n" marks insantiation of subgoal n
418 * <li> ":n" marks repetition n or branch n, for {@link
419 * LoopGoal}, {@link ParallelGoal}, {@link RepeatGoal} and
421 * <li> "*n" marks attempt n for a {@link BDIGoal}
424 final public String head;
427 * A more verbose intention name, which consists of the head
428 * and the goal name. This is used as key for the {@link
429 * Data.Bindings} of this intention.
431 final public String thread_name;
434 * The Data object for this instance.
436 public Data data = null;
439 * The monitoring state. This is the state an intention
440 * monitor should return; it starts off as BLOCKED, and turns
441 * into PASSED, FAILED or STOPPED (upon exception).
443 public States state = States.BLOCKED;
446 * Returns the goal of this instance.
448 public Goal getGoal() {
453 * Returns the plan argument object, if any, for this
456 public Object getArgs() {
457 return data.getArgs( head );
461 * Determine the plan-local variable name for the given short
464 public String local(String name) {
465 int i = head.lastIndexOf( "*" );
468 return head.substring( 0, i ) + "-" + name;
472 * Returns the value of a data element as seen by this
475 public Object getValue(String name) {
478 return data.getValue( name, thread_name );
482 * Sets a data value onto the innermost lookup path as seen by
483 * this intention. If an element of the name is found, then
484 * that element gains a new value, otherwise a new element is
485 * created for the most local binding of this intention.
487 public void setValue(String name,Object value) {
488 data.setValue( thread_name, name, value );
492 * Utility method for updating the monitoring state, which
493 * also notifies any waiting thread.
495 synchronized public void setMonitoring(States s) {
497 trace( "-- noting", " (" + s + ")" );
499 if ( state == States.BLOCKED )
506 * Utility method to cancel the execution of this instance.
507 * This method is typically overridden by extension classes in
508 * order to propagate cancellation.
510 public void cancel() {
512 System.err.println( "** cancel " + head + " " + this );
513 if ( state == States.BLOCKED || state == States.STOPPED )
514 state = States.CANCEL;
515 // Notify Goal about cancelling this intention.
518 data.dropBindings( thread_name );
522 * Utility thread control method to wait until this instance
525 synchronized public States waitDone()
526 throws LoopEndException, ParallelEndException {
527 if ( state == States.BLOCKED ) {
528 trace( "-- waiting on ", "" );
531 throwSavedException();
536 * Utility funtion that throws the particular exception, if
537 * any, that has been saved for this instance, or just
540 public void throwSavedException()
541 throws LoopEndException, ParallelEndException {
542 if ( exception instanceof LoopEndException )
543 throw (LoopEndException) exception;
544 if ( exception instanceof ParallelEndException )
545 throw (ParallelEndException) exception;
549 * Keeps any LoopEndException or ParallelEndException thrown
552 public Exception exception = null;
555 * The instance performer.
557 public Performer performer = null;
560 * Constructor, taking the execution point name as argument.
562 public Instance(String h) {
563 head = h.replace( '\"', ':' );
564 thread_name = head + " " + nameString( name );
568 * Utility method to issue a standard trace message.
570 public void trace(String prefix,String suffix) {
574 ( performer == null? "" : ( performer.name + ":" ) ) +
575 thread_name + suffix );
580 * This is the primary entry point for executing this goal
581 * instance. It is invoked repeatedly by the calling goal
582 * instance for the purpose of progressing this goal instance
583 * via its {@link Goal#execute(Data,Instance)} method. On the
584 * first invokation, which is detected by virtue of {@link
585 * #data} being unset, this method registers the instance to
586 * the intention data and sets up the {@link #PERFORMER} data
589 * <p> This method also invokes {@link
590 * Performer#propagateBeliefs()} before progressing the
593 * <p> If the enclosing goal's {@link Goal#group} attribute is
594 * non-null, then it is understood to name a {@link
595 * Performer.TodoGroup} for coordinating instances of this
596 * goal. Therefore, instead of actually progressing the
597 * intention, this instance gets added to the named {@link
598 * Performer.TodoGroup} (by {@link Performer#execute}), and
599 * subsequently all progress will occur "in parallel" via
600 * calls to the {@link #action()} method, while this method
601 * henceforth results in merely polling the instance state
602 * (which happens indirectly via the subsequent calls to
603 * {@link Performer#execute}).
605 public States perform(Data d)
606 throws LoopEndException, ParallelEndException {
610 return performIt( d );
611 } catch(LoopEndException x) {
613 } catch(ParallelEndException x) {
615 } catch (Throwable x) {
616 throw new GoriteError( this, x );
619 t.enterPerform( this, d );
620 States b = States.STOPPED;
621 boolean returns = false;
627 } catch (LoopEndException x) {
630 } catch (ParallelEndException x) {
633 } catch (Throwable x) {
634 throw new GoriteError( this, x );
637 t.exitPerform( this, b );
639 t.exceptionPerform( this, e );
645 * The working method for performing this Instance. It used to
646 * be {@link #perform}, but nowadays that method only deals
647 * with the {@link #tracer}, while invoking this method for
651 private States performIt(Data d)
652 throws LoopEndException, ParallelEndException {
654 if ( state == States.CANCEL ) {
655 trace( "--", " (cancel)" );
657 return States.CANCEL;
660 boolean reentry = true;
661 if ( data == null ) {
662 trace( "=>", " (group=" + group + ")" );
664 data.link( thread_name );
666 performer = (Performer) data.getValue( PERFORMER );
667 if ( performer == null ) {
669 "** Missing performer for goal \"" +
670 name + "\" at " + head );
671 System.err.println( "---------------------\n" + data );
675 if ( state == States.BLOCKED &&
676 ! data.isRunning( thread_name ) )
677 return States.BLOCKED;
678 trace( "=>", " (resume)" );
681 if ( ! reentry && performer.changeFocus() ) {
683 return States.STOPPED ;
686 performer.propagateBeliefs();
689 if ( group == null ) {
691 b = execute( data, this );
692 if ( b == States.PASSED || b == States.FAILED ) {
693 data.dropBindings( thread_name );
696 b = performer.execute( reentry, this, group );
701 trace( "<=", " (" + b + ")" );
705 } catch (LoopEndException e) {
707 data.dropBindings( thread_name );
709 } catch (ParallelEndException e) {
711 data.dropBindings( thread_name );
717 * Utility method that runs this instance's
718 * action(String,Data). This is used for resuming the instance
719 * from a {@link Performer.TodoGroup}.
721 public States action() {
722 if ( state == States.CANCEL ) {
723 trace( "--*", " (cancel)" );
725 return States.CANCEL;
727 state = States.BLOCKED;
729 data.setThreadName( thread_name );
730 if ( performer == null ) {
732 "** Missing performer for goal \"" +
733 name + "\" at " + head );
735 data.replaceValue( Goal.PERFORMER, performer );
736 trace( "=>*", " (resume)" );
737 performer.propagateBeliefs();
738 return execute( data, this );
739 } catch (LoopEndException e) {
741 return States.STOPPED;
742 } catch (ParallelEndException e) {
744 return States.STOPPED;
749 * Utility method to access a data element relative to this
752 public Data.Element getDataElement(String name) {
753 return data.getLocalElement( thread_name, name );
757 * Implements how a goal is achieved by means of its sub
758 * goals. This base implementation invokes the goal's {@link
761 public States action(String head,Data d)
762 throws LoopEndException, ParallelEndException {
765 } catch (InterruptedException e) {
767 return States.STOPPED;
773 * Utility method to quote a string.
775 public static String nameString(String n)
779 return "\"" + n + "\"";
783 * Returns a {@link java.lang.String} identifying the type of the
786 public String getType()
788 String s = getClass().getName();
789 int i = s.lastIndexOf( "." );
790 return s.substring( i + 1 );
794 * Makes a deep-structure string representation of this goal and
795 * all its sub goals, using a given depth prefix. It does not
796 * check for cyclic goal structure.
798 public String toString(String counter) {
799 StringBuffer s = new StringBuffer();
801 if ( counter != null ) {
806 s.append( nameString( name ) );
808 s.append( getType() );
810 if ( subgoals != null ) {
811 for ( int i = 0; i < subgoals.length; i++ ) {
813 s.append( subgoals[ i ].toString( x + (i+1) ) );
815 //s.append( " // end " + counter + "\n" );
821 * Makes a deep-structure string representation of this goal and
824 public String toString() {
825 return toString( null );
829 * Returns the goal {@link #name} attribute.
831 public String getGoalName() {
836 * Returns the goal {@link #control} attribute.
838 public String getGoalControl() {
843 * Returns the {@link #subgoals} attribute.
845 public Goal [] getGoalSubgoals() {
850 * Returns the {@link #group} attribute.
852 public String getGoalGroup() {
857 * Assigns the {@link #control} attribute.
859 public void setGoalControl(String v) {
864 * Assigns the {@link #subgoals} attribute.
866 public void setGoalSubgoals(Goal [] s) {
871 * Assigns the {@link #group} attribute.
873 public void setGoalGroup(String v) {