1 8 9 package mx4j.server; 10 11 import java.lang.reflect.Constructor ; 12 import java.lang.reflect.Method ; 13 import java.lang.reflect.Modifier ; 14 import java.security.AccessController ; 15 import java.security.PrivilegedAction ; 16 import java.util.ArrayList ; 17 import java.util.HashMap ; 18 import java.util.WeakHashMap ; 19 import javax.management.DynamicMBean ; 20 import javax.management.MBeanAttributeInfo ; 21 import javax.management.MBeanConstructorInfo ; 22 import javax.management.MBeanInfo ; 23 import javax.management.MBeanNotificationInfo ; 24 import javax.management.MBeanOperationInfo ; 25 import javax.management.MBeanParameterInfo ; 26 import javax.management.NotificationBroadcaster ; 27 import javax.management.loading.MLet ; 28 29 import mx4j.MBeanDescription; 30 import mx4j.MBeanDescriptionAdapter; 31 import mx4j.MX4JSystemKeys; 32 import mx4j.log.Log; 33 import mx4j.log.Logger; 34 import mx4j.util.Utils; 35 36 58 public class MBeanIntrospector 59 { 60 private static final MBeanDescriptionAdapter DEFAULT_DESCRIPTION = new MBeanDescriptionAdapter(); 61 private static final MBeanConstructorInfo [] EMPTY_CONSTRUCTORS = new MBeanConstructorInfo [0]; 62 private static final MBeanParameterInfo [] EMPTY_PARAMETERS = new MBeanParameterInfo [0]; 63 private static final MBeanAttributeInfo [] EMPTY_ATTRIBUTES = new MBeanAttributeInfo [0]; 64 private static final MBeanNotificationInfo [] EMPTY_NOTIFICATIONS = new MBeanNotificationInfo [0]; 65 private static final MBeanOperationInfo [] EMPTY_OPERATIONS = new MBeanOperationInfo [0]; 66 67 private boolean extendedMBeanInterfaces = false; 68 private boolean bcelAvailable = false; 69 private String mbeanInvokerClass = null; 70 71 private final WeakHashMap mbeanInfoCache = new WeakHashMap (); 72 private final WeakHashMap mbeanInvokerCache = new WeakHashMap (); 73 74 public MBeanIntrospector() 75 { 76 String strict = (String )AccessController.doPrivileged(new PrivilegedAction () 77 { 78 public Object run() 79 { 80 return System.getProperty(MX4JSystemKeys.MX4J_STRICT_MBEAN_INTERFACE); 81 } 82 }); 83 if (strict != null && !Boolean.valueOf(strict).booleanValue()) 84 { 85 extendedMBeanInterfaces = true; 86 } 87 88 try 90 { 91 ClassLoader loader = getClass().getClassLoader(); 92 if (loader == null) loader = Thread.currentThread().getContextClassLoader(); 93 loader.loadClass("org.apache.bcelAvailable.generic.Type"); 94 bcelAvailable = true; 95 } 96 catch (Throwable ignored) 97 { 98 } 99 100 mbeanInvokerClass = (String )AccessController.doPrivileged(new PrivilegedAction () 102 { 103 public Object run() 104 { 105 return System.getProperty(MX4JSystemKeys.MX4J_MBEAN_INVOKER); 106 } 107 }); 108 } 109 110 116 public void introspect(MBeanMetaData metadata) 117 { 118 introspectType(metadata); 119 introspectMBeanInfo(metadata); 120 } 121 122 126 public boolean isMBeanCompliant(MBeanMetaData metadata) 127 { 128 return isMBeanClassCompliant(metadata) && isMBeanTypeCompliant(metadata) && isMBeanInfoCompliant(metadata); 129 } 130 131 135 private boolean testCompliance(MBeanMetaData metadata) 136 { 137 introspect(metadata); 138 return isMBeanCompliant(metadata); 139 } 140 141 private boolean isMBeanClassCompliant(MBeanMetaData metadata) 142 { 143 Logger logger = getLogger(); 146 if (metadata.getMBeanInterface() != null) 147 { 148 boolean isPublic = Modifier.isPublic(metadata.getMBeanInterface().getModifiers()); 149 if (!isPublic && logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean interface is not public"); 150 return isPublic; 151 } 152 return true; 153 } 154 155 private boolean isMBeanTypeCompliant(MBeanMetaData metadata) 156 { 157 Logger logger = getLogger(); 158 159 if (metadata.isMBeanStandard() && metadata.isMBeanDynamic()) 160 { 161 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is both standard and dynamic"); 162 return false; 163 } 164 if (!metadata.isMBeanStandard() && !metadata.isMBeanDynamic()) 165 { 166 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is not standard nor dynamic"); 167 return false; 168 } 169 170 return true; 171 } 172 173 private boolean isMBeanInfoCompliant(MBeanMetaData metadata) 174 { 175 Logger logger = getLogger(); 176 177 MBeanInfo info = metadata.getMBeanInfo(); 178 if (info == null) 179 { 180 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBeanInfo is null"); 181 return false; 182 } 183 if (info.getClassName() == null) 184 { 185 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBeanInfo.getClassName() is null"); 186 return false; 187 } 188 return true; 189 } 190 191 private void introspectType(MBeanMetaData metadata) 192 { 193 if (metadata.isMBeanStandard()) 195 { 196 introspectStandardMBean(metadata); 197 return; 198 } 199 200 if (metadata.getMBean() instanceof DynamicMBean ) 201 { 202 metadata.setMBeanDynamic(true); 203 return; 204 } 205 else 206 { 207 metadata.setMBeanDynamic(false); 208 } 210 211 introspectStandardMBean(metadata); 213 } 214 215 private void introspectStandardMBean(MBeanMetaData metadata) 216 { 217 Class management = metadata.getMBeanInterface(); 218 if (management != null) 219 { 220 if (management.isInstance(metadata.getMBean())) 222 { 223 metadata.setMBeanInvoker(createInvoker(metadata)); 224 return; 225 } 226 else 227 { 228 metadata.setMBeanStandard(false); 230 metadata.setMBeanInterface(null); 231 metadata.setMBeanInvoker(null); 232 return; 233 } 234 } 235 else 236 { 237 Class cls = metadata.getMBean().getClass(); 238 for (Class c = cls; c != null; c = c.getSuperclass()) 239 { 240 Class [] intfs = c.getInterfaces(); 241 for (int i = 0; i < intfs.length; ++i) 242 { 243 Class intf = intfs[i]; 244 245 if (implementsMBean(c.getName(), intf.getName())) 246 { 247 metadata.setMBeanStandard(true); 249 metadata.setMBeanInterface(intf); 250 metadata.setMBeanInvoker(createInvoker(metadata)); 251 return; 252 } 253 } 254 } 255 256 metadata.setMBeanStandard(false); 258 metadata.setMBeanInterface(null); 259 metadata.setMBeanInvoker(null); 260 } 261 } 262 263 private void introspectMBeanInfo(MBeanMetaData metadata) 264 { 265 if (metadata.isMBeanDynamic()) 266 { 267 metadata.setMBeanInfo(getDynamicMBeanInfo(metadata)); 268 } 269 else if (metadata.isMBeanStandard()) 270 { 271 metadata.setMBeanInfo(createStandardMBeanInfo(metadata)); 272 } 273 else 274 { 275 metadata.setMBeanInfo(null); 277 } 278 } 279 280 private MBeanInfo getDynamicMBeanInfo(MBeanMetaData metadata) 281 { 282 Logger logger = getLogger(); 283 284 MBeanInfo info = null; 285 286 try 287 { 288 info = ((DynamicMBean )metadata.getMBean()).getMBeanInfo(); 289 } 290 catch (Exception x) 291 { 292 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("getMBeanInfo threw: " + x.toString()); 293 } 294 295 if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Dynamic MBeanInfo is: " + info); 296 297 if (info == null) 298 { 299 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBeanInfo cannot be null"); 300 return null; 301 } 302 303 return info; 304 } 305 306 private MBeanInfo createStandardMBeanInfo(MBeanMetaData metadata) 307 { 308 synchronized (mbeanInfoCache) 309 { 310 MBeanInfo info = (MBeanInfo )mbeanInfoCache.get(metadata.getMBean().getClass()); 311 if (info != null) return info; 312 } 313 314 MBeanDescription description = createMBeanDescription(metadata); 316 317 MBeanConstructorInfo [] ctors = createMBeanConstructorInfo(metadata, description); 318 if (ctors == null) return null; 319 MBeanAttributeInfo [] attrs = createMBeanAttributeInfo(metadata, description); 320 if (attrs == null) return null; 321 MBeanOperationInfo [] opers = createMBeanOperationInfo(metadata, description); 322 if (opers == null) return null; 323 MBeanNotificationInfo [] notifs = createMBeanNotificationInfo(metadata); 324 if (notifs == null) return null; 325 326 MBeanInfo info = new MBeanInfo (metadata.getMBean().getClass().getName(), description.getMBeanDescription(), attrs, ctors, opers, notifs); 327 synchronized (mbeanInfoCache) 328 { 329 mbeanInfoCache.put(metadata.getMBean().getClass(), info); 331 } 332 return info; 333 } 334 335 private MBeanDescription createMBeanDescription(MBeanMetaData metadata) 336 { 337 341 343 Logger logger = getLogger(); 344 if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Looking for standard MBean description..."); 345 346 String descrClassName = metadata.getMBeanInterface().getName() + "Description"; 348 try 350 { 351 Class descrClass = null; 352 ClassLoader loader = metadata.getClassLoader(); 353 if (loader == null) loader = Thread.currentThread().getContextClassLoader(); 354 if (loader.getClass() == MLet .class) 357 descrClass = ((MLet )loader).loadClass(descrClassName, null); 358 else 359 descrClass = loader.loadClass(descrClassName); 360 361 Object descrInstance = descrClass.newInstance(); 362 if (descrInstance instanceof MBeanDescription) 363 { 364 MBeanDescription description = (MBeanDescription)descrInstance; 365 if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Found provided standard MBean description: " + description); 366 return description; 367 } 368 } 369 catch (ClassNotFoundException ignored) 370 { 371 } 372 catch (InstantiationException ignored) 373 { 374 } 375 catch (IllegalAccessException ignored) 376 { 377 } 378 379 MBeanDescription description = DEFAULT_DESCRIPTION; 380 if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Cannot find standard MBean description, using default: " + description); 381 return description; 382 } 383 384 private MBeanOperationInfo [] createMBeanOperationInfo(MBeanMetaData metadata, MBeanDescription description) 385 { 386 ArrayList operations = new ArrayList (); 387 388 Method [] methods = metadata.getMBeanInterface().getMethods(); 389 for (int j = 0; j < methods.length; ++j) 390 { 391 Method method = methods[j]; 392 if (!Utils.isAttributeGetter(method) && !Utils.isAttributeSetter(method)) 393 { 394 String descr = description == null ? null : description.getOperationDescription(method); 395 Class [] params = method.getParameterTypes(); 396 int paramsNumber = params.length; 397 MBeanParameterInfo [] paramsInfo = paramsNumber == 0 ? EMPTY_PARAMETERS : new MBeanParameterInfo [paramsNumber]; 398 for (int k = 0; k < paramsNumber; ++k) 399 { 400 Class param = params[k]; 401 String paramName = description == null ? null : description.getOperationParameterName(method, k); 402 String paramDescr = description == null ? null : description.getOperationParameterDescription(method, k); 403 paramsInfo[k] = new MBeanParameterInfo (paramName, param.getName(), paramDescr); 404 } 405 MBeanOperationInfo info = new MBeanOperationInfo (method.getName(), descr, paramsInfo, method.getReturnType().getName(), MBeanOperationInfo.UNKNOWN); 406 operations.add(info); 407 } 408 } 409 410 int opersNumber = operations.size(); 411 return opersNumber == 0 ? EMPTY_OPERATIONS : (MBeanOperationInfo [])operations.toArray(new MBeanOperationInfo [opersNumber]); 412 } 413 414 private MBeanAttributeInfo [] createMBeanAttributeInfo(MBeanMetaData metadata, MBeanDescription description) 415 { 416 Logger logger = getLogger(); 417 418 HashMap attributes = new HashMap (); 419 HashMap getterNames = new HashMap (); 420 421 Method [] methods = metadata.getMBeanInterface().getMethods(); 422 for (int j = 0; j < methods.length; ++j) 423 { 424 Method method = methods[j]; 425 if (Utils.isAttributeGetter(method)) 426 { 427 String name = method.getName(); 428 boolean isIs = name.startsWith("is"); 429 430 String attribute = null; 431 if (isIs) 432 attribute = name.substring(2); 433 else 434 attribute = name.substring(3); 435 436 String descr = description == null ? null : description.getAttributeDescription(attribute); 437 438 MBeanAttributeInfo info = (MBeanAttributeInfo )attributes.get(attribute); 439 440 if (info != null) 441 { 442 if (!info.getType().equals(method.getReturnType().getName())) 445 { 446 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is not compliant: has overloaded attribute " + attribute); 447 return null; 448 } 449 else 450 { 451 if (getterNames.get(name) != null) 453 { 454 continue; 457 } 458 459 if (info.isReadable()) 462 { 463 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is not compliant: has overloaded attribute " + attribute); 464 return null; 465 } 466 467 info = new MBeanAttributeInfo (attribute, info.getType(), info.getDescription(), true, info.isWritable(), isIs); 469 } 470 } 471 else 472 { 473 info = new MBeanAttributeInfo (attribute, method.getReturnType().getName(), descr, true, false, isIs); 474 } 475 476 attributes.put(attribute, info); 478 getterNames.put(name, method); 479 } 480 else if (Utils.isAttributeSetter(method)) 481 { 482 String name = method.getName(); 483 String attribute = name.substring(3); 484 485 String descr = description == null ? null : description.getAttributeDescription(attribute); 486 487 MBeanAttributeInfo info = (MBeanAttributeInfo )attributes.get(attribute); 488 489 if (info != null) 490 { 491 if (!info.getType().equals(method.getParameterTypes()[0].getName())) 494 { 495 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is not compliant: has overloaded attribute " + attribute); 496 return null; 497 } 498 else 499 { 500 info = new MBeanAttributeInfo (info.getName(), info.getType(), info.getDescription(), info.isReadable(), true, info.isIs()); 502 } 503 } 504 else 505 { 506 info = new MBeanAttributeInfo (attribute, method.getParameterTypes()[0].getName(), descr, false, true, false); 507 } 508 509 attributes.put(attribute, info); 511 } 512 } 513 514 int size = attributes.size(); 515 return size == 0 ? EMPTY_ATTRIBUTES : (MBeanAttributeInfo [])attributes.values().toArray(new MBeanAttributeInfo [size]); 516 } 517 518 private MBeanNotificationInfo [] createMBeanNotificationInfo(MBeanMetaData metadata) 519 { 520 MBeanNotificationInfo [] notifs = null; 521 Object mbean = metadata.getMBean(); 522 if (mbean instanceof NotificationBroadcaster ) 523 { 524 notifs = ((NotificationBroadcaster )mbean).getNotificationInfo(); 525 } 526 if (notifs == null || notifs.length == 0) notifs = EMPTY_NOTIFICATIONS; 527 return notifs; 528 } 529 530 private MBeanConstructorInfo [] createMBeanConstructorInfo(MBeanMetaData metadata, MBeanDescription descrs) 531 { 532 Class mbeanClass = metadata.getMBean().getClass(); 533 534 Constructor [] ctors = mbeanClass.getConstructors(); 535 536 int ctorsNumber = ctors.length; 537 MBeanConstructorInfo [] constructors = ctorsNumber == 0 ? EMPTY_CONSTRUCTORS : new MBeanConstructorInfo [ctorsNumber]; 538 for (int i = 0; i < ctorsNumber; ++i) 539 { 540 Constructor constructor = ctors[i]; 541 String descr = descrs == null ? null : descrs.getConstructorDescription(constructor); 542 Class [] params = constructor.getParameterTypes(); 543 int paramsNumber = params.length; 544 MBeanParameterInfo [] paramsInfo = paramsNumber == 0 ? EMPTY_PARAMETERS : new MBeanParameterInfo [paramsNumber]; 545 for (int j = 0; j < paramsNumber; ++j) 546 { 547 Class param = params[j]; 548 String paramName = descrs == null ? null : descrs.getConstructorParameterName(constructor, j); 549 String paramDescr = descrs == null ? null : descrs.getConstructorParameterDescription(constructor, j); 550 paramsInfo[j] = new MBeanParameterInfo (paramName, param.getName(), paramDescr); 551 } 552 553 String ctorName = constructor.getName(); 554 MBeanConstructorInfo info = new MBeanConstructorInfo (ctorName, descr, paramsInfo); 555 constructors[i] = info; 556 } 557 return constructors; 558 } 559 560 private boolean implementsMBean(String clsName, String intfName) 561 { 562 if (intfName.equals(clsName + "MBean")) return true; 563 564 if (extendedMBeanInterfaces) 565 { 566 568 int clsDot = clsName.lastIndexOf('.'); 570 if (clsDot > 0) clsName = clsName.substring(clsDot + 1); 571 int intfDot = intfName.lastIndexOf('.'); 572 if (intfDot > 0) intfName = intfName.substring(intfDot + 1); 573 if (intfName.equals(clsName + "MBean")) return true; 575 576 int clsDollar = clsName.lastIndexOf('$'); 578 if (clsDollar > 0) clsName = clsName.substring(clsDollar + 1); 579 int intfDollar = intfName.lastIndexOf('$'); 580 if (intfDollar > 0) intfName = intfName.substring(intfDollar + 1); 581 if (intfName.equals(clsName + "MBean")) return true; 583 } 584 585 return false; 587 } 588 589 private MBeanInvoker createInvoker(MBeanMetaData metadata) 590 { 591 MBeanInvoker invoker = null; 592 593 synchronized (mbeanInvokerCache) 594 { 595 invoker = (MBeanInvoker)mbeanInvokerCache.get(metadata.getMBeanInterface()); 596 if (invoker != null) return invoker; 597 } 598 599 Logger logger = getLogger(); 600 601 if (mbeanInvokerClass != null) 602 { 603 if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Custom MBeanInvoker class is: " + mbeanInvokerClass); 604 try 605 { 606 invoker = (MBeanInvoker)Thread.currentThread().getContextClassLoader().loadClass(mbeanInvokerClass).newInstance(); 607 if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Using custom MBeanInvoker: " + invoker); 608 } 609 catch (Exception x) 610 { 611 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Cannot instantiate custom MBeanInvoker, using default", x); 612 } 613 } 614 615 if (invoker == null) 616 { 617 if (bcelAvailable) 618 { 619 invoker = BCELMBeanInvoker.create(metadata); 620 if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Using default BCEL MBeanInvoker for MBean " + metadata.getObjectName() + ", " + invoker); 621 } 622 else 623 { 624 invoker = new CachingReflectionMBeanInvoker(); 625 if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Using default Reflection MBeanInvoker for MBean " + metadata.getObjectName() + ", " + invoker); 626 } 627 } 628 629 synchronized (mbeanInvokerCache) 630 { 631 mbeanInvokerCache.put(metadata.getMBeanInterface(), invoker); 633 } 634 return invoker; 635 } 636 637 private Logger getLogger() 638 { 639 return Log.getLogger(getClass().getName()); 640 } 641 } 642 | Popular Tags |