1 29 30 package com.caucho.jmx; 31 32 import javax.management.*; 33 import javax.management.openmbean.ArrayType ; 34 import javax.management.openmbean.OpenType ; 35 import javax.management.openmbean.SimpleType ; 36 import java.lang.annotation.Annotation ; 37 import java.lang.ref.SoftReference ; 38 import java.lang.reflect.InvocationTargetException ; 39 import java.lang.reflect.Method ; 40 import java.lang.reflect.Modifier ; 41 import java.util.ArrayList ; 42 import java.util.Arrays ; 43 import java.util.Collections ; 44 import java.util.Comparator ; 45 import java.util.HashMap ; 46 import java.util.WeakHashMap ; 47 import java.util.logging.Level ; 48 import java.util.logging.Logger ; 49 50 53 public class IntrospectionMBean implements DynamicMBean { 54 private static final Logger log 55 = Logger.getLogger(IntrospectionMBean.class.getName()); 56 57 private static final Class [] NULL_ARG = new Class [0]; 58 59 private static final Class _descriptionAnn; 60 private static final Class _nameAnn; 61 62 private static final WeakHashMap <Class ,SoftReference <MBeanInfo>> _cachedInfo 63 = new WeakHashMap <Class ,SoftReference <MBeanInfo>>(); 64 65 private final Object _impl; 66 private final Class _mbeanInterface; 67 private final boolean _isLowercaseAttributeNames; 68 69 private final MBeanInfo _mbeanInfo; 70 71 private final HashMap <String ,OpenModelMethod> _attrGetMap 72 = new HashMap <String ,OpenModelMethod>(); 73 74 77 public IntrospectionMBean(Object impl, Class mbeanInterface) 78 throws NotCompliantMBeanException 79 { 80 this(impl, mbeanInterface, false); 81 } 82 83 89 public IntrospectionMBean(Object impl, 90 Class mbeanInterface, 91 boolean isLowercaseAttributeNames) 92 throws NotCompliantMBeanException 93 { 94 if (impl == null) 95 throw new NullPointerException (); 96 97 _mbeanInterface = mbeanInterface; 98 _isLowercaseAttributeNames = isLowercaseAttributeNames; 99 100 _mbeanInfo = introspect(impl, mbeanInterface, isLowercaseAttributeNames); 101 _impl = impl; 102 103 } 104 105 108 public Object getImplementation() 109 { 110 return _impl; 111 } 112 113 116 public Object getAttribute(String attribute) 117 throws AttributeNotFoundException, MBeanException, ReflectionException 118 { 119 try { 120 OpenModelMethod method = getGetMethod(attribute); 121 122 if (method != null) 123 return method.invoke(_impl, (Object []) null); 124 else 125 throw new AttributeNotFoundException(attribute); 126 } catch (IllegalAccessException e) { 127 throw new MBeanException(e); 128 } catch (InvocationTargetException e) { 129 if (e.getCause() instanceof Exception ) 130 throw new ReflectionException((Exception ) e.getCause()); 131 else 132 throw (Error ) e.getCause(); 133 } catch (Throwable e) { 134 throw new RuntimeException (e); 135 } 136 } 137 138 141 public void setAttribute(Attribute attribute) 142 throws AttributeNotFoundException, InvalidAttributeValueException, 143 MBeanException, ReflectionException 144 { 145 try { 146 Method method = getSetMethod(attribute.getName(), attribute.getValue()); 147 148 if (method != null) 149 method.invoke(_impl, new Object [] { attribute.getValue() }); 150 else 151 throw new AttributeNotFoundException(attribute.getName()); 152 } catch (IllegalAccessException e) { 153 throw new MBeanException(e); 154 } catch (InvocationTargetException e) { 155 throw new MBeanException(e); 156 } catch (Throwable e) { 157 throw new RuntimeException (e.toString()); 158 } 159 } 160 161 164 public AttributeList getAttributes(String []attributes) 165 { 166 AttributeList list = new AttributeList(); 167 168 for (int i = 0; i < attributes.length; i++) { 169 try { 170 OpenModelMethod method = getGetMethod(attributes[i]); 171 172 if (method != null) { 173 Object value = method.invoke(_impl, (Object []) null); 174 175 list.add(new Attribute(attributes[i], value)); 176 } 177 } catch (Throwable e) { 178 log.log(Level.WARNING, e.toString(), e); 179 } 180 } 181 182 return list; 183 } 184 185 188 public AttributeList setAttributes(AttributeList attributes) 189 { 190 AttributeList list = new AttributeList(); 191 192 for (int i = 0; i < attributes.size(); i++) { 193 try { 194 Attribute attr = (Attribute) attributes.get(i); 195 Method method = getSetMethod(attr.getName(), attr.getValue()); 196 197 if (method != null) { 198 method.invoke(_impl, new Object [] { attr.getValue() }); 199 list.add(new Attribute(attr.getName(), attr.getValue())); 200 } 201 } catch (Throwable e) { 202 log.log(Level.WARNING, e.toString(), e); 203 } 204 } 205 206 return list; 207 } 208 209 212 private OpenModelMethod getGetMethod(String name) 213 { 214 OpenModelMethod method = _attrGetMap.get(name); 215 216 if (method != null) 217 return method; 218 219 method = createGetMethod(name); 220 221 if (method != null) 222 _attrGetMap.put(name, method); 223 224 return method; 225 } 226 227 230 private OpenModelMethod createGetMethod(String name) 231 { 232 String methodName; 233 234 if (_isLowercaseAttributeNames) { 235 StringBuilder builder = new StringBuilder (name); 236 builder.setCharAt(0, Character.toUpperCase(builder.charAt(0))); 237 methodName = builder.toString(); 238 } 239 else 240 methodName = name; 241 242 String getName = "get" + methodName; 243 String isName = "is" + methodName; 244 245 Method []methods = _mbeanInterface.getMethods(); 246 247 for (int i = 0; i < methods.length; i++) { 248 if (! methods[i].getName().equals(getName) && 249 ! methods[i].getName().equals(isName)) 250 continue; 251 252 Class []args = methods[i].getParameterTypes(); 253 254 if (args.length == 0 && 255 ! methods[i].getReturnType().equals(void.class)) { 256 Class retType = methods[i].getReturnType(); 257 258 return new OpenModelMethod(methods[i], createUnmarshall(retType)); 259 } 260 } 261 262 return null; 263 } 264 265 268 private Unmarshall createUnmarshall(Class cl) 269 { 270 Unmarshall mbean = getMBeanObjectName(cl); 271 272 if (mbean != null) 273 return mbean; 274 275 if (cl.isArray()) { 276 Class componentType = cl.getComponentType(); 277 278 mbean = getMBeanObjectName(componentType); 279 280 if (mbean != null) 281 return new UnmarshallArray(ObjectName.class, mbean); 282 } 283 284 return Unmarshall.IDENTITY; 285 } 286 287 private Unmarshall getMBeanObjectName(Class cl) 288 { 289 try { 290 Method method = cl.getMethod("getObjectName"); 291 292 if (method != null 293 && ObjectName.class.equals(method.getReturnType())) 294 return new UnmarshallMBean(method); 295 } catch (NoSuchMethodException e) { 296 } catch (Exception e) { 297 log.log(Level.FINER, e.toString(), e); 298 } 299 300 return null; 301 } 302 303 306 private Method getSetMethod(String name, Object value) 307 { 308 String methodName; 309 310 if (_isLowercaseAttributeNames) { 311 StringBuilder builder = new StringBuilder (name); 312 builder.setCharAt(0, Character.toUpperCase(builder.charAt(0))); 313 methodName = builder.toString(); 314 } 315 else 316 methodName = name; 317 318 String setName = "set" + methodName; 319 320 Method []methods = _mbeanInterface.getMethods(); 321 322 for (int i = 0; i < methods.length; i++) { 323 if (! methods[i].getName().equals(setName)) 324 continue; 325 326 Class []args = methods[i].getParameterTypes(); 327 328 if (args.length != 1) 329 continue; 330 331 335 336 return methods[i]; 337 } 338 339 return null; 340 } 341 342 345 public Object invoke(String actionName, 346 Object []params, 347 String []signature) 348 throws MBeanException, ReflectionException 349 { 350 try { 351 Method []methods = _mbeanInterface.getMethods(); 352 353 int length = 0; 354 if (signature != null) 355 length = signature.length; 356 if (params != null) 357 length = params.length; 358 359 for (int i = 0; i < methods.length; i++) { 360 if (! methods[i].getName().equals(actionName)) 361 continue; 362 363 Class []args = methods[i].getParameterTypes(); 364 365 if (args.length != length) 366 continue; 367 368 boolean isMatch = true; 369 for (int j = length - 1; j >= 0; j--) { 370 if (! args[j].getName().equals(signature[j])) 371 isMatch = false; 372 } 373 374 if (isMatch) 375 return methods[i].invoke(_impl, params); 376 } 377 378 if (actionName.equals("hashCode") && signature.length == 0) 379 return _impl.hashCode(); 380 else if (actionName.equals("toString") && signature.length == 0) 381 return _impl.toString(); 382 else 383 return null; 384 } catch (IllegalAccessException e) { 385 throw new MBeanException(e); 386 } catch (InvocationTargetException e) { 387 if (e.getCause() instanceof Exception ) 388 throw new ReflectionException((Exception ) e.getCause()); 389 else 390 throw (Error ) e.getCause(); 391 } 392 } 393 394 397 public MBeanInfo getMBeanInfo() 398 { 399 return _mbeanInfo; 400 } 401 402 static MBeanInfo introspect(Object obj, Class cl, boolean isLowercaseAttributeNames) 403 throws NotCompliantMBeanException 404 { 405 try { 406 SoftReference <MBeanInfo> infoRef = _cachedInfo.get(cl); 407 MBeanInfo info = null; 408 409 if (infoRef != null && (info = infoRef.get()) != null) 410 return info; 411 412 String className = cl.getName(); 413 414 HashMap <String ,MBeanAttributeInfo> attributes 415 = new HashMap <String ,MBeanAttributeInfo>(); 416 417 ArrayList <MBeanConstructorInfo> constructors 418 = new ArrayList <MBeanConstructorInfo>(); 419 420 ArrayList <MBeanOperationInfo> operations 421 = new ArrayList <MBeanOperationInfo>(); 422 423 Method []methods = cl.getMethods(); 424 for (int i = 0; i < methods.length; i++) { 425 Method method = methods[i]; 426 427 if (method.getDeclaringClass() == Object .class) 428 continue; 429 430 if (Modifier.isStatic(method.getModifiers())) 431 continue; 432 433 String methodName = method.getName(); 434 Class []args = method.getParameterTypes(); 435 Class retType = method.getReturnType(); 436 437 if (methodName.startsWith("get") && args.length == 0 && 438 ! retType.equals(void.class)) { 439 Method getter = method; 440 String name = methodName.substring(3); 441 442 Method setter = getSetter(methods, name, retType); 443 444 String attributeName; 445 446 if (isLowercaseAttributeNames) { 447 StringBuilder builder = new StringBuilder (name); 448 builder.setCharAt(0, Character.toLowerCase(builder.charAt(0))); 449 attributeName = builder.toString(); 450 } 451 else 452 attributeName = name; 453 454 Class type = method.getReturnType(); 455 456 MBeanAttributeInfo attr; 457 458 attr = new MBeanAttributeInfo(attributeName, 459 getDescription(method), 460 getter, 461 setter); 462 463 477 478 if (attributes.get(attributeName) == null) 479 attributes.put(attributeName, attr); 480 } 481 else if (methodName.startsWith("is") && args.length == 0 && 482 (retType.equals(boolean.class) || 483 retType.equals(Boolean .class))) { 484 Method getter = method; 485 String name = methodName.substring(2); 486 487 Method setter = getSetter(methods, name, retType); 488 489 String attributeName; 490 491 if (isLowercaseAttributeNames) { 492 StringBuilder builder = new StringBuilder (name); 493 builder.setCharAt(0, Character.toLowerCase(builder.charAt(0))); 494 attributeName = builder.toString(); 495 } 496 else 497 attributeName = name; 498 499 if (attributes.get(attributeName) == null) { 500 attributes.put(attributeName, 501 new MBeanAttributeInfo(attributeName, 502 getDescription(method), 503 getter, 504 setter)); 505 } 506 } 507 else if (methodName.startsWith("set") && args.length == 1) { 508 Method setter = method; 509 String name = methodName.substring(3); 510 511 Method getter = getGetter(methods, name, args[0]); 512 513 if (getter == null) { 514 String attributeName; 515 516 if (isLowercaseAttributeNames) { 517 StringBuilder builder = new StringBuilder (name); 518 builder.setCharAt(0, Character.toLowerCase(builder.charAt(0))); 519 attributeName = builder.toString(); 520 } 521 else 522 attributeName = name; 523 524 if (attributes.get(attributeName) == null) { 525 attributes.put(attributeName, 526 new MBeanAttributeInfo(attributeName, 527 getDescription(method), 528 null, 529 setter)); 530 } 531 } 532 } 533 else { 534 operations.add(new MBeanOperationInfo(getName(method), 535 getDescription(method), 536 getSignature(method), 537 method.getReturnType().getName(), 538 MBeanOperationInfo.UNKNOWN)); 539 } 540 } 541 542 ArrayList <MBeanNotificationInfo> notifications 543 = new ArrayList <MBeanNotificationInfo>(); 544 545 if (obj instanceof NotificationBroadcaster) { 546 NotificationBroadcaster broadcaster; 547 broadcaster = (NotificationBroadcaster) obj; 548 549 MBeanNotificationInfo[] notifs = broadcaster.getNotificationInfo(); 550 551 if (notifs != null) { 552 for (int i = 0; i < notifs.length; i++) { 553 MBeanNotificationInfo notif = notifs[i]; 554 555 notifications.add((MBeanNotificationInfo) notifs[i].clone()); 556 } 557 } 558 } 559 560 Collections.sort(notifications, MBEAN_FEATURE_INFO_COMPARATOR); 561 562 MBeanAttributeInfo []attrArray = new MBeanAttributeInfo[attributes.size()]; 563 attributes.values().toArray(attrArray); 564 Arrays.sort(attrArray, MBEAN_FEATURE_INFO_COMPARATOR); 565 566 MBeanConstructorInfo []conArray = new MBeanConstructorInfo[constructors.size()]; 567 constructors.toArray(conArray); 568 569 MBeanOperationInfo []opArray = new MBeanOperationInfo[operations.size()]; 570 operations.toArray(opArray); 571 Arrays.sort(opArray, MBEAN_FEATURE_INFO_COMPARATOR); 572 MBeanNotificationInfo []notifArray = new MBeanNotificationInfo[notifications.size()]; 573 notifications.toArray(notifArray); 574 Arrays.sort(notifArray, MBEAN_FEATURE_INFO_COMPARATOR); 575 576 MBeanInfo modelInfo; 577 578 modelInfo = new MBeanInfo(cl.getName(), 579 getDescription(cl), 580 attrArray, 581 conArray, 582 opArray, 583 notifArray); 584 591 592 info = modelInfo; 593 594 _cachedInfo.put(cl, new SoftReference <MBeanInfo>(info)); 595 596 return info; 597 } catch (Exception e) { 598 NotCompliantMBeanException exn; 599 exn = new NotCompliantMBeanException(String.valueOf(e)); 600 601 exn.initCause(e); 602 603 throw exn; 604 } 605 } 606 607 610 static Method getSetter(Method []methods, String property, Class type) 611 { 612 String name = "set" + property; 613 614 for (int i = 0; i < methods.length; i++) { 615 if (! methods[i].getName().equals(name)) 616 continue; 617 618 Class []args = methods[i].getParameterTypes(); 619 620 if (args.length != 1 || ! args[0].equals(type)) 621 continue; 622 623 return methods[i]; 624 } 625 626 return null; 627 } 628 629 632 static Method getGetter(Method []methods, String property, Class type) 633 { 634 String getName = "get" + property; 635 String isName = "is" + property; 636 637 for (int i = 0; i < methods.length; i++) { 638 if (! methods[i].getName().equals(getName) && 639 ! methods[i].getName().equals(isName)) 640 continue; 641 642 Class []args = methods[i].getParameterTypes(); 643 644 if (args.length != 0) 645 continue; 646 647 Class retType = methods[i].getReturnType(); 648 649 if (! retType.equals(type)) 650 continue; 651 652 return methods[i]; 653 } 654 655 return null; 656 } 657 658 661 static String getDescription(Class cl) 662 { 663 try { 664 Description desc = (Description) cl.getAnnotation(_descriptionAnn); 665 666 if (desc != null) 667 return desc.value(); 668 else 669 return ""; 670 } catch (Throwable e) { 671 return ""; 672 } 673 } 674 675 678 static String getDescription(Method method) 679 { 680 try { 681 Description desc = (Description) method.getAnnotation(_descriptionAnn); 682 683 if (desc != null) 684 return desc.value(); 685 else 686 return ""; 687 } catch (Throwable e) { 688 return ""; 689 } 690 } 691 692 695 static String getName(Method method) 696 { 697 try { 698 Name name = (Name) method.getAnnotation(_nameAnn); 699 700 if (name != null) 701 return name.value(); 702 else 703 return method.getName(); 704 } catch (Throwable e) { 705 return method.getName(); 706 } 707 } 708 709 private static MBeanParameterInfo[] getSignature(Method method) 710 { 711 Class [] params = method.getParameterTypes(); 712 MBeanParameterInfo[] paramInfos = new MBeanParameterInfo[params.length]; 713 714 for (int i = 0; i < params.length; i++) { 715 Class cl = params[i]; 716 717 String name = getName(method, i); 718 String description = getDescription(method, i); 719 720 paramInfos[i] = new MBeanParameterInfo(name, cl.getName(), description); 721 } 722 723 return paramInfos; 724 } 725 726 private static String getName(Method method, int i) 727 { 728 try { 729 for (Annotation ann : method.getParameterAnnotations()[i]) { 730 if (ann instanceof Name) 731 return ((Name) ann).value(); 732 } 733 } catch (Throwable e) { 734 log.log(Level.FINER, e.toString(), e); 735 } 736 737 return "p" + i; 738 } 739 740 private static String getDescription(Method method, int i) 741 { 742 try { 743 for (Annotation ann : method.getParameterAnnotations()[i]) { 744 if (ann instanceof Description) 745 return ((Description) ann).value(); 746 } 747 } catch (Throwable e) { 748 log.log(Level.FINER, e.toString(), e); 749 } 750 751 return ""; 752 } 753 754 private static String getTypeName(Class type) 755 { 756 if (type.isArray()) 757 return getTypeName(type.getComponentType()) + "[]"; 758 else 759 return type.getName(); 760 } 761 762 private static OpenType getOpenType(Class type) 763 { 764 try { 765 if (type.isArray()) { 766 OpenType component = getOpenType(type.getComponentType()); 767 768 if (component != null) 769 return new ArrayType (1, component); 770 else 771 return null; 772 } 773 else if (type.getName().endsWith("MXBean") 774 || type.getName().endsWith("MBean")) 775 return SimpleType.OBJECTNAME; 776 else if (void.class.equals(type)) 777 return SimpleType.VOID; 778 else if (boolean.class.equals(type) || Boolean .class.equals(type)) 779 return SimpleType.BOOLEAN; 780 else if (byte.class.equals(type) || Byte .class.equals(type)) 781 return SimpleType.BYTE; 782 else if (short.class.equals(type) || Short .class.equals(type)) 783 return SimpleType.SHORT; 784 else if (int.class.equals(type) || Integer .class.equals(type)) 785 return SimpleType.INTEGER; 786 else if (long.class.equals(type) || Long .class.equals(type)) 787 return SimpleType.LONG; 788 else if (float.class.equals(type) || Float .class.equals(type)) 789 return SimpleType.FLOAT; 790 else if (double.class.equals(type) || Double .class.equals(type)) 791 return SimpleType.DOUBLE; 792 else if (String .class.equals(type)) 793 return SimpleType.STRING; 794 else if (char.class.equals(type) || Character .class.equals(type)) 795 return SimpleType.CHARACTER; 796 else if (java.util.Date .class.equals(type)) 797 return SimpleType.DATE; 798 else if (java.util.Calendar .class.equals(type)) 799 return SimpleType.DATE; 800 else 801 return null; } catch (Exception e) { 803 log.log(Level.FINER, e.toString(), e); 804 805 return null; 806 } 807 } 808 809 private static Class findClass(String name) 810 { 811 try { 812 return Class.forName(name); 813 } catch (Throwable e) { 814 return null; 815 } 816 } 817 818 private static final Comparator <MBeanFeatureInfo> MBEAN_FEATURE_INFO_COMPARATOR 819 = new Comparator <MBeanFeatureInfo>() { 820 821 public int compare(MBeanFeatureInfo o1, MBeanFeatureInfo o2) 822 { 823 return o1.getName().compareTo(o2.getName()); 824 } 825 }; 826 827 static { 828 _descriptionAnn = findClass("com.caucho.jmx.Description"); 829 _nameAnn = findClass("com.caucho.jmx.Name"); 830 } 831 } 832 | Popular Tags |