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.Method; 23 import java.util.HashMap; 24 import java.util.Map; 25 26 import javax.management.AttributeNotFoundException; 27 import javax.management.DynamicMBean; 28 import javax.management.InstanceNotFoundException; 29 import javax.management.MBeanAttributeInfo; 30 import javax.management.MBeanConstructorInfo; 31 import javax.management.MBeanException; 32 import javax.management.MBeanInfo; 33 import javax.management.MBeanNotificationInfo; 34 import javax.management.MBeanOperationInfo; 35 import javax.management.ReflectionException; 36 import javax.management.RuntimeOperationsException; 37 import javax.management.ServiceNotFoundException; 38 //import javax.management.modelmbean.InvalidTargetObjectTypeException; 39 40 41 /** 42 * <p>Internal configuration information for a managed bean (MBean) 43 * descriptor.</p> 44 * 45 * @author Craig R. McClanahan 46 * @version $Revision: 610929 $ $Date: 2008-01-10 22:04:31 +0100 (Thu, 10 Jan 2008) $ 47 */ 48 49 public class ManagedBean implements java.io.Serializable 50 { 51 private static final String BASE_MBEAN = "org.apache.tomcat.util.modeler.BaseModelMBean"; 52 // ----------------------------------------------------- Instance Variables 53 static final Object[] NO_ARGS_PARAM=new Object[0]; 54 static final Class[] NO_ARGS_PARAM_SIG=new Class[0]; 55 56 57 /** 58 * The <code>ModelMBeanInfo</code> object that corresponds 59 * to this <code>ManagedBean</code> instance. 60 */ 61 transient MBeanInfo info = null; 62 // Map<AttributeInfo> 63 private Map attributes = new HashMap(); 64 //Map<OperationInfo> 65 private Map operations = new HashMap(); 66 67 protected String className = BASE_MBEAN; 68 //protected ConstructorInfo constructors[] = new ConstructorInfo[0]; 69 protected String description = null; 70 protected String domain = null; 71 protected String group = null; 72 protected String name = null; 73 74 //protected List fields = new ArrayList(); 75 protected NotificationInfo notifications[] = new NotificationInfo[0]; 76 protected String type = null; 77 78 /** Constructor. Will add default attributes. 79 * 80 */ 81 public ManagedBean() { 82 AttributeInfo ai=new AttributeInfo(); 83 ai.setName("modelerType"); 84 ai.setDescription("Type of the modeled resource. Can be set only once"); 85 ai.setType("java.lang.String"); 86 ai.setWriteable(false); 87 addAttribute(ai); 88 } 89 90 // ------------------------------------------------------------- Properties 91 92 93 /** 94 * The collection of attributes for this MBean. 95 */ 96 public AttributeInfo[] getAttributes() { 97 AttributeInfo result[] = new AttributeInfo[attributes.size()]; 98 attributes.values().toArray(result); 99 return result; 100 } 101 102 103 /** 104 * The fully qualified name of the Java class of the MBean 105 * described by this descriptor. If not specified, the standard JMX 106 * class (<code>javax.management.modelmbean.RequiredModeLMBean</code>) 107 * will be utilized. 108 */ 109 public String getClassName() { 110 return (this.className); 111 } 112 113 public void setClassName(String className) { 114 this.className = className; 115 this.info = null; 116 } 117 118 119 // /** 120 // * The collection of constructors for this MBean. 121 // */ 122 // public ConstructorInfo[] getConstructors() { 123 // return (this.constructors); 124 // } 125 126 127 /** 128 * The human-readable description of this MBean. 129 */ 130 public String getDescription() { 131 return (this.description); 132 } 133 134 public void setDescription(String description) { 135 this.description = description; 136 this.info = null; 137 } 138 139 140 /** 141 * The (optional) <code>ObjectName</code> domain in which this MBean 142 * should be registered in the MBeanServer. 143 */ 144 public String getDomain() { 145 return (this.domain); 146 } 147 148 public void setDomain(String domain) { 149 this.domain = domain; 150 } 151 152 153 /** 154 * <p>Return a <code>List</code> of the {@link FieldInfo} objects for 155 * the name/value pairs that should be 156 * added to the Descriptor created from this metadata.</p> 157 */ 158 // public List getFields() { 159 // return (this.fields); 160 // } 161 // 162 163 /** 164 * The (optional) group to which this MBean belongs. 165 */ 166 public String getGroup() { 167 return (this.group); 168 } 169 170 public void setGroup(String group) { 171 this.group = group; 172 } 173 174 175 /** 176 * The name of this managed bean, which must be unique among all 177 * MBeans managed by a particular MBeans server. 178 */ 179 public String getName() { 180 return (this.name); 181 } 182 183 public void setName(String name) { 184 this.name = name; 185 this.info = null; 186 } 187 188 189 /** 190 * The collection of notifications for this MBean. 191 */ 192 public NotificationInfo[] getNotifications() { 193 return (this.notifications); 194 } 195 196 197 /** 198 * The collection of operations for this MBean. 199 */ 200 public OperationInfo[] getOperations() { 201 OperationInfo[] result = new OperationInfo[operations.size()]; 202 operations.values().toArray(result); 203 return result; 204 } 205 206 207 /** 208 * The fully qualified name of the Java class of the resource 209 * implementation class described by the managed bean described 210 * by this descriptor. 211 */ 212 public String getType() { 213 return (this.type); 214 } 215 216 public void setType(String type) { 217 this.type = type; 218 this.info = null; 219 } 220 221 222 // --------------------------------------------------------- Public Methods 223 224 225 /** 226 * Add a new attribute to the set of attributes for this MBean. 227 * 228 * @param attribute The new attribute descriptor 229 */ 230 public void addAttribute(AttributeInfo attribute) { 231 attributes.put(attribute.getName(), attribute); 232 } 233 234 235 /** 236 * Add a new constructor to the set of constructors for this MBean. 237 * 238 * @param constructor The new constructor descriptor 239 */ 240 // public void addConstructor(ConstructorInfo constructor) { 241 // 242 // synchronized (constructors) { 243 // ConstructorInfo results[] = 244 // new ConstructorInfo[constructors.length + 1]; 245 // System.arraycopy(constructors, 0, results, 0, constructors.length); 246 // results[constructors.length] = constructor; 247 // constructors = results; 248 // this.info = null; 249 // } 250 // 251 // } 252 253 254 /** 255 * <p>Add a new field to the fields associated with the 256 * Descriptor that will be created from this metadata.</p> 257 * 258 * @param field The field to be added 259 */ 260 // public void addField(FieldInfo field) { 261 // fields.add(field); 262 // } 263 264 265 /** 266 * Add a new notification to the set of notifications for this MBean. 267 * 268 * @param notification The new notification descriptor 269 */ 270 public void addNotification(NotificationInfo notification) { 271 272 synchronized (notifications) { 273 NotificationInfo results[] = 274 new NotificationInfo[notifications.length + 1]; 275 System.arraycopy(notifications, 0, results, 0, 276 notifications.length); 277 results[notifications.length] = notification; 278 notifications = results; 279 this.info = null; 280 } 281 282 } 283 284 285 /** 286 * Add a new operation to the set of operations for this MBean. 287 * 288 * @param operation The new operation descriptor 289 */ 290 public void addOperation(OperationInfo operation) { 291 operations.put(operation.getName(), operation); 292 } 293 294 295 /** 296 * Create and return a <code>ModelMBean</code> that has been 297 * preconfigured with the <code>ModelMBeanInfo</code> information 298 * for this managed bean, but is not associated with any particular 299 * managed resource. The returned <code>ModelMBean</code> will 300 * <strong>NOT</strong> have been registered with our 301 * <code>MBeanServer</code>. 302 * 303 * @exception InstanceNotFoundException if the managed resource 304 * object cannot be found 305 * @exception InvalidTargetObjectTypeException if our MBean cannot 306 * handle object references (should never happen) 307 * @exception MBeanException if a problem occurs instantiating the 308 * <code>ModelMBean</code> instance 309 * @exception RuntimeOperationsException if a JMX runtime error occurs 310 */ 311 public DynamicMBean createMBean() 312 throws InstanceNotFoundException, 313 MBeanException, RuntimeOperationsException { 314 315 return (createMBean(null)); 316 317 } 318 319 320 /** 321 * Create and return a <code>ModelMBean</code> that has been 322 * preconfigured with the <code>ModelMBeanInfo</code> information 323 * for this managed bean, and is associated with the specified 324 * managed object instance. The returned <code>ModelMBean</code> 325 * will <strong>NOT</strong> have been registered with our 326 * <code>MBeanServer</code>. 327 * 328 * @param instance Instanced of the managed object, or <code>null</code> 329 * for no associated instance 330 * 331 * @exception InstanceNotFoundException if the managed resource 332 * object cannot be found 333 * @exception InvalidTargetObjectTypeException if our MBean cannot 334 * handle object references (should never happen) 335 * @exception MBeanException if a problem occurs instantiating the 336 * <code>ModelMBean</code> instance 337 * @exception RuntimeOperationsException if a JMX runtime error occurs 338 */ 339 public DynamicMBean createMBean(Object instance) 340 throws InstanceNotFoundException, 341 MBeanException, RuntimeOperationsException { 342 343 BaseModelMBean mbean = null; 344 345 // Load the ModelMBean implementation class 346 if(getClassName().equals(BASE_MBEAN)) { 347 // Skip introspection 348 mbean = new BaseModelMBean(); 349 } else { 350 Class clazz = null; 351 Exception ex = null; 352 try { 353 clazz = Class.forName(getClassName()); 354 } catch (Exception e) { 355 } 356 357 if( clazz==null ) { 358 try { 359 ClassLoader cl= Thread.currentThread().getContextClassLoader(); 360 if ( cl != null) 361 clazz= cl.loadClass(getClassName()); 362 } catch (Exception e) { 363 ex=e; 364 } 365 } 366 367 if( clazz==null) { 368 throw new MBeanException 369 (ex, "Cannot load ModelMBean class " + getClassName()); 370 } 371 try { 372 // Stupid - this will set the default minfo first.... 373 mbean = (BaseModelMBean) clazz.newInstance(); 374 } catch (RuntimeOperationsException e) { 375 throw e; 376 } catch (Exception e) { 377 throw new MBeanException 378 (e, "Cannot instantiate ModelMBean of class " + 379 getClassName()); 380 } 381 } 382 383 mbean.setManagedBean(this); 384 385 // Set the managed resource (if any) 386 try { 387 if (instance != null) 388 mbean.setManagedResource(instance, "ObjectReference"); 389 } catch (InstanceNotFoundException e) { 390 throw e; 391 } 392 return (mbean); 393 394 } 395 396 397 /** 398 * Create and return a <code>ModelMBeanInfo</code> object that 399 * describes this entire managed bean. 400 */ 401 MBeanInfo getMBeanInfo() { 402 403 // Return our cached information (if any) 404 if (info != null) 405 return (info); 406 407 // Create subordinate information descriptors as required 408 AttributeInfo attrs[] = getAttributes(); 409 MBeanAttributeInfo attributes[] = 410 new MBeanAttributeInfo[attrs.length]; 411 for (int i = 0; i < attrs.length; i++) 412 attributes[i] = attrs[i].createAttributeInfo(); 413 414 OperationInfo opers[] = getOperations(); 415 MBeanOperationInfo operations[] = 416 new MBeanOperationInfo[opers.length]; 417 for (int i = 0; i < opers.length; i++) 418 operations[i] = opers[i].createOperationInfo(); 419 420 421 // ConstructorInfo consts[] = getConstructors(); 422 // ModelMBeanConstructorInfo constructors[] = 423 // new ModelMBeanConstructorInfo[consts.length]; 424 // for (int i = 0; i < consts.length; i++) 425 // constructors[i] = consts[i].createConstructorInfo(); 426 427 NotificationInfo notifs[] = getNotifications(); 428 MBeanNotificationInfo notifications[] = 429 new MBeanNotificationInfo[notifs.length]; 430 for (int i = 0; i < notifs.length; i++) 431 notifications[i] = notifs[i].createNotificationInfo(); 432 433 434 // Construct and return a new ModelMBeanInfo object 435 info = new MBeanInfo(getClassName(), 436 getDescription(), 437 attributes, 438 new MBeanConstructorInfo[] {}, 439 operations, 440 notifications); 441 // try { 442 // Descriptor descriptor = info.getMBeanDescriptor(); 443 // Iterator fields = getFields().iterator(); 444 // while (fields.hasNext()) { 445 // FieldInfo field = (FieldInfo) fields.next(); 446 // descriptor.setField(field.getName(), field.getValue()); 447 // } 448 // info.setMBeanDescriptor(descriptor); 449 // } catch (MBeanException e) { 450 // ; 451 // } 452 453 return (info); 454 455 } 456 457 458 /** 459 * Return a string representation of this managed bean. 460 */ 461 public String toString() { 462 463 StringBuffer sb = new StringBuffer("ManagedBean["); 464 sb.append("name="); 465 sb.append(name); 466 sb.append(", className="); 467 sb.append(className); 468 sb.append(", description="); 469 sb.append(description); 470 if (group != null) { 471 sb.append(", group="); 472 sb.append(group); 473 } 474 sb.append(", type="); 475 sb.append(type); 476 sb.append("]"); 477 return (sb.toString()); 478 479 } 480 481 Method getGetter(String aname, BaseModelMBean mbean, Object resource) 482 throws AttributeNotFoundException, MBeanException, ReflectionException { 483 // TODO: do we need caching ? JMX is for management, it's not supposed to require lots of performance. 484 Method m=null; // (Method)getAttMap.get( name ); 485 486 if( m==null ) { 487 AttributeInfo attrInfo = (AttributeInfo)attributes.get(aname); 488 // Look up the actual operation to be used 489 if (attrInfo == null) 490 throw new AttributeNotFoundException(" Cannot find attribute " + aname + " for " + resource); 491 492 String getMethod = attrInfo.getGetMethod(); 493 if (getMethod == null) 494 throw new AttributeNotFoundException("Cannot find attribute " + aname + " get method name"); 495 496 Object object = null; 497 NoSuchMethodException exception = null; 498 try { 499 object = mbean; 500 m = object.getClass().getMethod(getMethod, NO_ARGS_PARAM_SIG); 501 } catch (NoSuchMethodException e) { 502 exception = e;; 503 } 504 if( m== null && resource != null ) { 505 try { 506 object = resource; 507 m = object.getClass().getMethod(getMethod, NO_ARGS_PARAM_SIG); 508 exception=null; 509 } catch (NoSuchMethodException e) { 510 exception = e; 511 } 512 } 513 if( exception != null ) 514 throw new ReflectionException(exception, 515 "Cannot find getter method " + getMethod); 516 //getAttMap.put( name, m ); 517 } 518 519 return m; 520 } 521 522 public Method getSetter(String aname, BaseModelMBean bean, Object resource) 523 throws AttributeNotFoundException, MBeanException, ReflectionException { 524 // Cache may be needed for getters, but it is a really bad idea for setters, this is far 525 // less frequent. 526 Method m=null;//(Method)setAttMap.get( name ); 527 528 if( m==null ) { 529 AttributeInfo attrInfo = (AttributeInfo)attributes.get(aname); 530 if (attrInfo == null) 531 throw new AttributeNotFoundException(" Cannot find attribute " + aname); 532 533 // Look up the actual operation to be used 534 String setMethod = attrInfo.getSetMethod(); 535 if (setMethod == null) 536 throw new AttributeNotFoundException("Cannot find attribute " + aname + " set method name"); 537 538 String argType=attrInfo.getType(); 539 540 Class signature[] = new Class[] { BaseModelMBean.getAttributeClass( argType ) }; 541 542 Object object = null; 543 NoSuchMethodException exception = null; 544 try { 545 object = bean; 546 m = object.getClass().getMethod(setMethod, signature); 547 } catch (NoSuchMethodException e) { 548 exception = e;; 549 } 550 if( m== null && resource != null ) { 551 try { 552 object = resource; 553 m = object.getClass().getMethod(setMethod, signature); 554 exception=null; 555 } catch (NoSuchMethodException e) { 556 exception = e; 557 } 558 } 559 if( exception != null ) 560 throw new ReflectionException(exception, 561 "Cannot find setter method " + setMethod + 562 " " + resource); 563 //setAttMap.put( name, m ); 564 } 565 566 return m; 567 } 568 569 public Method getInvoke(String aname, Object[] params, String[] signature, BaseModelMBean bean, Object resource) 570 throws MBeanException, ReflectionException { 571 Method method = null; 572 if (method == null) { 573 if (params == null) 574 params = new Object[0]; 575 if (signature == null) 576 signature = new String[0]; 577 if (params.length != signature.length) 578 throw new RuntimeOperationsException( 579 new IllegalArgumentException( 580 "Inconsistent arguments and signature"), 581 "Inconsistent arguments and signature"); 582 583 // Acquire the ModelMBeanOperationInfo information for 584 // the requested operation 585 OperationInfo opInfo = (OperationInfo)operations.get(aname); 586 if (opInfo == null) 587 throw new MBeanException(new ServiceNotFoundException( 588 "Cannot find operation " + aname), 589 "Cannot find operation " + aname); 590 591 // Prepare the signature required by Java reflection APIs 592 // FIXME - should we use the signature from opInfo? 593 Class types[] = new Class[signature.length]; 594 for (int i = 0; i < signature.length; i++) { 595 types[i] = BaseModelMBean.getAttributeClass(signature[i]); 596 } 597 598 // Locate the method to be invoked, either in this MBean itself 599 // or in the corresponding managed resource 600 // FIXME - Accessible methods in superinterfaces? 601 Object object = null; 602 Exception exception = null; 603 try { 604 object = bean; 605 method = object.getClass().getMethod(aname, types); 606 } catch (NoSuchMethodException e) { 607 exception = e; 608 ; 609 } 610 try { 611 if ((method == null) && (resource != null)) { 612 object = resource; 613 method = object.getClass().getMethod(aname, types); 614 } 615 } catch (NoSuchMethodException e) { 616 exception = e; 617 } 618 if (method == null) { 619 throw new ReflectionException(exception, "Cannot find method " 620 + aname + " with this signature"); 621 } 622 // invokeAttMap.put(mkey, method); 623 } 624 return method; 625 } 626 627 628 }