View Javadoc

1   /*
2    * The contents of this file are subject to the BT "ZEUS" Open Source
3    * Licence (L77741), Version 1.0 (the "Licence"); you may not use this file
4    * except in compliance with the Licence. You may obtain a copy of the Licence
5    * from $ZEUS_INSTALL/licence.html or alternatively from
6    * http://www.labs.bt.com/projects/agents/zeus/licence.htm
7    *
8    * Except as stated in Clause 7 of the Licence, software distributed under the
9    * Licence is distributed WITHOUT WARRANTY OF ANY KIND, either express or
10   * implied. See the Licence for the specific language governing rights and
11   * limitations under the Licence.
12   *
13   * The Original Code is within the package zeus.*.
14   * The Initial Developer of the Original Code is British Telecommunications
15   * public limited company, whose registered office is at 81 Newgate Street,
16   * London, EC1A 7AJ, England. Portions created by British Telecommunications
17   * public limited company are Copyright 1996-9. All Rights Reserved.
18   *
19   * THIS NOTICE MUST BE INCLUDED ON ANY COPY OF THIS FILE
20   */
21  
22  
23  
24  /*
25   * @(#)MsgHandler.java 1.00
26   */
27  
28  package zeus.actors;
29  
30  import java.util.*;
31  import zeus.util.*;
32  import zeus.concepts.*;
33  import zeus.actors.rtn.util.DelegationStruct;
34  import zeus.actors.rtn.Engine;
35  import zeus.actors.event.*;
36  import zeus.agents.*;
37  import gnu.regexp.*;
38  import java.lang.reflect.*;
39  
40  /***
41   * The Message Handler component implements each Zeus agent's internal "mail
42   * sorting office", continually checking the incoming-message-queue of the
43   * {@link MailBox} for new messages, and forwarding them to the relevant
44   * components within the agent. <p>
45   *
46   * The Message Handler's behaviour is controlled by two factors: first,
47   * whether a new message represents the start of a new dialogue or it is part
48   * of an existing dialogue; and second, on the message processing rules
49   * registered with it by other components of the agent.<p>
50   * Change Log <br>
51   * ----------<p>
52   * 22/08/00 Altered spelling of organization to correct version for about 6 lines - Simon
53   *
54   */
55  
56  public class MsgHandler extends Thread {
57      protected HSet[] eventMonitor = new HSet[4];
58      
59      protected static final int ADD    = 0;
60      protected static final int FIRE   = 1;
61      protected static final int DELETE = 2;
62      protected static final int FAIL   = 3;
63      
64      protected Hashtable     addressQueryTable = new Hashtable();
65      protected Hashtable     abilityQueryTable = new Hashtable();
66      protected Hashtable     reportQueryTable = new Hashtable();
67      
68      protected ReportLogger  reportLogger = null;
69      protected AbilityLogger abilityLogger = null;
70      
71      protected boolean       processing;
72      protected AgentContext  context = null;
73      protected RootNode      MessageRootNode = null;
74      Vector                  attributeNodes = new Vector();
75      static int cleanupInterval = 100;
76      
77      protected Queue handleQ = null;
78      
79      // meaningless init  to allow rearch
80      public MsgHandler() {
81          ;
82      }
83      
84      
85      public MsgHandler(AgentContext context) {
86          super();
87          handleQ = new Queue("msg handler processing Q");
88          Assert.notNull(context);
89          this.context = context;
90          context.set(this);
91          
92          for(int i = 0; i < eventMonitor.length; i++ )
93              eventMonitor[i] = new HSet();
94          
95          MessageRootNode = new RootNode(this);
96          
97          /* Initialize rule base */
98          String[][] msg_pattern = {
99              /* 00 */  { "type", "request", "content", "//Aregister//Z" },
100             /* 01 */  { "type", "request", "content", "//Aderegister//Z" },
101             
102             /* 02 */  { "type", "subscribe", "content", "//Alog_message//Z"},
103             /* 03 */  { "type", "subscribe", "content", "//Alog_report//Z"},
104             /* 04 */  { "type", "subscribe", "content", "//Alog_address//Z"},
105             
106             /* 05 */  { "type", "cancel", "content", "//Alog_message//Z"},
107             /* 06 */  { "type", "cancel", "content", "//Alog_report//Z"},
108             /* 07 */  { "type", "cancel", "content", "//Alog_address//Z"},
109             
110             /* 08 */  { "type", "query-ref", "content", "//Ayour_abilities//Z"},
111             /* 09 */  { "type", "query-ref", "content", "//Aaddress_of(//s+)(//w)(.*)//Z"},
112             /* 10 */  { "type", "query-ref", "content", "//Ayour_relations//Z"},
113             
114             /* 11 */  { "type", "query-ref", "content", "//Ahas_ability(//s+)(.*)//Z"},
115             
116             /* 12 */  { "type", "inform", "content", "//Aisa_facilitator(//s+)(//w)(.*)//Z"},
117             
118             /* 13 */  { "type", "request", "content", "//Akill_yourself//Z"},
119             
120             
121             /* 14 */  { "type", "subscribe", "content", "//Ahas_ability(//s+)(.*)//Z"},
122             /* 15 */  { "type", "cancel", "content", "//Ahas_ability(//s+)(.*)//Z"},
123             /* 16 */  { "type", "inform", "content", "//Amy_abilities(//s+)(.*)//Z"},
124             /* 17 */  { "type", "inform", "content", "//Adata(//s+)(.*)//Z"},
125             
126             // UNUSED
127             /* 00 */  { "type", "request", "content", "resume_goals//s*"},
128             /* 00 */  { "type", "request", "content", "cancel_goals//s*"},
129             
130             /* 00 */  { "type", "request", "content", "db_del//s*"},
131             /* 00 */  { "type", "request", "content", "optimize//s"},
132             /* 00 */  { "type", "request", "content", "add_task//s*"},
133             /* 00 */  { "type", "request", "content", "modify_task//s*"},
134             /* 00 */  { "type", "request", "content", "delete_task//s*"},
135             
136             /* 00 */  { "type", "subscribe", "content", "log_goal"},
137             /* 00 */  { "type", "subscribe", "content", "log_task"},
138             /* 00 */  { "type", "subscribe", "content", "log_state"},
139             
140             /* 00 */  { "type", "cancel", "content", "log_goal"},
141             /* 00 */  { "type", "cancel", "content", "log_task"},
142             /* 00 */  { "type", "cancel", "content", "log_state"},
143         };
144         
145         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[0], this, "register"));
146         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[1], this, "deregister"));
147         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[2], this, "log_message"));
148         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[3], this, "log_report"));
149         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[4], this, "log_address"));
150         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[5], this, "cancel_message"));
151         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[6], this, "cancel_report"));
152         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[7], this, "cancel_address"));
153         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[8], this, "your_abilities"));
154         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[9], this, "address_of"));
155         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[10],this, "your_relations"));
156         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[11],this, "has_ability"));
157         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[12],this, "isa_facilitator"));
158         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[13],this, "kill_yourself"));
159         
160         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[14],this, "log_specified_ability"));
161         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[15],this, "cancel_specified_ability"));
162         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[16],this, "add_agents_abilities"));
163         addRule(new MessageRuleImpl(context.newId("Rule"),msg_pattern[17],this, "add_information"));
164         this.setName("MsgHandler");
165         this.start();
166     }
167     
168     public AgentContext getAgentContext() {
169         return context;
170     }
171     
172     public void lowerStatus() {
173         
174         // this.setPriority(Thread.NORM_PRIORITY-2);
175     }
176     
177     public void stopProcessing() {
178         processing = false;
179     }
180     
181     
182     
183     /***
184      * refactored to help readability - this method loops until it gets
185      * a valid MailBox from the agent, so that the system can initialise in
186      * the right order
187      */
188     private MailBox waitForSetUp() {
189         MailBox mbox = context.MailBox();
190         while ( mbox == null ) {
191             try {
192                 sleep(300); // sleep until mbox is built
193                 mbox = context.MailBox();
194             }
195             catch(InterruptedException e) {
196                 e.printStackTrace();
197             }
198         }
199         return mbox;
200     }
201     
202     
203     /***
204      * thread main method - try and read incomming messages until
205      * the universe ends
206      */
207     public void run() {
208         try {
209         
210             Performative msg;
211             processing = true;
212             MailBox mbox = waitForSetUp();
213         
214             int count = 0;
215             while(processing) {
216           
217                 synchronized (handleQ) {
218              
219                     msg = (Performative) handleQ.dequeue();
220 
221                     debug("processing:" + msg.toString());
222                     Core.DEBUG(1,"MsgHandler processing msg from ...\n" + msg.getSender());
223                     try {
224 
225                         MessageRootNode.evaluate(null, msg);
226                     } catch (Exception e) {
227                         e.printStackTrace();
228                         // 30/09/01 - don't fall out of loop if this node is incorrectly instantiated.
229                     }
230                 }
231                 yield();
232                 count++;
233                 if (count > 100) {
234                     System.gc();
235                     count = 0;
236                 }
237             }
238         }
239         catch (Exception e) {
240             e.printStackTrace();
241         }
242     }
243     
244     
245     /***
246      * in old versions of Zeus the msgHandler was responsible for ensuring that  incomming
247      * messages were processed. However this approach meant that reengineering of the msgHandler would
248      * be necessary whenever a mailbox was built that had more than one server - basically
249      * this was a problem in terms of both coupling the mailbox,server and msgHandler
250      * objects and reducing the coheasion of the msgHandler. <p>
251      * solution: processMessage is a method that can be called by any
252      * object to see if message rules have been set that apply to this message. The
253      * servers (inboxes) are responsible for calling this method which will process the message
254      * for them - or not as they like. <p>
255      * This method puts the message on a queue, which is internal to the msgHandler. The thread
256      * run by msgHandler will then process it. //synch
257      */
258     public void processMessage(Performative msg) {
259         handleQ.enqueue(msg);
260     }
261     
262     
263     
264     public Queue getMessageQueue() {
265         return (handleQ);
266     }
267     
268     
269     //added sycn 4/6/03
270     // unadded it 16/10/03
271     public void addRule(MessageRule r) {
272         MsgNode node = null;
273         MsgNode term;
274         boolean found;
275         Core.DEBUG(1,"Compiling msg processing rule ...\n\t" + r);
276         MessagePattern token = r.getPattern();
277         
278         // link to RootNode
279         term = MessageRootNode;
280         Core.DEBUG(3,"=r");
281         Core.DEBUG(4,"=r[" + term + "]");
282         term.use_count++;
283         node = term;
284         
285         // For each attribute value create attribute node
286         // check if 'attributeNodes' already contains node
287         RE[] value = token.listValues();
288         String[] attribute = token.listAttributes();
289         
290         for(int k = 0; k < value.length; k++ ) {
291             if ( value[k] != null  ) {
292                 term = new AttributeNode(this,attribute[k],value[k]);
293                 found = false;
294                 for(int j = 0; !found && j < attributeNodes.size(); j++ ) {
295                     found = term.equals(attributeNodes.elementAt(j));
296                     if (found)
297                         term = (MsgNode)attributeNodes.elementAt(j);
298                 }
299                 if ( !found ) {
300                     attributeNodes.addElement(term);
301                     Core.DEBUG(3,"+m");
302                     Core.DEBUG(4,"+m[" + term + "]");
303                 }
304                 else {
305                     Core.DEBUG(3,"=m");
306                     Core.DEBUG(4,"=m[" + term + "]");
307                 }
308                 term.use_count++;
309                 node.addSuccessor(r.getName(),term);
310                 node = term;
311             }
312         }
313         
314         // now, all that is left is the action node
315         // first determine the last node: which should be 'term'
316         ActionNode action = new ActionNode(this,r.getName(),r.getAction());
317         action.use_count++;
318         term.addSuccessor(r.getName(),action);
319         Core.DEBUG(3,"+a");
320         Core.DEBUG(4,"+a[" + action + "]");
321         notifyMonitors(ADD,new MessageHandlerEvent(this,r,MessageHandlerEvent.ADD_MASK));
322     }
323     
324     //sync??
325     public void removeRule(String name) {
326         MessageRootNode.remove(name);
327         notifyMonitors(DELETE,new MessageHandlerEvent(this,name,MessageHandlerEvent.DELETE_MASK));
328     }
329     
330     
331     public synchronized Object execRule(String rule, Object object, String method, Performative input) {
332         Class c = object.getClass();
333         try {
334             Class[] parameter_types = new Class[1];
335             parameter_types[0] = input.getClass();
336             Object[] arglist = new Object[1];
337             arglist[0] = input;
338             
339             Core.DEBUG(2,"Invoking method " + method + " of class " +
340             c.getName() + " with parameter " + input);
341             
342             Method m = c.getMethod(method,parameter_types);
343             notifyMonitors(FIRE,new MessageHandlerEvent(this,rule,object,method,input,MessageHandlerEvent.FIRE_MASK));
344             return m.invoke(object,arglist);
345         }
346         catch(Throwable err) {
347             Core.USER_ERROR("MsgHandler - Error invoking target: [" + rule +
348             "::" + object.getClass().getName() + "." + method +
349             "()]\nwith args...\n" + input + "\n" + err);
350             notifyMonitors(FAIL,new MessageHandlerEvent(this,rule,object,method,input,MessageHandlerEvent.FAIL_MASK));
351         }
352         return null;
353     }
354     
355     protected void adviseAll(Address addr) {
356         Enumeration enum = addressQueryTable.keys();
357         String name, reply_with;
358         Performative reply;
359         MailBox mbox = context.MailBox();
360         
361         while ( enum.hasMoreElements() ) {
362             name = (String) enum.nextElement();
363             reply_with = (String) addressQueryTable.get(name);
364             
365             reply = new Performative("inform");
366             reply.setReceiver(name);
367             reply.setContent(addr.toString());
368             reply.setInReplyTo(reply_with);
369             mbox.sendMsg(reply);
370         }
371     }
372     
373     public void register(Performative msg) {
374         try {
375             if (context.whoami().equalsIgnoreCase("ANServer")) {
376                 Performative forwarder = new Performative(msg);
377                 forwarder.setReceiver("ams");
378                 forwarder.send(context);
379             }
380             String reply_with, content;
381             if ( (reply_with = msg.getReplyWith()) == null ) {
382                 refuse(msg,"no reply-with tag");
383                 return;
384             }
385             
386             if ( (content = context.Clock().initData()) != null ) {
387                 Performative reply = new Performative("inform");
388                 reply.setReceiver(msg.getSender());
389                 reply.setInReplyTo(reply_with);
390                 reply.setContent(content + " " + System.currentTimeMillis());
391                 context.MailBox().sendMsg(reply);
392             }
393             adviseAll(msg.getAddress());
394         }
395         catch(Exception e) {
396             e.printStackTrace();
397         }
398     }
399     
400     public void deregister(Performative msg) {
401         Address address;
402         
403         String name = msg.getSender();
404         if ( (address = msg.getAddress()) != null )
405             context.MailBox().del(address);
406         
407         if ( addressQueryTable.containsKey(name) )
408             addressQueryTable.remove(name);
409         
410         String reply_with = msg.getReplyWith();
411         if ( reply_with != null ) {
412             Performative reply = new Performative("inform");
413             reply.setReceiver(msg.getSender());
414             reply.setInReplyTo(reply_with);
415             reply.setContent("done deregister");
416             context.MailBox().sendMsg(reply);
417         }
418         debug("Deregister message : " + msg.toString());
419     }
420     
421     public void kill_yourself(Performative msg) {
422         // inform done; send deregistration msg
423         context.Agent().notifyMonitors(BasicAgent.DEATH);
424         System.exit(0);
425     }
426     
427     public void add_task(Performative msg) {
428         MsgContentHandler hd = new MsgContentHandler(msg.getContent());
429         if ( context.TaskDb() != null ) {
430             Vector List = ZeusParser.taskList(context.OntologyDb(),hd.data());
431             context.TaskDb().add(List);
432             inform(msg,"done add_task");
433         }
434         else
435             refuse(msg,"no task database");
436     }
437     
438     public void modify_task(Performative msg) {
439         MsgContentHandler hd = new MsgContentHandler(msg.getContent());
440         if ( context.TaskDb() != null ) {
441             Vector List = ZeusParser.taskList(context.OntologyDb(),hd.data());
442             context.TaskDb().add(List);
443             inform(msg,"done modify_task");
444         }
445         else
446             refuse(msg,"no task database");
447     }
448     
449     
450     public void delete_task(Performative msg) {
451         MsgContentHandler hd = new MsgContentHandler(msg.getContent());
452         if ( context.TaskDb() != null ) {
453             context.TaskDb().deleteTask(hd.data());
454             inform(msg,"done delete_task");
455         }
456         else
457             refuse(msg,"no task database");
458     }
459     
460     
461     public void cancel_message(Performative msg) {
462         context.MailBox().stopLoggingMessages(msg.getSender());
463     }
464     
465     
466     public void cancel_report(Performative msg) {
467         reportQueryTable.remove(msg.getSender());
468         if ( reportQueryTable.isEmpty() ) {
469             reportLogger.stopLogging();
470             reportLogger = null;
471         }
472     }
473     
474     public void cancel_specified_ability(Performative msg) {
475         MsgContentHandler hd = new MsgContentHandler(msg.getContent());
476         if ( hd.data() == null ) {
477             refuse(msg,"no key specified");
478             return;
479         }
480         abilityQueryTable.remove(hd.data());
481         if ( abilityQueryTable.isEmpty() ) {
482             abilityLogger.stopLogging();
483             abilityLogger = null;
484         }
485     }
486     
487     public void cancel_address(Performative msg) {
488         addressQueryTable.remove(msg.getSender());
489     }
490     
491     public void log_message(Performative msg) {
492         String reply_with = msg.getReplyWith();
493         if ( reply_with == null ) {
494             refuse(msg,"no reply-with key");
495             return;
496         }
497         context.MailBox().logMessages(msg.getSender(),reply_with);
498     }
499     
500     public void log_report(Performative msg) {
501         String reply_with = msg.getReplyWith();
502         if ( reply_with == null ) {
503             refuse(msg,"no reply-with key");
504             return;
505         }
506         else if ( context.Planner() == null ) {
507             refuse(msg,"no planner");
508             return;
509         }
510         reportQueryTable.put(msg.getSender(),reply_with);
511         if ( reportLogger == null ) reportLogger = new ReportLogger();
512     }
513     
514     protected class ReportLogger extends PlanStepAdapter {
515         public ReportLogger() {
516             context.Planner().addPlanStepMonitor(this,PlanStepEvent.STATE_CHANGE_MASK);
517         }
518         public void stopLogging() {
519             context.Planner().addPlanStepMonitor(this,PlanStepEvent.STATE_CHANGE_MASK);
520         }
521         public void planStepStateChangedEvent(PlanStepEvent event) {
522             if ( reportQueryTable.isEmpty() ) return;
523             
524             PlanRecord rec = event.getPlanRecord();
525             switch( rec.getState() ) {
526                 case PlanRecord.FREE:
527                 case PlanRecord.TEMP:
528                 case PlanRecord.TENTATIVE:
529                     break;
530                     
531                 case PlanRecord.FIRM:
532                 case PlanRecord.RUNNING:
533                 case PlanRecord.FAILED:
534                 case PlanRecord.COMPLETED:
535                 case PlanRecord.JEOPARDY:
536                 case PlanRecord.AGREEMENT:
537                     ReportRec report = rec.report();
538                     Enumeration enum = reportQueryTable.keys();
539                     String agent, reply_with;
540                     Performative reply;
541                     while( enum.hasMoreElements() ) {
542                         agent = (String)enum.nextElement();
543                         reply_with = (String)reportQueryTable.get(agent);
544                         
545                         reply = new Performative("inform");
546                         reply.setContent(report.toString());
547                         reply.setReceiver(agent);
548                         reply.setInReplyTo(reply_with);
549                         context.MailBox().sendMsg(reply);
550                     }
551                     break;
552             }
553         }
554     }
555     
556     public void log_address(Performative msg) {
557         String reply_with = msg.getReplyWith();
558         if ( reply_with == null ) {
559             refuse(msg,"no reply-with key");
560             return;
561         }
562         addressQueryTable.put(msg.getSender(),reply_with);
563         Vector List = context.MailBox().listAddresses();
564         if ( !List.isEmpty() ) {
565             Performative reply = new Performative("inform");
566             reply.setReceiver(msg.getSender());
567             reply.setInReplyTo(reply_with);
568             reply.setContent(Misc.concat(List));
569             context.MailBox().sendMsg(reply);
570         }
571     }
572     
573     public void log_specified_ability(Performative msg) {
574         String reply_with = msg.getReplyWith();
575         if ( reply_with == null ) {
576             refuse(msg,"no reply-with key");
577             return;
578         }
579         MsgContentHandler hd = new MsgContentHandler(msg.getContent());
580         if ( hd.data() == null ) {
581             refuse(msg,"no ability specified");
582             return;
583         }
584         else if ( context.OrganisationDb() == null ) {
585             // 1.04 change of spelling by simon
586             refuse(msg,"no organization database");
587             return;
588         }
589         AbilitySpec a = ZeusParser.abilitySpec(context.OntologyDb(),hd.data());
590         if ( a == null ) {
591             refuse(msg,"no ability specified");
592             return;
593         }
594         Vector List = context.OrganisationDb().findAll(a);
595         if ( !List.isEmpty() ) {
596             Performative reply = new Performative("inform");
597             reply.setContent(Misc.concat(List));
598             reply.setReceiver(msg.getSender());
599             reply.setInReplyTo(reply_with);
600             context.MailBox().sendMsg(reply);
601         }
602         
603         Object[] data = new Object[2];
604         data[0] = msg.getSender();
605         data[1] = a;
606         
607         abilityQueryTable.put(reply_with,data);
608         if ( abilityLogger == null ) abilityLogger = new AbilityLogger();
609     }
610     
611     class AbilityLogger extends AbilityAdapter {
612         public AbilityLogger() {
613             context.OrganisationDb().addAbilityMonitor(this,AbilityEvent.ADD_MASK);
614         }
615         public void stopLogging() {
616             context.OrganisationDb().removeAbilityMonitor(this,AbilityEvent.ADD_MASK);
617         }
618         public void abilityAddedEvent(AbilityEvent event) {
619             if ( abilityQueryTable.isEmpty() ) return;
620             
621             Enumeration enum = abilityQueryTable.keys();
622             String reply_with;
623             Performative reply;
624             Object[] data;
625             AbilitySpec b, a = event.getAbility();
626             
627             Fact f2, f1 = a.getFact();
628             int   t2, t1 = a.getTime();
629             double  c2, c1 = a.getCost();
630             
631             Bindings bindings = new Bindings(context.whoami());
632             
633             while( enum.hasMoreElements() ) {
634                 bindings.clear();
635                 reply_with = (String)enum.nextElement();
636                 data = (Object[])abilityQueryTable.get(reply_with);
637                 
638                 b = (AbilitySpec)data[1];
639                 f2 = b.getFact();
640                 t2 = b.getTime();
641                 c2 = b.getCost();
642                 if ( (t1 == 0 || t2 <= t1) && (c1 == 0 || c2 <= c1) &&
643                 f2.unifiesWith(f1,bindings) ) {
644                     reply = new Performative("inform");
645                     reply.setContent(event.getAbilityDbItem().toString());
646                     reply.setReceiver((String)data[0]);
647                     reply.setInReplyTo(reply_with);
648                     context.MailBox().sendMsg(reply);
649                 }
650             }
651         }
652     }
653     
654     public void address_of(Performative msg) {
655         String content = msg.getContent();
656         String reply_with = msg.getReplyWith();
657         if ( reply_with == null ) {
658             refuse(msg,"no reply-with key");
659             return;
660         }
661         else if ( content == null ) {
662             failure(msg,"no content");
663             return;
664         }
665         MsgContentHandler hd = new MsgContentHandler(content);
666         Address addr;
667         if ( hd.data() != null &&
668         (addr = context.MailBox().lookup(hd.data())) != null ) {
669             Performative reply = new Performative("inform");
670             reply.setReceiver(msg.getSender());
671             reply.setInReplyTo(reply_with);
672             reply.setContent(addr.toString());
673             context.MailBox().sendMsg(reply);
674         }
675         else
676             failure(msg,"address not known");
677     }
678     
679     public void your_abilities(Performative msg) {
680         try {
681         String content = msg.getContent();
682         String reply_with = msg.getReplyWith();
683         if ( reply_with == null ) {
684             refuse(msg,"no reply-with key");
685             return;
686         }
687         else if ( context.OrganisationDb() == null ) {
688             // 1.04 change of spelling by simon
689             refuse(msg,"no organization database");
690             return;
691         }
692         Vector List = context.OrganisationDb().abilitiesOf(context.whoami());
693         if ( List.isEmpty() ) {
694             failure(msg,"no abilities");
695             return;
696         }
697         Performative reply = new Performative("inform");
698         reply.setContent(Misc.concat(List));
699         reply.setReceiver(msg.getSender());
700         reply.setInReplyTo(reply_with);
701         context.MailBox().sendMsg(reply);
702         sendServiceDescriptions(msg.getSender(), msg.getReplyWith());
703         }
704         catch (Throwable t) {
705             t.printStackTrace(); 
706         }
707     }
708     
709     
710     protected void sendServiceDescriptions(String target, String replyWith) {
711 
712        try {
713         TaskDb tasks = context.getTaskDb();
714         Enumeration keys = tasks.keys();
715         while (keys.hasMoreElements()) {
716             Hashtable ofType = (Hashtable) tasks.get(keys.nextElement());
717             Enumeration allElements = ofType.elements();
718             while (allElements.hasMoreElements()) { 
719 
720 	      Task task = (Task) allElements.nextElement();
721 
722 	      sendProfile(task, context, target, replyWith);
723 	      sendInstance(task, context, target, replyWith);
724 	      sendInstanceRange(task, context, target, replyWith);
725 	      sendProcess(task, context, target, replyWith);
726 
727             }//end while
728         }// end while
729        }
730        catch (Exception e) { 
731            e.printStackTrace(); 
732        }
733     }
734     
735   private void sendProfile(Task task, AgentContext context, String target,
736 			   String replyWith) {
737 
738     String description = task.getServiceDesc(context); 
739     if(description != null && description.length() > 0) {
740       Performative sendMsg = new Performative("inform");
741       sendMsg.setReceiver(target);
742       sendMsg.setInReplyTo(replyWith);
743       sendMsg.setContent("( :serviceProfile (\"" + description + "\"))");
744       sendMsg.send(context);
745     }
746   }
747     
748   private void sendInstance(Task task, AgentContext context, String target,
749 			    String replyWith) {
750 
751     String instanceDetails = task.getInstanceDetails(context);
752     if(instanceDetails != null && instanceDetails.length() > 0) {
753       Performative instanceDesc = new Performative("inform");
754       instanceDesc.setReceiver(target);
755       instanceDesc.setInReplyTo(replyWith);
756       instanceDesc.setContent("( :serviceInstance (\"" +
757 			      instanceDetails + "\"))");
758       instanceDesc.send(context);
759     }
760   }
761     
762   private void sendInstanceRange(Task task, AgentContext context,
763 				 String target, String replyWith) {
764 
765     String instanceRange = task.getInstanceRange(context);
766     if(instanceRange != null && instanceRange.length() > 0) {
767       Performative instanceDesc = new Performative("inform");
768       instanceDesc.setReceiver(target);
769       instanceDesc.setInReplyTo(replyWith);
770       instanceDesc.setContent("( :serviceRange (:task " + task.getName() +
771 			      " :content \"" + instanceRange + "\"))");
772       instanceDesc.send(context);
773     }
774   }
775 
776   private void sendProcess(Task task, AgentContext context, String target,
777 			   String replyWith) {
778 
779     String processModel = task.getProcessModel(context);
780     if(processModel != null && processModel.length() > 0) {
781       Performative process = new Performative("inform");
782       process.setReceiver(target);
783       process.setInReplyTo(replyWith);
784       process.setContent("( :processModel (\"" + processModel + "\"))");
785       process.send(context);
786     }
787   }
788     
789     public void your_relations(Performative msg) {
790         String content = msg.getContent();
791         String reply_with = msg.getReplyWith();
792         if ( reply_with == null ) {
793             refuse(msg,"no reply-with key");
794             return;
795         }
796         else if ( context.OrganisationDb() == null ) {
797             // 1.04 change of spelling by simon
798             refuse(msg,"no organization database");
799             return;
800         }
801         Vector List = context.OrganisationDb().allRelations();
802         if ( List.isEmpty() ) {
803             failure(msg,"no relations");
804             return;
805         }
806         Performative reply = new Performative("inform");
807         reply.setContent(Misc.concat(List));
808         reply.setReceiver(msg.getSender());
809         reply.setInReplyTo(reply_with);
810         context.MailBox().sendMsg(reply);
811     }
812     
813     public void has_ability(Performative msg) {
814         String content = msg.getContent();
815         String reply_with = msg.getReplyWith();
816         if ( reply_with == null ) {
817             refuse(msg,"no reply-with key");
818             return;
819         }
820         MsgContentHandler hd = new MsgContentHandler(content);
821         if ( hd.data() == null ) {
822             refuse(msg,"no ability specified");
823             return;
824         }
825         else if ( context.OrganisationDb() == null ) {
826             // 1.04 change of spelling by simon
827             refuse(msg,"no organization database");
828             return;
829         }
830         AbilitySpec a = ZeusParser.abilitySpec(context.OntologyDb(),hd.data());
831         if ( a == null ) {
832             refuse(msg,"no ability specified");
833             return;
834         }
835         Vector List = context.OrganisationDb().findAll(a);
836         if ( List.isEmpty() ) {
837             failure(msg,"no abilities known");
838             return;
839         }
840         Performative reply = new Performative("inform");
841         reply.setContent(Misc.concat(List));
842         reply.setReceiver(msg.getSender());
843         reply.setInReplyTo(reply_with);
844         context.MailBox().sendMsg(reply);
845     }
846     
847     public void isa_facilitator(Performative msg) {
848         MsgContentHandler hd = new MsgContentHandler(msg.getContent());
849         context.addFacilitator(hd.data());
850     }
851     
852     public void add_agents_abilities(Performative msg) {
853         MsgContentHandler hd = new MsgContentHandler(msg.getContent());
854         Vector List = ZeusParser.abilitySpecList(context.OntologyDb(),hd.data());
855         String agent = msg.getSender();
856         context.OrganisationDb().add(agent,List);
857     }
858     
859     public void add_information(Performative msg) {
860         try {
861             MsgContentHandler hd = new MsgContentHandler(msg.getContent());
862             Vector List = ZeusParser.factList(context.OntologyDb(),hd.data());
863             // **BUG**
864             // should re-reference variables in factList before adding to my database
865             context.ResourceDb().add(dereference(List));
866         }
867         catch(Exception e) {
868             //e.printStackTrace();
869         }
870     }
871     
872     protected void refuse(Performative msg, String content) {
873         Performative reply = new Performative("refuse");
874         reply.setReceiver(msg.getSender());
875         reply.setContent(content);
876         String reply_with = msg.getReplyWith();
877         if ( reply_with != null )
878             reply.setInReplyTo(reply_with);
879         context.MailBox().sendMsg(reply);
880     }
881     
882     protected void failure(Performative msg, String content) {
883         Performative reply = new Performative("failure");
884         reply.setReceiver(msg.getSender());
885         reply.setContent(content);
886         String reply_with = msg.getReplyWith();
887         if ( reply_with != null )
888             reply.setInReplyTo(reply_with);
889         context.MailBox().sendMsg(reply);
890     }
891     
892     protected void not_understood(Performative msg, String content) {
893         Performative reply = new Performative("failure");
894         reply.setReceiver(msg.getSender());
895         reply.setContent(content);
896         String reply_with = msg.getReplyWith();
897         if ( reply_with != null )
898             reply.setInReplyTo(reply_with);
899         context.MailBox().sendMsg(reply);
900     }
901     
902     protected void inform(Performative msg, String content) {
903         Performative reply = new Performative("inform");
904         reply.setReceiver(msg.getSender());
905         reply.setContent(content);
906         String reply_with = msg.getReplyWith();
907         if ( reply_with != null )
908             reply.setInReplyTo(reply_with);
909         context.MailBox().sendMsg(reply);
910     }
911     
912     
913     /***
914      * If your code needs to react to changes in the MessageHandler use this
915      * to add a MessageHandlerMonitor. This provides a programatic alternative to
916      * writing reaction rules
917      */
918     public void addMessageHandlerMonitor(MessageHandlerMonitor monitor,
919     long event_type) {
920         if ( (event_type & MessageHandlerEvent.ADD_MASK) != 0 )
921             eventMonitor[ADD].add(monitor);
922         if ( (event_type & MessageHandlerEvent.FIRE_MASK) != 0 )
923             eventMonitor[FIRE].add(monitor);
924         if ( (event_type & MessageHandlerEvent.DELETE_MASK) != 0 )
925             eventMonitor[DELETE].add(monitor);
926         if ( (event_type & MessageHandlerEvent.FAIL_MASK) != 0 )
927             eventMonitor[FAIL].add(monitor);
928     }
929     
930     public void removeMessageHandlerMonitor(MessageHandlerMonitor monitor,
931     long event_type) {
932         if ( (event_type & MessageHandlerEvent.ADD_MASK) != 0 )
933             eventMonitor[ADD].remove(monitor);
934         if ( (event_type & MessageHandlerEvent.FIRE_MASK) != 0 )
935             eventMonitor[FIRE].remove(monitor);
936         if ( (event_type & MessageHandlerEvent.DELETE_MASK) != 0 )
937             eventMonitor[DELETE].remove(monitor);
938         if ( (event_type & MessageHandlerEvent.FAIL_MASK) != 0 )
939             eventMonitor[FAIL].remove(monitor);
940     }
941     
942     protected void notifyMonitors(int type, MessageHandlerEvent event) {
943         if ( eventMonitor[type].isEmpty() ) return;
944         
945         MessageHandlerMonitor monitor;
946         Enumeration enum = eventMonitor[type].elements();
947         
948         switch(type) {
949             case ADD:
950                 while( enum.hasMoreElements() ) {
951                     monitor = (MessageHandlerMonitor)enum.nextElement();
952                     monitor.messageRuleAddedEvent(event);
953                 }
954                 break;
955             case FIRE:
956                 while( enum.hasMoreElements() ) {
957                     monitor = (MessageHandlerMonitor)enum.nextElement();
958                     monitor.messageRuleFiredEvent(event);
959                 }
960                 break;
961             case DELETE:
962                 while( enum.hasMoreElements() ) {
963                     monitor = (MessageHandlerMonitor)enum.nextElement();
964                     monitor.messageRuleDeletedEvent(event);
965                 }
966                 break;
967             case FAIL:
968                 while( enum.hasMoreElements() ) {
969                     monitor = (MessageHandlerMonitor)enum.nextElement();
970                     monitor.messageRuleFailedEvent(event);
971                 }
972                 break;
973         }
974     }
975     
976     /***
977      * Replication to handle the problem of variable clashes when
978      * agents communicate with one another with variables in the
979      * content data;
980      */
981     protected final Vector dereference(Vector input) {
982         Fact f;
983         Goal g;
984         Vector result = new Vector();
985         try {
986             Object x = input.elementAt(0);
987             if ( x instanceof zeus.concepts.Fact ) {
988                 for(int i = 0; i < input.size(); i++ ) {
989                     f = (Fact)input.elementAt(i);
990                     f = f.duplicate(Fact.VAR,context.GenSym());
991                     result.addElement(f);
992                 }
993             }
994             else {
995                 for(int i = 0; i < input.size(); i++ ) {
996                     g = (Goal)input.elementAt(i);
997                     g = g.duplicate(Fact.VAR,context.GenSym());
998                     result.addElement(g);
999                 }
1000             }
1001         }catch (Exception e) {
1002             // e.printStackTrace();
1003         }
1004         
1005         return result;
1006     }
1007     
1008     private void debug(String str) {
1009      //   System.out.println("Free memory = " +Runtime.getRuntime().freeMemory());
1010        // System.out.println("MsgHandler>> " + str);
1011     }
1012     
1013 }