KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > fr > dyade > aaa > agent > Agent


1 /*
2  * Copyright (C) 2001 - 2005 ScalAgent Distributed Technologies
3  * Copyright (C) 1996 - 2000 BULL
4  * Copyright (C) 1996 - 2000 INRIA
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  * USA.
20  */

21 package fr.dyade.aaa.agent;
22
23 import java.io.*;
24 import java.util.*;
25 import java.lang.reflect.*;
26
27 import org.objectweb.util.monolog.api.BasicLevel;
28 import org.objectweb.util.monolog.api.Logger;
29
30 import fr.dyade.aaa.util.*;
31 import fr.dyade.aaa.util.management.MXWrapper;
32
33 /**
34  * The <code>Agent</code> class represents the basic component in our model.
35  * <i>agents</i> are "reactive" objects which behave according to
36  * "event -> reaction"model: an event embodies a significant state change
37  * which one or many agents may react to.<p>
38  * Class <code>Agent</code> defines the generic interface and the common
39  * behavior for all agents; every agent is an object of a class deriving
40  * from class Agent. Agents are the elementary programming and execution
41  * entities; they only communicate using notifications through the message
42  * bus, and are controlled by the execution engine.<p>
43  * The reactive behavior is implemented by function member React, which
44  * defines the reaction of the agent when receiving a notification; this
45  * function member is called by the execution engine.<p>
46  * Agents are persistent objects, and the Agent class realizes a
47  * "swap-in/swap-out" mechanism which allows loading (or finding) in main
48  * memory the agents to activate, and unloading the agents idle since a while.
49  * <p><hr>
50  * Agents must be created in two steps:
51  * <ul>
52  * <li>locally creating the object in memory (via constructor),
53  * <li>configure it (for example via get/set methods),
54  * <li>the deploy it .
55  * </ul>
56  * <p>
57  * The following code would then create a simple agent and deploy it:
58  * <p><blockquote><pre>
59  * Agent ag = new Agent();
60  * ag.deploy();
61  * </pre></blockquote>
62  * <p>
63  *
64  * @see Notification
65  * @see Engine
66  * @see Channel
67  */

