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 }