001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 019 package org.apache.commons.modeler; 020 021 022 import java.lang.reflect.InvocationTargetException; 023 import java.lang.reflect.Method; 024 import java.util.HashMap; 025 import java.util.Hashtable; 026 import java.util.Iterator; 027 028 import javax.management.Attribute; 029 import javax.management.AttributeChangeNotification; 030 import javax.management.AttributeList; 031 import javax.management.AttributeNotFoundException; 032 import javax.management.Descriptor; 033 import javax.management.DynamicMBean; 034 import javax.management.InstanceNotFoundException; 035 import javax.management.InvalidAttributeValueException; 036 import javax.management.ListenerNotFoundException; 037 import javax.management.MBeanException; 038 import javax.management.MBeanInfo; 039 import javax.management.MBeanNotificationInfo; 040 import javax.management.MBeanRegistration; 041 import javax.management.MBeanServer; 042 import javax.management.Notification; 043 import javax.management.NotificationFilter; 044 import javax.management.NotificationListener; 045 import javax.management.ObjectName; 046 import javax.management.ReflectionException; 047 import javax.management.RuntimeErrorException; 048 import javax.management.RuntimeOperationsException; 049 import javax.management.ServiceNotFoundException; 050 import javax.management.modelmbean.DescriptorSupport; 051 import javax.management.modelmbean.InvalidTargetObjectTypeException; 052 import javax.management.modelmbean.ModelMBean; 053 import javax.management.modelmbean.ModelMBeanAttributeInfo; 054 import javax.management.modelmbean.ModelMBeanInfo; 055 import javax.management.modelmbean.ModelMBeanInfoSupport; 056 import javax.management.modelmbean.ModelMBeanNotificationInfo; 057 import javax.management.modelmbean.ModelMBeanOperationInfo; 058 059 import org.apache.commons.logging.Log; 060 import org.apache.commons.logging.LogFactory; 061 import org.apache.commons.modeler.modules.ModelerSource; 062 063 // TODO: enable ant-like substitutions ? ( or at least discuss it ) 064 065 /** 066 * <p>Basic implementation of the <code>ModelMBean</code> interface, which 067 * supports the minimal requirements of the interface contract.</p> 068 * 069 * <p>This can be used directly to wrap an existing java bean, or inside 070 * an mlet or anywhere an MBean would be used. The String parameter 071 * passed to the constructor will be used to construct an instance of the 072 * real object that we wrap. 073 * 074 * Limitations: 075 * <ul> 076 * <li>Only managed resources of type <code>objectReference</code> are 077 * supportd.</li> 078 * <li>Caching of attribute values and operation results is not supported. 079 * All calls to <code>invoke()</code> are immediately executed.</li> 080 * <li>Logging (under control of descriptors) is not supported.</li> 081 * <li>Persistence of MBean attributes and operations is not supported.</li> 082 * <li>All classes referenced as attribute types, operation parameters, or 083 * operation return values must be one of the following: 084 * <ul> 085 * <li>One of the Java primitive types (boolean, byte, char, double, 086 * float, integer, long, short). Corresponding value will be wrapped 087 * in the appropriate wrapper class automatically.</li> 088 * <li>Operations that return no value should declare a return type of 089 * <code>void</code>.</li> 090 * </ul> 091 * <li>Attribute caching is not supported</li> 092 * </ul> 093 * 094 * @author Craig R. McClanahan 095 * @author Costin Manolache 096 * @version $Revision: 480402 $ $Date: 2006-11-29 05:43:23 +0100 (Wed, 29 Nov 2006) $ 097 */ 098 099 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 }