68 public abstract class Agent implements AgentMBean, Serializable {
69   static final long serialVersionUID = 2955513886633164244L;
70
71   /**
72    * <code>true</code> if the agent state has changed.
73    * <p>
74    * This field value is initialized as <code>true</code>, so that by default
75    * the agent state is saved after a reaction.
76    */

77   private transient boolean updated = true;
78
79   /**
80    * Sets the <code>updated</code> field to <code>false</code> so that the
81    * agent state is not saved after the current reaction; the field is set
82    * back to <code>true</code> for the next reaction.
83    */

84   protected void setNoSave() {
85     updated = false;
86   }
87
88   /**
89    * Sets the <code>updated</code> field to <code>true</code> so that the
90    * agent state is saved after the current reaction.
91    */

92   protected void setSave() {
93     updated = true;
94   }
95
96   /**
97    *
98    */

99   protected final boolean needToBeCommited() {
100     try {
101       ((EngineThread) Thread.currentThread()).engine.needToBeCommited = true;
102       return true;
103     } catch (ClassCastException JavaDoc exc) {
104       return false;
105     }
106   }
107
108   /**
109    * Saves the agent state unless not requested.
110    */

111   protected final void save() throws IOException {
112     if (updated) {
113       AgentServer.getTransaction().save(this, id.toString());
114       if (logmon.isLoggable(BasicLevel.DEBUG))
115         logmon.log(BasicLevel.DEBUG,
116                    "Agent" + id + " [" + name + "] saved");
117     } else {
118       updated = true;
119       if (logmon.isLoggable(BasicLevel.DEBUG))
120         logmon.log(BasicLevel.DEBUG,
121                    "Agent" + id + " [" + name + "] not saved");
122     }
123   }
124
125   /**
126    * Restores the object state from the persistent storage.
127    *
128    * @exception IOException
129    * when accessing the stored image
130    * @exception ClassNotFoundException
131    * if the stored image class may not be found
132    */

133   final static Agent
134   load(AgentId id) throws IOException, ClassNotFoundException JavaDoc {
135     Agent ag = (Agent) AgentServer.getTransaction().load(id.toString());
136     if (ag != null) {
137       ag.id = id;
138       ag.deployed = true;
139     }
140     return ag;
141   }
142
143   // Declares all fields transient in order to avoid useless
144
// description of each during serialization.
145

146   /**
147    * Global unique identifier of the agent. Each agent is identified by a
148    * unique identifier allowing the agent to be found. The identifiers format
149    * is detailed in <a HREF="AgentId.html">AgentId</a> class.
150    */

151   transient AgentId id;
152   /** Symbolic name of the agent */
153   public transient String JavaDoc name;
154
155   /**
156    * Returns this <code>Agent</code>'s name.
157    *
158    * @return this <code>Agent</code>'s name.
159    */

160   public String JavaDoc getName() {
161     if ((name == null) || (name == nullName)) {
162       return getClass().getName() + id.toString();
163     } else {
164       return name;
165     }
166   }
167
168   /**
169    * Sets this <code>Agent</code>'s name.
170    *
171    * @param name the <code>Agent</code>'s name.
172    */

173   public void setName(String JavaDoc name) {
174     if (name == null)
175       this.name = nullName;
176     else
177       this.name = name;
178   }
179
180   /**
181    * Some agents must be loaded at any time, this can be enforced by this
182    * member variable. If <code>true</code> agent is pinned in memory.
183    */

184   protected transient boolean fixed;
185   
186   protected transient Logger logmon = null;
187
188   /**
189    * Returns default log topic for agents. Its method should be overridden
190    * in subclass in order to permit fine configuration of logging system.
191    * By default it returns <code>Debug.A3Agent</code>.
192    */

193   protected String JavaDoc getLogTopic() {
194     return fr.dyade.aaa.agent.Debug.A3Agent;
195   }
196
197   protected static final String JavaDoc nullName = "";
198
199   /**
200    * the <code>last</code> variable contains the virtual time of the
201    * last access. It is used by swap-out policy.
202    *
203    * @see garbage
204    */

205   transient long last;
206
207   private void writeObject(java.io.ObjectOutputStream JavaDoc out)
208     throws IOException {
209       out.writeUTF(name);
210       out.writeBoolean(fixed);
211   }
212
213   private void readObject(java.io.ObjectInputStream JavaDoc in)
214     throws IOException, ClassNotFoundException JavaDoc {
215       if ((name = in.readUTF()).equals(nullName))
216     name = nullName;
217       fixed = in.readBoolean();
218       updated = true;
219   }
220
221   /**
222    * Allocates a new Agent object. The resulting object <b>is not an agent</b>;
223    * before it can react to a notification you must deploy it. This constructor
224    * has the same effect as
225    * <code>Agent(AgentServer.getServerId(), null, false)</code>.
226    *
227    * @see Agent#Agent(short, java.lang.String, boolean)
228    * @see #deploy()
229    */

230   public Agent() {
231     this(null, false);
232   }
233
234   /**
235    * Allocates a new Agent object. This constructor has the same effect
236    * as <code>Agent(AgentServer.getServerId(), null, fixed)</code>.
237    *
238    * @param fixed if <code>true</code> agent is pinned in memory
239    *
240    * @see Agent#Agent(short, String, boolean)
241    */

242   public Agent(boolean fixed) {
243     this(null, fixed);
244   }
245
246   /**
247    * Allocates a new Agent object. This constructor has the same effect
248    * as <code>Agent(AgentServer.getServerId(), name, false)</code>.
249    *
250    * @param name symbolic name
251    *
252    * @see Agent#Agent(short, java.lang.String, boolean)
253    */

254   public Agent(String JavaDoc name) {
255     this(name, false);
256   }
257
258   /**
259    * Allocates a new Agent object. This constructor has the same effect
260    * as <code>Agent(AgentServer.getServerId(), name, fixed)</code>.
261    *
262    * @param name symbolic name
263    * @param fixed if <code>true</code> agent is pinned in memory
264    *
265    * @see Agent#Agent(short, java.lang.String, boolean)
266    */

267   public Agent(String JavaDoc name, boolean fixed) {
268     this(AgentServer.getServerId(), name, fixed);
269   }
270
271   /**
272    * Allocates a new Agent object. This constructor has the same effect
273    * as <code>Agent(to, null, false)</code>.
274    *
275    * @param to Identication of target agent server
276    *
277    * @see Agent#Agent(short, java.lang.String, boolean)
278    */

279   public Agent(short to) {
280     this(to, null, false);
281   }
282
283   /**
284    * Allocates a new Agent object. This constructor has the same effect
285    * as <code>Agent(to, name, false)</code>.
286    *
287    * @param to Identication of target agent server
288    * @param name symbolic name
289    *
290    * @see Agent#Agent(short, java.lang.String, boolean)
291    */

292   public Agent(short to, String JavaDoc name) {
293     this(to, name, false);
294   }
295
296   /**
297    * Allocates a new Agent object. This constructor has the same effect
298    * as <code>Agent(to, null, fixed)</code>.
299    *
300    * @param to Identication of target agent server
301    * @param fixed if <code>true</code> agent is pinned in memory
302    *
303    * @see Agent#Agent(short, java.lang.String, boolean)
304    */

305   public Agent(short to, boolean fixed) {
306     this(to, null, fixed);
307   }
308
309   /**
310    * Allocates a new Agent object. The resulting object <b>is not an agent</b>;
311    * before it can react to a notification you must deploy it.
312    *
313    * @param to Identication of target agent server
314    * @param name symbolic name
315    * @param fixed if <code>true</code> agent is pinned in memory
316    *
317    * @see #deploy()
318    */

319   public Agent(short to, String JavaDoc name, boolean fixed) {
320     AgentId id = null;
321
322     try {
323       id = new AgentId(to);
324     } catch (IOException exc) {
325       logmon = Debug.getLogger(fr.dyade.aaa.agent.Debug.A3Agent +
326                                ".#" + AgentServer.getServerId());
327       logmon.log(BasicLevel.ERROR,
328                  AgentServer.getName() + ", can't allocate new AgentId", exc);
329       // TODO: throw an exception...
330
}
331     initState(name, fixed, id);
332   }
333
334   /**
335    * Constructor used to build "system" agents like <code>AgentFactory</code>.
336    * System agents are created from the <code>agent</code> package. This
337    * constructor takes the agent id as a parameter instead of building it.
338    *
339    * @param name symbolic name
340    * @param fixed if <code>true</code> agent is pinned in memory
341    * @param stamp well known stamp
342    */

343   Agent(String JavaDoc name, boolean fixed, AgentId id) {
344     initState(name, fixed, id);
345   }
346
347   private void initState(String JavaDoc name, boolean fixed, AgentId id) {
348     if (name == null)
349       this.name = nullName;
350     else
351       this.name = name;
352     this.fixed = fixed;
353     this.id = id;
354     // Get the logging monitor from current server MonologLoggerFactory
355
this.logmon = Debug.getLogger(getLogTopic());
356   }
357
358   /**
359    * Constructor used to build Well Known Services agents.
360    * <p>
361    * System agents are created from the <code>agent</code> package.
362    * WKS agents are similar to system agents, except that they may be
363    * defined in separate packages, and they do not necessarily exist on all
364    * agent servers. Their creation is controlled from the configuration file
365    * of the agent server.<p>
366    * This constructor takes the agent id as a parameter instead of building it.
367    * Since the constructor has been made public, the consistency of agent ids
368    * allocation must be enforced. This is done by the constructor checking
369    * that the id stamp is comprised in the <code>AgentId.MinWKSIdStamp</code>
370    * - <code>AgentId.MaxWKSIdStamp</code> interval.
371    *
372    * @param name symbolic name
373    * @param fixed if <code>true</code> agent is pinned in memory
374    * @param stamp well known stamp
375    */

376   public Agent(String JavaDoc name, boolean fixed, int stamp) {
377     if (stamp < AgentId.MinWKSIdStamp ||
378     stamp > AgentId.MaxWKSIdStamp) {
379       logmon = Debug.getLogger(fr.dyade.aaa.agent.Debug.A3Agent +
380                                ".#" + AgentServer.getServerId());
381       logmon.log(BasicLevel.ERROR,
382                  AgentServer.getName() +
383                  ", well known service stamp out of range: " + stamp);
384       throw new IllegalArgumentException JavaDoc(
385     "Well known service stamp out of range: " + stamp);
386     }
387     AgentId id = new AgentId(AgentServer.getServerId(),
388                              AgentServer.getServerId(),
389                              stamp);
390     initState(name, fixed, id);
391   }
392
393   /**
394    * Determines if the current <code>Agent</code> has already been deployed.
395    */

396   transient boolean deployed = false;
397
398   /**
399    * Returns if the currently <code>Agent</code> has already been deployed.
400    */

401   public boolean isDeployed() {
402     return deployed;
403   }
404
405   /**
406    * Deploys a new <i>agent</i>.
407    * It works by sending a notification to a special agent, of class Factory,
408    * running on the target agent server. The notification asks for a remote
409    * creation of the agent. This solution presents the advantage of reusing
410    * the standard communication mechanisms of the agent machine.<p>
411    * The whole process involves then the following steps:
412    * <ul>
413    * <li>serializing the object state,
414    * <li>building an <code>AgentCreateRequest</code> notification with the
415    * resulting bytes stream,
416    * <li>sending it to the target Factory agent.
417    * </ul>
418    * In reaction, the factory agent builds the agent in the target server
419    * from the serialized image, and saves it into operational storage.
420    *
421    * @exception IOException
422    * unspecialized exception
423    */

424   public final void deploy() throws IOException {
425     deploy(null);
426   }
427
428   /**
429    * Deploys a new <i>agent</i>.
430    * It works as <a HREF="#deploy()">deploy()</a> method above; after the
431    * agent creation, the Factory agent sends an <code>AgentCreateReply</code>
432    * notification.
433    *
434    * @param reply agent to reply to
435    * @exception IOException
436    * unspecialized exception
437    */

438   public final void deploy(AgentId reply) throws IOException {
439     if ((id == null) || id.isNullId()) {
440       logmon.log(BasicLevel.ERROR,
441                  AgentServer.getName() +
442                  ", can't deploy " + this.toString() + ", id is null");
443       throw new IOException("Can't deploy agent, id is null");
444     }
445     if (deployed) {
446       logmon.log(BasicLevel.ERROR,
447                  AgentServer.getName() +
448                  ", can't deploy " + this.toString() + ", already deployed");
449       throw new IOException("Can't deploy agent, already deployed");
450     }
451
452     // If we use sendTo agent's method the from field is the agent id, and
453
// on reception the from node (from.to) can be false.
454
Channel.sendTo(AgentId.factoryId(id.getTo()),
455            new AgentCreateRequest(this, reply));
456     deployed = true;
457
458     if (logmon.isLoggable(BasicLevel.DEBUG))
459       logmon.log(BasicLevel.DEBUG, this.toString() + " deployed");
460   }
461
462   /**
463    * Returns a string representation of this agent, including the agent's
464    * class, name, global identication, and fixed property.
465    *
466    * @return A string representation of this agent.
467    */

468   public String JavaDoc toString() {
469     StringBuffer JavaDoc strbuf = new StringBuffer JavaDoc();
470
471     strbuf.append('(').append(super.toString());
472     strbuf.append(",name=").append(name);
473     strbuf.append(",id=").append(id.toString());
474     strbuf.append(",fixed=").append(fixed);
475     strbuf.append(')');
476
477     return strbuf.toString();
478   }
479
480   /**
481    * Returns String format of the global unique identifier of the agent.
482    *
483    * @return the global unique identifier of the agent.
484    */

485   public final String JavaDoc getAgentId() {
486     return id.toString();
487   }
488
489   /**
490    * Returns the global unique identifier of the agent. Each agent is
491    * identified by a unique identifier allowing the agent to be found.
492    * The identifiers format is detailed in <a HREF="AgentId.html">AgentId</a>
493    * class.
494    *
495    * @return the global unique identifier of the agent.
496    */

497   public final AgentId getId() {
498     return id;
499   }
500
501   /**
502    * Tests if the agent is pinned in memory.
503    *
504    * @return true if this agent is a pinned in memory; false otherwise.
505    */

506   public final boolean isFixed() {
507     return fixed;
508   }
509
510   /**
511    * Gives this agent an opportunity to initialize after having been deployed,
512    * and each time it is loaded into memory.
513    * <p>
514    * This function is first called by the factory agent, just after it deploys
515    * the agent.
516    * <p>
517    * This function is used by agents with a <code>fixed</code> field set to
518    * <code>true</code> to initialize their transient variables, as it is called
519    * each time the agent server is restarted.
520    * <p>
521    * This function is not declared <code>final</code> so that derived classes
522    * may change their reload policy. The implementation of this method provided
523    * by the <code>Agent</code> class does nothing.
524    *
525    * @param firstTime true when first called by the factory
526    *
527    * @exception Exception
528    * unspecialized exception
529    */

530   protected void agentInitialize(boolean firstTime) throws Exception JavaDoc {
531     // Get the logging monitor from current server MonologLoggerFactory
532
this.logmon = Debug.getLogger(getLogTopic());
533     // Initializes the updated field to true:
534
this.updated = true;
535
536     try {
537       MXWrapper.registerMBean(this, "AgentServer", getMBeanName());
538     } catch (Exception JavaDoc exc) {
539       logmon.log(BasicLevel.ERROR, getName() + " jmx failed", exc);
540     }
541
542     if (logmon.isLoggable(BasicLevel.DEBUG))
543       logmon.log(BasicLevel.DEBUG,
544                  "Agent" + id + " [" + name +
545                  (firstTime?"] , first initialized":"] , initialized"));
546   }
547
548   private String JavaDoc getMBeanName() {
549     return new StringBuffer JavaDoc()
550       .append("server=").append(AgentServer.getName())
551       .append(",cons=Engine#").append(getId().getTo())
552       .append(",agent=").append((name == nullName)?getId().toString():name)
553       .toString();
554   }
555
556   /**
557    * This method sends a notification to the agent which id is given in
558    * parameter. During an agent reaction alls notifications sent are buffered
559    * until reaction commit.
560    * <p>
561    * Be careful if you use this method outside of an agent reaction,
562    * its behavior is slightly different: each notification is immediatly
563    * sent using a local transaction.
564    *
565    * @see Channel#sendTo
566    *
567    * @param to the unique id. of destination <code>Agent</code>.
568    * @param not the notification to send.
569    */

570   protected final void
571   sendTo(AgentId to, Notification not) {
572 // try {
573
// EngineThread thread = (EngineThread) Thread.currentThread();
574
// // Use the engine's sendTo method that push message in temporary queue
575
// // until the end of current reaction.
576
// thread.engine.push(getId(), to, not);
577
// } catch (ClassCastException exc) {
578
// // Be careful, the destination node use the from.to field to
579
// // get the from node id.
580
// Channel.channel.directSendTo(getId(), to, not);
581
// }
582

583 // if (Class.EngineThread.isAssignable(Thread.currentThread())) {
584
if (Thread.currentThread() == AgentServer.engine.thread) {
585       AgentServer.engine.push(getId(), to, not);
586     } else {
587       Channel.channel.directSendTo(getId(), to, not);
588     }
589   }
590
591   /**
592    * This method sends a notification to the agent which id is wrapped
593    * in the specified role.
594    *
595    * @param role the destination <code>Role</code>.
596    * @param not the notification to send.
597    */

598   protected final void sendTo(Role role, Notification not) {
599     if (role == null) return;
600     sendTo(role.getListener(), not);
601   }
602  
603   /**
604    * Sends a notification to all the agents registered in a role.
605    *
606    * @param role the destination <code>MultiplRole</code>.
607    * @param not the notification to send.
608    */

609   protected final void
610   sendTo(RoleMultiple role, Notification not) {
611     if (role == null) return;
612     Enumeration to = role.getListeners();
613     if (to == null)
614       return;
615     while (to.hasMoreElements())
616       sendTo((AgentId) to.nextElement(), not);
617   }
618
619   /**
620    * Permits this agent to destroy it. If necessary, its method should be
621    * overloaded to work properly.
622    */

623   public void delete() {
624     delete(null);
625   }
626
627   /**
628    * Permits this agent to destroy it. If necessary, its method should be
629    *overloaded to work properly.
630    *
631    * @param agent Id of agent to notify.
632    */

633   public void delete(AgentId agent) {
634     if (deployed)
635       sendTo(AgentId.factoryId(id.getTo()),
636          new AgentDeleteRequest(agent));
637   }
638  
639   /**
640    * Defines the reaction of the agent when receiving a notification. This
641    * member function implements the common reactive behavior of an agent, it
642    * is called by the execution engine (see <a HREF="Engine.html">Engine</a>
643    * class).<p>
644    * If there is no corresponding reaction, the agent send an
645    * <code>UnknownNotification</code> notification to the sender.
646    *
647    * @param from agent sending notification
648    * @param not notification to react to
649    *
650    * @exception Exception
651    * unspecialized exception
652    */

653   public void react(AgentId from, Notification not) throws Exception JavaDoc {
654     if (not instanceof DeleteNot) {
655       delete(((DeleteNot) not).reply);
656     } else if ((not instanceof UnknownAgent) ||
657                (not instanceof UnknownNotification) ||
658                (not instanceof ExceptionNotification)) {
659       logmon.log(BasicLevel.WARN,
660                  this.toString() + ".react(" + from + ", " + not + ")");
661      } else {
662       logmon.log(BasicLevel.ERROR,
663                  this.toString() + ".react(" + from + ", " + not + ")");
664       sendTo(from, new UnknownNotification(id, not));
665     }
666   }
667
668   /**
669    * Called to inform this agent that it is garbaged and that it should free
670    * any active ressources that it has allocated.
671    * A subclass of <code>Agent</code> should override this method if it has
672    * any operation that it wants to perform before it is garbaged. For example,
673    * an agent with threads (a ProxyAgent for example) would use the initialize
674    * method to create the threads and the <code>agentFinalize</code> method to
675    * stop them.
676    * The implementation of this method provided by the <code>Agent</code> class
677    * does nothing.
678    *
679    * @param lastTime true when last called by the factory on agent deletion.
680    */

681   public void agentFinalize(boolean lastTime) {
682     try {
683       MXWrapper.unregisterMBean("AgentServer", getMBeanName());
684     } catch (Exception JavaDoc exc) {
685       logmon.log(BasicLevel.ERROR, getName() + " jmx failed", exc);
686     }
687   }
688 }
689
Popular Tags