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 | } |