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