1 7 8 package com.sun.jmx.mbeanserver; 9 10 import static com.sun.jmx.mbeanserver.Util.*; 11 12 import com.sun.jmx.remote.util.EnvHelp; 13 14 import java.beans.ConstructorProperties ; 15 import java.io.InvalidObjectException ; 16 import java.lang.ref.WeakReference ; 17 import java.lang.reflect.Array ; 18 import java.lang.reflect.Constructor ; 19 import java.lang.reflect.Field ; 20 import java.lang.reflect.GenericArrayType ; 21 import java.lang.reflect.Method ; 22 import java.lang.reflect.Modifier ; 23 import java.lang.reflect.ParameterizedType ; 24 import java.lang.reflect.Proxy ; 25 import java.lang.reflect.Type ; 26 import java.util.Arrays ; 27 import java.util.ArrayList ; 28 import java.util.BitSet ; 29 import java.util.Collection ; 30 import java.util.Comparator ; 31 import java.util.HashSet ; 32 import java.util.List ; 33 import java.util.Map ; 34 import java.util.Set ; 35 import java.util.SortedMap ; 36 import java.util.SortedSet ; 37 import java.util.TreeSet ; 38 import java.util.WeakHashMap ; 39 import javax.management.JMX ; 40 import javax.management.ObjectName ; 41 import javax.management.openmbean.ArrayType ; 42 import javax.management.openmbean.CompositeData ; 43 import javax.management.openmbean.CompositeDataInvocationHandler ; 44 import javax.management.openmbean.CompositeDataSupport ; 45 import javax.management.openmbean.CompositeDataView ; 46 import javax.management.openmbean.CompositeType ; 47 import javax.management.openmbean.OpenDataException ; 48 import javax.management.openmbean.OpenType ; 49 import javax.management.openmbean.SimpleType ; 50 import javax.management.openmbean.TabularData ; 51 import javax.management.openmbean.TabularDataSupport ; 52 import javax.management.openmbean.TabularType ; 53 import static javax.management.openmbean.SimpleType .*; 54 55 96 public abstract class OpenConverter { 97 private OpenConverter(Type targetType, OpenType openType, 98 Class openClass) { 99 this.targetType = targetType; 100 this.openType = openType; 101 this.openClass = openClass; 102 } 103 104 105 public final Object fromOpenValue(MXBeanLookup lookup, Object value) 106 throws InvalidObjectException { 107 if (value == null) 108 return null; 109 else 110 return fromNonNullOpenValue(lookup, value); 111 } 112 113 abstract Object fromNonNullOpenValue(MXBeanLookup lookup, Object value) 114 throws InvalidObjectException ; 115 116 118 void checkReconstructible() throws InvalidObjectException { 119 } 121 122 123 final Object toOpenValue(MXBeanLookup lookup, Object value) 124 throws OpenDataException { 125 if (value == null) 126 return null; 127 else 128 return toNonNullOpenValue(lookup, value); 129 } 130 131 abstract Object toNonNullOpenValue(MXBeanLookup lookup, Object value) 132 throws OpenDataException ; 133 134 136 boolean isIdentity() { 137 return false; 138 } 139 140 final Type getTargetType() { 141 return targetType; 142 } 143 144 final OpenType getOpenType() { 145 return openType; 146 } 147 148 151 final Class getOpenClass() { 152 return openClass; 153 } 154 155 private final Type targetType; 156 private final OpenType openType; 157 private final Class openClass; 158 159 private static final class ConverterMap 160 extends WeakHashMap <Type , WeakReference <OpenConverter>> {} 161 162 private static final ConverterMap converterMap = new ConverterMap(); 163 164 166 private static final List <OpenConverter> permanentConverters = newList(); 167 168 private static synchronized OpenConverter getConverter(Type type) { 169 if (type instanceof GenericArrayType ) { 171 Type component = ((GenericArrayType ) type).getGenericComponentType(); 172 if (component instanceof Class ) 173 type = Array.newInstance((Class <?>) component, 0).getClass(); 174 } 175 176 WeakReference <OpenConverter> wr = converterMap.get(type); 177 return (wr == null) ? null : wr.get(); 178 } 179 180 private static synchronized void putConverter(Type type, 181 OpenConverter conv) { 182 WeakReference <OpenConverter> wr = 183 new WeakReference <OpenConverter>(conv); 184 converterMap.put(type, wr); 185 } 186 187 private static synchronized void putPermanentConverter(Type type, 188 OpenConverter conv) { 189 putConverter(type, conv); 190 permanentConverters.add(conv); 191 } 192 193 static { 194 195 196 final OpenType [] simpleTypes = { 197 BIGDECIMAL, BIGINTEGER, BOOLEAN, BYTE, CHARACTER, DATE, 198 DOUBLE, FLOAT, INTEGER, LONG, OBJECTNAME, SHORT, STRING, 199 VOID, 200 }; 201 202 for (int i = 0; i < simpleTypes.length; i++) { 203 final OpenType t = simpleTypes[i]; 204 Class c; 205 try { 206 c = Class.forName(t.getClassName(), false, 207 ObjectName .class.getClassLoader()); 208 } catch (ClassNotFoundException e) { 209 throw new Error (e); 211 } 212 final OpenConverter conv = new IdentityConverter(c, t, c); 213 putPermanentConverter(c, conv); 214 215 if (c.getName().startsWith("java.lang.")) { 216 try { 217 final Field typeField = c.getField("TYPE"); 218 final Class primitiveType = (Class ) typeField.get(null); 219 final OpenConverter primitiveConv = 220 new IdentityConverter(primitiveType, t, primitiveType); 221 putPermanentConverter(primitiveType, 222 primitiveConv); 223 if (primitiveType != void.class) { 224 final Class primitiveArrayType = 225 Array.newInstance(primitiveType, 0).getClass(); 226 final OpenType primitiveArrayOpenType = 227 ArrayType.getPrimitiveArrayType(primitiveArrayType); 228 final OpenConverter primitiveArrayConv = 229 new IdentityConverter(primitiveArrayType, 230 primitiveArrayOpenType, 231 primitiveArrayType); 232 putPermanentConverter(primitiveArrayType, 233 primitiveArrayConv); 234 } 235 } catch (NoSuchFieldException e) { 236 } catch (IllegalAccessException e) { 238 assert(false); 240 } 241 } 242 } 243 } 244 245 246 public static synchronized OpenConverter toConverter(Type objType) 247 throws OpenDataException { 248 249 if (inProgress.containsKey(objType)) 250 throw new OpenDataException ("Recursive data structure"); 251 252 OpenConverter conv; 253 254 conv = getConverter(objType); 255 if (conv != null) 256 return conv; 257 258 inProgress.put(objType, objType); 259 try { 260 conv = makeConverter(objType); 261 } finally { 262 inProgress.remove(objType); 263 } 264 265 putConverter(objType, conv); 266 return conv; 267 } 268 269 private static OpenConverter makeConverter(Type objType) 270 throws OpenDataException { 271 272 275 if (objType instanceof GenericArrayType ) { 276 Type componentType = 277 ((GenericArrayType ) objType).getGenericComponentType(); 278 return makeArrayOrCollectionConverter(objType, componentType); 279 } else if (objType instanceof Class ) { 280 Class objClass = (Class <?>) objType; 281 if (objClass.isEnum()) { 282 return makeEnumConverter(objClass); 283 } else if (objClass.isArray()) { 284 Type componentType = objClass.getComponentType(); 285 return makeArrayOrCollectionConverter(objClass, componentType); 286 } else if (JMX.isMXBeanInterface(objClass)) { 287 return makeMXBeanConverter(objClass); 288 } else { 289 return makeCompositeConverter(objClass); 290 } 291 } else if (objType instanceof ParameterizedType ) { 292 return makeParameterizedConverter((ParameterizedType ) objType); 293 } else 294 throw new OpenDataException ("Cannot map type: " + objType); 295 } 296 297 private static <T extends Enum <T>> OpenConverter 298 makeEnumConverter(Class <T> enumClass) { 299 return new EnumConverter<T>(enumClass); 300 } 301 302 307 private static OpenConverter 308 makeArrayOrCollectionConverter(Type collectionType, Type elementType) 309 throws OpenDataException { 310 311 final OpenConverter elementConverter = toConverter(elementType); 312 final OpenType elementOpenType = elementConverter.getOpenType(); 313 final ArrayType openType = new ArrayType (1, elementOpenType); 314 final Class elementOpenClass = elementConverter.getOpenClass(); 315 316 final Class openArrayClass; 317 final String openArrayClassName; 318 if (elementOpenClass.isArray()) 319 openArrayClassName = "[" + elementOpenClass.getName(); 320 else 321 openArrayClassName = "[L" + elementOpenClass.getName() + ";"; 322 try { 323 openArrayClass = Class.forName(openArrayClassName); 324 } catch (ClassNotFoundException e) { 325 throw openDataException("Cannot obtain array class", e); 326 } 327 328 if (collectionType instanceof ParameterizedType ) { 329 return new CollectionConverter(collectionType, 330 openType, openArrayClass, 331 elementConverter); 332 } else { 333 if (elementConverter.isIdentity()) { 334 return new IdentityConverter(collectionType, 335 openType, 336 openArrayClass); 337 } else { 338 return new ArrayConverter(collectionType, 339 openType, 340 openArrayClass, 341 elementConverter); 342 } 343 } 344 } 345 346 private static final String [] keyArray = {"key"}; 347 private static final String [] keyValueArray = {"key", "value"}; 348 349 private static OpenConverter 350 makeTabularConverter(Type objType, boolean sortedMap, 351 Type keyType, Type valueType) 352 throws OpenDataException { 353 354 final String objTypeName = objType.toString(); 355 final OpenConverter keyConverter = toConverter(keyType); 356 final OpenConverter valueConverter = toConverter(valueType); 357 final OpenType keyOpenType = keyConverter.getOpenType(); 358 final OpenType valueOpenType = valueConverter.getOpenType(); 359 final CompositeType rowType = 360 new CompositeType (objTypeName, 361 objTypeName, 362 keyValueArray, 363 keyValueArray, 364 new OpenType [] {keyOpenType, valueOpenType}); 365 final TabularType tabularType = 366 new TabularType (objTypeName, objTypeName, rowType, keyArray); 367 return new TabularConverter(objType, sortedMap, tabularType, 368 keyConverter, valueConverter); 369 } 370 371 376 private static OpenConverter 377 makeParameterizedConverter(ParameterizedType objType) throws OpenDataException { 378 379 final Type rawType = objType.getRawType(); 380 381 if (rawType instanceof Class ) { 382 Class c = (Class <?>) rawType; 383 if (c == List .class || c == Set .class || c == SortedSet .class) { 384 Type [] actuals = 385 ((ParameterizedType ) objType).getActualTypeArguments(); 386 assert(actuals.length == 1); 387 if (c == SortedSet .class) 388 mustBeComparable(c, actuals[0]); 389 return makeArrayOrCollectionConverter(objType, actuals[0]); 390 } else { 391 boolean sortedMap = (c == SortedMap .class); 392 if (c == Map .class || sortedMap) { 393 Type [] actuals = 394 ((ParameterizedType ) objType).getActualTypeArguments(); 395 assert(actuals.length == 2); 396 if (sortedMap) 397 mustBeComparable(c, actuals[0]); 398 return makeTabularConverter(objType, sortedMap, 399 actuals[0], actuals[1]); 400 } 401 } 402 } 403 throw new OpenDataException ("Cannot convert type: " + objType); 404 } 405 406 private static OpenConverter makeMXBeanConverter(Type t) 407 throws OpenDataException { 408 return new MXBeanConverter(t); 409 } 410 411 private static OpenConverter makeCompositeConverter(Class c) 412 throws OpenDataException { 413 414 final boolean gcInfoHack = 418 (c.getName().equals("com.sun.management.GcInfo") && 419 c.getClassLoader() == null); 420 421 final List <Method > methods = 422 MBeanAnalyzer.eliminateCovariantMethods(c.getMethods()); 423 final SortedMap <String ,Method > getterMap = newSortedMap(); 424 425 428 for (Method method : methods) { 429 final String propertyName = propertyName(method); 430 431 if (propertyName == null) 432 continue; 433 if (gcInfoHack && propertyName.equals("CompositeType")) 434 continue; 435 436 Method old = 437 getterMap.put(decapitalize(propertyName), 438 method); 439 if (old != null) { 440 final String msg = 441 "Class " + c.getName() + " has method name clash: " + 442 old.getName() + ", " + method.getName(); 443 throw new OpenDataException (msg); 444 } 445 } 446 447 final int nitems = getterMap.size(); 448 449 if (nitems == 0) { 450 throw new OpenDataException ("Can't map " + c.getName() + 451 " to an open data type"); 452 } 453 454 final Method [] getters = new Method [nitems]; 455 final String [] itemNames = new String [nitems]; 456 final OpenType [] openTypes = new OpenType [nitems]; 457 int i = 0; 458 for (Map.Entry <String ,Method > entry : getterMap.entrySet()) { 459 itemNames[i] = entry.getKey(); 460 final Method getter = entry.getValue(); 461 getters[i] = getter; 462 final Type retType = getter.getGenericReturnType(); 463 openTypes[i] = toConverter(retType).getOpenType(); 464 i++; 465 } 466 467 CompositeType compositeType = 468 new CompositeType (c.getName(), 469 c.getName(), 470 itemNames, itemNames, openTypes); 473 474 return new CompositeConverter(c, 475 compositeType, 476 itemNames, 477 getters); 478 } 479 480 486 private static final class IdentityConverter extends OpenConverter { 487 IdentityConverter(Type targetType, OpenType openType, 488 Class openClass) { 489 super(targetType, openType, openClass); 490 } 491 492 boolean isIdentity() { 493 return true; 494 } 495 496 final Object toNonNullOpenValue(MXBeanLookup lookup, Object value) { 497 return value; 498 } 499 500 public final Object fromNonNullOpenValue(MXBeanLookup lookup, Object value) { 501 return value; 502 } 503 } 504 505 private static final class EnumConverter<T extends Enum <T>> 506 extends OpenConverter { 507 508 EnumConverter(Class <T> enumClass) { 509 super(enumClass, SimpleType.STRING, String .class); 510 this.enumClass = enumClass; 511 } 512 513 final Object toNonNullOpenValue(MXBeanLookup lookup, Object value) { 514 return ((Enum ) value).name(); 515 } 516 517 public final Object fromNonNullOpenValue(MXBeanLookup lookup, Object value) 520 throws InvalidObjectException { 521 try { 522 return Enum.valueOf(enumClass, (String ) value); 523 } catch (Exception e) { 524 throw invalidObjectException("Cannot convert to enum: " + 525 value, e); 526 } 527 } 528 529 private final Class <T> enumClass; 530 } 531 532 private static final class ArrayConverter extends OpenConverter { 533 ArrayConverter(Type targetType, 534 ArrayType openArrayType, Class openArrayClass, 535 OpenConverter elementConverter) { 536 super(targetType, openArrayType, openArrayClass); 537 this.elementConverter = elementConverter; 538 } 539 540 final Object toNonNullOpenValue(MXBeanLookup lookup, Object value) 541 throws OpenDataException { 542 Object [] valueArray = (Object []) value; 543 final int len = valueArray.length; 544 final Object [] openArray = (Object []) 545 Array.newInstance(getOpenClass().getComponentType(), len); 546 for (int i = 0; i < len; i++) { 547 openArray[i] = 548 elementConverter.toOpenValue(lookup, valueArray[i]); 549 } 550 return openArray; 551 } 552 553 public final Object fromNonNullOpenValue(MXBeanLookup lookup, Object openValue) 554 throws InvalidObjectException { 555 final Object [] openArray = (Object []) openValue; 556 final Type targetType = getTargetType(); 557 final Object [] valueArray; 558 final Type componentType; 559 if (targetType instanceof GenericArrayType ) { 560 componentType = 561 ((GenericArrayType ) targetType).getGenericComponentType(); 562 } else if (targetType instanceof Class && 563 ((Class <?>) targetType).isArray()) { 564 componentType = ((Class <?>) targetType).getComponentType(); 565 } else { 566 throw new IllegalArgumentException ("Not an array: " + 567 targetType); 568 } 569 valueArray = (Object []) Array.newInstance((Class <?>) componentType, 570 openArray.length); 571 for (int i = 0; i < openArray.length; i++) { 572 valueArray[i] = 573 elementConverter.fromOpenValue(lookup, openArray[i]); 574 } 575 return valueArray; 576 } 577 578 void checkReconstructible() throws InvalidObjectException { 579 elementConverter.checkReconstructible(); 580 } 581 582 585 private final OpenConverter elementConverter; 586 } 587 588 private static final class CollectionConverter extends OpenConverter { 589 CollectionConverter(Type targetType, 590 ArrayType openArrayType, 591 Class openArrayClass, 592 OpenConverter elementConverter) { 593 super(targetType, openArrayType, openArrayClass); 594 this.elementConverter = elementConverter; 595 596 600 Type raw = ((ParameterizedType ) targetType).getRawType(); 601 Class c = (Class <?>) raw; 602 if (c == List .class) 603 collectionClass = ArrayList .class; 604 else if (c == Set .class) 605 collectionClass = HashSet .class; 606 else if (c == SortedSet .class) 607 collectionClass = TreeSet .class; 608 else { assert(false); 610 collectionClass = null; 611 } 612 } 613 614 final Object toNonNullOpenValue(MXBeanLookup lookup, Object value) 615 throws OpenDataException { 616 final Collection valueCollection = (Collection ) value; 617 if (valueCollection instanceof SortedSet ) { 618 Comparator comparator = 619 ((SortedSet ) valueCollection).comparator(); 620 if (comparator != null) { 621 final String msg = 622 "Cannot convert SortedSet with non-null comparator: " + 623 comparator; 624 IllegalArgumentException iae = new IllegalArgumentException (msg); 625 OpenDataException ode = new OpenDataException (msg); 626 ode.initCause(iae); 627 throw ode; 628 } 629 } 630 final Object [] openArray = (Object []) 631 Array.newInstance(getOpenClass().getComponentType(), 632 valueCollection.size()); 633 int i = 0; 634 for (Object o : valueCollection) 635 openArray[i++] = elementConverter.toOpenValue(lookup, o); 636 return openArray; 637 } 638 639 public final Object fromNonNullOpenValue(MXBeanLookup lookup, Object openValue) 640 throws InvalidObjectException { 641 final Object [] openArray = (Object []) openValue; 642 final Collection <Object > valueCollection; 643 try { 644 valueCollection = collectionClass.newInstance(); 645 } catch (Exception e) { 646 throw invalidObjectException("Cannot create collection", e); 647 } 648 for (Object o : openArray) { 649 Object value = elementConverter.fromOpenValue(lookup, o); 650 if (!valueCollection.add(value)) { 651 final String msg = 652 "Could not add " + o + " to " + 653 collectionClass.getName() + 654 " (duplicate set element?)"; 655 throw new InvalidObjectException (msg); 656 } 657 } 658 return valueCollection; 659 } 660 661 void checkReconstructible() throws InvalidObjectException { 662 elementConverter.checkReconstructible(); 663 } 664 665 private final Class <? extends Collection > collectionClass; 666 private final OpenConverter elementConverter; 667 } 668 669 private static final class MXBeanConverter extends OpenConverter { 670 MXBeanConverter(Type intf) { 671 super(intf, SimpleType.OBJECTNAME, ObjectName .class); 672 } 673 674 final Object toNonNullOpenValue(MXBeanLookup lookup, Object value) 675 throws OpenDataException { 676 lookupNotNull(lookup, OpenDataException .class); 677 ObjectName name = lookup.mxbeanToObjectName(value); 678 if (name == null) 679 throw new OpenDataException ("No name for object: " + value); 680 return name; 681 } 682 683 public final Object fromNonNullOpenValue(MXBeanLookup lookup, Object value) 684 throws InvalidObjectException { 685 lookupNotNull(lookup, InvalidObjectException .class); 686 ObjectName name = (ObjectName ) value; 687 Object mxbean = 688 lookup.objectNameToMXBean(name, (Class <?>) getTargetType()); 689 if (mxbean == null) { 690 final String msg = 691 "No MXBean for name: " + name; 692 throw new InvalidObjectException (msg); 693 } 694 return mxbean; 695 } 696 697 private <T extends Exception > void 698 lookupNotNull(MXBeanLookup lookup, Class <T> excClass) 699 throws T { 700 if (lookup == null) { 701 final String msg = 702 "Cannot convert MXBean interface in this context"; 703 T exc; 704 try { 705 Constructor <T> con = excClass.getConstructor(String .class); 706 exc = con.newInstance(msg); 707 } catch (Exception e) { 708 throw new RuntimeException (e); 709 } 710 throw exc; 711 } 712 } 713 } 714 715 private static final class TabularConverter extends OpenConverter { 716 TabularConverter(Type targetType, 717 boolean sortedMap, 718 TabularType tabularType, 719 OpenConverter keyConverter, 720 OpenConverter valueConverter) { 721 super(targetType, tabularType, TabularData .class); 722 this.sortedMap = sortedMap; 723 this.keyConverter = keyConverter; 724 this.valueConverter = valueConverter; 725 } 726 727 final Object toNonNullOpenValue(MXBeanLookup lookup, Object value) 728 throws OpenDataException { 729 final Map <Object , Object > valueMap = (Map <Object , Object >) value; 730 if (valueMap instanceof SortedMap ) { 731 Comparator comparator = ((SortedMap ) valueMap).comparator(); 732 if (comparator != null) { 733 final String msg = 734 "Cannot convert SortedMap with non-null comparator: " + 735 comparator; 736 IllegalArgumentException iae = new IllegalArgumentException (msg); 737 OpenDataException ode = new OpenDataException (msg); 738 ode.initCause(iae); 739 throw ode; 740 } 741 } 742 final TabularType tabularType = (TabularType ) getOpenType(); 743 final TabularData table = new TabularDataSupport (tabularType); 744 final CompositeType rowType = tabularType.getRowType(); 745 for (Map.Entry entry : valueMap.entrySet()) { 746 final Object openKey = 747 keyConverter.toOpenValue(lookup, entry.getKey()); 748 final Object openValue = 749 valueConverter.toOpenValue(lookup, entry.getValue()); 750 final CompositeData row; 751 row = 752 new CompositeDataSupport (rowType, keyValueArray, 753 new Object [] {openKey, 754 openValue}); 755 table.put(row); 756 } 757 return table; 758 } 759 760 public final Object fromNonNullOpenValue(MXBeanLookup lookup, Object openValue) 761 throws InvalidObjectException { 762 final TabularData table = (TabularData ) openValue; 763 final Collection <CompositeData > rows = 764 (Collection <CompositeData >) table.values(); 765 final Map <Object , Object > valueMap = 766 sortedMap ? newSortedMap() : newMap(); 767 for (CompositeData row : rows) { 768 final Object key = 769 keyConverter.fromOpenValue(lookup, row.get("key")); 770 final Object value = 771 valueConverter.fromOpenValue(lookup, row.get("value")); 772 if (valueMap.put(key, value) != null) { 773 final String msg = 774 "Duplicate entry in TabularData: key=" + key; 775 throw new InvalidObjectException (msg); 776 } 777 } 778 return valueMap; 779 } 780 781 void checkReconstructible() throws InvalidObjectException { 782 keyConverter.checkReconstructible(); 783 valueConverter.checkReconstructible(); 784 } 785 786 private final boolean sortedMap; 787 private final OpenConverter keyConverter; 788 private final OpenConverter valueConverter; 789 } 790 791 private static final class CompositeConverter extends OpenConverter { 792 CompositeConverter(Class targetClass, 793 CompositeType compositeType, 794 String [] itemNames, 795 Method [] getters) throws OpenDataException { 796 super(targetClass, compositeType, CompositeData .class); 797 798 assert(itemNames.length == getters.length); 799 800 this.itemNames = itemNames; 801 this.getters = getters; 802 this.getterConverters = new OpenConverter[getters.length]; 803 for (int i = 0; i < getters.length; i++) { 804 Type retType = getters[i].getGenericReturnType(); 805 getterConverters[i] = OpenConverter.toConverter(retType); 806 } 807 } 808 809 final Object toNonNullOpenValue(MXBeanLookup lookup, Object value) 810 throws OpenDataException { 811 CompositeType ct = (CompositeType ) getOpenType(); 812 if (value instanceof CompositeDataView ) 813 return ((CompositeDataView ) value).toCompositeData(ct); 814 if (value == null) 815 return null; 816 817 Object [] values = new Object [getters.length]; 818 for (int i = 0; i < getters.length; i++) { 819 try { 820 Object got = getters[i].invoke(value, (Object []) null); 821 values[i] = getterConverters[i].toOpenValue(lookup, got); 822 } catch (Exception e) { 823 throw openDataException("Error calling getter for " + 824 itemNames[i] + ": " + e, e); 825 } 826 } 827 return new CompositeDataSupport (ct, itemNames, values); 828 } 829 830 834 private synchronized void makeCompositeBuilder() 835 throws InvalidObjectException { 836 if (compositeBuilder != null) 837 return; 838 839 Class targetClass = (Class <?>) getTargetType(); 840 843 CompositeBuilder[][] builders = { 844 { 845 new CompositeBuilderViaFrom(targetClass, itemNames), 846 }, 847 { 848 new CompositeBuilderViaConstructor(targetClass, itemNames), 849 }, 850 { 851 new CompositeBuilderCheckGetters(targetClass, itemNames, 852 getterConverters), 853 new CompositeBuilderViaSetters(targetClass, itemNames), 854 new CompositeBuilderViaProxy(targetClass, itemNames), 855 }, 856 }; 857 CompositeBuilder foundBuilder = null; 858 861 StringBuffer whyNots = new StringBuffer (); 862 find: 863 for (CompositeBuilder[] relatedBuilders : builders) { 864 for (int i = 0; i < relatedBuilders.length; i++) { 865 CompositeBuilder builder = relatedBuilders[i]; 866 String whyNot = builder.applicable(getters); 867 if (whyNot == null) { 868 foundBuilder = builder; 869 break find; 870 } 871 if (whyNot.length() > 0) { 872 if (whyNots.length() > 0) 873 whyNots.append("; "); 874 whyNots.append(whyNot); 875 if (i == 0) 876 break; } 878 } 879 } 880 if (foundBuilder == null) { 881 final String msg = 882 "Do not know how to make a " + targetClass.getName() + 883 " from a CompositeData: " + whyNots; 884 throw new InvalidObjectException (msg); 885 } 886 compositeBuilder = foundBuilder; 887 } 888 889 void checkReconstructible() throws InvalidObjectException { 890 makeCompositeBuilder(); 891 } 892 893 public final Object fromNonNullOpenValue(MXBeanLookup lookup, Object value) 894 throws InvalidObjectException { 895 makeCompositeBuilder(); 896 return compositeBuilder.fromCompositeData(lookup, 897 (CompositeData ) value, 898 itemNames, 899 getterConverters); 900 } 901 902 private final String [] itemNames; 903 private final Method [] getters; 904 private final OpenConverter[] getterConverters; 905 private CompositeBuilder compositeBuilder; 906 } 907 908 909 private static abstract class CompositeBuilder { 910 CompositeBuilder(Class targetClass, String [] itemNames) { 911 this.targetClass = targetClass; 912 this.itemNames = itemNames; 913 } 914 915 Class getTargetClass() { 916 return targetClass; 917 } 918 919 String [] getItemNames() { 920 return itemNames; 921 } 922 923 928 abstract String applicable(Method [] getters) 929 throws InvalidObjectException ; 930 931 abstract Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, 932 String [] itemNames, 933 OpenConverter[] converters) 934 throws InvalidObjectException ; 935 936 private final Class targetClass; 937 private final String [] itemNames; 938 } 939 940 942 private static final class CompositeBuilderViaFrom 943 extends CompositeBuilder { 944 945 CompositeBuilderViaFrom(Class targetClass, String [] itemNames) { 946 super(targetClass, itemNames); 947 } 948 949 String applicable(Method [] getters) throws InvalidObjectException { 950 Class targetClass = getTargetClass(); 953 try { 954 Method fromMethod = 955 targetClass.getMethod("from", 956 new Class [] {CompositeData .class}); 957 958 if (!Modifier.isStatic(fromMethod.getModifiers())) { 959 final String msg = 960 "Method from(CompositeData) is not static"; 961 throw new InvalidObjectException (msg); 962 } 963 964 if (fromMethod.getReturnType() != getTargetClass()) { 965 final String msg = 966 "Method from(CompositeData) returns " + 967 fromMethod.getReturnType().getName() + 968 " not " + targetClass.getName(); 969 throw new InvalidObjectException (msg); 970 } 971 972 this.fromMethod = fromMethod; 973 return null; } catch (InvalidObjectException e) { 975 throw e; 976 } catch (Exception e) { 977 return "no method from(CompositeData)"; 979 } 980 } 981 982 final Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, 983 String [] itemNames, 984 OpenConverter[] converters) 985 throws InvalidObjectException { 986 try { 987 return fromMethod.invoke(null, cd); 988 } catch (Exception e) { 989 final String msg = "Failed to invoke from(CompositeData)"; 990 throw invalidObjectException(msg, e); 991 } 992 } 993 994 private Method fromMethod; 995 } 996 997 1007 private static class CompositeBuilderCheckGetters extends CompositeBuilder { 1008 CompositeBuilderCheckGetters(Class targetClass, String [] itemNames, 1009 OpenConverter[] getterConverters) { 1010 super(targetClass, itemNames); 1011 this.getterConverters = getterConverters; 1012 } 1013 1014 String applicable(Method [] getters) { 1015 for (int i = 0; i < getters.length; i++) { 1016 try { 1017 getterConverters[i].checkReconstructible(); 1018 } catch (InvalidObjectException e) { 1019 return "method " + getters[i].getName() + " returns type " + 1020 "that cannot be mapped back from OpenData"; 1021 } 1022 } 1023 return ""; 1024 } 1025 1026 final Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, 1027 String [] itemNames, 1028 OpenConverter[] converters) { 1029 throw new Error (); 1030 } 1031 1032 private final OpenConverter[] getterConverters; 1033 } 1034 1035 1036 private static class CompositeBuilderViaSetters extends CompositeBuilder { 1037 1038 CompositeBuilderViaSetters(Class targetClass, String [] itemNames) { 1039 super(targetClass, itemNames); 1040 } 1041 1042 String applicable(Method [] getters) { 1043 try { 1044 Constructor c = getTargetClass().getConstructor((Class []) null); 1045 } catch (Exception e) { 1046 return "does not have a public no-arg constructor"; 1047 } 1048 1049 Method [] setters = new Method [getters.length]; 1050 for (int i = 0; i < getters.length; i++) { 1051 Method getter = getters[i]; 1052 Class returnType = getter.getReturnType(); 1053 String name = propertyName(getter); 1054 String setterName = "set" + name; 1055 Method setter; 1056 try { 1057 setter = getTargetClass().getMethod(setterName, returnType); 1058 if (setter.getReturnType() != void.class) 1059 throw new Exception (); 1060 } catch (Exception e) { 1061 return "not all getters have corresponding setters " + 1062 "(" + getter + ")"; 1063 } 1064 setters[i] = setter; 1065 } 1066 this.setters = setters; 1067 return null; 1068 } 1069 1070 Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, 1071 String [] itemNames, 1072 OpenConverter[] converters) 1073 throws InvalidObjectException { 1074 Object o; 1075 try { 1076 o = getTargetClass().newInstance(); 1077 for (int i = 0; i < itemNames.length; i++) { 1078 if (cd.containsKey(itemNames[i])) { 1079 Object openItem = cd.get(itemNames[i]); 1080 Object javaItem = 1081 converters[i].fromOpenValue(lookup, openItem); 1082 setters[i].invoke(o, javaItem); 1083 } 1084 } 1085 } catch (Exception e) { 1086 throw invalidObjectException(e); 1087 } 1088 return o; 1089 } 1090 1091 private Method [] setters; 1092 } 1093 1094 1097 private static final class CompositeBuilderViaConstructor 1098 extends CompositeBuilder { 1099 1100 CompositeBuilderViaConstructor(Class targetClass, String [] itemNames) { 1101 super(targetClass, itemNames); 1102 } 1103 1104 String applicable(Method [] getters) throws InvalidObjectException { 1105 1106 final Class <ConstructorProperties > propertyNamesClass = ConstructorProperties .class; 1107 1108 Class targetClass = getTargetClass(); 1109 Constructor [] constrs = targetClass.getConstructors(); 1110 1111 List <Constructor > annotatedConstrList = newList(); 1113 for (Constructor constr : constrs) { 1114 if (Modifier.isPublic(constr.getModifiers()) 1115 && constr.getAnnotation(propertyNamesClass) != null) 1116 annotatedConstrList.add(constr); 1117 } 1118 1119 if (annotatedConstrList.isEmpty()) 1120 return "no constructor has @ConstructorProperties annotation"; 1121 1122 annotatedConstructors = newList(); 1123 1124 1127 Map <String , Integer > getterMap = newMap(); 1129 String [] itemNames = getItemNames(); 1130 for (int i = 0; i < itemNames.length; i++) 1131 getterMap.put(itemNames[i], i); 1132 1133 Set <BitSet > getterIndexSets = newSet(); 1143 for (Constructor constr : annotatedConstrList) { 1144 String [] propertyNames = 1145 constr.getAnnotation(propertyNamesClass).value(); 1146 1147 Set <String > propertyNameSet = 1148 newSet(Arrays.asList(propertyNames)); 1149 1150 Type [] paramTypes = constr.getGenericParameterTypes(); 1151 if (paramTypes.length != propertyNames.length) { 1152 final String msg = 1153 "Number of constructor params does not match " + 1154 "@ConstructorProperties annotation: " + constr; 1155 throw new InvalidObjectException (msg); 1156 } 1157 1158 int[] paramIndexes = new int[getters.length]; 1159 for (int i = 0; i < getters.length; i++) 1160 paramIndexes[i] = -1; 1161 BitSet present = new BitSet (); 1162 1163 for (int i = 0; i < propertyNames.length; i++) { 1164 String propertyName = propertyNames[i]; 1165 if (!getterMap.containsKey(propertyName)) { 1166 final String msg = 1167 "@ConstructorProperties includes name " + propertyName + 1168 " which does not correspond to a property: " + 1169 constr; 1170 throw new InvalidObjectException (msg); 1171 } 1172 int getterIndex = getterMap.get(propertyName); 1173 paramIndexes[getterIndex] = i; 1174 if (present.get(getterIndex)) { 1175 final String msg = 1176 "@ConstructorProperties contains property " + 1177 propertyName + " more than once: " + constr; 1178 throw new InvalidObjectException (msg); 1179 } 1180 present.set(getterIndex); 1181 Method getter = getters[getterIndex]; 1182 Type propertyType = getter.getGenericReturnType(); 1183 if (!propertyType.equals(paramTypes[i])) { 1184 final String msg = 1185 "@ConstructorProperties gives property " + propertyName + 1186 " of type " + propertyType + " for parameter " + 1187 " of type " + paramTypes[i] + ": " + constr; 1188 throw new InvalidObjectException (msg); 1189 } 1190 } 1191 1192 if (!getterIndexSets.add(present)) { 1193 final String msg = 1194 "More than one constructor has a @ConstructorProperties " + 1195 "annotation with this set of names: " + 1196 Arrays.toString(propertyNames); 1197 throw new InvalidObjectException (msg); 1198 } 1199 1200 Constr c = new Constr(constr, paramIndexes, present); 1201 annotatedConstructors.add(c); 1202 } 1203 1204 1219 for (BitSet a : getterIndexSets) { 1220 boolean seen = false; 1221 for (BitSet b : getterIndexSets) { 1222 if (a == b) 1223 seen = true; 1224 else if (seen) { 1225 BitSet u = new BitSet (); 1226 u.or(a); u.or(b); 1227 if (!getterIndexSets.contains(u)) { 1228 Set <String > names = new TreeSet <String >(); 1229 for (int i = u.nextSetBit(0); i >= 0; 1230 i = u.nextSetBit(i+1)) 1231 names.add(itemNames[i]); 1232 final String msg = 1233 "Constructors with @ConstructorProperties annotation " + 1234 " would be ambiguous for these items: " + 1235 names; 1236 throw new InvalidObjectException (msg); 1237 } 1238 } 1239 } 1240 } 1241 1242 return null; } 1244 1245 Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, 1246 String [] itemNames, 1247 OpenConverter[] converters) 1248 throws InvalidObjectException { 1249 CompositeType ct = cd.getCompositeType(); 1255 BitSet present = new BitSet (); 1256 for (int i = 0; i < itemNames.length; i++) { 1257 if (ct.getType(itemNames[i]) != null) 1258 present.set(i); 1259 } 1260 1261 Constr max = null; 1262 for (Constr constr : annotatedConstructors) { 1263 if (subset(constr.presentParams, present) && 1264 (max == null || 1265 subset(max.presentParams, constr.presentParams))) 1266 max = constr; 1267 } 1268 1269 if (max == null) { 1270 final String msg = 1271 "No constructor has a @ConstructorProperties for this set of " + 1272 "items: " + ct.keySet(); 1273 throw new InvalidObjectException (msg); 1274 } 1275 1276 Object [] params = new Object [max.presentParams.cardinality()]; 1277 for (int i = 0; i < itemNames.length; i++) { 1278 if (!max.presentParams.get(i)) 1279 continue; 1280 Object openItem = cd.get(itemNames[i]); 1281 Object javaItem = converters[i].fromOpenValue(lookup, openItem); 1282 int index = max.paramIndexes[i]; 1283 if (index >= 0) 1284 params[index] = javaItem; 1285 } 1286 1287 try { 1288 return max.constructor.newInstance(params); 1289 } catch (Exception e) { 1290 final String msg = 1291 "Exception constructing " + getTargetClass().getName(); 1292 throw invalidObjectException(msg, e); 1293 } 1294 } 1295 1296 private static boolean subset(BitSet sub, BitSet sup) { 1297 BitSet subcopy = (BitSet ) sub.clone(); 1298 subcopy.andNot(sup); 1299 return subcopy.isEmpty(); 1300 } 1301 1302 private static class Constr { 1303 final Constructor constructor; 1304 final int[] paramIndexes; 1305 final BitSet presentParams; 1306 Constr(Constructor constructor, int[] paramIndexes, 1307 BitSet presentParams) { 1308 this.constructor = constructor; 1309 this.paramIndexes = paramIndexes; 1310 this.presentParams = presentParams; 1311 } 1312 } 1313 1314 private List <Constr> annotatedConstructors; 1315 } 1316 1317 1321 private static final class CompositeBuilderViaProxy 1322 extends CompositeBuilder { 1323 1324 CompositeBuilderViaProxy(Class targetClass, String [] itemNames) { 1325 super(targetClass, itemNames); 1326 } 1327 1328 String applicable(Method [] getters) { 1329 Class targetClass = getTargetClass(); 1330 if (!targetClass.isInterface()) 1331 return "not an interface"; 1332 Set <Method > methods = 1333 newSet(Arrays.asList(targetClass.getMethods())); 1334 methods.removeAll(Arrays.asList(getters)); 1335 1338 String bad = null; 1339 for (Method m : methods) { 1340 String mname = m.getName(); 1341 Class [] mparams = m.getParameterTypes(); 1342 try { 1343 Method om = Object .class.getMethod(mname, mparams); 1344 if (!Modifier.isPublic(om.getModifiers())) 1345 bad = mname; 1346 } catch (NoSuchMethodException e) { 1347 bad = mname; 1348 } 1349 1353 } 1354 if (bad != null) 1355 return "contains methods other than getters (" + bad + ")"; 1356 return null; } 1358 1359 final Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, 1360 String [] itemNames, 1361 OpenConverter[] converters) { 1362 final Class targetClass = getTargetClass(); 1363 return 1364 Proxy.newProxyInstance(targetClass.getClassLoader(), 1365 new Class [] {targetClass}, 1366 new CompositeDataInvocationHandler (cd)); 1367 } 1368 } 1369 1370 static InvalidObjectException invalidObjectException(String msg, 1371 Throwable cause) { 1372 return EnvHelp.initCause(new InvalidObjectException (msg), cause); 1373 } 1374 1375 static InvalidObjectException invalidObjectException(Throwable cause) { 1376 return invalidObjectException(cause.getMessage(), cause); 1377 } 1378 1379 static OpenDataException openDataException(String msg, Throwable cause) { 1380 return EnvHelp.initCause(new OpenDataException (msg), cause); 1381 } 1382 1383 static OpenDataException openDataException(Throwable cause) { 1384 return openDataException(cause.getMessage(), cause); 1385 } 1386 1387 static void mustBeComparable(Class collection, Type element) 1388 throws OpenDataException { 1389 if (!(element instanceof Class ) 1390 || !Comparable .class.isAssignableFrom((Class <?>) element)) { 1391 final String msg = 1392 "Parameter class " + element + " of " + 1393 collection.getName() + " does not implement " + 1394 Comparable .class.getName(); 1395 throw new OpenDataException (msg); 1396 } 1397 } 1398 1399 1412 public static String decapitalize(String name) { 1413 if (name == null || name.length() == 0) { 1414 return name; 1415 } 1416 int offset1 = Character.offsetByCodePoints(name, 0, 1); 1417 if (offset1 < name.length() && 1419 Character.isUpperCase(name.codePointAt(offset1))) 1420 return name; 1421 return name.substring(0, offset1).toLowerCase() + 1422 name.substring(offset1); 1423 } 1424 1425 1431 static String capitalize(String name) { 1432 if (name == null || name.length() == 0) 1433 return name; 1434 int offset1 = name.offsetByCodePoints(0, 1); 1435 return name.substring(0, offset1).toUpperCase() + 1436 name.substring(offset1); 1437 } 1438 1439 public static String propertyName(Method m) { 1440 String rest = null; 1441 String name = m.getName(); 1442 if (name.startsWith("get")) 1443 rest = name.substring(3); 1444 else if (name.startsWith("is") && m.getReturnType() == boolean.class) 1445 rest = name.substring(2); 1446 if (rest == null || rest.length() == 0 1447 || m.getParameterTypes().length > 0 1448 || m.getReturnType() == void.class 1449 || name.equals("getClass")) 1450 return null; 1451 return rest; 1452 } 1453 1454 private final static Map <Type , Type > inProgress = newIdentityHashMap(); 1455 } 1457 | Popular Tags |