001 // jademx - JADE management using JMX
002 // Copyright 2005 Caboodle Networks, Inc.
003 //
004 // This library is free software; you can redistribute it and/or
005 // modify it under the terms of the GNU Lesser General Public
006 // License as published by the Free Software Foundation; either
007 // version 2.1 of the License, or (at your option) any later version.
008 //
009 // This library is distributed in the hope that it will be useful,
010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 // Lesser General Public License for more details.
013 //
014 // You should have received a copy of the GNU Lesser General Public
015 // License along with this library; if not, write to the Free Software
016 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
017
018 package jade.jademx.agent;
019
020 import jade.content.ContentManager;
021 import jade.content.lang.Codec;
022 import jade.content.lang.sl.SLCodec;
023 import jade.content.onto.BasicOntology;
024 import jade.content.onto.Ontology;
025 import jade.content.onto.OntologyException;
026 import jade.content.onto.basic.Action;
027 import jade.content.onto.basic.Result;
028 import jade.content.schema.ConceptSchema;
029 import jade.core.AID;
030 import jade.core.Agent;
031 import jade.domain.FIPANames;
032 import jade.domain.FIPAAgentManagement.NotUnderstoodException;
033 import jade.domain.FIPAAgentManagement.RefuseException;
034 import jade.jademx.util.MBeanUtil;
035 import jade.lang.acl.ACLMessage;
036 import jade.lang.acl.MessageTemplate;
037 import jade.proto.SimpleAchieveREInitiator;
038 import jade.proto.SimpleAchieveREResponder;
039 import jade.util.Logger;
040
041 import javax.management.Attribute;
042 import javax.management.AttributeNotFoundException;
043 import javax.management.InvalidAttributeValueException;
044 import javax.management.MBeanAttributeInfo;
045 import javax.management.MBeanConstructorInfo;
046 import javax.management.MBeanException;
047 import javax.management.MBeanInfo;
048 import javax.management.MBeanNotificationInfo;
049 import javax.management.MBeanOperationInfo;
050 import javax.management.MBeanParameterInfo;
051 import javax.management.Notification;
052 import javax.management.ReflectionException;
053
054 /**
055 * sample agent to demonstrate building agent using jademx.
056 * @author David Bernstein, <a href="http://www.caboodlenetworks.com"
057 * >Caboodle Networks, Inc.</a>
058 */
059 public class JademxPingAgent extends JademxAgent {
060
061 /**
062 * make a Jademx ping agent
063 */
064 public JademxPingAgent() {
065 super();
066 mBeanInfoThisLevel = constructMBeanInfo();
067 logger.log(Logger.FINE,"made MBeanInfo "+mBeanInfoThisLevel);
068 mBeanInfoSuper = super.getMBeanInfo();
069 mBeanInfoMerged = MBeanUtil.mergeMBeanInfo(
070 getClass().getName(), DESCRIPTION,
071 mBeanInfoThisLevel, mBeanInfoSuper );
072 logger.log(Logger.FINE,"made merged MBeanInfo "+mBeanInfoMerged);
073 //setMBeanInfo( constructMBeanInfo() );
074 }
075
076 /** default answer */
077 public final static String DEFAULT_ANSWER = "pong";
078 /**
079 * response to a ping.
080 * access must be synchronized because it's accessed both by the
081 * MBeanServer thread and by the JADE agent thread.
082 */
083 private String response = DEFAULT_ANSWER;
084 /** my logger */
085 private final Logger logger =
086 Logger.getMyLogger(JademxPingAgent.class.getName());
087 /** my codec */
088 private Codec codec = new SLCodec();
089 /** my ontology */
090 private Ontology ontology = PingOntology.getInstance();
091 /** my content manager */
092 private ContentManager contentManager = null;
093
094 // MBEAN FEATURE INFO
095
096 // attributes
097
098 /** response attribute name */
099 public final static String ATTR_RESPONSE_NAME = "Response";
100 /** response attribute description */
101 private final static String ATTR_RESPONSE_DESC = "ping response";
102 /** response attribute type */
103 private final static String ATTR_RESPONSE_TYPE = String.class.getName();
104
105 // operations
106
107 /** ping operation name */
108 public final static String OPER_PING_NAME = "ping";
109 /** ping operation description */
110 private final static String OPER_PING_DESC = "ping another agent";
111 /** ping agentFullName parameter name */
112 private final static String OPER_PING_AGENT_FULL_NAME_PARM_NAME =
113 "agentFullName";
114 /** ping agentFullName parameter type */
115 private final static String OPER_PING_AGENT_FULL_NAME_PARM_TYPE =
116 String.class.getName();
117 /** ping agentFullName parameter description */
118 private final static String OPER_PING_AGENT_FULL_NAME_PARM_DESC =
119 "full JADE name of agent that pinged this agent";
120 /** signature for a ping operation */
121 public final static String OPER_PING_SIGNATURE[] = {
122 OPER_PING_AGENT_FULL_NAME_PARM_TYPE
123 };
124 /** return type for ping operation */
125 public final static String OPER_PING_TYPE = String.class.getName();
126
127 // nofifications
128
129 /** notification that have been pinged */
130 public final static String NOTIF_PINGED_NAME = "pinged";
131
132
133 // END MBEAN FEATURE INFO
134
135 /** description for MBeanInfo */
136 private final static String DESCRIPTION =
137 "sample ping agent under Jademx control";
138
139
140 /** MBeanInfo for this class and superclass(es) */
141 private MBeanInfo mBeanInfoMerged = null;
142 /** MBeanInfo for superclass(es) */
143 private MBeanInfo mBeanInfoSuper = null;
144 /** MBeanInfo for for this class but not superclass(es) */
145 private MBeanInfo mBeanInfoThisLevel = null;
146
147 /**
148 * create a new MBeanInfo object to describe this agent.
149 * @return new MBeanInfo object to describe this agent.
150 */
151 private MBeanInfo constructMBeanInfo() {
152
153 // attributes
154
155 MBeanAttributeInfo aI[] = new MBeanAttributeInfo[] {
156 new MBeanAttributeInfo(
157 ATTR_RESPONSE_NAME,
158 ATTR_RESPONSE_TYPE,
159 ATTR_RESPONSE_DESC,
160 true, true, false )
161 };
162
163 // constructors
164
165 MBeanConstructorInfo cI[] = new MBeanConstructorInfo[0];
166
167 // operations
168
169 MBeanParameterInfo pIPing[] = {
170 new MBeanParameterInfo(
171 OPER_PING_AGENT_FULL_NAME_PARM_NAME,
172 OPER_PING_AGENT_FULL_NAME_PARM_TYPE,
173 OPER_PING_AGENT_FULL_NAME_PARM_DESC)
174 };
175 MBeanOperationInfo oI[] = new MBeanOperationInfo[] {
176 new MBeanOperationInfo(
177 OPER_PING_NAME,
178 OPER_PING_DESC,
179 pIPing,
180 OPER_PING_TYPE,
181 MBeanOperationInfo.INFO )
182 };
183
184 // notifications
185
186 String notifications[] = {
187 NOTIF_PINGED_NAME
188 };
189 String NOTIF_INFO_DESCRIPTION =
190 "notification set for " + getClass().getName();
191 MBeanNotificationInfo nI[] = new MBeanNotificationInfo[] {
192 new MBeanNotificationInfo(
193 notifications,
194 Notification.class.getName(),
195 NOTIF_INFO_DESCRIPTION )
196 };
197
198 // now, MBeanInfo for this level of class hierarchy
199 MBeanInfo mBeanInfo = new MBeanInfo( getClass().getName(),
200 DESCRIPTION,
201 aI, cI, oI, nI );
202
203 return mBeanInfo;
204
205 }
206
207
208 /**
209 * this is a merge of the agent, this class, and this class's superclass.
210 * @return MBean information for ping agent including JademxAgent
211 */
212 public MBeanInfo getMBeanInfo() {
213 return mBeanInfoMerged;
214 }
215
216
217
218 /* (non-Javadoc)
219 * @see jade.core.Agent#setup()
220 */
221 protected void setup() {
222 // make sure set up as a JademxAgent
223 super.setup();
224 // get content manager set up
225 contentManager = getContentManager();
226 contentManager.registerLanguage( codec );
227 contentManager.registerOntology( ontology );
228 // add the PingResponderBehaviour
229 addBehaviour(
230 new PingResponder(
231 this,
232 MessageTemplate.MatchPerformative( ACLMessage.REQUEST ) ) );
233 }
234 // ACTUAL ATTRIBUTE AND OPERATION
235
236 /** count pings for benefit of reply-with in messages */
237 private int pingCount = 0;
238 /**
239 * perform ping operation
240 * @param pingeeAgentFullName full JADE name of agent to ping
241 * @return current response
242 */
243 public String ping( String pingeeAgentFullName ) throws Exception {
244
245 // get the ping message ready
246
247 ACLMessage request = new ACLMessage( ACLMessage.REQUEST );
248 AID receiver = new AID( pingeeAgentFullName, AID.ISGUID );
249 request.addReceiver( receiver );
250 request.setConversationId( "ping-conv-" +
251 Long.toString( System.currentTimeMillis() ) +
252 "-" +
253 Long.toString( this.hashCode() ) );
254 request.setLanguage( codec.getName() );
255 request.setOntology( ontology.getName() );
256 request.setProtocol( FIPANames.InteractionProtocol.FIPA_REQUEST );
257 request.setReplyWith( "ping" + Integer.toString( ++pingCount ) );
258 request.setSender( getAID() );
259
260 Ping ping = new Ping();
261 Action pingAction = new Action();
262 pingAction.setActor( receiver );
263 pingAction.setAction( ping );
264
265 contentManager.fillContent( request, pingAction );
266
267 logger.log( Logger.FINE, "request: "+request);
268
269 // start behaviour to perform ping and wait for response.
270 // OK to use wait() because this is a thread belonging to the
271 // MBeanServer and the behaviour runs in a different thread.
272
273 String response = null;
274 Exception exception = null;
275 PingResponse pingResponse = new PingResponse();
276 synchronized( pingResponse ) {
277 // enqueue behaviour to perform ping
278 addBehaviour( new PingInitiator( this, request, pingResponse ) );
279 // block until have response
280 while ( ( null == ( exception = pingResponse.getException() ) ) &&
281 ( null == ( response = pingResponse.getResponse() ) ) ) {
282 pingResponse.wait();
283 }
284 }
285
286 // if got an exception performing ping, re-throw it
287
288 if ( null != exception ) {
289 throw exception;
290 }
291
292 // return the response
293
294 return response;
295 }
296
297 /**
298 * set the response
299 * @param response response to set
300 */
301 public void setResponse( String response ) {
302 synchronized ( this.response ) {
303 this.response = response;
304 }
305 }
306
307 /**
308 * get response
309 * @return response
310 */
311 public String getResponse() {
312 synchronized ( this.response ) {
313 return response;
314 }
315 }
316
317 // MBEAN INTERFACE TO ATTRIBUTE AND OPERATION
318
319 /**
320 * get an attribute value
321 * @param attribute name of attribute to get
322 * @return attribute value
323 * @throws AttributeNotFoundException no such attribute
324 * @throws MBeanException exception from getter
325 * @throws ReflectionException exception invoking getter
326 */
327 public Object getAttribute( String attribute )
328 throws AttributeNotFoundException,
329 MBeanException,
330 ReflectionException {
331 Object o;
332 // attributes known about directly at this level
333 if ( ATTR_RESPONSE_NAME.equals( attribute ) ) {
334 o = getResponse();
335 }
336 // attributes known by parent class
337 else {
338 o = super.getAttribute( attribute );
339 }
340 return o;
341 }
342
343 /**
344 * @param attribute
345 * @throws AttributeNotFoundException no such attribte
346 * @throws InvalidAttributeValueException bad attribute value
347 * @throws MBeanException exception from setter
348 * @throws ReflectionException exception invoking setter
349 */
350 public void setAttribute( Attribute attribute )
351 throws AttributeNotFoundException,
352 InvalidAttributeValueException,
353 MBeanException,
354 ReflectionException {
355
356 String attributeName = attribute.getName();
357 Object v = attribute.getValue();
358 // attributes known about directly at this level
359 if ( ATTR_RESPONSE_NAME.equals( attributeName ) ) {
360 String s;
361 try {
362 s = (String) v;
363 }
364 catch ( ClassCastException cce ){
365 throw new InvalidAttributeValueException( "for attribute "+
366 " need "+String.class.getName()+" got "+
367 v.getClass().getName() );
368 }
369 try {
370 setResponse( s );
371 // invoking directly, not indirectly,
372 // so can't throw ReflectionException
373 }
374 catch ( Exception e ) {
375 throw new MBeanException( e,
376 "exception setting "+ATTR_RESPONSE_NAME+" attribute" );
377 }
378 }
379 // attributes known by parent class
380 else {
381 super.setAttribute( attribute );
382 }
383 }
384
385
386 /**
387 * invoke an action
388 * @param actionName name of action to invoke
389 * @param params action parameters
390 * @param signature action signature
391 * @return object result
392 * @exception MBeanException wrap action exception
393 * @exception ReflectionException wrap action invocation exception
394 */
395 public Object invoke(String actionName, Object params[], String signature[])
396 throws MBeanException, ReflectionException {
397 Object o = null;
398 if ( OPER_PING_NAME.equals( actionName ) &&
399 MBeanUtil.signaturesEqual( OPER_PING_SIGNATURE, signature ) ) {
400 try {
401 o = ping( (String)params[0] );
402 }
403 catch ( Exception e ) {
404 throw new MBeanException( e,
405 "exception invoking "+OPER_PING_NAME+" operation");
406 }
407 }
408 else {
409 super.invoke( actionName, params, signature );
410 }
411 return o;
412 }
413
414 /** name of ping schema */
415 public static final String PING = "ping";
416
417 /** the ping ontology */
418 private static class PingOntology extends Ontology {
419 /** constant identifying the ontology name **/
420 public static final String NAME = "ping";
421 /** the singleton instance */
422 private static Ontology theInstance = new PingOntology();
423 /** return singleton instance */
424 public static Ontology getInstance() {
425 return theInstance;
426 }
427 /** make a ping ontology */
428 private PingOntology() {
429 super( NAME, BasicOntology.getInstance());
430 try {
431 ConceptSchema pingSchema = new ConceptSchema( PING );
432 add( pingSchema, Ping.class );
433 }
434 catch ( OntologyException oe ) {
435 oe.printStackTrace();
436 throw new RuntimeException( "Unrecoverable OntologyException",
437 oe );
438 }
439 }
440 }
441
442
443 /**
444 * response to ping.
445 * meant as wrapper to be handled with <code>Object.wait()</code> and
446 * <code>Object.notify()</code>.
447 */
448 private class PingResponse {
449 /** hold the response */
450 private String response = null;
451 /** hold any Exception generated trying to get response */
452 private Exception exception = null;
453 /**
454 * get the ping response
455 * @return Returns the response.
456 */
457 public String getResponse() {
458 return response;
459 }
460 /**
461 * set the ping response
462 * @param response The response to set.
463 */
464 public void setResponse(String response) {
465 this.response = response;
466 }
467 /**
468 * get any exception set
469 * @return Returns the exception
470 */
471 public Exception getException() {
472 return exception;
473 }
474 /**
475 * set a exception received trying to get response
476 * @param exception The exception to set.
477 */
478 public void setException(Exception exception) {
479 this.exception = exception;
480 }
481 }
482
483
484 /**
485 * behaviour to send a ping and get response
486 */
487 private class PingInitiator extends SimpleAchieveREInitiator {
488
489 /** ping response object */
490 private PingResponse pingResponse;
491
492 /**
493 * @param a my agent
494 * @param msg message to send
495 * @param pingResponse PingResponse object to set and notify()
496 */
497 public PingInitiator(
498 Agent a,
499 ACLMessage msg,
500 PingResponse pingResponse ) {
501 super( a, msg );
502 this.pingResponse = pingResponse;
503 }
504
505 /* (non-Javadoc)
506 * @see jade.proto.SimpleAchieveREInitiator#handleInform(
507 * jade.lang.acl.ACLMessage)
508 */
509 protected void handleInform( ACLMessage msg ) {
510 logger.log( Logger.FINE, "ping response:"+msg);
511 String response = null;
512 synchronized( pingResponse ) {
513 // extract content
514 Result result = null;
515 try {
516 result = (Result)contentManager.extractContent( msg );
517 response = (String)result.getValue();
518 // set the response in the response object
519 pingResponse.setResponse( response );
520 }
521 catch ( Exception e ) {
522 // let ping() know that encountered a problem
523 pingResponse.setException( e );
524 }
525 catch ( Throwable t ) {
526 // this way the MBeanServer won't wait unnecessarily
527 pingResponse.setException( new RuntimeException(
528 "throwable encountered performing ping", t ) );
529 }
530 // notify waiting thread that response has been received
531 pingResponse.notify();
532 }
533 }
534 }
535
536
537 /**
538 * behaviour to respond to a ping
539 */
540 private class PingResponder extends SimpleAchieveREResponder {
541
542 /** my content manager */
543 ContentManager contentManager;
544
545 /**
546 * @param a my agent
547 * @param mt message template to use
548 */
549 public PingResponder( JademxPingAgent a, MessageTemplate mt ) {
550 super(a, mt);
551 pingAgent = a;
552 contentManager = a.getContentManager();
553 }
554 /** my typed agent */
555 private JademxPingAgent pingAgent;
556
557 /* (non-Javadoc)
558 * @see jade.proto.SimpleAchieveREResponder#prepareResponse(
559 * jade.lang.acl.ACLMessage)
560 */
561 protected ACLMessage prepareResponse( ACLMessage request )
562 throws NotUnderstoodException, RefuseException {
563
564 logger.log( Logger.FINE, "ping request: "+request);
565
566 // extract content
567 Action action;
568 try {
569 action = (Action)contentManager.extractContent( request );
570
571 }
572 catch ( Exception e ) {
573 e.printStackTrace();
574 throw new NotUnderstoodException(
575 "didn't understand "+request+":"+ e );
576 }
577
578 // create reply
579 ACLMessage reply = request.createReply();
580 reply.setPerformative( ACLMessage.INFORM );
581 Result result = new Result( action, pingAgent.getResponse() );
582 try {
583 contentManager.fillContent( reply, result );
584 }
585 catch ( Exception e ) {
586 throw new RefuseException(
587 "can't create reply to "+request+":"+ e );
588 }
589
590 // notify any JMX listeners that we were pinged
591 String pinger = request.getSender().getName();
592 pingAgent.notifyListeners(
593 NOTIF_PINGED_NAME,
594 "this agent was pinged by "+pinger,
595 pinger );
596
597 // send back reply
598 logger.log( Logger.FINE, "reply: "+reply);
599 return reply;
600 }
601 }
602
603 }