View Javadoc

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