1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18
19 package org.apache.tomcat.util.modeler;
20
21
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.util.Iterator;
25
26 import javax.management.Attribute;
27 import javax.management.AttributeChangeNotification;
28 import javax.management.AttributeList;
29 import javax.management.AttributeNotFoundException;
30 import javax.management.DynamicMBean;
31 import javax.management.InstanceNotFoundException;
32 import javax.management.InvalidAttributeValueException;
33 import javax.management.ListenerNotFoundException;
34 import javax.management.MBeanException;
35 import javax.management.MBeanInfo;
36 import javax.management.MBeanNotificationInfo;
37 import javax.management.MBeanRegistration;
38 import javax.management.MBeanServer;
39 import javax.management.Notification;
40 import javax.management.NotificationFilter;
41 import javax.management.NotificationListener;
42 import javax.management.ObjectName;
43 import javax.management.ReflectionException;
44 import javax.management.RuntimeErrorException;
45 import javax.management.RuntimeOperationsException;
46 import javax.management.modelmbean.InvalidTargetObjectTypeException;
47 import javax.management.modelmbean.ModelMBeanNotificationBroadcaster;
48
49 import org.apache.juli.logging.Log;
50 import org.apache.juli.logging.LogFactory;
51 import org.apache.tomcat.util.res.StringManager;
52
53 /*
54  * Changes from commons.modeler:
55  *
56  *  - use DynamicMBean
57  *  - remove methods not used in tomcat and redundant/not very generic
58  *  - must be created from the ManagedBean - I don't think there were any direct
59  *    uses, but now it is required.
60  *  - some of the gratuitous flexibility removed - instead this is more predictive and
61  *    strict with the use cases.
62  *  - all Method and metadata is stored in ManagedBean. BaseModelBMean and ManagedBean act
63  *    like Object and Class.
64  *  - setModelMBean is no longer called on resources ( not used in tomcat )
65  *  - no caching of Methods for now - operations and setters are not called repeatedly in most
66  *  management use cases. Getters shouldn't be called very frequently either - and even if they
67  *  are, the overhead of getting the method should be small compared with other JMX costs ( RMI, etc ).
68  *  We can add getter cache if needed.
69  *  - removed unused constructor, fields
70  *
71  *  TODO:
72  *   - clean up catalina.mbeans, stop using weird inheritance
73  */

74
75 /**
76  * <p>Basic implementation of the <code>DynamicMBean</code> interface, which
77  * supports the minimal requirements of the interface contract.</p>
78  *
79  * <p>This can be used directly to wrap an existing java bean, or inside
80  * an mlet or anywhere an MBean would be used.
81  *
82  * Limitations:
83  * <ul>
84  * <li>Only managed resources of type <code>objectReference</code> are
85  *     supported.</li>
86  * <li>Caching of attribute values and operation results is not supported.
87  *     All calls to <code>invoke()</code> are immediately executed.</li>
88  * <li>Persistence of MBean attributes and operations is not supported.</li>
89  * <li>All classes referenced as attribute types, operation parameters, or
90  *     operation return values must be one of the following:
91  *     <ul>
92  *     <li>One of the Java primitive types (booleanbytechardouble,
93  *         float, integer, longshort).  Corresponding value will be wrapped
94  *         in the appropriate wrapper class automatically.</li>
95  *     <li>Operations that return no value should declare a return type of
96  *         <code>void</code>.</li>
97  *     </ul>
98  * <li>Attribute caching is not supported</li>
99  * </ul>
100  *
101  * @author Craig R. McClanahan
102  * @author Costin Manolache
103  */

