| 1 | // jademx - JADE management using JMX |
| 2 | // Copyright 2005 Caboodle Networks, Inc. |
| 3 | // |
| 4 | // This library is free software; you can redistribute it and/or |
| 5 | // modify it under the terms of the GNU Lesser General Public |
| 6 | // License as published by the Free Software Foundation; either |
| 7 | // version 2.1 of the License, or (at your option) any later version. |
| 8 | // |
| 9 | // This library is distributed in the hope that it will be useful, |
| 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | // Lesser General Public License for more details. |
| 13 | // |
| 14 | // You should have received a copy of the GNU Lesser General Public |
| 15 | // License along with this library; if not, write to the Free Software |
| 16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
| 17 | |
| 18 | package jade.jademx.mbean; |
| 19 | |
| 20 | import jade.jademx.agent.JademxAgent; |
| 21 | import jade.jademx.util.MBeanUtil; |
| 22 | import jade.util.Logger; |
| 23 | import jade.wrapper.AgentController; |
| 24 | |
| 25 | import javax.management.Attribute; |
| 26 | import javax.management.AttributeList; |
| 27 | import javax.management.AttributeNotFoundException; |
| 28 | import javax.management.InvalidAttributeValueException; |
| 29 | import javax.management.MBeanAttributeInfo; |
| 30 | import javax.management.MBeanConstructorInfo; |
| 31 | import javax.management.MBeanException; |
| 32 | import javax.management.MBeanInfo; |
| 33 | import javax.management.MBeanNotificationInfo; |
| 34 | import javax.management.MBeanOperationInfo; |
| 35 | import javax.management.MBeanParameterInfo; |
| 36 | import javax.management.ReflectionException; |
| 37 | |
| 38 | /** |
| 39 | * Implementation class for JadeAgentMBeanDef. |
| 40 | * <p>The integration between jademx and jade.core.Agent is not as clean |
| 41 | * as it could be. The problem is that jademx is a management subsystem |
| 42 | * that really should have some access to JADE internals, but it's been |
| 43 | * developed as a piece of application code so that it can use the standard |
| 44 | * distribution. A better solution would be for part of this to be taken |
| 45 | * as a core part of the distribution or for a tiny bit more of the |
| 46 | * management interface to be exposed.</p> |
| 47 | * <p>The specific problem is a combination of information hiding and |
| 48 | * the sequence of actions and design constraints. Specifically:</p> |
| 49 | * <ul> |
| 50 | * <li>JadeAgent and JademxAgent need to have references to each other |
| 51 | * so that the full set of JMX attributes, operations, and notifications can |
| 52 | * be implemented.</li> |
| 53 | * <li>For certain actions, synchronous operation is desired, so behaviour |
| 54 | * scheduling is inappropriate.</li> |
| 55 | * <li>It is a design goal that jademx should be able to run <em>non</em>-jademx |
| 56 | * JADE agents.</li> |
| 57 | * <li>It is a design goal that a JademxAgent should be able to run when |
| 58 | * <em>not</em> under the control of jademx.</li> |
| 59 | * <li>It is a design goal to be able to have an MBean for an agent even |
| 60 | * if that agent was created within JADE instead of via jademx.</li> |
| 61 | * <li>The relevant part of agent initialization occurs in the following |
| 62 | * order: |
| 63 | * <ul> |
| 64 | * <li>The JADE agent constructor is invoked, but: |
| 65 | * <ul> |
| 66 | * <li>A JADE agent constructor does not have access to its own agent name, |
| 67 | * so it can't tell jademx that an agent with that name should exchange |
| 68 | * references.</li> |
| 69 | * <li>A JADE agent may be created by another agent instead of via jademx, |
| 70 | * and jademx can't find out the java class of such agents, so jademx may not |
| 71 | * be able to tell that such an agent should exchange references</li> |
| 72 | * </ul> |
| 73 | * </li> |
| 74 | * <li>After the agent object has been instantiated, its thread is started, |
| 75 | * for example by calling <code>AgentController.start()</code>. During that |
| 76 | * process, <code>Agent.setup()</code> is called. At that point, the agent |
| 77 | * has access to its name but no clean way to get a reference to jademx so |
| 78 | * that references can be exchanged.</li> |
| 79 | * </ul> |
| 80 | * </li> |
| 81 | * </ul> |
| 82 | * <p>The solution chosen is to expose: |
| 83 | * <ul> |
| 84 | * <li>the name of a class that can be instantiated to get the MBeanServer, |
| 85 | * and</li> |
| 86 | * <li>the name of the root of the jademx MBean hierarchy</li> |
| 87 | * </ul> |
| 88 | * via java system properties. In this way, the agent can obtain a reference |
| 89 | * to jademx and register itself with jademx</p> |
| 90 | * <p>Because of the conditions described, it's not feasible to use JADE's |
| 91 | * O2A mechanism to pass object references.</p> |
| 92 | * <p>Somewhat cleaner ways of doing this could be implemented if: |
| 93 | * <ul> |
| 94 | * <li>the holder of a platform controller object could register some sort |
| 95 | * of environment object that were accessible to the JADE agent, or</li> |
| 96 | * <li>the holder of a platform controller object could gain access to the |
| 97 | * JADE agent object, or</li> |
| 98 | * <li>the holder of a platform controller object could find out what class |
| 99 | * an agent is, or</li> |
| 100 | * <li>there were a tighter integration between jademx and JADE itself.</li> |
| 101 | * </ul> |
| 102 | * The first two options, in particular, may violate the principle of |
| 103 | * information hiding too much for some people's tastes. |
| 104 | * </p> |
| 105 | * <p> |
| 106 | * Another area where a JADE change could make for a cleaner implementation |
| 107 | * would be to open up the NotificationService so that could can see every |
| 108 | * message sent rather than having to get sniffed messages from AMS. |
| 109 | * </p> |
| 110 | * |
| 111 | * |
| 112 | * |
| 113 | * |
| 114 | * |
| 115 | * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
| 116 | * TODO: add aclmsgcmp variables as Properties or one one property at at time |
| 117 | * setProperty |
| 118 | * getProperty |
| 119 | * setProperties |
| 120 | * getProperties |
| 121 | * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
| 122 | * |
| 123 | * |
| 124 | * |
| 125 | * |
| 126 | * |
| 127 | * |
| 128 | * |
| 129 | * |
| 130 | * |
| 131 | * |
| 132 | * @author David Bernstein, <a href="http://www.caboodlenetworks.com" |
| 133 | * >Caboodle Networks, Inc.</a> |
| 134 | */ |
| 135 | public class JadeAgent extends JadeBase implements JadeAgentMBeanDef { |
| 136 | |
| 137 | /** JADE MBean type */ |
| 138 | public final static String TYPE = "Agent"; |
| 139 | |
| 140 | // |
| 141 | // ATTRIBUTES |
| 142 | // |
| 143 | |
| 144 | // basic attributes |
| 145 | |
| 146 | /** arguments attribute name */ |
| 147 | public static final String ATTR_ARGUMENTS = "Arguments"; |
| 148 | /** class name attribute name */ |
| 149 | public static final String ATTR_CLASS_NAME = "ClassName"; |
| 150 | /** local name attribute name */ |
| 151 | public static final String ATTR_LOCAL_NAME = "LocalName"; |
| 152 | /** platform object name string attribute name */ |
| 153 | public static final String ATTR_PLATFORM_OBJECT_NAME_STRING = |
| 154 | "PlatformObjectNameString"; |
| 155 | /** full name attribute name */ |
| 156 | public static final String ATTR_FULL_NAME = "FullName"; |
| 157 | /** state code attribute name */ |
| 158 | public static final String ATTR_STATE_CODE = "StateCode"; |
| 159 | /** state name attribute name */ |
| 160 | public static final String ATTR_STATE_NAME = "StateName"; |
| 161 | |
| 162 | |
| 163 | |
| 164 | // |
| 165 | // OPERATIONS |
| 166 | // |
| 167 | |
| 168 | // basic operations |
| 169 | |
| 170 | // /** activation operation name */ |
| 171 | // public static final String OPER_ACTIVATE = "activate"; |
| 172 | /** shutdown operation name */ |
| 173 | public static final String OPER_KILL_NAME = "kill"; |
| 174 | /** unitTestInject operation description */ |
| 175 | private final static String OPER_KILL_DESC = |
| 176 | "kill this agent"; |
| 177 | /** signature for a kill operation */ |
| 178 | public final static String OPER_KILL_SIGNATURE[] = { |
| 179 | // intentionally empty |
| 180 | }; |
| 181 | /** return type for unitTestInject operation */ |
| 182 | public final static String OPER_KILL_TYPE = |
| 183 | void.class.getName(); |
| 184 | // /** startup operation name */ |
| 185 | // public static final String OPER_START = "start"; |
| 186 | // /** suspension operation name */ |
| 187 | // public static final String OPER_SUSPEND = "suspend"; |
| 188 | |
| 189 | |
| 190 | // |
| 191 | // NOTIFICATIONS |
| 192 | // |
| 193 | |
| 194 | // base notifications |
| 195 | |
| 196 | /* none at this level */ |
| 197 | |
| 198 | |
| 199 | ///** this agent's platform */ |
| 200 | //private JadePlatform jadePlatform; |
| 201 | /** agent controller this MBean represents */ |
| 202 | private AgentController agentController; |
| 203 | /** ObjectName string for this agent's platform */ |
| 204 | private String platformObjectNameString; |
| 205 | /** local (nick) name for agent */ |
| 206 | private String localName; |
| 207 | /** class name for agent */ |
| 208 | private String className; |
| 209 | /** creation arguments */ |
| 210 | private Object[] arguments; |
| 211 | |
| 212 | |
| 213 | /** my logger */ |
| 214 | private final Logger logger = |
| 215 | Logger.getMyLogger(JadeAgent.class.getName()); |
| 216 | |
| 217 | // LOCK MONITORING |
| 218 | ///** the threadmxbean for lock monitoring */ |
| 219 | //java.lang.management.ThreadMXBean tmxb = |
| 220 | // java.lang.management.ManagementFactory.getThreadMXBean(); |
| 221 | |
| 222 | |
| 223 | |
| 224 | |
| 225 | |
| 226 | /** |
| 227 | * Construct a JADE agent MBean implementation. |
| 228 | * @param jadePlatform superior mbean that proxies platform |
| 229 | * @param agentController agent controller to set for this MBean |
| 230 | */ |
| 231 | public JadeAgent( |
| 232 | JadePlatform jadePlatform, |
| 233 | AgentController agentController, |
| 234 | String platformObjectNameString, |
| 235 | String localName, |
| 236 | String className, |
| 237 | Object[] arguments |
| 238 | ) { |
| 239 | super( jadePlatform.getJadeFactory(), TYPE, localName ); |
| 240 | //this.jadePlatform = jadePlatform; |
| 241 | setAgentController( agentController ); |
| 242 | this.platformObjectNameString = platformObjectNameString; |
| 243 | this.localName = localName; |
| 244 | this.className = className; |
| 245 | this.arguments = arguments; |
| 246 | //JadeRuntime jadeRuntime = jadePlatform.getJadeRuntime(); |
| 247 | // don't register here: done by JadePlatform.bornAgent() |
| 248 | |
| 249 | |
| 250 | // LOCK MONITORING |
| 251 | //tmxb.setThreadContentionMonitoringEnabled( true ); |
| 252 | |
| 253 | } |
| 254 | |
| 255 | /** |
| 256 | * set agent controller for this MBean. |
| 257 | * must be set via this method or constructor before can do anything. |
| 258 | * @param agentController agent controller to set for this MBean |
| 259 | */ |
| 260 | void setAgentController( AgentController agentController ) { |
| 261 | this.agentController = agentController; |
| 262 | } |
| 263 | |
| 264 | ///** |
| 265 | // * only for use by this package: get this agent's controller. |
| 266 | // * @return agent's controller |
| 267 | // */ |
| 268 | //AgentController getAgentController() { |
| 269 | // return agentController; |
| 270 | //} |
| 271 | |
| 272 | /* (non-Javadoc) |
| 273 | * @see jade.management.mbean.JadeAgentMBean#getArguments() |
| 274 | */ |
| 275 | public Object[] getArguments() { |
| 276 | return arguments; |
| 277 | } |
| 278 | /* (non-Javadoc) |
| 279 | * @see jade.management.mbean.JadeAgentMBean#getClassName() |
| 280 | */ |
| 281 | public String getClassName() { |
| 282 | return className; |
| 283 | } |
| 284 | /* (non-Javadoc) |
| 285 | * @see jade.management.mbean.JadeAgentMBean#getLocalName() |
| 286 | */ |
| 287 | public String getLocalName() { |
| 288 | return localName; |
| 289 | } |
| 290 | /* (non-Javadoc) |
| 291 | * @see jade.management.mbean.JadeAgentMBean#getPlatformObjectNameString() |
| 292 | */ |
| 293 | public String getPlatformObjectNameString() { |
| 294 | return platformObjectNameString; |
| 295 | } |
| 296 | |
| 297 | // BEGIN AgentController MEDIATION |
| 298 | |
| 299 | // attributes |
| 300 | |
| 301 | /* (non-Javadoc) |
| 302 | * @see jade.management.mbean.JadeAgentMBean#getFullName() |
| 303 | */ |
| 304 | public String getFullName() throws JademxException { |
| 305 | String fullName; |
| 306 | try { |
| 307 | fullName = agentController.getName(); |
| 308 | } |
| 309 | catch ( Exception e ) { |
| 310 | throw new JademxException( |
| 311 | "exception getting full JADE name for agent \""+ |
| 312 | localName+"\" in platform \""+platformObjectNameString+"\"", |
| 313 | e ); |
| 314 | } |
| 315 | return fullName; |
| 316 | } |
| 317 | /* (non-Javadoc) |
| 318 | * @see jade.management.mbean.JadeAgentMBean#getStateCode() |
| 319 | */ |
| 320 | public int getStateCode() throws JademxException { |
| 321 | int stateCode; |
| 322 | try { |
| 323 | stateCode = agentController.getState().getCode(); |
| 324 | } |
| 325 | catch ( Exception e ) { |
| 326 | throw new JademxException( |
| 327 | "exception getting state code for agent \""+ |
| 328 | localName+"\" in platform \""+platformObjectNameString+"\"", |
| 329 | e ); |
| 330 | } |
| 331 | return stateCode; |
| 332 | } |
| 333 | /* (non-Javadoc) |
| 334 | * @see jade.management.mbean.JadeAgentMBean#getStateName() |
| 335 | */ |
| 336 | public String getStateName() throws JademxException { |
| 337 | String stateName; |
| 338 | try { |
| 339 | stateName = agentController.getState().getName(); |
| 340 | } |
| 341 | catch ( Exception e ) { |
| 342 | throw new JademxException( |
| 343 | "exception getting state name for agent \""+ |
| 344 | localName+"\" in platform \""+platformObjectNameString+"\"", |
| 345 | e ); |
| 346 | } |
| 347 | return stateName; |
| 348 | } |
| 349 | |
| 350 | // operations |
| 351 | |
| 352 | // /* (non-Javadoc) |
| 353 | // * @see jade.management.mbean.JadeAgentMBean#activate() |
| 354 | // */ |
| 355 | // public void activate() throws StaleProxyException { |
| 356 | // agentController.activate(); |
| 357 | // } |
| 358 | /* (non-Javadoc) |
| 359 | * @see jade.management.mbean.JadeAgentMBean#kill() |
| 360 | */ |
| 361 | public void kill() throws JademxException { |
| 362 | try { |
| 363 | agentController.kill(); |
| 364 | } |
| 365 | catch ( Exception e ) { |
| 366 | throw new JademxException( |
| 367 | "exception killing agent \""+ |
| 368 | localName+"\" in platform \""+platformObjectNameString+"\"", |
| 369 | e ); |
| 370 | } |
| 371 | } |
| 372 | ///* (non-Javadoc) |
| 373 | // * @see jade.management.mbean.JadeAgentMBean#start() |
| 374 | // */ |
| 375 | //public void start() throws StaleProxyException { |
| 376 | // logger.log( Logger.FINE, "entering JadeAgent.start()..." ); |
| 377 | // agentController.start(); |
| 378 | //} |
| 379 | // /* (non-Javadoc) |
| 380 | // * @see jade.management.mbean.JadeAgentMBean#suspend() |
| 381 | // */ |
| 382 | // public void suspend() throws StaleProxyException { |
| 383 | // agentController.suspend(); |
| 384 | // } |
| 385 | |
| 386 | // END AgentController MEDIATION |
| 387 | |
| 388 | |
| 389 | // DYNAMIC MBEAN SUPPORT |
| 390 | |
| 391 | /** MBean information for superclass(es) class hierarchy */ |
| 392 | private MBeanInfo mBeanInfoSuper = null; |
| 393 | /** MBean information for JadeAgent level in class hierarchy */ |
| 394 | private MBeanInfo mBeanInfoThisLevel = null; |
| 395 | /** merged MBean information for JadeAgent and its superclass(es) */ |
| 396 | private MBeanInfo mBeanInfoJademx = null; |
| 397 | /** MBean information from JMXAgent */ |
| 398 | private MBeanInfo mBeanInfoAgent = null; |
| 399 | /** merged MBean information from JadeAgent and JMXAgent */ |
| 400 | private MBeanInfo mBeanInfo = null; |
| 401 | /** agent's DynamicMBean */ |
| 402 | private JademxAgent jademxAgent = null; |
| 403 | /** has mBeanInfo changed since last looked */ |
| 404 | private Boolean mBeanInfoChanged = Boolean.FALSE; |
| 405 | /** class name for MBeanInfo */ |
| 406 | private final static String MBI_CLASS_NAME = JadeAgent.class.getName(); |
| 407 | /** description for MBeanInfo */ |
| 408 | private final static String MBI_DESCRIPTION = |
| 409 | "MBeanInfo for JadeAgent in class hierarchy"; |
| 410 | |
| 411 | |
| 412 | // /** |
| 413 | // * non-nanosecond version of waitForJademxAgentBinding() |
| 414 | // * @param timeout milliseconds to wait |
| 415 | // * @return whether binding occurred before timeout |
| 416 | // */ |
| 417 | // public boolean waitForJademxAgentBinding( String timeoutStr ) { |
| 418 | // Duration timeout = new Duration( timeoutStr ); |
| 419 | // long timeoutMS = timeout.toMilliseconds(); |
| 420 | // return waitForJademxAgentBinding( timeoutMS, 0 ); |
| 421 | // } |
| 422 | |
| 423 | /** |
| 424 | * wait given amount of time or until interrupted or jademxAgent bound. |
| 425 | * if nanosecond argument zero or running on pre-5.0 Java, then the timeout |
| 426 | * is calculated in milliseconds (rounded up if nanoseconds specified). |
| 427 | * <p><b>IMPLEMENTATION NOTE:</b>Using the Windows version of J2SE 5.0 |
| 428 | * there is some problem with <code>wait()</code>/<code>notify()</code>, |
| 429 | * such that even though the thread calling <code>setJademxAgent()</code> |
| 430 | * has the mBeanInfoChgd lock by virtue of a synchronized block |
| 431 | * (verified by checking the lock owner using the ThreadMXBean), the |
| 432 | * JVM throws: |
| 433 | * <code>java.lang.IllegalMonitorStateException: |
| 434 | * current thread not owner</code>. |
| 435 | * when the code tries to use <code>notify()</code>.</p> |
| 436 | * <p>As a workaround, the code now repeatedly sleeps rather than depending |
| 437 | * on being notified when the desired condition has changed. Once the |
| 438 | * wait/notify problem is fixed, the approach should be reverted (and |
| 439 | * rip out all sleep code).</p> |
| 440 | * @param timeout milliseconds to wait |
| 441 | * @param nanos nanoseconds to wait on top of milliseconds |
| 442 | * @return whether MBean bound to instance of JademxAgent |
| 443 | */ |
| 444 | public boolean waitForJademxAgentBinding( long timeout, int nanos ) { |
| 445 | final int THOUSAND = 1000; |
| 446 | final int MILLION = THOUSAND * THOUSAND; |
| 447 | final int BILLION = THOUSAND * MILLION; |
| 448 | //final long MILLISECOND_MS = 1; |
| 449 | //final long CENTISECOND_MS = 10; |
| 450 | //final long DECISECOND_MS = 100; |
| 451 | final long ONE_SECOND_MS = THOUSAND; |
| 452 | final long ONE_SECOND_NS = ONE_SECOND_MS * MILLION; |
| 453 | final long ONE_MINUTE_MS = 60 * ONE_SECOND_MS; |
| 454 | final long TEN_SEC_MS = 10 * ONE_SECOND_MS; |
| 455 | final long ONE_MINUTE_NS = 60 * ONE_SECOND_NS; |
| 456 | final int MICROSECOND_NS = THOUSAND; |
| 457 | long sleepMs = 0; |
| 458 | int sleepNs = 0; |
| 459 | if ( ( nanos < 0 ) || ( nanos >= MILLION ) ) { |
| 460 | throw new IllegalArgumentException("nanosecond argument "+nanos+ |
| 461 | " must be >=0 and <"+MILLION); |
| 462 | } |
| 463 | boolean isBound; |
| 464 | boolean msResolution = false; |
| 465 | long startTime = 0; |
| 466 | long stopTime; |
| 467 | long timeoutMS = timeout; |
| 468 | //int timeoutNS = nanos; |
| 469 | if ( 0 == nanos ) { |
| 470 | msResolution = true; |
| 471 | } |
| 472 | else { |
| 473 | try { |
| 474 | startTime = System.nanoTime(); // JDK 1.5 |
| 475 | } |
| 476 | catch ( NoSuchMethodError nsme1 ) { |
| 477 | msResolution = true; |
| 478 | } |
| 479 | } |
| 480 | if ( msResolution ) { |
| 481 | startTime = System.currentTimeMillis(); |
| 482 | stopTime = startTime + timeoutMS; |
| 483 | if ( nanos > 0 ) { |
| 484 | // gracefully handle < JDK 1.5 |
| 485 | stopTime++; |
| 486 | timeoutMS++; |
| 487 | } |
| 488 | if ( timeoutMS >= ONE_MINUTE_MS ) { |
| 489 | sleepMs = TEN_SEC_MS; |
| 490 | } |
| 491 | else if ( timeoutMS >= ONE_SECOND_MS ) { |
| 492 | sleepMs = ONE_SECOND_MS; |
| 493 | } |
| 494 | else if ( 0 == timeoutMS ) { |
| 495 | sleepMs = ONE_SECOND_MS; |
| 496 | } |
| 497 | else { |
| 498 | sleepMs = timeoutMS; |
| 499 | } |
| 500 | } |
| 501 | else { |
| 502 | long waitNs = ((MILLION*timeoutMS)+nanos); |
| 503 | stopTime = startTime + waitNs; |
| 504 | if ( waitNs >= ONE_MINUTE_NS ) { |
| 505 | sleepMs = TEN_SEC_MS; |
| 506 | } |
| 507 | else if ( waitNs >= ONE_SECOND_NS ) { |
| 508 | sleepMs = ONE_SECOND_MS; |
| 509 | } |
| 510 | else if ( waitNs >= MICROSECOND_NS ) { |
| 511 | sleepMs = MICROSECOND_NS; |
| 512 | } |
| 513 | else { |
| 514 | sleepNs = nanos; |
| 515 | } |
| 516 | } |
| 517 | long crntTime = startTime; |
| 518 | Thread myThread = Thread.currentThread(); |
| 519 | synchronized ( mBeanInfoChanged ) { |
| 520 | logger.log( Logger.FINER, |
| 521 | "JadeAgent.waitForJademxAgentBinding(): this.jademxAgent="+ |
| 522 | this.jademxAgent); |
| 523 | isBound = ( null != jademxAgent ); |
| 524 | } |
| 525 | |
| 526 | logger.log(Logger.FINEST,"isBound="+isBound); |
| 527 | boolean interrupted = false; |
| 528 | while ( !( isBound || interrupted || |
| 529 | ( ( crntTime >= stopTime ) && |
| 530 | ( stopTime > startTime ) ) ) ) { |
| 531 | synchronized ( mBeanInfoChanged ) { |
| 532 | logger.log(Logger.FINEST,"isBound="+isBound+",waiting..."); |
| 533 | myThread.yield(); //............... |
| 534 | try { |
| 535 | // WAIT/NOTIFY |
| 536 | //waiting = true; |
| 537 | |
| 538 | // LOCK MONITORING |
| 539 | //Thread crntThread = Thread.currentThread(); |
| 540 | //long waitingThreadId = crntThread.getId(); |
| 541 | //String waitingThreadName = crntThread.getName(); |
| 542 | //logger.log(Logger.INFO,"waiting thread: id="+ |
| 543 | // waitingThreadId+",waitingThreadName="+waitingThreadName); |
| 544 | //waitingTI = tmxb.getThreadInfo( waitingThreadId); |
| 545 | |
| 546 | |
| 547 | logger.log(Logger.FINER,"sleeping "+ |
| 548 | sleepMs+"ms, "+sleepNs+"ns..."); |
| 549 | if ( msResolution ) { |
| 550 | myThread.sleep( sleepMs ); |
| 551 | // WAIT/NOTIFY |
| 552 | //mBeanInfoChanged.wait( timeoutMS ); |
| 553 | } |
| 554 | else { |
| 555 | myThread.sleep( sleepMs, sleepNs ); |
| 556 | // WAIT/NOTIFY |
| 557 | //mBeanInfoChanged.wait( timeoutMS, timeoutNS ); |
| 558 | } |
| 559 | } |
| 560 | catch ( InterruptedException ie ) { |
| 561 | interrupted = true; |
| 562 | } |
| 563 | isBound = ( null != jademxAgent ); |
| 564 | } |
| 565 | crntTime = ( msResolution |
| 566 | ? System.currentTimeMillis() |
| 567 | : System.nanoTime() ); |
| 568 | // WAIT/NOTIFY |
| 569 | //if ( !( isBound || interrupted || |
| 570 | // ( ( crntTime >= stopTime ) && |
| 571 | // ( stopTime > startTime ) ) ) ) { |
| 572 | // long timeLeft = stopTime - crntTime; |
| 573 | // if ( msResolution ) { |
| 574 | // timeoutMS = timeLeft; |
| 575 | // } |
| 576 | // else { |
| 577 | // timeoutMS = timeLeft / MILLION; |
| 578 | // timeoutNS = (int)(timeoutMS - timeLeft); |
| 579 | // } |
| 580 | //} |
| 581 | |
| 582 | } |
| 583 | double elapsedTime = crntTime - startTime; |
| 584 | logger.log( Logger.FINE, "isBound="+isBound+", elapsed wait time "+ |
| 585 | (elapsedTime/(msResolution?THOUSAND:BILLION)+ |
| 586 | " seconds")); |
| 587 | return isBound; |
| 588 | } |
| 589 | |
| 590 | // LOCK MONITORING |
| 591 | ///** ThreadInfo about the waiting thread */ |
| 592 | //java.lang.management.ThreadInfo waitingTI = null; |
| 593 | |
| 594 | // WAIT/NOTIFY |
| 595 | ///** is there a thread waiting for jademxAgent to be set */ |
| 596 | //private boolean waiting = false; |
| 597 | |
| 598 | |
| 599 | /** |
| 600 | * callback for agent to make its MBeanInfo available. |
| 601 | * can be called repeatedly without problem as way of indicating |
| 602 | * the MBeanInfo has changed since previous invocation or for first time. |
| 603 | * @param jademxAgent DynamicMBean for the agent |
| 604 | */ |
| 605 | public void setJademxAgent( JademxAgent jademxAgent ) { |
| 606 | synchronized ( mBeanInfoChanged ) { |
| 607 | |
| 608 | //LOCK MONITORING |
| 609 | //Thread crntThread = Thread.currentThread(); |
| 610 | //long crntThreadId = crntThread.getId(); |
| 611 | //String crntThreadName = crntThread.getName(); |
| 612 | //logger.log( Logger.INFO,"crntThreadId="+crntThreadId+", |
| 613 | // crntThreadName="+crntThreadName ); |
| 614 | //java.lang.management.ThreadInfo ti = |
| 615 | // tmxb.getThreadInfo( crntThread.getId()); |
| 616 | //long threadIds[] = tmxb.getAllThreadIds(); |
| 617 | //System.err.println("mBeanInfoChanged.hashCode="+ |
| 618 | // mBeanInfoChanged.hashCode()); |
| 619 | //for ( int i = 0; i < threadIds.length; i++ ) { |
| 620 | // long threadId = threadIds[i]; |
| 621 | // ti = tmxb.getThreadInfo( threadId ); |
| 622 | // String lockname = ti.getLockName(); |
| 623 | // if ( ( null != lockname ) && |
| 624 | // lockname.startsWith("java.lang.Boolean") ) { |
| 625 | // System.err.println(i+". "+threadId+": "+"lockname:"+ |
| 626 | // ti.getLockName()+",lockownerid:"+ti.getLockOwnerId()+ |
| 627 | // ",lockownername:"+ti.getLockOwnerName()); |
| 628 | // } |
| 629 | //} |
| 630 | |
| 631 | |
| 632 | logger.log( Logger.FINE, |
| 633 | "entering JadeAgent.setJademxAgent(),JadeAgent="+this+ |
| 634 | ",JadeAgent.hashCode()="+this.hashCode()+",jademxAgent="+ |
| 635 | jademxAgent+",jademxAgent.hashCode()="+ |
| 636 | jademxAgent.hashCode()); |
| 637 | this.jademxAgent = jademxAgent; |
| 638 | mBeanInfoChanged = Boolean.TRUE; |
| 639 | // WAIT/NOTIFY |
| 640 | //if ( waiting ) { |
| 641 | // waiting = false; |
| 642 | // mBeanInfoChanged.notifyAll(); |
| 643 | //} |
| 644 | logger.log( Logger.FINE, |
| 645 | "JadeAgent.setJademxAgent(): this.jademxAgent="+ |
| 646 | this.jademxAgent+",this.jademxAgent.hashCode()="+ |
| 647 | jademxAgent.hashCode()); |
| 648 | } |
| 649 | } |
| 650 | |
| 651 | /** |
| 652 | * return MBean information for base JADE MBean class. |
| 653 | * this is a merge of the agent, this class, and this class's superclass. |
| 654 | * @return MBean information for base JADE MBean class |
| 655 | */ |
| 656 | public MBeanInfo getMBeanInfo() { |
| 657 | // lazy evaluation |
| 658 | if ( null == mBeanInfoJademx ) { |
| 659 | |
| 660 | // attributes |
| 661 | MBeanAttributeInfo aI[] = new MBeanAttributeInfo[] { |
| 662 | new MBeanAttributeInfo( |
| 663 | ATTR_ARGUMENTS, |
| 664 | Object[].class.getName(), |
| 665 | "agent startup arguments", |
| 666 | true, false, false ), |
| 667 | new MBeanAttributeInfo( |
| 668 | ATTR_CLASS_NAME, |
| 669 | String.class.getName(), |
| 670 | "agent class name", |
| 671 | true, false, false ), |
| 672 | new MBeanAttributeInfo( |
| 673 | ATTR_LOCAL_NAME, |
| 674 | String.class.getName(), |
| 675 | "agent local name", |
| 676 | true, false, false ), |
| 677 | new MBeanAttributeInfo( |
| 678 | ATTR_PLATFORM_OBJECT_NAME_STRING, |
| 679 | String.class.getName(), |
| 680 | "agent's platform object name string", |
| 681 | true, false, false ), |
| 682 | new MBeanAttributeInfo( |
| 683 | ATTR_FULL_NAME, |
| 684 | String.class.getName(), |
| 685 | "agent full name", |
| 686 | true, false, false ), |
| 687 | new MBeanAttributeInfo( |
| 688 | ATTR_STATE_CODE, |
| 689 | int.class.getName(), |
| 690 | "agent state code", |
| 691 | true, false, false ), |
| 692 | new MBeanAttributeInfo( |
| 693 | ATTR_STATE_NAME, |
| 694 | String.class.getName(), |
| 695 | "agent state name", |
| 696 | true, false, false ) |
| 697 | }; |
| 698 | |
| 699 | // constructors |
| 700 | MBeanConstructorInfo cI[] = new MBeanConstructorInfo[0]; |
| 701 | |
| 702 | // operations |
| 703 | MBeanParameterInfo pIKill[] = new MBeanParameterInfo[0]; |
| 704 | |
| 705 | MBeanOperationInfo oI[] = new MBeanOperationInfo[] { |
| 706 | //new MBeanOperationInfo( OPER_ACTIVATE, |
| 707 | // "activate this agent", |
| 708 | // pI, |
| 709 | // void.class.getName(), |
| 710 | // MBeanOperationInfo.ACTION ), |
| 711 | new MBeanOperationInfo( OPER_KILL_NAME, |
| 712 | OPER_KILL_DESC, |
| 713 | pIKill, |
| 714 | OPER_KILL_TYPE, |
| 715 | MBeanOperationInfo.ACTION )//, |
| 716 | //new MBeanOperationInfo( OPER_START, |
| 717 | // "start this agent", |
| 718 | // pI, |
| 719 | // void.class.getName(), |
| 720 | // MBeanOperationInfo.ACTION ), |
| 721 | //new MBeanOperationInfo( OPER_SUSPEND, |
| 722 | // "suspend this agent", |
| 723 | // pI, |
| 724 | // void.class.getName(), |
| 725 | // MBeanOperationInfo.ACTION ) |
| 726 | }; |
| 727 | |
| 728 | // notifications |
| 729 | MBeanNotificationInfo nI[] = new MBeanNotificationInfo[0]; |
| 730 | |
| 731 | // now, MBeanInfo for this level of class hierarchy |
| 732 | mBeanInfoThisLevel = new MBeanInfo( MBI_CLASS_NAME, MBI_DESCRIPTION, |
| 733 | aI, cI, oI, nI ); |
| 734 | |
| 735 | // finally, MBeanInfo for jademx functionality |
| 736 | mBeanInfoSuper = super.getMBeanInfo(); |
| 737 | mBeanInfoJademx = MBeanUtil.mergeMBeanInfo( |
| 738 | MBI_CLASS_NAME, MBI_DESCRIPTION, |
| 739 | mBeanInfoThisLevel, mBeanInfoSuper ); |
| 740 | |
| 741 | synchronized ( mBeanInfoChanged ) { |
| 742 | mBeanInfoChanged = Boolean.TRUE; |
| 743 | } |
| 744 | |
| 745 | } |
| 746 | |
| 747 | // if there's been a change in constituent MBeanInfo, |
| 748 | // reconstruct merged version |
| 749 | synchronized ( mBeanInfoChanged ) { |
| 750 | if ( mBeanInfoChanged.booleanValue() ) { |
| 751 | if ( null == jademxAgent ) { |
| 752 | mBeanInfoAgent = null; |
| 753 | mBeanInfo = mBeanInfoJademx; |
| 754 | } |
| 755 | else { |
| 756 | mBeanInfoAgent = jademxAgent.getMBeanInfo(); |
| 757 | if ( mBeanInfoJademx == mBeanInfoAgent ) { |
| 758 | mBeanInfo = mBeanInfoJademx; |
| 759 | } |
| 760 | else { |
| 761 | mBeanInfo = MBeanUtil.mergeMBeanInfo( |
| 762 | MBI_CLASS_NAME, MBI_DESCRIPTION, |
| 763 | mBeanInfoJademx, mBeanInfoAgent ); |
| 764 | } |
| 765 | } |
| 766 | mBeanInfoChanged = Boolean.FALSE; |
| 767 | } |
| 768 | } |
| 769 | |
| 770 | // this is the merge between this class's info, parent class's info |
| 771 | // and any info set by the actual JADE agent |
| 772 | return mBeanInfo; |
| 773 | } |
| 774 | |
| 775 | |
| 776 | /** |
| 777 | * get an attribute value |
| 778 | * @param attribute name of attribute to get |
| 779 | * @return attribute value |
| 780 | * @throws AttributeNotFoundException no such attribute |
| 781 | * @throws MBeanException exception from getter |
| 782 | * @throws ReflectionException exception invoking getter |
| 783 | */ |
| 784 | public Object getAttribute( String attribute ) |
| 785 | throws AttributeNotFoundException, |
| 786 | MBeanException, |
| 787 | ReflectionException { |
| 788 | getMBeanInfo(); // make sure have MBeanInfo |
| 789 | Object o; |
| 790 | if ( MBeanUtil.mBeanHasAttr( mBeanInfoThisLevel, attribute ) ) { |
| 791 | try { |
| 792 | // base attributes |
| 793 | if ( ATTR_ARGUMENTS.equals( attribute ) ) { |
| 794 | o = getArguments(); |
| 795 | } |
| 796 | else if ( ATTR_CLASS_NAME.equals( attribute ) ) { |
| 797 | o = getClassName(); |
| 798 | } |
| 799 | else if ( ATTR_LOCAL_NAME.equals( attribute ) ) { |
| 800 | o = getLocalName(); |
| 801 | } |
| 802 | else if ( ATTR_PLATFORM_OBJECT_NAME_STRING.equals( attribute )){ |
| 803 | o = getPlatformObjectNameString(); |
| 804 | } |
| 805 | else if ( ATTR_FULL_NAME.equals( attribute ) ) { |
| 806 | o = getFullName(); |
| 807 | } |
| 808 | else if ( ATTR_STATE_CODE.equals( attribute ) ) { |
| 809 | o = new Integer( getStateCode() ); |
| 810 | } |
| 811 | else if ( ATTR_STATE_NAME.equals( attribute ) ) { |
| 812 | o = getStateName(); |
| 813 | } |
| 814 | // should never get here |
| 815 | else { |
| 816 | throw new RuntimeException("unreachable"); |
| 817 | } |
| 818 | } |
| 819 | catch ( JademxException je ) { |
| 820 | throw new MBeanException( je ); |
| 821 | } |
| 822 | } |
| 823 | else if ( MBeanUtil.mBeanHasAttr( mBeanInfoSuper, attribute ) ) { |
| 824 | o = super.getAttribute( attribute ); |
| 825 | } |
| 826 | else if ( ( null != mBeanInfoAgent ) && |
| 827 | MBeanUtil.mBeanHasAttr( mBeanInfoAgent, attribute ) ) { |
| 828 | synchronized ( mBeanInfoChanged ) { |
| 829 | o = jademxAgent.getAttribute( attribute ); |
| 830 | } |
| 831 | } |
| 832 | else { |
| 833 | throw new AttributeNotFoundException( |
| 834 | "no such readable attribute \""+attribute+"\""); |
| 835 | } |
| 836 | logger.log(Logger.FINE,"for attribute "+attribute+" returning "+o); |
| 837 | return o; |
| 838 | } |
| 839 | |
| 840 | /** |
| 841 | * get multiple attribute values |
| 842 | * @param attributes attributes to be retrieved |
| 843 | * @return retrieved attributes |
| 844 | */ |
| 845 | public AttributeList getAttributes(String[] attributes) { |
| 846 | AttributeList aL = null; |
| 847 | int attrCount = attributes.length; |
| 848 | if ( attrCount >= 0 ) { |
| 849 | aL = new AttributeList( attrCount ); |
| 850 | for ( int i = 0; i < attrCount; i++ ) { |
| 851 | try { |
| 852 | String attrName = attributes[i]; |
| 853 | aL.add( new Attribute( attrName, getAttribute( attrName ))); |
| 854 | } |
| 855 | catch ( Exception e ) { |
| 856 | throw new RuntimeException( e ); |
| 857 | } |
| 858 | } |
| 859 | } |
| 860 | return aL; |
| 861 | } |
| 862 | |
| 863 | /** |
| 864 | * @param attribute |
| 865 | * @throws AttributeNotFoundException |
| 866 | * @throws InvalidAttributeValueException |
| 867 | * @throws MBeanException |
| 868 | * @throws ReflectionException |
| 869 | */ |
| 870 | public void setAttribute( Attribute attribute ) |
| 871 | throws AttributeNotFoundException, |
| 872 | InvalidAttributeValueException, |
| 873 | MBeanException, |
| 874 | ReflectionException { |
| 875 | getMBeanInfo(); // make sure have MBeanInfo |
| 876 | String attributeName = attribute.getName(); |
| 877 | if ( MBeanUtil.mBeanHasAttr( mBeanInfoThisLevel, attributeName ) ) { |
| 878 | // i have no writable attributes |
| 879 | //throw new AttributeNotFoundException( |
| 880 | // "no such writable attribute"+attribute.getName() ); |
| 881 | |
| 882 | //Object v = attribute.getValue(); |
| 883 | |
| 884 | // should never get here |
| 885 | //else { |
| 886 | throw new AttributeNotFoundException( |
| 887 | "no such writable attribute"+attributeName ); |
| 888 | //} |
| 889 | |
| 890 | } |
| 891 | else if ( MBeanUtil.mBeanHasAttr( mBeanInfoSuper, attributeName ) ) { |
| 892 | super.setAttribute( attribute ); |
| 893 | } |
| 894 | else if ( ( null != mBeanInfoAgent ) && |
| 895 | MBeanUtil.mBeanHasAttr( mBeanInfoAgent, attributeName ) ) { |
| 896 | synchronized ( mBeanInfoChanged ) { |
| 897 | jademxAgent.setAttribute( attribute ); |
| 898 | } |
| 899 | } |
| 900 | else { |
| 901 | throw new AttributeNotFoundException( |
| 902 | "no such writable attribute \""+attributeName+"\""); |
| 903 | } |
| 904 | } |
| 905 | |
| 906 | /** |
| 907 | * set multiple attribute values |
| 908 | * @param attributes attribute values to set |
| 909 | * @return set attribute values |
| 910 | */ |
| 911 | public AttributeList setAttributes(AttributeList attributes) { |
| 912 | int attrCount = attributes.size(); |
| 913 | for ( int i = 0; i < attrCount; i++ ) { |
| 914 | try { |
| 915 | setAttribute( (Attribute)attributes.get(i) ); |
| 916 | } |
| 917 | catch ( RuntimeException re ) { |
| 918 | throw re; |
| 919 | } |
| 920 | catch ( Exception e ) { |
| 921 | throw new RuntimeException( e ); |
| 922 | } |
| 923 | } |
| 924 | return attributes; |
| 925 | } |
| 926 | |
| 927 | |
| 928 | |
| 929 | |
| 930 | /** |
| 931 | * invoke an action |
| 932 | * @param actionName name of action to invoke |
| 933 | * @param params action parameters |
| 934 | * @param signature action signature |
| 935 | * @return object result |
| 936 | * @exception MBeanException wrap action exception |
| 937 | * @exception ReflectionException wrap action invocation exception |
| 938 | */ |
| 939 | public Object invoke(String actionName, Object params[], String signature[]) |
| 940 | throws MBeanException, ReflectionException { |
| 941 | getMBeanInfo(); // make sure have MBeanInfo |
| 942 | Object o = null; |
| 943 | if ( MBeanUtil.mBeanHasOper( mBeanInfoThisLevel, actionName, signature ) ) { |
| 944 | try { |
| 945 | |
| 946 | // base operations |
| 947 | |
| 948 | //if ( OPER_ACTIVATE.equals( actionName ) ) { |
| 949 | // activate(); |
| 950 | //} |
| 951 | /*else*/ |
| 952 | if ( OPER_KILL_NAME.equals( actionName ) && |
| 953 | MBeanUtil.signaturesEqual( OPER_KILL_SIGNATURE, signature ) ) { |
| 954 | kill(); |
| 955 | } |
| 956 | //else if ( OPER_START.equals( actionName ) ) { |
| 957 | // start(); |
| 958 | //} |
| 959 | //else if ( OPER_SUSPEND.equals( actionName ) ) { |
| 960 | // suspend(); |
| 961 | //} |
| 962 | |
| 963 | // should never get here |
| 964 | |
| 965 | else { |
| 966 | throw new ReflectionException( new RuntimeException(), |
| 967 | "no action named \""+actionName+"\""); |
| 968 | } |
| 969 | } |
| 970 | catch ( Exception e ) { |
| 971 | throw new MBeanException( e ); |
| 972 | } |
| 973 | } |
| 974 | else if ( MBeanUtil.mBeanHasOper( mBeanInfoSuper, actionName, signature ) ) { |
| 975 | o = super.invoke( actionName, params, signature ); |
| 976 | } |
| 977 | else if ( ( null != mBeanInfoAgent ) && |
| 978 | MBeanUtil.mBeanHasOper( mBeanInfoAgent, actionName, signature ) ) { |
| 979 | synchronized ( mBeanInfoChanged ) { |
| 980 | o = jademxAgent.invoke( actionName, params, signature ); |
| 981 | } |
| 982 | } |
| 983 | else { |
| 984 | throw new ReflectionException( new RuntimeException(), |
| 985 | "no action named \""+actionName+"\" with given signature"); |
| 986 | } |
| 987 | return o; |
| 988 | } |
| 989 | |
| 990 | // // UNIT TESTING |
| 991 | // |
| 992 | // |
| 993 | // /** |
| 994 | // * assert that agent has bound to JMX |
| 995 | // */ |
| 996 | // public void assertIsJademxBound() { |
| 997 | // JademxAgent.assertIsJademxBound( getObjectName(), |
| 998 | // getJadeFactory().getJadeMXServer().getMBeanServer() ); |
| 999 | // } |
| 1000 | |
| 1001 | } |