1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package zeus.actors;
29
30 import java.net.*;
31 import java.io.*;
32 import java.util.*;
33 import zeus.util.*;
34 import zeus.concepts.*;
35 import zeus.actors.event.*;
36 import zeus.actors.intrays.*;
37
38 /***
39 * Each agent has a Mailbox component that implements its communication
40 * mechanism. The sub-components of the MailBox are together responsible for
41 * creating and reading the TCP/IP sockets that send and receive messages. <p>
42 *
43 * The MailBox maintains two independent threads of activity, one is a reader
44 * thread, which continually listens for incoming socket connections, whereupon
45 * a new transient thread is created to read the message and deliver it to the
46 * {@link MsgHandler}, which processes it. This approach delegates
47 * responsibility for reading messages to the new connection thread, leaving the
48 * main {@link Server} thread free to continue listening for incoming messages, (thus
49 * enabling several messages to be received simultaneously). When the incoming
50 * message is read, the connection thread terminates. <p>
51 *
52 * The other Mailbox thread is a {@link PostMan} object, which creates transient
53 * threads that open sockets to the message recipients. If the connection is made
54 * the message is then streamed down the socket, this allows the agent to
55 * dispatch more than one message at a time. <p>
56 *
57 * More details on the workings of the communication mechanism are provided
58 * in the Zeus Technical Manual.
59 */
60 public class MailBox
61 {
62 /***
63 eventMonitor used to be private, but I needed to alter this so
64 that I could build an effective sub-class
65 */
66 protected HSet[] eventMonitor = new HSet[4];
67
68 public static final int RECEIVE = 0;
69 public static final int QUEUE = 1;
70 public static final int DISPATCH = 2;
71 public static final int NOT_DISPATCH = 3;
72
73 /*** A data structure holding the agent's incoming mail messages */
74 protected Queue inMail = new Queue("Zeus inMail");
75
76 /*** A data structure holding the agent's outgoing mail messages */
77 protected Queue outMail = new Queue("Zeus outMail");
78
79 /*** Holds mail messages that need to be CC'ed to Visualiser agents */
80 protected Queue ccMail = new Queue("Zeus ccMail");
81
82 protected Hashtable asTable = new Hashtable();
83 protected Hashtable visualisers = new Hashtable();
84
85 /*** The sub-component responsible for reading incoming mail */
86 protected Server server;
87
88 /*** The sub-component responsible for dispatching outgoing mail */
89 protected PostMan[] postman;
90
91 protected Address myAddress;
92 protected AgentContext context;
93
94 public MailBox () {;}
95
96 public MailBox(AgentContext context) {
97 Assert.notNull(context);
98 this.context = context;
99 context.set(this);
100
101 Address addr;
102 Performative msg;
103
104
105 for(int i = 0; i < eventMonitor.length; i++ )
106 eventMonitor[i] = new HSet();
107
108 context.set(new AddressBook());
109 server = new Server(context,this,inMail);
110 myAddress = server.getAddress();
111
112 postman = new PostMan[2];
113 postman[0] = new PostMan(this,outMail,ccMail,myAddress);
114 postman[1] = new PostMan(this,ccMail,myAddress);
115
116
117 String key = context.newId();
118 String[] pattern = { "type", "inform", "in-reply-to", key };
119
120 context.MsgHandler().addRule(new MessageRuleImpl(context.newId("Rule"),
121 pattern,MessageActionImpl.EXECUTE_ONCE,this,"register")
122 );
123
124 for(int i = 0; i < context.nameservers().size(); i++ ) {
125 addr = (Address)context.nameservers().elementAt(i);
126 context.AddressBook().add(addr);
127
128 msg = new Performative("request");
129 msg.setReceiver(addr.getName());
130 msg.setReplyWith(key);
131 msg.setContent("register");
132 sendMsg(msg);
133 }
134 }
135
136 public void register(Performative msg) {
137 String content = msg.getContent();
138 if ( context.Clock() == null && content != null ) {
139 StringTokenizer st = new StringTokenizer(content);
140 long prev, incr, now0, now1;
141 long deltaX, deltaT = 160;
142
143 prev = Long.parseLong(st.nextToken());
144 incr = Long.parseLong(st.nextToken());
145 now0 = Long.parseLong(st.nextToken());
146 now1 = System.currentTimeMillis();
147 deltaX = now1 - (now0+deltaT);
148 context.set(new Clock(prev+deltaX,incr));
149 }
150 }
151
152 public AgentContext getAgentContext() { return context; }
153
154 public void del( Address addr ) {
155 Assert.notNull(addr);
156 context.AddressBook().del( addr );
157
158
159
160
161
162
163 KeyValue data = (KeyValue) asTable.remove(addr.getName());
164 if ( data != null)
165 for(int i = 0; i < postman.length; i++ )
166 postman[i].addressReceived(data.key);
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184 }
185
186 public void del( Vector v ) {
187 Assert.notNull(v);
188 for(int i = 0; i < v.size(); i++ )
189 context.AddressBook().del( (Address)v.elementAt(i) );
190 }
191
192 public void add( Address addr ) {
193 Assert.notNull(addr);
194 context.AddressBook().add( addr );
195 }
196
197 public void add( Vector v ) {
198 Assert.notNull(v);
199 for(int i = 0; i < v.size(); i++ )
200 context.AddressBook().add( (Address)v.elementAt(i) );
201 }
202
203 public Address lookup( String name ) {
204 Assert.notNull(name);
205 return context.AddressBook().lookup( name );
206 }
207
208 public void stopDispatching() {
209 for( int i = 0; i < postman.length; i++ )
210 postman[i].stopDispatching();
211 }
212
213 public void stopProcessing() {
214 server.stopProcessing();
215 }
216
217 public void lowerStatus() {
218 for( int i = 0; i < postman.length; i++ )
219 postman[i].lowerStatus();
220 server.lowerStatus();
221 }
222
223 /***
224 changed to public so that subclasses of the classes in this package can have access
225 (Simon)
226 */
227 public String addressSought(String agent) {
228
229
230
231
232
233 String name;
234 KeyValue data;
235 double now = context.now();
236 Enumeration enum = asTable.keys();
237 while( enum.hasMoreElements() ) {
238 name = (String) enum.nextElement();
239 data = (KeyValue)asTable.get(name);
240 if ( now-data.value >= context.getAddressBookRefresh() ) {
241 asTable.remove(name);
242 context.MsgHandler().removeRule(data.key);
243 }
244 }
245 data = (KeyValue)asTable.get(agent);
246 if (data == null) {
247
248 Performative query;
249 Address addr;
250 String key = context.newId();
251 String[] pattern = { "type", "inform", "in-reply-to", key };
252 context.MsgHandler().addRule(new MessageRuleImpl(key,pattern,
253 MessageActionImpl.EXECUTE_ONCE,this,"addressReceived")
254 );
255 for(int i = 0; i < context.nameservers().size(); i++ ) {
256 addr = (Address)context.nameservers().elementAt(i);
257 query = new Performative("query-ref");
258 query.setReceiver(addr.getName());
259 query.setReplyWith(key);
260 query.setContent("address_of " + agent);
261 sendMsg(query);
262 }
263
264
265
266 now = context.now();
267 if ( !context.nameservers().isEmpty() ) {
268 now += context.getAddressTimeout();
269 asTable.put(agent,new KeyValue(key,now));
270 return key;
271 }
272 else {
273 return null;
274 }
275 }
276
277 else if ( data.value > context.now()){
278 return data.key;}
279 else{
280 return null;}
281 }
282
283 public void addressReceived(Performative msg) {
284 String key = msg.getInReplyTo();
285 Address address = ZeusParser.address(msg.getContent());
286 add(address);
287 asTable.remove(address.getName());
288 for(int i = 0; i < postman.length; i++ )
289 postman[i].addressReceived(key);
290 }
291
292 public void logMessages(String agent, String tag) {
293 Assert.notNull(agent);
294 Assert.notNull(tag);
295 visualisers.put(agent,tag);
296 }
297
298
299 public void stopLoggingMessages(String agent) {
300 Assert.notNull(agent);
301 visualisers.remove(agent);
302 }
303
304
305 public void informVisualisers(Performative msg) {
306 Enumeration keys = visualisers.keys();
307 String agent, replyTag;
308 Performative inform;
309 while( keys.hasMoreElements() ) {
310 agent = (String) keys.nextElement();
311 replyTag = (String)visualisers.get(agent);
312 inform = new Performative("inform");
313 inform.setReceiver(agent);
314 inform.setInReplyTo(replyTag);
315 inform.setContent((msg.toString()).trim());
316 ccMail.enqueue(inform);
317 notifyMonitors(inform,QUEUE);
318 }
319 }
320
321
322 public Address getAddress() {
323 return myAddress;
324 }
325
326
327 public void shutdown() {
328 Address addr;
329 Performative msg;
330
331 for(int i = 0; i < context.nameservers().size(); i++ ) {
332 addr = (Address)context.nameservers().elementAt(i);
333 msg = new Performative("request");
334 msg.setContent("deregister");
335 msg.setReceiver(addr.getName());
336 sendMsg(msg);
337 }
338 }
339
340
341 public void postErrorMsg(Performative msg, String content) {
342 notifyMonitors(msg,NOT_DISPATCH);
343 String reply_with;
344 Time time;
345 Performative errorMsg = new Performative("failure");
346 errorMsg.setSender(myAddress.getName());
347 errorMsg.setReceiver(myAddress.getName());
348 errorMsg.setAddress(myAddress);
349 if ( (reply_with = msg.getReplyWith()) != null )
350 errorMsg.setInReplyTo(reply_with);
351 errorMsg.setContent(content + " " + msg);
352 if ( (time = context.currentTime()) != null )
353 errorMsg.setSendTime(time);
354 server.newMsg(errorMsg);
355 }
356
357
358 public void sendMsg(Performative msg) {
359 postman[0].push(msg);
360 notifyMonitors(msg,QUEUE);
361 }
362
363 /***
364 redundant now
365 */
366 public Performative nextMsg() {
367 Object obj = inMail.dequeue();
368 try {
369 Performative perf = (Performative) obj;
370 return (perf);
371 } catch (Exception e) {
372 e.printStackTrace();
373 return new Performative (obj.toString());
374 }
375
376
377 }
378
379 public Vector listAddresses() {
380 Vector result = new Vector(10);
381 Enumeration enum = context.AddressBook().elements();
382 Address addr;
383
384 while( enum.hasMoreElements() ) {
385 addr = (Address) enum.nextElement();
386 result.addElement(new ZeusAddress(addr));
387 }
388 return result;
389 }
390
391 /***
392 * Use this method to add a MessageMonitor if your code needs to react to
393 * changes in the state of the mailbox. This is programatic alternative to
394 * writing reaction rules
395 */
396 public void addMessageMonitor(MessageMonitor monitor, long event_type) {
397 if ( (event_type & MessageEvent.RECEIVE_MASK) != 0 )
398 eventMonitor[RECEIVE].add(monitor);
399 if ( (event_type & MessageEvent.QUEUE_MASK) != 0 )
400 eventMonitor[QUEUE].add(monitor);
401 if ( (event_type & MessageEvent.DISPATCH_MASK) != 0 )
402 eventMonitor[DISPATCH].add(monitor);
403 if ( (event_type & MessageEvent.NOT_DISPATCH_MASK) != 0 )
404 eventMonitor[NOT_DISPATCH].add(monitor);
405 }
406
407 public void removeMessageMonitor(MessageMonitor monitor, long event_type) {
408 if ( (event_type & MessageEvent.RECEIVE_MASK) != 0 )
409 eventMonitor[RECEIVE].remove(monitor);
410 if ( (event_type & MessageEvent.QUEUE_MASK) != 0 )
411 eventMonitor[QUEUE].remove(monitor);
412 if ( (event_type & MessageEvent.DISPATCH_MASK) != 0 )
413 eventMonitor[DISPATCH].remove(monitor);
414 if ( (event_type & MessageEvent.NOT_DISPATCH_MASK) != 0 )
415 eventMonitor[NOT_DISPATCH].remove(monitor);
416 }
417
418
419 public void notifyMonitors(Performative message, int type) {
420 if ( eventMonitor[type].isEmpty() ) return;
421
422 MessageMonitor monitor;
423 MessageEvent event;
424 Enumeration enum = eventMonitor[type].elements();
425 switch(type) {
426 case RECEIVE:
427 event = new MessageEvent(this,message,MessageEvent.RECEIVE_MASK);
428 while( enum.hasMoreElements() ) {
429 monitor = (MessageMonitor)enum.nextElement();
430 monitor.messageReceivedEvent(event);
431 }
432 break;
433 case QUEUE:
434 event = new MessageEvent(this,message,MessageEvent.QUEUE_MASK);
435 while( enum.hasMoreElements() ) {
436 monitor = (MessageMonitor)enum.nextElement();
437 monitor.messageQueuedEvent(event);
438 }
439 break;
440 case DISPATCH:
441 event = new MessageEvent(this,message,MessageEvent.DISPATCH_MASK);
442 while( enum.hasMoreElements() ) {
443 monitor = (MessageMonitor)enum.nextElement();
444 monitor.messageDispatchedEvent(event);
445 }
446 break;
447 case NOT_DISPATCH:
448 event = new MessageEvent(this,message,MessageEvent.NOT_DISPATCH_MASK);
449 while( enum.hasMoreElements() ) {
450 monitor = (MessageMonitor)enum.nextElement();
451 monitor.messageNotDispatchedEvent(event);
452 }
453 break;
454 }
455 }
456
457
458
459 /***
460 added so that transports can be accessed
461 @since 1.1
462 @author Simon "guilty party" Thompson
463 */
464 public InTray getInTray () {
465 return server;
466 }
467 }