104 public class BaseModelMBean implements DynamicMBean, MBeanRegistration,
105         ModelMBeanNotificationBroadcaster {
106
107     private static final Log log = LogFactory.getLog(BaseModelMBean.class);
108     private static final StringManager sm = StringManager.getManager(BaseModelMBean.class);
109
110     // ----------------------------------------------------- Instance Variables
111
112     protected ObjectName oname=null;
113
114     /**
115      * Notification broadcaster for attribute changes.
116      */

117     protected BaseNotificationBroadcaster attributeBroadcaster = null;
118
119     /**
120      * Notification broadcaster for general notifications.
121      */

122     protected BaseNotificationBroadcaster generalBroadcaster = null;
123
124     /** Metadata for the mbean instance.
125      */

126     protected ManagedBean managedBean = null;
127
128     /**
129      * The managed resource this MBean is associated with (if any).
130      */

131     protected Object resource = null;
132
133     // --------------------------------------------------- DynamicMBean Methods
134     // TODO: move to ManagedBean
135     static final Object[] NO_ARGS_PARAM = new Object[0];
136
137     protected String resourceType = null;
138
139     // key: operation val: invoke method
140     //private Hashtable invokeAttMap=new Hashtable();
141
142     /**
143      * Obtain and return the value of a specific attribute of this MBean.
144      *
145      * @param name Name of the requested attribute
146      *
147      * @exception AttributeNotFoundException if this attribute is not
148      *  supported by this MBean
149      * @exception MBeanException if the initializer of an object
150      *  throws an exception
151      * @exception ReflectionException if a Java reflection exception
152      *  occurs when invoking the getter
153      */

154     @Override
155     public Object getAttribute(String name)
156         throws AttributeNotFoundException, MBeanException,
157             ReflectionException {
158         // Validate the input parameters
159         if (name == null)
160             throw new RuntimeOperationsException
161                 (new IllegalArgumentException(sm.getString("baseModelMBean.nullAttributeName")),
162                         sm.getString("baseModelMBean.nullAttributeName"));
163
164         if( (resource instanceof DynamicMBean) &&
165              ! ( resource instanceof BaseModelMBean )) {
166             return ((DynamicMBean)resource).getAttribute(name);
167         }
168
169         Method m=managedBean.getGetter(name, this, resource);
170         Object result = null;
171         try {
172             Class<?> declaring = m.getDeclaringClass();
173             // workaround for catalina weird mbeans - the declaring class is BaseModelMBean.
174             // but this is the catalina class.
175             if( declaring.isAssignableFrom(this.getClass()) ) {
176                 result = m.invoke(this, NO_ARGS_PARAM );
177             } else {
178                 result = m.invoke(resource, NO_ARGS_PARAM );
179             }
180         } catch (InvocationTargetException e) {
181             Throwable t = e.getTargetException();
182             if (t == null)
183                 t = e;
184             if (t instanceof RuntimeException)
185                 throw new RuntimeOperationsException
186                     ((RuntimeException) t, sm.getString("baseModelMBean.invokeError", name));
187             else if (t instanceof Error)
188                 throw new RuntimeErrorException
189                     ((Error) t, sm.getString("baseModelMBean.invokeError", name));
190             else
191                 throw new MBeanException
192                     (e, sm.getString("baseModelMBean.invokeError", name));
193         } catch (Exception e) {
194             throw new MBeanException
195                 (e, sm.getString("baseModelMBean.invokeError", name));
196         }
197
198         // Return the results of this method invocation
199         // FIXME - should we validate the return type?
200         return result;
201     }
202
203
204     /**
205      * Obtain and return the values of several attributes of this MBean.
206      *
207      * @param names Names of the requested attributes
208      */

209     @Override
210     public AttributeList getAttributes(String names[]) {
211
212         // Validate the input parameters
213         if (names == null)
214             throw new RuntimeOperationsException
215                 (new IllegalArgumentException(sm.getString("baseModelMBean.nullAttributeNameList")),
216                         sm.getString("baseModelMBean.nullAttributeNameList"));
217
218         // Prepare our response, eating all exceptions
219         AttributeList response = new AttributeList();
220         for (int i = 0; i < names.length; i++) {
221             try {
222                 response.add(new Attribute(names[i],getAttribute(names[i])));
223             } catch (Exception e) {
224                 // Not having a particular attribute in the response
225                 // is the indication of a getter problem
226             }
227         }
228         return response;
229
230     }
231
232     public void setManagedBean(ManagedBean managedBean) {
233         this.managedBean = managedBean;
234     }
235
236     /**
237      * Return the <code>MBeanInfo</code> object for this MBean.
238      */

239     @Override
240     public MBeanInfo getMBeanInfo() {
241         return managedBean.getMBeanInfo();
242     }
243
244
245     /**
246      * Invoke a particular method on this MBean, and return any returned
247      * value.
248      *
249      * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation will
250      * attempt to invoke this method on the MBean itself, or (if not
251      * available) on the managed resource object associated with this
252      * MBean.</p>
253      *
254      * @param name Name of the operation to be invoked
255      * @param params Array containing the method parameters of this operation
256      * @param signature Array containing the class names representing
257      *  the signature of this operation
258      *
259      * @exception MBeanException if the initializer of an object
260      *  throws an exception
261      * @exception ReflectionException if a Java reflection exception
262      *  occurs when invoking a method
263      */

264     @Override
265     public Object invoke(String name, Object params[], String signature[])
266         throws MBeanException, ReflectionException
267     {
268         if( (resource instanceof DynamicMBean) &&
269              ! ( resource instanceof BaseModelMBean )) {
270             return ((DynamicMBean)resource).invoke(name, params, signature);
271         }
272
273         // Validate the input parameters
274         if (name == null)
275             throw new RuntimeOperationsException
276                 (new IllegalArgumentException(sm.getString("baseModelMBean.nullMethodName")),
277                         sm.getString("baseModelMBean.nullMethodName"));
278
279         if( log.isDebugEnabled()) log.debug("Invoke " + name);
280
281         Method method= managedBean.getInvoke(name, params, signature, this, resource);
282
283         // Invoke the selected method on the appropriate object
284         Object result = null;
285         try {
286             if( method.getDeclaringClass().isAssignableFrom( this.getClass()) ) {
287                 result = method.invoke(this, params );
288             } else {
289                 result = method.invoke(resource, params);
290             }
291         } catch (InvocationTargetException e) {
292             Throwable t = e.getTargetException();
293             log.error(sm.getString("baseModelMBean.invokeError", name), t );
294             if (t == null)
295                 t = e;
296             if (t instanceof RuntimeException)
297                 throw new RuntimeOperationsException
298                     ((RuntimeException) t, sm.getString("baseModelMBean.invokeError", name));
299             else if (t instanceof Error)
300                 throw new RuntimeErrorException
301                     ((Error) t, sm.getString("baseModelMBean.invokeError", name));
302             else
303                 throw new MBeanException
304                     ((Exception)t, sm.getString("baseModelMBean.invokeError", name));
305         } catch (Exception e) {
306             log.error(sm.getString("baseModelMBean.invokeError", name), e );
307             throw new MBeanException
308                 (e, sm.getString("baseModelMBean.invokeError", name));
309         }
310
311         // Return the results of this method invocation
312         // FIXME - should we validate the return type?
313         return result;
314
315     }
316
317     static Class<?> getAttributeClass(String signature)
318         throws ReflectionException
319     {
320         if (signature.equals(Boolean.TYPE.getName()))
321             return Boolean.TYPE;
322         else if (signature.equals(Byte.TYPE.getName()))
323             return Byte.TYPE;
324         else if (signature.equals(Character.TYPE.getName()))
325             return Character.TYPE;
326         else if (signature.equals(Double.TYPE.getName()))
327             return Double.TYPE;
328         else if (signature.equals(Float.TYPE.getName()))
329             return Float.TYPE;
330         else if (signature.equals(Integer.TYPE.getName()))
331             return Integer.TYPE;
332         else if (signature.equals(Long.TYPE.getName()))
333             return Long.TYPE;
334         else if (signature.equals(Short.TYPE.getName()))
335             return Short.TYPE;
336         else {
337             try {
338                 ClassLoader cl=Thread.currentThread().getContextClassLoader();
339                 if( cl!=null )
340                     return cl.loadClass(signature);
341             } catch( ClassNotFoundException e ) {
342             }
343             try {
344                 return Class.forName(signature);
345             } catch (ClassNotFoundException e) {
346                 throw new ReflectionException(e, sm.getString("baseModelMBean.cnfeForSignature", signature));
347             }
348         }
349     }
350
351     /**
352      * Set the value of a specific attribute of this MBean.
353      *
354      * @param attribute The identification of the attribute to be set
355      *  and the new value
356      *
357      * @exception AttributeNotFoundException if this attribute is not
358      *  supported by this MBean
359      * @exception MBeanException if the initializer of an object
360      *  throws an exception
361      * @exception ReflectionException if a Java reflection exception
362      *  occurs when invoking the getter
363      */

364     @Override
365     public void setAttribute(Attribute attribute)
366         throws AttributeNotFoundException, MBeanException,
367         ReflectionException
368     {
369         if( log.isDebugEnabled() )
370             log.debug("Setting attribute " + this + " " + attribute );
371
372         if( (resource instanceof DynamicMBean) &&
373              ! ( resource instanceof BaseModelMBean )) {
374             try {
375                 ((DynamicMBean)resource).setAttribute(attribute);
376             } catch (InvalidAttributeValueException e) {
377                 throw new MBeanException(e);
378             }
379             return;
380         }
381
382         // Validate the input parameters
383         if (attribute == null)
384             throw new RuntimeOperationsException
385                 (new IllegalArgumentException(sm.getString("baseModelMBean.nullAttribute")),
386                         sm.getString("baseModelMBean.nullAttribute"));
387
388         String name = attribute.getName();
389         Object value = attribute.getValue();
390
391         if (name == null)
392             throw new RuntimeOperationsException
393                 (new IllegalArgumentException(sm.getString("baseModelMBean.nullAttributeName")),
394                         sm.getString("baseModelMBean.nullAttributeName"));
395
396         Object oldValue=null;
397         //if( getAttMap.get(name) != null )
398         //    oldValue=getAttribute( name );
399
400         Method m=managedBean.getSetter(name,this,resource);
401
402         try {
403             if( m.getDeclaringClass().isAssignableFrom( this.getClass()) ) {
404                 m.invoke(thisnew Object[] { value });
405             } else {
406                 m.invoke(resource, new Object[] { value });
407             }
408         } catch (InvocationTargetException e) {
409             Throwable t = e.getTargetException();
410             if (t == null)
411                 t = e;
412             if (t instanceof RuntimeException)
413                 throw new RuntimeOperationsException
414                     ((RuntimeException) t, sm.getString("baseModelMBean.invokeError", name));
415             else if (t instanceof Error)
416                 throw new RuntimeErrorException
417                     ((Error) t, sm.getString("baseModelMBean.invokeError", name));
418             else
419                 throw new MBeanException
420                     (e, sm.getString("baseModelMBean.invokeError", name));
421         } catch (Exception e) {
422             log.error(sm.getString("baseModelMBean.invokeError", name) , e );
423             throw new MBeanException
424                 (e, sm.getString("baseModelMBean.invokeError", name));
425         }
426         try {
427             sendAttributeChangeNotification(new Attribute( name, oldValue),
428                     attribute);
429         } catch(Exception ex) {
430             log.error(sm.getString("baseModelMBean.notificationError", name), ex);
431         }
432         //attributes.put( name, value );
433 //        if( source != null ) {
434 //            // this mbean is associated with a source - maybe we want to persist
435 //            source.updateField(oname, name, value);
436 //        }
437     }
438
439     @Override
440     public String toString() {
441         if( resource==null )
442             return "BaseModelMbean[" + resourceType + "]";
443         return resource.toString();
444     }
445
446     /**
447      * Set the values of several attributes of this MBean.
448      *
449      * @param attributes THe names and values to be set
450      *
451      * @return The list of attributes that were set and their new values
452      */

453     @Override
454     public AttributeList setAttributes(AttributeList attributes) {
455         AttributeList response = new AttributeList();
456
457         // Validate the input parameters
458         if (attributes == null)
459             return response;
460
461         // Prepare and return our response, eating all exceptions
462         String names[] = new String[attributes.size()];
463         int n = 0;
464         Iterator<?> items = attributes.iterator();
465         while (items.hasNext()) {
466             Attribute item = (Attribute) items.next();
467             names[n++] = item.getName();
468             try {
469                 setAttribute(item);
470             } catch (Exception e) {
471                 // Ignore all exceptions
472             }
473         }
474
475         return getAttributes(names);
476
477     }
478
479
480     // ----------------------------------------------------- ModelMBean Methods
481
482
483     /**
484      * Get the instance handle of the object against which we execute
485      * all methods in this ModelMBean management interface.
486      *
487      * @return the backend managed object
488      * @exception InstanceNotFoundException if the managed resource object
489      *  cannot be found
490      * @exception InvalidTargetObjectTypeException if the managed resource
491      *  object is of the wrong type
492      * @exception MBeanException if the initializer of the object throws
493      *  an exception
494      * @exception RuntimeOperationsException if the managed resource or the
495      *  resource type is <code>null</code> or invalid
496      */

497     public Object getManagedResource()
498         throws InstanceNotFoundException, InvalidTargetObjectTypeException,
499         MBeanException, RuntimeOperationsException {
500
501         if (resource == null)
502             throw new RuntimeOperationsException
503                 (new IllegalArgumentException(sm.getString("baseModelMBean.nullResource")),
504                         sm.getString("baseModelMBean.nullResource"));
505
506         return resource;
507
508     }
509
510
511     /**
512      * Set the instance handle of the object against which we will execute
513      * all methods in this ModelMBean management interface.
514      *
515      * The caller can provide the mbean instance or the object name to
516      * the resource, if needed.
517      *
518      * @param resource The resource object to be managed
519      * @param type The type of reference for the managed resource
520      *  ("ObjectReference""Handle""IOR""EJBHandle", or
521      *  "RMIReference")
522      *
523      * @exception InstanceNotFoundException if the managed resource object
524      *  cannot be found
525      * @exception MBeanException if the initializer of the object throws
526      *  an exception
527      * @exception RuntimeOperationsException if the managed resource or the
528      *  resource type is <code>null</code> or invalid
529      */

530     public void setManagedResource(Object resource, String type)
531         throws InstanceNotFoundException,
532         MBeanException, RuntimeOperationsException
533     {
534         if (resource == null)
535             throw new RuntimeOperationsException
536                 (new IllegalArgumentException(sm.getString("baseModelMBean.nullResource")),
537                         sm.getString("baseModelMBean.nullResource"));
538
539 //        if (!"objectreference".equalsIgnoreCase(type))
540 //            throw new InvalidTargetObjectTypeException(type);
541
542         this.resource = resource;
543         this.resourceType = resource.getClass().getName();
544
545 //        // Make the resource aware of the model mbean.
546 //        try {
547 //            Method m=resource.getClass().getMethod("setModelMBean",
548 //                    new Class[] {ModelMBean.class});
549 //            if( m!= null ) {
550 //                m.invoke(resource, new Object[] {this});
551 //            }
552 //        } catch( NoSuchMethodException t ) {
553 //            // ignore
554 //        } catch( Throwable t ) {
555 //            log.error( "Can't set model mbean ", t );
556 //        }
557     }
558
559
560     // ------------------------------ ModelMBeanNotificationBroadcaster Methods
561
562
563     /**
564      * Add an attribute change notification event listener to this MBean.
565      *
566      * @param listener Listener that will receive event notifications
567      * @param name Name of the attribute of interest, or <code>null</code>
568      *  to indicate interest in all attributes
569      * @param handback Handback object to be sent along with event
570      *  notifications
571      *
572      * @exception IllegalArgumentException if the listener parameter is null
573      */

574     @Override
575     public void addAttributeChangeNotificationListener
576         (NotificationListener listener, String name, Object handback)
577         throws IllegalArgumentException {
578
579         if (listener == null)
580             throw new IllegalArgumentException(sm.getString("baseModelMBean.nullListener"));
581         if (attributeBroadcaster == null)
582             attributeBroadcaster = new BaseNotificationBroadcaster();
583
584         if( log.isDebugEnabled() )
585             log.debug("addAttributeNotificationListener " + listener);
586
587         BaseAttributeFilter filter = new BaseAttributeFilter(name);
588         attributeBroadcaster.addNotificationListener
589             (listener, filter, handback);
590
591     }
592
593
594     /**
595      * Remove an attribute change notification event listener from
596      * this MBean.
597      *
598      * @param listener The listener to be removed
599      * @param name The attribute name for which no more events are required
600      *
601      *
602      * @exception ListenerNotFoundException if this listener is not
603      *  registered in the MBean
604      */

605     @Override
606     public void removeAttributeChangeNotificationListener
607         (NotificationListener listener, String name)
608         throws ListenerNotFoundException {
609
610         if (listener == null)
611             throw new IllegalArgumentException(sm.getString("baseModelMBean.nullListener"));
612
613         // FIXME - currently this removes *all* notifications for this listener
614         if (attributeBroadcaster != null) {
615             attributeBroadcaster.removeNotificationListener(listener);
616         }
617
618     }
619
620
621     /**
622      * Send an <code>AttributeChangeNotification</code> to all registered
623      * listeners.
624      *
625      * @param notification The <code>AttributeChangeNotification</code>
626      *  that will be passed
627      *
628      * @exception MBeanException if an object initializer throws an
629      *  exception
630      * @exception RuntimeOperationsException wraps IllegalArgumentException
631      *  when the specified notification is <code>null</code> or invalid
632      */

633     @Override
634     public void sendAttributeChangeNotification
635         (AttributeChangeNotification notification)
636         throws MBeanException, RuntimeOperationsException {
637
638         if (notification == null)
639             throw new RuntimeOperationsException
640                 (new IllegalArgumentException(sm.getString("baseModelMBean.nullNotification")),
641                         sm.getString("baseModelMBean.nullNotification"));
642         if (attributeBroadcaster == null)
643             return// This means there are no registered listeners
644         if( log.isDebugEnabled() )
645             log.debug( "AttributeChangeNotification " + notification );
646         attributeBroadcaster.sendNotification(notification);
647
648     }
649
650
651     /**
652      * Send an <code>AttributeChangeNotification</code> to all registered
653      * listeners.
654      *
655      * @param oldValue The original value of the <code>Attribute</code>
656      * @param newValue The new value of the <code>Attribute</code>
657      *
658      * @exception MBeanException if an object initializer throws an
659      *  exception
660      * @exception RuntimeOperationsException wraps IllegalArgumentException
661      *  when the specified notification is <code>null</code> or invalid
662      */

663     @Override
664     public void sendAttributeChangeNotification
665         (Attribute oldValue, Attribute newValue)
666         throws MBeanException, RuntimeOperationsException {
667
668         // Calculate the class name for the change notification
669         String type = null;
670         if (newValue.getValue() != null)
671             type = newValue.getValue().getClass().getName();
672         else if (oldValue.getValue() != null)
673             type = oldValue.getValue().getClass().getName();
674         else
675             return;  // Old and new are both null == no change
676
677         AttributeChangeNotification notification =
678             new AttributeChangeNotification
679             (this, 1, System.currentTimeMillis(),
680              "Attribute value has changed",
681              oldValue.getName(), type,
682              oldValue.getValue(), newValue.getValue());
683         sendAttributeChangeNotification(notification);
684
685     }
686
687
688     /**
689      * Send a <code>Notification</code> to all registered listeners as a
690      * <code>jmx.modelmbean.general</code> notification.
691      *
692      * @param notification The <code>Notification</code> that will be passed
693      *
694      * @exception MBeanException if an object initializer throws an
695      *  exception
696      * @exception RuntimeOperationsException wraps IllegalArgumentException
697      *  when the specified notification is <code>null</code> or invalid
698      */

699     @Override
700     public void sendNotification(Notification notification)
701         throws MBeanException, RuntimeOperationsException {
702
703         if (notification == null)
704             throw new RuntimeOperationsException
705                 (new IllegalArgumentException(sm.getString("baseModelMBean.nullNotification")),
706                         sm.getString("baseModelMBean.nullNotification"));
707         if (generalBroadcaster == null)
708             return// This means there are no registered listeners
709         generalBroadcaster.sendNotification(notification);
710
711     }
712
713
714     /**
715      * Send a <code>Notification</code> which contains the specified string
716      * as a <code>jmx.modelmbean.generic</code> notification.
717      *
718      * @param message The message string to be passed
719      *
720      * @exception MBeanException if an object initializer throws an
721      *  exception
722      * @exception RuntimeOperationsException wraps IllegalArgumentException
723      *  when the specified notification is <code>null</code> or invalid
724      */

725     @Override
726     public void sendNotification(String message)
727         throws MBeanException, RuntimeOperationsException {
728
729         if (message == null)
730             throw new RuntimeOperationsException
731                 (new IllegalArgumentException(sm.getString("baseModelMBean.nullMessage")),
732                         sm.getString("baseModelMBean.nullMessage"));
733         Notification notification = new Notification
734             ("jmx.modelmbean.generic"this, 1, message);
735         sendNotification(notification);
736
737     }
738
739
740     // ---------------------------------------- NotificationBroadcaster Methods
741
742
743     /**
744      * Add a notification event listener to this MBean.
745      *
746      * @param listener Listener that will receive event notifications
747      * @param filter Filter object used to filter event notifications
748      *  actually delivered, or <code>null</code> for no filtering
749      * @param handback Handback object to be sent along with event
750      *  notifications
751      *
752      * @exception IllegalArgumentException if the listener parameter is null
753      */

754     @Override
755     public void addNotificationListener(NotificationListener listener,
756                                         NotificationFilter filter,
757                                         Object handback)
758         throws IllegalArgumentException {
759
760         if (listener == null)
761             throw new IllegalArgumentException(sm.getString("baseModelMBean.nullListener"));
762
763         if( log.isDebugEnabled() ) log.debug("addNotificationListener " + listener);
764
765         if (generalBroadcaster == null)
766             generalBroadcaster = new BaseNotificationBroadcaster();
767         generalBroadcaster.addNotificationListener
768             (listener, filter, handback);
769
770         // We'll send the attribute change notifications to all listeners ( who care )
771         // The normal filtering can be used.
772         // The problem is that there is no other way to add attribute change listeners
773         // to a model mbean ( AFAIK ). I suppose the spec should be fixed.
774         if (attributeBroadcaster == null)
775             attributeBroadcaster = new BaseNotificationBroadcaster();
776
777         if( log.isDebugEnabled() )
778             log.debug("addAttributeNotificationListener " + listener);
779
780         attributeBroadcaster.addNotificationListener
781                 (listener, filter, handback);
782     }
783
784
785     /**
786      * Return an <code>MBeanNotificationInfo</code> object describing the
787      * notifications sent by this MBean.
788      */

789     @Override
790     public MBeanNotificationInfo[] getNotificationInfo() {
791
792         // Acquire the set of application notifications
793         MBeanNotificationInfo current[] = getMBeanInfo().getNotifications();
794         MBeanNotificationInfo response[] =
795             new MBeanNotificationInfo[current.length + 2];
796  //       Descriptor descriptor = null;
797
798         // Fill in entry for general notifications
799 //        descriptor = new DescriptorSupport
800 //            (new String[] { "name=GENERIC",
801 //                            "descriptorType=notification",
802 //                            "log=T",
803 //                            "severity=5",
804 //                            "displayName=jmx.modelmbean.generic" });
805         response[0] = new MBeanNotificationInfo
806             (new String[] { "jmx.modelmbean.generic" },
807              "GENERIC",
808              "Text message notification from the managed resource");
809              //descriptor);
810
811         // Fill in entry for attribute change notifications
812 //        descriptor = new DescriptorSupport
813 //            (new String[] { "name=ATTRIBUTE_CHANGE",
814 //                            "descriptorType=notification",
815 //                            "log=T",
816 //                            "severity=5",
817 //                            "displayName=jmx.attribute.change" });
818         response[1] = new MBeanNotificationInfo
819             (new String[] { "jmx.attribute.change" },
820              "ATTRIBUTE_CHANGE",
821              "Observed MBean attribute value has changed");
822              //descriptor);
823
824         // Copy remaining notifications as reported by the application
825         System.arraycopy(current, 0, response, 2, current.length);
826         return response;
827
828     }
829
830
831     /**
832      * Remove a notification event listener from this MBean.
833      *
834      * @param listener The listener to be removed (any and all registrations
835      *  for this listener will be eliminated)
836      *
837      * @exception ListenerNotFoundException if this listener is not
838      *  registered in the MBean
839      */

840     @Override
841     public void removeNotificationListener(NotificationListener listener)
842         throws ListenerNotFoundException {
843
844         if (listener == null)
845             throw new IllegalArgumentException(sm.getString("baseModelMBean.nullListener"));
846
847         if (generalBroadcaster != null) {
848             generalBroadcaster.removeNotificationListener(listener);
849         }
850
851         if (attributeBroadcaster != null) {
852             attributeBroadcaster.removeNotificationListener(listener);
853         }
854      }
855
856
857     public String getModelerType() {
858         return resourceType;
859     }
860
861     public String getClassName() {
862         return getModelerType();
863     }
864
865     public ObjectName getJmxName() {
866         return oname;
867     }
868
869     public String getObjectName() {
870         if (oname != null) {
871             return oname.toString();
872         } else {
873             return null;
874         }
875     }
876
877
878     // -------------------- Registration  --------------------
879     // XXX We can add some method patterns here- like setName() and
880     // setDomain() for code that doesn't implement the Registration
881
882     @Override
883     public ObjectName preRegister(MBeanServer server,
884                                   ObjectName name)
885             throws Exception
886     {
887         if( log.isDebugEnabled())
888             log.debug("preRegister " + resource + " " + name );
889         oname=name;
890         if( resource instanceof MBeanRegistration ) {
891             oname = ((MBeanRegistration)resource).preRegister(server, name );
892         }
893         return oname;
894     }
895
896     @Override
897     public void postRegister(Boolean registrationDone) {
898         if( resource instanceof MBeanRegistration ) {
899             ((MBeanRegistration)resource).postRegister(registrationDone);
900         }
901     }
902
903     @Override
904     public void preDeregister() throws Exception {
905         if( resource instanceof MBeanRegistration ) {
906             ((MBeanRegistration)resource).preDeregister();
907         }
908     }
909
910     @Override
911     public void postDeregister() {
912         if( resource instanceof MBeanRegistration ) {
913             ((MBeanRegistration)resource).postDeregister();
914         }
915     }
916 }
917