1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19 package org.apache.tomcat.util.modeler;
20
21
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.util.Iterator;
25
26 import javax.management.Attribute;
27 import javax.management.AttributeChangeNotification;
28 import javax.management.AttributeList;
29 import javax.management.AttributeNotFoundException;
30 import javax.management.DynamicMBean;
31 import javax.management.InstanceNotFoundException;
32 import javax.management.InvalidAttributeValueException;
33 import javax.management.ListenerNotFoundException;
34 import javax.management.MBeanException;
35 import javax.management.MBeanInfo;
36 import javax.management.MBeanNotificationInfo;
37 import javax.management.MBeanRegistration;
38 import javax.management.MBeanServer;
39 import javax.management.Notification;
40 import javax.management.NotificationFilter;
41 import javax.management.NotificationListener;
42 import javax.management.ObjectName;
43 import javax.management.ReflectionException;
44 import javax.management.RuntimeErrorException;
45 import javax.management.RuntimeOperationsException;
46 import javax.management.modelmbean.InvalidTargetObjectTypeException;
47 import javax.management.modelmbean.ModelMBeanNotificationBroadcaster;
48
49 import org.apache.juli.logging.Log;
50 import org.apache.juli.logging.LogFactory;
51
52 /*
53 * Changes from commons.modeler:
54 *
55 * - use DynamicMBean
56 * - remove methods not used in tomcat and redundant/not very generic
57 * - must be created from the ManagedBean - I don't think there were any direct
58 * uses, but now it is required.
59 * - some of the gratuituous flexibility removed - instead this is more predictive and
60 * strict with the use cases.
61 * - all Method and metadata is stored in ManagedBean. BaseModelBMean and ManagedBean act
62 * like Object and Class.
63 * - setModelMBean is no longer called on resources ( not used in tomcat )
64 * - no caching of Methods for now - operations and setters are not called repeatedly in most
65 * management use cases. Getters should't be called very frequently either - and even if they
66 * are, the overhead of getting the method should be small compared with other JMX costs ( RMI, etc ).
67 * We can add getter cache if needed.
68 * - removed unused constructor, fields
69 *
70 * TODO:
71 * - clean up catalina.mbeans, stop using weird inheritance
72 */
73
74 /**
75 * <p>Basic implementation of the <code>DynamicMBean</code> interface, which
76 * supports the minimal requirements of the interface contract.</p>
77 *
78 * <p>This can be used directly to wrap an existing java bean, or inside
79 * an mlet or anywhere an MBean would be used.
80 *
81 * Limitations:
82 * <ul>
83 * <li>Only managed resources of type <code>objectReference</code> are
84 * supportd.</li>
85 * <li>Caching of attribute values and operation results is not supported.
86 * All calls to <code>invoke()</code> are immediately executed.</li>
87 * <li>Persistence of MBean attributes and operations is not supported.</li>
88 * <li>All classes referenced as attribute types, operation parameters, or
89 * operation return values must be one of the following:
90 * <ul>
91 * <li>One of the Java primitive types (boolean, byte, char, double,
92 * float, integer, long, short). Corresponding value will be wrapped
93 * in the appropriate wrapper class automatically.</li>
94 * <li>Operations that return no value should declare a return type of
95 * <code>void</code>.</li>
96 * </ul>
97 * <li>Attribute caching is not supported</li>
98 * </ul>
99 *
100 * @author Craig R. McClanahan
101 * @author Costin Manolache
102 */
103 public class BaseModelMBean implements DynamicMBean, MBeanRegistration, ModelMBeanNotificationBroadcaster {
104 private static Log log = LogFactory.getLog(BaseModelMBean.class);
105
106 // ----------------------------------------------------------- Constructors
107
108 /**
109 * Construct a <code>ModelMBean</code> with default
110 * <code>ModelMBeanInfo</code> information.
111 *
112 * @exception MBeanException if the initializer of an object
113 * throws an exception
114 * @exception RuntimeOperationsException if an IllegalArgumentException
115 * occurs
116 */
117 protected BaseModelMBean() throws MBeanException, RuntimeOperationsException {
118 super();
119 }
120
121 // ----------------------------------------------------- Instance Variables
122
123 protected ObjectName oname=null;
124
125 /**
126 * Notification broadcaster for attribute changes.
127 */
128 protected BaseNotificationBroadcaster attributeBroadcaster = null;
129
130 /**
131 * Notification broadcaster for general notifications.
132 */
133 protected BaseNotificationBroadcaster generalBroadcaster = null;
134
135 /** Metadata for the mbean instance.
136 */
137 protected ManagedBean managedBean = null;
138
139 /**
140 * The managed resource this MBean is associated with (if any).
141 */
142 protected Object resource = null;
143
144 // --------------------------------------------------- DynamicMBean Methods
145 // TODO: move to ManagedBean
146 static final Object[] NO_ARGS_PARAM=new Object[0];
147 static final Class[] NO_ARGS_PARAM_SIG=new Class[0];
148
149 protected String resourceType = null;
150
151 // key: operation val: invoke method
152 //private Hashtable invokeAttMap=new Hashtable();
153
154 /**
155 * Obtain and return the value of a specific attribute of this MBean.
156 *
157 * @param name Name of the requested attribute
158 *
159 * @exception AttributeNotFoundException if this attribute is not
160 * supported by this MBean
161 * @exception MBeanException if the initializer of an object
162 * throws an exception
163 * @exception ReflectionException if a Java reflection exception
164 * occurs when invoking the getter
165 */
166 public Object getAttribute(String name)
167 throws AttributeNotFoundException, MBeanException,
168 ReflectionException {
169 // Validate the input parameters
170 if (name == null)
171 throw new RuntimeOperationsException
172 (new IllegalArgumentException("Attribute name is null"),
173 "Attribute name is null");
174
175 if( (resource instanceof DynamicMBean) &&
176 ! ( resource instanceof BaseModelMBean )) {
177 return ((DynamicMBean)resource).getAttribute(name);
178 }
179
180 Method m=managedBean.getGetter(name, this, resource);
181 Object result = null;
182 try {
183 Class declaring=m.getDeclaringClass();
184 // workaround for catalina weird mbeans - the declaring class is BaseModelMBean.
185 // but this is the catalina class.
186 if( declaring.isAssignableFrom(this.getClass()) ) {
187 result = m.invoke(this, NO_ARGS_PARAM );
188 } else {
189 result = m.invoke(resource, NO_ARGS_PARAM );
190 }
191 } catch (InvocationTargetException e) {
192 Throwable t = e.getTargetException();
193 if (t == null)
194 t = e;
195 if (t instanceof RuntimeException)
196 throw new RuntimeOperationsException
197 ((RuntimeException) t, "Exception invoking method " + name);
198 else if (t instanceof Error)
199 throw new RuntimeErrorException
200 ((Error) t, "Error invoking method " + name);
201 else
202 throw new MBeanException
203 (e, "Exception invoking method " + name);
204 } catch (Exception e) {
205 throw new MBeanException
206 (e, "Exception invoking method " + name);
207 }
208
209 // Return the results of this method invocation
210 // FIXME - should we validate the return type?
211 return (result);
212 }
213
214
215 /**
216 * Obtain and return the values of several attributes of this MBean.
217 *
218 * @param names Names of the requested attributes
219 */
220 public AttributeList getAttributes(String names[]) {
221
222 // Validate the input parameters
223 if (names == null)
224 throw new RuntimeOperationsException
225 (new IllegalArgumentException("Attribute names list is null"),
226 "Attribute names list is null");
227
228 // Prepare our response, eating all exceptions
229 AttributeList response = new AttributeList();
230 for (int i = 0; i < names.length; i++) {
231 try {
232 response.add(new Attribute(names[i],getAttribute(names[i])));
233 } catch (Exception e) {
234 ; // Not having a particular attribute in the response
235 ; // is the indication of a getter problem
236 }
237 }
238 return (response);
239
240 }
241
242 public void setManagedBean(ManagedBean managedBean) {
243 this.managedBean = managedBean;
244 }
245
246 /**
247 * Return the <code>MBeanInfo</code> object for this MBean.
248 */
249 public MBeanInfo getMBeanInfo() {
250 return managedBean.getMBeanInfo();
251 }
252
253
254 /**
255 * Invoke a particular method on this MBean, and return any returned
256 * value.
257 *
258 * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation will
259 * attempt to invoke this method on the MBean itself, or (if not
260 * available) on the managed resource object associated with this
261 * MBean.</p>
262 *
263 * @param name Name of the operation to be invoked
264 * @param params Array containing the method parameters of this operation
265 * @param signature Array containing the class names representing
266 * the signature of this operation
267 *
268 * @exception MBeanException if the initializer of an object
269 * throws an exception
270 * @exception ReflectioNException if a Java reflection exception
271 * occurs when invoking a method
272 */
273 public Object invoke(String name, Object params[], String signature[])
274 throws MBeanException, ReflectionException
275 {
276 if( (resource instanceof DynamicMBean) &&
277 ! ( resource instanceof BaseModelMBean )) {
278 return ((DynamicMBean)resource).invoke(name, params, signature);
279 }
280
281 // Validate the input parameters
282 if (name == null)
283 throw new RuntimeOperationsException
284 (new IllegalArgumentException("Method name is null"),
285 "Method name is null");
286
287 if( log.isDebugEnabled()) log.debug("Invoke " + name);
288 MethodKey mkey = new MethodKey(name, signature);
289 Method method= managedBean.getInvoke(name, params, signature, this, resource);
290
291 // Invoke the selected method on the appropriate object
292 Object result = null;
293 try {
294 if( method.getDeclaringClass().isAssignableFrom( this.getClass()) ) {
295 result = method.invoke(this, params );
296 } else {
297 result = method.invoke(resource, params);
298 }
299 } catch (InvocationTargetException e) {
300 Throwable t = e.getTargetException();
301 log.error("Exception invoking method " + name , t );
302 if (t == null)
303 t = e;
304 if (t instanceof RuntimeException)
305 throw new RuntimeOperationsException
306 ((RuntimeException) t, "Exception invoking method " + name);
307 else if (t instanceof Error)
308 throw new RuntimeErrorException
309 ((Error) t, "Error invoking method " + name);
310 else
311 throw new MBeanException
312 ((Exception)t, "Exception invoking method " + name);
313 } catch (Exception e) {
314 log.error("Exception invoking method " + name , e );
315 throw new MBeanException
316 (e, "Exception invoking method " + name);
317 }
318
319 // Return the results of this method invocation
320 // FIXME - should we validate the return type?
321 return (result);
322
323 }
324
325 static Class getAttributeClass(String signature)
326 throws ReflectionException
327 {
328 if (signature.equals(Boolean.TYPE.getName()))
329 return Boolean.TYPE;
330 else if (signature.equals(Byte.TYPE.getName()))
331 return Byte.TYPE;
332 else if (signature.equals(Character.TYPE.getName()))
333 return Character.TYPE;
334 else if (signature.equals(Double.TYPE.getName()))
335 return Double.TYPE;
336 else if (signature.equals(Float.TYPE.getName()))
337 return Float.TYPE;
338 else if (signature.equals(Integer.TYPE.getName()))
339 return Integer.TYPE;
340 else if (signature.equals(Long.TYPE.getName()))
341 return Long.TYPE;
342 else if (signature.equals(Short.TYPE.getName()))
343 return Short.TYPE;
344 else {
345 try {
346 ClassLoader cl=Thread.currentThread().getContextClassLoader();
347 if( cl!=null )
348 return cl.loadClass(signature);
349 } catch( ClassNotFoundException e ) {
350 }
351 try {
352 return Class.forName(signature);
353 } catch (ClassNotFoundException e) {
354 throw new ReflectionException
355 (e, "Cannot find Class for " + signature);
356 }
357 }
358 }
359
360 /**
361 * Set the value of a specific attribute of this MBean.
362 *
363 * @param attribute The identification of the attribute to be set
364 * and the new value
365 *
366 * @exception AttributeNotFoundException if this attribute is not
367 * supported by this MBean
368 * @exception MBeanException if the initializer of an object
369 * throws an exception
370 * @exception ReflectionException if a Java reflection exception
371 * occurs when invoking the getter
372 */
373 public void setAttribute(Attribute attribute)
374 throws AttributeNotFoundException, MBeanException,
375 ReflectionException
376 {
377 if( log.isDebugEnabled() )
378 log.debug("Setting attribute " + this + " " + attribute );
379
380 if( (resource instanceof DynamicMBean) &&
381 ! ( resource instanceof BaseModelMBean )) {
382 try {
383 ((DynamicMBean)resource).setAttribute(attribute);
384 } catch (InvalidAttributeValueException e) {
385 throw new MBeanException(e);
386 }
387 return;
388 }
389
390 // Validate the input parameters
391 if (attribute == null)
392 throw new RuntimeOperationsException
393 (new IllegalArgumentException("Attribute is null"),
394 "Attribute is null");
395
396 String name = attribute.getName();
397 Object value = attribute.getValue();
398
399 if (name == null)
400 throw new RuntimeOperationsException
401 (new IllegalArgumentException("Attribute name is null"),
402 "Attribute name is null");
403
404 Object oldValue=null;
405 //if( getAttMap.get(name) != null )
406 // oldValue=getAttribute( name );
407
408 Method m=managedBean.getSetter(name,this,resource);
409
410 try {
411 if( m.getDeclaringClass().isAssignableFrom( this.getClass()) ) {
412 m.invoke(this, new Object[] { value });
413 } else {
414 m.invoke(resource, new Object[] { value });
415 }
416 } catch (InvocationTargetException e) {
417 Throwable t = e.getTargetException();
418 if (t == null)
419 t = e;
420 if (t instanceof RuntimeException)
421 throw new RuntimeOperationsException
422 ((RuntimeException) t, "Exception invoking method " + name);
423 else if (t instanceof Error)
424 throw new RuntimeErrorException
425 ((Error) t, "Error invoking method " + name);
426 else
427 throw new MBeanException
428 (e, "Exception invoking method " + name);
429 } catch (Exception e) {
430 log.error("Exception invoking method " + name , e );
431 throw new MBeanException
432 (e, "Exception invoking method " + name);
433 }
434 try {
435 sendAttributeChangeNotification(new Attribute( name, oldValue),
436 attribute);
437 } catch(Exception ex) {
438 log.error("Error sending notification " + name, ex);
439 }
440 //attributes.put( name, value );
441 // if( source != null ) {
442 // // this mbean is asscoiated with a source - maybe we want to persist
443 // source.updateField(oname, name, value);
444 // }
445 }
446
447 public String toString() {
448 if( resource==null )
449 return "BaseModelMbean[" + resourceType + "]";
450 return resource.toString();
451 }
452
453 /**
454 * Set the values of several attributes of this MBean.
455 *
456 * @param attributes THe names and values to be set
457 *
458 * @return The list of attributes that were set and their new values
459 */
460 public AttributeList setAttributes(AttributeList attributes) {
461 AttributeList response = new AttributeList();
462
463 // Validate the input parameters
464 if (attributes == null)
465 return response;
466
467 // Prepare and return our response, eating all exceptions
468 String names[] = new String[attributes.size()];
469 int n = 0;
470 Iterator items = attributes.iterator();
471 while (items.hasNext()) {
472 Attribute item = (Attribute) items.next();
473 names[n++] = item.getName();
474 try {
475 setAttribute(item);
476 } catch (Exception e) {
477 ; // Ignore all exceptions
478 }
479 }
480
481 return (getAttributes(names));
482
483 }
484
485
486 // ----------------------------------------------------- ModelMBean Methods
487
488
489 /**
490 * Get the instance handle of the object against which we execute
491 * all methods in this ModelMBean management interface.
492 *
493 * @exception InstanceNotFoundException if the managed resource object
494 * cannot be found
495 * @exception MBeanException if the initializer of the object throws
496 * an exception
497 * @exception RuntimeOperationsException if the managed resource or the
498 * resource type is <code>null</code> or invalid
499 */
500 public Object getManagedResource()
501 throws InstanceNotFoundException, InvalidTargetObjectTypeException,
502 MBeanException, RuntimeOperationsException {
503
504 if (resource == null)
505 throw new RuntimeOperationsException
506 (new IllegalArgumentException("Managed resource is null"),
507 "Managed resource is null");
508
509 return resource;
510
511 }
512
513
514 /**
515 * Set the instance handle of the object against which we will execute
516 * all methods in this ModelMBean management interface.
517 *
518 * <strike>This method will detect and call "setModelMbean" method. A resource
519 * can implement this method to get a reference to the model mbean.
520 * The reference can be used to send notification and access the
521 * registry.
522 * </strike> The caller can provide the mbean instance or the object name to
523 * the resource, if needed.
524 *
525 * @param resource The resource object to be managed
526 * @param type The type of reference for the managed resource
527 * ("ObjectReference", "Handle", "IOR", "EJBHandle", or
528 * "RMIReference")
529 *
530 * @exception InstanceNotFoundException if the managed resource object
531 * cannot be found
532 * @exception InvalidTargetObjectTypeException if this ModelMBean is
533 * asked to handle a reference type it cannot deal with
534 * @exception MBeanException if the initializer of the object throws
535 * an exception
536 * @exception RuntimeOperationsException if the managed resource or the
537 * resource type is <code>null</code> or invalid
538 */
539 public void setManagedResource(Object resource, String type)
540 throws InstanceNotFoundException,
541 MBeanException, RuntimeOperationsException
542 {
543 if (resource == null)
544 throw new RuntimeOperationsException
545 (new IllegalArgumentException("Managed resource is null"),
546 "Managed resource is null");
547
548 // if (!"objectreference".equalsIgnoreCase(type))
549 // throw new InvalidTargetObjectTypeException(type);
550
551 this.resource = resource;
552 this.resourceType = resource.getClass().getName();
553
554 // // Make the resource aware of the model mbean.
555 // try {
556 // Method m=resource.getClass().getMethod("setModelMBean",
557 // new Class[] {ModelMBean.class});
558 // if( m!= null ) {
559 // m.invoke(resource, new Object[] {this});
560 // }
561 // } catch( NoSuchMethodException t ) {
562 // // ignore
563 // } catch( Throwable t ) {
564 // log.error( "Can't set model mbean ", t );
565 // }
566 }
567
568
569 // ------------------------------ ModelMBeanNotificationBroadcaster Methods
570
571
572 /**
573 * Add an attribute change notification event listener to this MBean.
574 *
575 * @param listener Listener that will receive event notifications
576 * @param name Name of the attribute of interest, or <code>null</code>
577 * to indicate interest in all attributes
578 * @param handback Handback object to be sent along with event
579 * notifications
580 *
581 * @exception IllegalArgumentException if the listener parameter is null
582 */
583 public void addAttributeChangeNotificationListener
584 (NotificationListener listener, String name, Object handback)
585 throws IllegalArgumentException {
586
587 if (listener == null)
588 throw new IllegalArgumentException("Listener is null");
589 if (attributeBroadcaster == null)
590 attributeBroadcaster = new BaseNotificationBroadcaster();
591
592 if( log.isDebugEnabled() )
593 log.debug("addAttributeNotificationListener " + listener);
594
595 BaseAttributeFilter filter = new BaseAttributeFilter(name);
596 attributeBroadcaster.addNotificationListener
597 (listener, filter, handback);
598
599 }
600
601
602 /**
603 * Remove an attribute change notification event listener from
604 * this MBean.
605 *
606 * @param listener The listener to be removed
607 * @param name The attribute name for which no more events are required
608 *
609 *
610 * @exception ListenerNotFoundException if this listener is not
611 * registered in the MBean
612 */
613 public void removeAttributeChangeNotificationListener
614 (NotificationListener listener, String name)
615 throws ListenerNotFoundException {
616
617 if (listener == null)
618 throw new IllegalArgumentException("Listener is null");
619 if (attributeBroadcaster == null)
620 attributeBroadcaster = new BaseNotificationBroadcaster();
621
622 // FIXME - currently this removes *all* notifications for this listener
623 attributeBroadcaster.removeNotificationListener(listener);
624
625 }
626
627
628 /**
629 * Remove an attribute change notification event listener from
630 * this MBean.
631 *
632 * @param listener The listener to be removed
633 * @param attributeName The attribute name for which no more events are required
634 * @param handback Handback object to be sent along with event
635 * notifications
636 *
637 *
638 * @exception ListenerNotFoundException if this listener is not
639 * registered in the MBean
640 */
641 public void removeAttributeChangeNotificationListener
642 (NotificationListener listener, String attributeName, Object handback)
643 throws ListenerNotFoundException {
644
645 removeAttributeChangeNotificationListener(listener, attributeName);
646
647 }
648
649
650 /**
651 * Send an <code>AttributeChangeNotification</code> to all registered
652 * listeners.
653 *
654 * @param notification The <code>AttributeChangeNotification</code>
655 * that will be passed
656 *
657 * @exception MBeanException if an object initializer throws an
658 * exception
659 * @exception RuntimeOperationsException wraps IllegalArgumentException
660 * when the specified notification is <code>null</code> or invalid
661 */
662 public void sendAttributeChangeNotification
663 (AttributeChangeNotification notification)
664 throws MBeanException, RuntimeOperationsException {
665
666 if (notification == null)
667 throw new RuntimeOperationsException
668 (new IllegalArgumentException("Notification is null"),
669 "Notification is null");
670 if (attributeBroadcaster == null)
671 return; // This means there are no registered listeners
672 if( log.isDebugEnabled() )
673 log.debug( "AttributeChangeNotification " + notification );
674 attributeBroadcaster.sendNotification(notification);
675
676 }
677
678
679 /**
680 * Send an <code>AttributeChangeNotification</code> to all registered
681 * listeners.
682 *
683 * @param oldValue The original value of the <code>Attribute</code>
684 * @param newValue The new value of the <code>Attribute</code>
685 *
686 * @exception MBeanException if an object initializer throws an
687 * exception
688 * @exception RuntimeOperationsException wraps IllegalArgumentException
689 * when the specified notification is <code>null</code> or invalid
690 */
691 public void sendAttributeChangeNotification
692 (Attribute oldValue, Attribute newValue)
693 throws MBeanException, RuntimeOperationsException {
694
695 // Calculate the class name for the change notification
696 String type = null;
697 if (newValue.getValue() != null)
698 type = newValue.getValue().getClass().getName();
699 else if (oldValue.getValue() != null)
700 type = oldValue.getValue().getClass().getName();
701 else
702 return; // Old and new are both null == no change
703
704 AttributeChangeNotification notification =
705 new AttributeChangeNotification
706 (this, 1, System.currentTimeMillis(),
707 "Attribute value has changed",
708 oldValue.getName(), type,
709 oldValue.getValue(), newValue.getValue());
710 sendAttributeChangeNotification(notification);
711
712 }
713
714
715
716
717 /**
718 * Send a <code>Notification</code> to all registered listeners as a
719 * <code>jmx.modelmbean.general</code> notification.
720 *
721 * @param notification The <code>Notification</code> that will be passed
722 *
723 * @exception MBeanException if an object initializer throws an
724 * exception
725 * @exception RuntimeOperationsException wraps IllegalArgumentException
726 * when the specified notification is <code>null</code> or invalid
727 */
728 public void sendNotification(Notification notification)
729 throws MBeanException, RuntimeOperationsException {
730
731 if (notification == null)
732 throw new RuntimeOperationsException
733 (new IllegalArgumentException("Notification is null"),
734 "Notification is null");
735 if (generalBroadcaster == null)
736 return; // This means there are no registered listeners
737 generalBroadcaster.sendNotification(notification);
738
739 }
740
741
742 /**
743 * Send a <code>Notification</code> which contains the specified string
744 * as a <code>jmx.modelmbean.generic</code> notification.
745 *
746 * @param message The message string to be passed
747 *
748 * @exception MBeanException if an object initializer throws an
749 * exception
750 * @exception RuntimeOperationsException wraps IllegalArgumentException
751 * when the specified notification is <code>null</code> or invalid
752 */
753 public void sendNotification(String message)
754 throws MBeanException, RuntimeOperationsException {
755
756 if (message == null)
757 throw new RuntimeOperationsException
758 (new IllegalArgumentException("Message is null"),
759 "Message is null");
760 Notification notification = new Notification
761 ("jmx.modelmbean.generic", this, 1, message);
762 sendNotification(notification);
763
764 }
765
766
767
768
769 // ---------------------------------------- NotificationBroadcaster Methods
770
771
772 /**
773 * Add a notification event listener to this MBean.
774 *
775 * @param listener Listener that will receive event notifications
776 * @param filter Filter object used to filter event notifications
777 * actually delivered, or <code>null</code> for no filtering
778 * @param handback Handback object to be sent along with event
779 * notifications
780 *
781 * @exception IllegalArgumentException if the listener parameter is null
782 */
783 public void addNotificationListener(NotificationListener listener,
784 NotificationFilter filter,
785 Object handback)
786 throws IllegalArgumentException {
787
788 if (listener == null)
789 throw new IllegalArgumentException("Listener is null");
790
791 if( log.isDebugEnabled() ) log.debug("addNotificationListener " + listener);
792
793 if (generalBroadcaster == null)
794 generalBroadcaster = new BaseNotificationBroadcaster();
795 generalBroadcaster.addNotificationListener
796 (listener, filter, handback);
797
798 // We'll send the attribute change notifications to all listeners ( who care )
799 // The normal filtering can be used.
800 // The problem is that there is no other way to add attribute change listeners
801 // to a model mbean ( AFAIK ). I suppose the spec should be fixed.
802 if (attributeBroadcaster == null)
803 attributeBroadcaster = new BaseNotificationBroadcaster();
804
805 if( log.isDebugEnabled() )
806 log.debug("addAttributeNotificationListener " + listener);
807
808 attributeBroadcaster.addNotificationListener
809 (listener, filter, handback);
810 }
811
812
813 /**
814 * Return an <code>MBeanNotificationInfo</code> object describing the
815 * notifications sent by this MBean.
816 */
817 public MBeanNotificationInfo[] getNotificationInfo() {
818
819 // Acquire the set of application notifications
820 MBeanNotificationInfo current[] = getMBeanInfo().getNotifications();
821 if (current == null)
822 current = new MBeanNotificationInfo[0];
823 MBeanNotificationInfo response[] =
824 new MBeanNotificationInfo[current.length + 2];
825 // Descriptor descriptor = null;
826
827 // Fill in entry for general notifications
828 // descriptor = new DescriptorSupport
829 // (new String[] { "name=GENERIC",
830 // "descriptorType=notification",
831 // "log=T",
832 // "severity=5",
833 // "displayName=jmx.modelmbean.generic" });
834 response[0] = new MBeanNotificationInfo
835 (new String[] { "jmx.modelmbean.generic" },
836 "GENERIC",
837 "Text message notification from the managed resource");
838 //descriptor);
839
840 // Fill in entry for attribute change notifications
841 // descriptor = new DescriptorSupport
842 // (new String[] { "name=ATTRIBUTE_CHANGE",
843 // "descriptorType=notification",
844 // "log=T",
845 // "severity=5",
846 // "displayName=jmx.attribute.change" });
847 response[1] = new MBeanNotificationInfo
848 (new String[] { "jmx.attribute.change" },
849 "ATTRIBUTE_CHANGE",
850 "Observed MBean attribute value has changed");
851 //descriptor);
852
853 // Copy remaining notifications as reported by the application
854 System.arraycopy(current, 0, response, 2, current.length);
855 return (response);
856
857 }
858
859
860 /**
861 * Remove a notification event listener from this MBean.
862 *
863 * @param listener The listener to be removed (any and all registrations
864 * for this listener will be eliminated)
865 *
866 * @exception ListenerNotFoundException if this listener is not
867 * registered in the MBean
868 */
869 public void removeNotificationListener(NotificationListener listener)
870 throws ListenerNotFoundException {
871
872 if (listener == null)
873 throw new IllegalArgumentException("Listener is null");
874 if (generalBroadcaster == null)
875 generalBroadcaster = new BaseNotificationBroadcaster();
876 generalBroadcaster.removeNotificationListener(listener);
877
878
879 }
880
881
882 /**
883 * Remove a notification event listener from this MBean.
884 *
885 * @param listener The listener to be removed (any and all registrations
886 * for this listener will be eliminated)
887 * @param handback Handback object to be sent along with event
888 * notifications
889 *
890 * @exception ListenerNotFoundException if this listener is not
891 * registered in the MBean
892 */
893 public void removeNotificationListener(NotificationListener listener,
894 Object handback)
895 throws ListenerNotFoundException {
896
897 removeNotificationListener(listener);
898
899 }
900
901
902 /**
903 * Remove a notification event listener from this MBean.
904 *
905 * @param listener The listener to be removed (any and all registrations
906 * for this listener will be eliminated)
907 * @param filter Filter object used to filter event notifications
908 * actually delivered, or <code>null</code> for no filtering
909 * @param handback Handback object to be sent along with event
910 * notifications
911 *
912 * @exception ListenerNotFoundException if this listener is not
913 * registered in the MBean
914 */
915 public void removeNotificationListener(NotificationListener listener,
916 NotificationFilter filter,
917 Object handback)
918 throws ListenerNotFoundException {
919
920 removeNotificationListener(listener);
921
922 }
923
924
925 // ------------------------------------------------ PersistentMBean Methods
926
927
928 /**
929 * Instantiates this MBean instance from data found in the persistent
930 * store. The data loaded could include attribute and operation values.
931 * This method should be called during construction or initialization
932 * of the instance, and before the MBean is registered with the
933 * <code>MBeanServer</code>.
934 *
935 * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation does
936 * not support persistence.</p>
937 *
938 * @exception InstanceNotFoundException if the managed resource object
939 * cannot be found
940 * @exception MBeanException if the initializer of the object throws
941 * an exception
942 * @exception RuntimeOperationsException if an exception is reported
943 * by the persistence mechanism
944 */
945 // public void load() throws InstanceNotFoundException,
946 // MBeanException, RuntimeOperationsException {
947 // // XXX If a context was set, use it to load the data
948 // throw new MBeanException
949 // (new IllegalStateException("Persistence is not supported"),
950 // "Persistence is not supported");
951 //
952 // }
953
954
955 /**
956 * Capture the current state of this MBean instance and write it out
957 * to the persistent store. The state stored could include attribute
958 * and operation values. If one of these methods of persistence is not
959 * supported, a "service not found" exception will be thrown.
960 *
961 * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation does
962 * not support persistence.</p>
963 *
964 * @exception InstanceNotFoundException if the managed resource object
965 * cannot be found
966 * @exception MBeanException if the initializer of the object throws
967 * an exception, or persistence is not supported
968 * @exception RuntimeOperationsException if an exception is reported
969 * by the persistence mechanism
970 */
971 // public void store() throws InstanceNotFoundException,
972 // MBeanException, RuntimeOperationsException {
973 //
974 // // XXX if a context was set, use it to store the data
975 // throw new MBeanException
976 // (new IllegalStateException("Persistence is not supported"),
977 // "Persistence is not supported");
978 //
979 // }
980
981 // -------------------- BaseModelMBean methods --------------------
982
983 /** Set the type of the mbean. This is used as a key to locate
984 * the description in the Registry.
985 *
986 * @param type the type of classname of the modeled object
987 */
988 // void setModeledType( String type ) {
989 // initModelInfo(type);
990 // createResource();
991 // }
992 /** Set the type of the mbean. This is used as a key to locate
993 * the description in the Registry.
994 *
995 * @param type the type of classname of the modeled object
996 */
997 // void initModelInfo( String type ) {
998 // try {
999 // if( log.isDebugEnabled())
1000 // log.debug("setModeledType " + type);
1001 //
1002 // log.debug( "Set model Info " + type);
1003 // if(type==null) {
1004 // return;
1005 // }
1006 // resourceType=type;
1007 // //Thread.currentThread().setContextClassLoader(BaseModelMBean.class.getClassLoader());
1008 // Class c=null;
1009 // try {
1010 // c=Class.forName( type);
1011 // } catch( Throwable t ) {
1012 // log.debug( "Error creating class " + t);
1013 // }
1014 //
1015 // // The class c doesn't need to exist
1016 // ManagedBean descriptor=getRegistry().findManagedBean(c, type);
1017 // if( descriptor==null )
1018 // return;
1019 // this.setModelMBeanInfo(descriptor.createMBeanInfo());
1020 // } catch( Throwable ex) {
1021 // log.error( "TCL: " + Thread.currentThread().getContextClassLoader(),
1022 // ex);
1023 // }
1024 // }
1025
1026 /** Set the type of the mbean. This is used as a key to locate
1027 * the description in the Registry.
1028 */
1029 // protected void createResource() {
1030 // try {
1031 // //Thread.currentThread().setContextClassLoader(BaseModelMBean.class.getClassLoader());
1032 // Class c=null;
1033 // try {
1034 // c=Class.forName( resourceType );
1035 // resource = c.newInstance();
1036 // } catch( Throwable t ) {
1037 // log.error( "Error creating class " + t);
1038 // }
1039 // } catch( Throwable ex) {
1040 // log.error( "TCL: " + Thread.currentThread().getContextClassLoader(),
1041 // ex);
1042 // }
1043 // }
1044
1045
1046 public String getModelerType() {
1047 return resourceType;
1048 }
1049
1050 public String getClassName() {
1051 return getModelerType();
1052 }
1053
1054 public ObjectName getJmxName() {
1055 return oname;
1056 }
1057
1058 public String getObjectName() {
1059 if (oname != null) {
1060 return oname.toString();
1061 } else {
1062 return null;
1063 }
1064 }
1065
1066 // public void setRegistry(Registry registry) {
1067 // this.registry = registry;
1068 // }
1069 //
1070 // public Registry getRegistry() {
1071 // // XXX Need a better solution - to avoid the static
1072 // if( registry == null )
1073 // registry=Registry.getRegistry();
1074 //
1075 // return registry;
1076 // }
1077
1078 // ------------------------------------------------------ Protected Methods
1079
1080
1081 /**
1082 * Create and return a default <code>ModelMBeanInfo</code> object.
1083 */
1084 // protected ModelMBeanInfo createDefaultModelMBeanInfo() {
1085 //
1086 // return (new ModelMBeanInfoSupport(this.getClass().getName(),
1087 // "Default ModelMBean",
1088 // null, null, null, null));
1089 //
1090 // }
1091
1092 /**
1093 * Is the specified <code>ModelMBeanInfo</code> instance valid?
1094 *
1095 * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation
1096 * does not check anything, but this method can be overridden
1097 * as required.</p>
1098 *
1099 * @param info The <code>ModelMBeanInfo object to check
1100 */
1101 // protected boolean isModelMBeanInfoValid(ModelMBeanInfo info) {
1102 // return (true);
1103 // }
1104
1105 // -------------------- Registration --------------------
1106 // XXX We can add some method patterns here- like setName() and
1107 // setDomain() for code that doesn't implement the Registration
1108
1109 public ObjectName preRegister(MBeanServer server,
1110 ObjectName name)
1111 throws Exception
1112 {
1113 if( log.isDebugEnabled())
1114 log.debug("preRegister " + resource + " " + name );
1115 oname=name;
1116 if( resource instanceof MBeanRegistration ) {
1117 oname = ((MBeanRegistration)resource).preRegister(server, name );
1118 }
1119 return oname;
1120 }
1121
1122 public void postRegister(Boolean registrationDone) {
1123 if( resource instanceof MBeanRegistration ) {
1124 ((MBeanRegistration)resource).postRegister(registrationDone);
1125 }
1126 }
1127
1128 public void preDeregister() throws Exception {
1129 if( resource instanceof MBeanRegistration ) {
1130 ((MBeanRegistration)resource).preDeregister();
1131 }
1132 }
1133
1134 public void postDeregister() {
1135 if( resource instanceof MBeanRegistration ) {
1136 ((MBeanRegistration)resource).postDeregister();
1137 }
1138 }
1139
1140 static class MethodKey {
1141 private String name;
1142 private String[] signature;
1143
1144 MethodKey(String name, String[] signature) {
1145 this.name = name;
1146 if(signature == null) {
1147 signature = new String[0];
1148 }
1149 this.signature = signature;
1150 }
1151
1152 public boolean equals(Object other) {
1153 if(!(other instanceof MethodKey)) {
1154 return false;
1155 }
1156 MethodKey omk = (MethodKey)other;
1157 if(!name.equals(omk.name)) {
1158 return false;
1159 }
1160 if(signature.length != omk.signature.length) {
1161 return false;
1162 }
1163 for(int i=0; i < signature.length; i++) {
1164 if(!signature[i].equals(omk.signature[i])) {
1165 return false;
1166 }
1167 }
1168 return true;
1169 }
1170
1171 public int hashCode() {
1172 return name.hashCode();
1173 }
1174 }
1175 }