View Javadoc

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