Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: edu/mit/media/hive/agent/AgentImpl.java


1   // $Id: AgentImpl.java,v 2.1 1999/11/13 06:46:00 raffik Exp $
2   // Hive. Copyright (c) 1998-1999, The Massachusetts Institute of Technology.
3   // All rights reserved. Distributed with no warranty, without even the
4   // implied warranty of merchantability or fitness for a particular purpose.
5   // For more details see COPYING.GPL, the GNU General Public License.
6   
7   package edu.mit.media.hive.agent;
8   import java.rmi.RemoteException;
9   import java.rmi.server.UnicastRemoteObject;
10  import java.io.Serializable;
11  import java.io.ObjectInputStream;
12  import java.io.ObjectOutputStream;
13  import java.io.IOException;
14  import java.io.InputStream;
15  import java.util.Vector;
16  import java.util.Hashtable;
17  import java.awt.event.ActionEvent;
18  
19  import edu.mit.media.hive.Global;
20  import edu.mit.media.hive.rdf.Description;
21  import edu.mit.media.hive.rdf.DescSet;
22  import edu.mit.media.hive.rdf.Lookup;
23  import edu.mit.media.hive.cell.RemoteCell;
24  import edu.mit.media.hive.cell.Cell;
25  import edu.mit.media.hive.cell.AgentDeliveryException;
26  import edu.mit.media.hive.shadow.cell.ComponentManagerShadow;
27  import edu.mit.media.hive.shadow.ShadowNotFoundException;
28  import edu.mit.media.hive.support.Debug;
29  import edu.mit.media.hive.support.CellAddress;
30  import edu.mit.media.hive.support.IconLoader;
31  import edu.mit.media.hive.support.PPM;
32  import edu.mit.media.hive.support.SerializableImage;
33  
34  import org.w3c.dom.Document;
35  
36  /** Base class for Hive agent implementations.
37   * 
38   * An agent has a life cycle.<ol>
39   * <li>Once in its lifetime, the agent is constructed by the no argument
40   * constructor.
41   * <li>The agent arrives on a server; its <code>doLocalSetup()</code>
42   * method is called. This happens whenever it first comes to a server,
43   * whether created by <code>Cell.createNewAgent(Class)</code> or
44   * when it moves to the server.
45   * <li>The agent's <code>doBehavior()</code> method is called. This method
46   * is the agent's main loop, where it's behavior is implemented. The
47   * agent is free to do what it wants, but should respect
48   * <code>timeToStop</code>.  The agent sould never exit this method,
49   * a convienience method <code>waitUntilDeath</code> is provided for this
50   * purpose.
51   * <li>When an agent is being killed, the notification <code>onDying</code>
52   * method is called.  The <code>onMoving</code> is called for the
53   * notification for moving
54   * <li><code>onLocalCleanup</code> is called to ask the agent to
55   * free all its local resources.  The reason for agent death can
56   * be discovered by looking at the value of <code>stopCode</code> if
57   * cleanup is dependant on the "type" of agent death
58   * </ol>
59  
60   * An agent might travel to another server, either by moving itself
61   * or being moved by someone authorized. The <code>moveTo()</code>
62   * method initiates the move: when the agent arrives at the remote
63   * server, <code>arriveAt()</code> and <code>doBehaviour()</code>
64   * will be called again.
65   * 
66   * All agents have a notion of a list of "connections", which agents
67   * they talk to. It is up to the agent to define the semantics of
68   * what a connect means. Agents also have an icon that represents
69   * them: by default this icon is loaded from
70   * icons/AgentClassName.ppm.
71   * 
72   * @author Nelson Minar <nelson@media.mit.edu>
73   * @version $Revision: 2.1 $
74  , */
75  public abstract class AgentImpl 
76  extends UnicastRemoteObject 
77  implements Agent, Serializable {
78  
79      /** Pointer to the server I am currently on.
80       */
81      protected transient Cell myCell = null;
82  
83      /**
84       * Pointer to the thread group for this agent -- ell behaved
85       * agents, if they need to spawn separate threads, will make those
86       * threads members of this agents thread group.  There is no way,
87       * unfortunately, for Hive to enforce this though 
88       */
89      protected transient ThreadGroup agentThreadGroup = null;
90  
91      /** Pointer to my RDF description.
92       */
93      protected Description description;
94    
95  
96      /**
97       * Just describing whether or not this agent is ready for
98       * operation or not 
99       */
100     protected transient boolean readyFlag = false;
101 
102     /** Variable indicating this agent should die soon. This is set
103      * asynchronously, typically by the server. Agents should inspect
104      * this in their main loop.
105      * 
106      * @see doBehavior
107      */
108     protected transient boolean timeToStop = false;
109     
110     /**
111      * Variable describing the reason why an agent was asked to stop
112      */
113     protected transient int stopCode;
114 
115     /**
116      * The different possible stop codes for the agents
117      */
118     public static final int AGENTKILLED = 1;
119     public static final int AGENTMOVED = 2;
120 
121 
122     /** The icon that represents this agent. Should be loaded at create time.
123      */
124     protected SerializableImage icon = null;
125     protected String iconName = "";
126 
127     /** Hashtable and list for the popup menu items or their non-graphical 
128      ** equivalents.
129      **/
130     protected Hashtable commands = new Hashtable();  /** Hashtable of CommandObjects */
131     protected Vector commandList = new Vector();     /** List of the CommandObject's names */
132 
133     /** Basic constructor for agents. This only gets called once in an
134      *  agent's lifetime, when it is first created. It is <b>not</b> called
135      *  when the agent arrives at a new host.
136      * 
137      *  @see doLocalSetup  Note - because of Java syntax rules, all subclasses 
138      *     must explicitly declare a constructor, even if all it does is call 
139      *     the superclass constructor.
140      */
141     public AgentImpl() throws RemoteException {
142         super();
143   loadIcon();
144     }
145 
146     /** Return the semantic description of this agent.
147      */
148     public Description getDescription() {
149   return description;
150     }
151 
152     /** Set the semantic deescription of this agent.
153      */
154     public void setDescription( Description desc ) { this.description = desc; }
155 
156 
157     /**
158      * Get the value of iconName.
159      * @return Value of iconName.
160      */
161     public String getIconName() { return iconName; }
162     
163     /**
164      * Set the value of iconName.
165      * @param v  Value to assign to iconName.
166      */
167     public void setIconName( String v ) {
168   this.iconName = v; 
169   loadIcon();
170     }
171     
172     public String getName() {
173   String nn = Lookup.getNickname(this);
174   if(nn == null)
175       return Global.shortClassName(this);
176   else
177       return nn;
178     }
179 
180     /** Tell this agent to connect to some other agent.
181      * It is up to the agent to define the semantics of what this means.
182      * By default, AgentImpl just prints an error message and returns false.
183      * 
184      * @param otherAgent reference to the agent to connect to
185      * @return whether the connection was successful.
186      */
187     public boolean connectTo( Agent otherAgent ) {
188   Debug.notice("There is no default connection for an agent.");
189   return false;
190     }
191   
192     /** Disconnects this agent from all agents.
193      */
194     public void disconnectFromAll() {
195   Debug.notice( "There is no default disconnection for an agent." );
196     }
197 
198     /** Disconnects this agent from the specified other agent.
199      */
200     public void disconnectFrom( Agent otherAgent ) {
201   Debug.notice( "There is no default disconnection for an agent." );
202     }
203 
204     /**
205      * Handle the setup when an agent arrives on a host -- it is
206      * appropriate to allocate system resources (file descriptors,
207      * etc) here and even to locate other agents that this agent needs
208      * to communicate with.
209      *
210      * This method replaces arriveAt
211      *
212      * @param s the server we are now on 
213      */
214     public void doLocalSetup() { }
215 
216     /** 
217      * Set the server flag on the agent. Normally user code should not call
218      * this - creation code in the server should handle this
219      */
220     public void setCell( Cell s ) {
221   // This shouldn't happen, but it's not exactly an error.
222   if( myCell != null ) {
223       Debug.warning( "Warning! setCell() was called on " + Global.shortString( this ) + " when it had a server set!" );
224   }
225   myCell = s;
226     }
227 
228     /**
229      * Set the thread group of this agent.  Normally user code should
230      * not call this -- the creation methods in the server will set
231      * this properly 
232      */
233     public void setThreadGroup( ThreadGroup tg ) {
234   this.agentThreadGroup = tg;
235     }
236     
237     /** 
238      * Do the basic behavior for an agent, the agent's main loop.
239      * Override this to provide your particular agent's behavior.
240      * <B>Note</b>: the doBehavior loop should not exit.  Please call
241      * waitUntilDeath() at the end of this method, this will prevent
242      * the Cell from believing this agent prematurely exited.
243      * 
244      * @see timeToStop
245      * @see doLocalSetup */
246     public abstract void doBehavior();
247 
248     /** This is a silly hack to prevent any of us from repeating the
249      * silliest and most aggravating error of all time. In America,
250      * behavior has no 'u'
251      * 
252      * @author Oliver Roup (oroup@mit.edu)
253      * @deprecated
254      */
255     public void doBehaviour() {
256   Debug.println( Debug.ERROR, "You misspelled behavior, and that is the source of your troubles..." );
257     }
258 
259     /**
260      * Used at the end of the doBehavior method.  Simply wait until
261      * the timeToStop flag has been set and the notify method has been
262      * called so we can exit properly,
263      *
264      * @see doBehavior 
265      */
266     protected void waitUntilDeath() {
267   while( !timeToStop ) 
268       synchronized( this ) {
269     try {
270         wait();
271     } catch( InterruptedException error ) { }
272       }
273     }
274 
275     /**
276      * Used to notify the agent that it is about to be moved
277      */
278     public void onMoving() { }
279 
280     /** 
281      * Move this agent to a new host.
282      * 
283      * @param address 
284      */
285     public void moveTo( CellAddress address ) throws AgentDeliveryException {
286   Debug.notice( Global.shortString( this ) + " moveTo(" + address + ")." );
287         myCell.moveAgent( this, address );
288     }
289 
290     /**
291      * Command the agent to free its local resources here.  It may or
292      * may not equal what happens when the agent is supposed to die --
293      * but here would be the appropriate place to free File
294      * descriptors, and stop threads, but do not do anything related
295      * to the agent community -- do not notify them that this agent is
296      * about to die.  When this method terminates, this agent should
297      * be ready to be killed 
298      */
299     public void doLocalCleanup() { }
300     
301     /**
302      * Used to notify the agent that it is about to be killed 
303      */
304     public void onDying() { }
305 
306     /**
307      * Ask the agent to politely die -- by default, the agent just
308      * relays the call to the server and asks it tkill itself 
309      */
310     public void diePlease() {
311   myCell.killAgent( (Agent)this );
312 
313     }
314 
315     /** Sets the time to stop flag */
316     public final void setTimeToStop( int flag ) {
317   timeToStop = true;
318   stopCode = flag;
319   synchronized( this ) {
320       notifyAll();
321   }
322     }
323 
324 
325     /** 
326      * Load the icon for an agent off of disk.  If the agent is class
327      * <code>edu.mit.media.hive.FooAgent</code> then this looks for
328      * <code>edu.mit.media.hive/icons/FooAgent.ppm</code> in the class
329      * path or Jar file.  If that doesn't exist, then a default is
330      * substituted.  
331      */
332     protected void loadIcon() {
333 
334    java.io.InputStream is = null;
335   if( iconName.equals("") ){
336       icon = IconLoader.loadIcon( this.getClass() );
337       return;
338 
339   } else {
340       is = Global.getConfigAsInputStream( iconName );    // Tries to load it from the user's home directory
341       if (is == null) {
342     Debug.notice("Trying to get user-specified icon from directory in CLASSPATH");
343     is = getClass().getResourceAsStream( iconName ); // Tries to load it from the current directory
344       }
345   }
346 
347   if( is == null ) {
348       // Note: if these warnings are ever thrown, something has
349       // gone very wrong... (UnknownAgent.ppm not in
350       // hive/agent/icons)
351       Debug.warning( "No icon found for " + this.getClass().toString() + ", substituting " + IconLoader.defaultIconName );
352       is = getClass().getResourceAsStream( IconLoader.defaultIconName );
353       if( is == null ) {
354     Debug.critical( "Argh, " + IconLoader.defaultIconName + " doesn't exist. Agent icons are broken.  Using blank icon." );
355     
356     icon = new SerializableImage( new int[]{ 1 }, new java.awt.Dimension( 1, 1 ) );
357     return;
358       }
359   }
360 
361   // OK, got the file as a stream, let's parse the PPM out and cast it into SerializableImage.
362   try {
363       icon = new PPM( is );
364   } catch( Exception ex ) { 
365       Debug.critical( "Error loading agent's icon " + ex ); 
366   }
367     }
368 
369     /** Return the SerializableImage object that is the agent's icon.
370      */
371     public SerializableImage getIcon() {
372   return icon;
373     }
374 
375     /** Return the server I'm living on.
376      */
377     public RemoteCell getCell() {
378   return myCell;
379     }
380 
381     /**
382      * Return the thread group that we are in
383      */
384     public ThreadGroup getThreadGroup() {
385   return agentThreadGroup;
386     }
387 
388     /**
389      * Return a vector of all the agents that are connected to me
390      */
391     public Vector listAllIncomingConnections() {
392   Debug.warning( "There is no default connection list for an agent." );
393   return new Vector();
394     }
395 
396     /**
397      * Return a vector of all that agents that i am connected to
398      */
399     public Vector listAllOutgoingConnections() {
400   Debug.warning( "There is no default connection list for an agent." );
401   return new Vector();
402     }
403 
404 
405     /**
406       * Tells the agent to configure itself from the DOM Node doc.
407       * Override this function to configure an agentfrom an XML file.
408       * This method is called by Hive if the agent was created through
409       * The user's ~/.hive/agentConfig file. If you want to load a
410       * configuration by hand, see Global.getConfigAsXMLDocument().
411       *
412       * @param doc The DOM Document (root node)
413       */
414     public void configure( Document doc ) throws RemoteException {}
415 
416 
417     /**
418      ** Add a popup menu item or non-graphical equivalent to the 
419      ** agent's behavior.
420      **/
421     public void addActionCommand( ActionCommand com ) {
422   commandList.addElement(com.getName());
423   commands.put(com.getName(), com);
424     }
425 
426     /**
427      ** Invoke an action command by name.
428      ** Currently, this will only be called in a 
429      ** baseUICanvas or its non-graphical equivalent.
430      **/
431     public boolean invokeActionCommand( String com ) {
432   if( commands.containsKey( com ) ) {
433       ((ActionCommand)commands.get( com )).invoke();
434       return true;
435   }
436   return false;
437     }
438 
439     /**
440      ** Get the names of the ActionCommands this agent uses in the
441      ** order in which they were entered.
442      **/
443     public Vector getActionCommands() { return (commandList); }
444 
445 
446     /** 
447      * A convenience method to return the ComponentManagerShadow or 
448      * throws an exception if one is not present. 
449      */
450     public ComponentManagerShadow getComponentManagerShadow() 
451     throws ShadowNotFoundException {
452   ComponentManagerShadow acms = (ComponentManagerShadow)myCell.getShadowDB().getShadow( "edu.mit.media.hive.shadow.cell.ComponentManagerShadow" );
453   if( acms == null ) {
454       throw new ShadowNotFoundException();
455   }
456   return acms;
457     }
458 
459     /** 
460      * Handle serialization requests.
461      */
462     private void writeObject( ObjectOutputStream out ) 
463     throws IOException {
464   out.defaultWriteObject();
465     }
466 
467     /**
468      * Handle deserialization
469      */
470     private void readObject( ObjectInputStream in ) 
471     throws IOException, ClassNotFoundException {
472   in.defaultReadObject();
473     }
474 
475 
476     /**
477      * be careful when overriding thismethod.  deciding when an agent
478      * is ready is up to the agent's author, however the agent should
479      * -not- report that it is ready until after the setIsReady( true
480      * ) has been called on it -- when that method call has occured,
481      * it means that the agent has been listed in the server's
482      * "directory" 
483      *
484      * @param flag true if this agent is ready
485      */
486     public final void setIsReady( boolean flag ) {
487   readyFlag = flag;
488   synchronized( this ) {
489       notifyAll();
490   }
491     }
492 
493     /**
494      * this flag tells another object whether this agent is ready to
495      * do its stuff.  the semantics of being ready are left up to the
496      * agent's author, however the only restriction is that the agent
497      * should -not- report that it is ready until the server calls
498      * setIsReady( true ) on this agent
499      *
500      * @return if the agen t is "ready" 
501      */
502     public boolean isReady() {
503   return readyFlag;
504     }
505 
506     /**
507      * this method should block until the agent is ready -- it just
508      * sits in the wait method until it gets notified from the
509      * setIsReady method 
510      */
511     public void blockUntilReady() {
512   while( !isReady() ) {
513       try {
514     synchronized( this ) {
515         wait();
516     } 
517       } catch( InterruptedException error ) { }
518   }
519     }
520 
521 
522 
523     /** Tell if the agent been told to stop. **/
524     public boolean isTimeToStop() {
525         return timeToStop;
526     }
527     
528 }
529 
530 
531 
532 
533 
534 
535 
536 
537 
538