1 /*
   2  * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.management;
  27 import java.lang.management.MemoryUsage;
  28 import java.lang.management.MemoryNotificationInfo;
  29 import java.lang.management.MonitorInfo;
  30 import java.lang.management.LockInfo;
  31 import java.lang.management.ThreadInfo;
  32 import java.lang.reflect.*;
  33 import java.util.List;
  34 import java.util.Map;
  35 import java.util.*;
  36 import java.io.InvalidObjectException;
  37 import java.security.AccessController;
  38 import java.security.PrivilegedAction;
  39 import java.security.PrivilegedActionException;
  40 import java.security.PrivilegedExceptionAction;
  41 import javax.management.openmbean.*;
  42 import static javax.management.openmbean.SimpleType.*;
  43 
  44 /**
  45  * A mapped mxbean type maps a Java type to an open type.
  46  * Only the following Java types are mappable
  47  * (currently required by the platform MXBeans):
  48  * <ol>
  49  *   <li>Primitive types</li>
  50  *   <li>Wrapper classes such java.lang.Integer, etc</li>
  51  *   <li>Classes with only getter methods and with a static "from" method
  52  *      that takes a CompositeData argument.</li>
  53  *   <li>{@code E[]} where {@code E} is a type of 1-4 (can be multi-dimensional array)</li>
  54  *   <li>{@code List<E>} where E is a type of 1-3</li>
  55  *   <li>{@code Map<K, V>} where {@code K} and {@code V} are a type of 1-4</li>
  56  * </ol>
  57  *
  58  * OpenDataException will be thrown if a Java type is not supported.
  59  */
  60 // Suppress unchecked cast warnings at line 442, 523 and 546
  61 // Suppress unchecked calls at line 235, 284, 380 and 430.
  62 @SuppressWarnings("unchecked")
  63 public abstract class MappedMXBeanType {
  64     private static final WeakHashMap<Type,MappedMXBeanType> convertedTypes =
  65         new WeakHashMap<>();
  66 
  67     boolean  isBasicType = false;
  68     OpenType<?> openType = inProgress;
  69     Class<?>    mappedTypeClass;
  70 
  71     static synchronized MappedMXBeanType newMappedType(Type javaType)
  72             throws OpenDataException {
  73 
  74         MappedMXBeanType mt = null;
  75         if (javaType instanceof Class) {
  76             final Class<?> c = (Class<?>) javaType;
  77             if (c.isEnum()) {
  78                 mt = new EnumMXBeanType(c);
  79             } else if (c.isArray()) {
  80                 mt = new ArrayMXBeanType(c);
  81             } else {
  82                 mt = new CompositeDataMXBeanType(c);
  83             }
  84         } else if (javaType instanceof ParameterizedType) {
  85             final ParameterizedType pt = (ParameterizedType) javaType;
  86             final Type rawType = pt.getRawType();
  87             if (rawType instanceof Class) {
  88                 final Class<?> rc = (Class<?>) rawType;
  89                 if (rc == List.class) {
  90                     mt = new ListMXBeanType(pt);
  91                 } else if (rc == Map.class) {
  92                     mt = new MapMXBeanType(pt);
  93                 }
  94             }
  95         } else if (javaType instanceof GenericArrayType) {
  96            final GenericArrayType t = (GenericArrayType) javaType;
  97            mt = new GenericArrayMXBeanType(t);
  98         }
  99         // No open type mapped for the javaType
 100         if (mt == null) {
 101             throw new OpenDataException(javaType +
 102                 " is not a supported MXBean type.");
 103         }
 104         convertedTypes.put(javaType, mt);
 105         return mt;
 106     }
 107 
 108     // basic types do not require data mapping
 109     static synchronized MappedMXBeanType newBasicType(Class<?> c, OpenType<?> ot)
 110             throws OpenDataException {
 111         MappedMXBeanType mt = new BasicMXBeanType(c, ot);
 112         convertedTypes.put(c, mt);
 113         return mt;
 114     }
 115 
 116     public static synchronized MappedMXBeanType getMappedType(Type t)
 117             throws OpenDataException {
 118         MappedMXBeanType mt = convertedTypes.get(t);
 119         if (mt == null) {
 120             mt = newMappedType(t);
 121         }
 122 
 123         if (mt.getOpenType() instanceof InProgress) {
 124             throw new OpenDataException("Recursive data structure");
 125         }
 126         return mt;
 127     }
 128 
 129     // Convert a class to an OpenType
 130     public static synchronized OpenType<?> toOpenType(Type t)
 131             throws OpenDataException {
 132         MappedMXBeanType mt = getMappedType(t);
 133         return mt.getOpenType();
 134     }
 135 
 136     public static Object toJavaTypeData(Object openData, Type t)
 137             throws OpenDataException, InvalidObjectException {
 138         if (openData == null) {
 139             return null;
 140         }
 141         MappedMXBeanType mt = getMappedType(t);
 142         return mt.toJavaTypeData(openData);
 143     }
 144 
 145     public static Object toOpenTypeData(Object data, Type t)
 146             throws OpenDataException {
 147         if (data == null) {
 148             return null;
 149         }
 150         MappedMXBeanType mt = getMappedType(t);
 151         return mt.toOpenTypeData(data);
 152     }
 153 
 154     // Return the mapped open type
 155     public OpenType<?> getOpenType() {
 156         return openType;
 157     }
 158 
 159     boolean isBasicType() {
 160         return isBasicType;
 161     }
 162 
 163     // Return the type name of the mapped open type
 164     // For primitive types, the type name is the same as the javaType
 165     // but the mapped open type is the wrapper class
 166     String getTypeName() {
 167         return getMappedTypeClass().getName();
 168     }
 169 
 170     // Return the mapped open type
 171     Class<?> getMappedTypeClass() {
 172         return mappedTypeClass;
 173     }
 174 
 175     abstract Type getJavaType();
 176 
 177     // return name of the class or the generic type
 178     abstract String getName();
 179 
 180     public abstract Object toOpenTypeData(Object javaTypeData)
 181         throws OpenDataException;
 182 
 183     public abstract Object toJavaTypeData(Object openTypeData)
 184         throws OpenDataException, InvalidObjectException;
 185 
 186     // Basic Types - Classes that do not require data conversion
 187     //               including primitive types and all SimpleType
 188     //
 189     //   Mapped open type: SimpleType for corresponding basic type
 190     //
 191     // Data Mapping:
 192     //   T <-> T (no conversion)
 193     //
 194     static class BasicMXBeanType extends MappedMXBeanType {
 195         final Class<?> basicType;
 196         BasicMXBeanType(Class<?> c, OpenType<?> openType) {
 197             this.basicType = c;
 198             this.openType = openType;
 199             this.mappedTypeClass = c;
 200             this.isBasicType = true;
 201         }
 202 
 203         Type getJavaType() {
 204             return basicType;
 205         }
 206 
 207         String getName() {
 208             return basicType.getName();
 209         }
 210 
 211         public Object toOpenTypeData(Object data) throws OpenDataException {
 212             return data;
 213         }
 214 
 215         public Object toJavaTypeData(Object data)
 216             throws OpenDataException, InvalidObjectException {
 217 
 218             return data;
 219         }
 220     }
 221 
 222 
 223     // Enum subclasses
 224     //   Mapped open type - String
 225     //
 226     // Data Mapping:
 227     //   Enum <-> enum's name
 228     //
 229     static class EnumMXBeanType extends MappedMXBeanType {
 230         @SuppressWarnings("rawtypes")
 231         final Class enumClass;
 232         EnumMXBeanType(Class<?> c) {
 233             this.enumClass = c;
 234             this.openType = STRING;
 235             this.mappedTypeClass = String.class;
 236         }
 237 
 238         Type getJavaType() {
 239             return enumClass;
 240         }
 241 
 242         String getName() {
 243             return enumClass.getName();
 244         }
 245 
 246         public Object toOpenTypeData(Object data) throws OpenDataException {
 247             return ((Enum) data).name();
 248         }
 249 
 250         public Object toJavaTypeData(Object data)
 251             throws OpenDataException, InvalidObjectException {
 252 
 253             try {
 254                 return Enum.valueOf(enumClass, (String) data);
 255             } catch (IllegalArgumentException e) {
 256                 // missing enum constants
 257                 final InvalidObjectException ioe =
 258                     new InvalidObjectException("Enum constant named " +
 259                     (String) data + " is missing");
 260                 ioe.initCause(e);
 261                 throw ioe;
 262             }
 263         }
 264     }
 265 
 266     // Array E[]
 267     //   Mapped open type - Array with element of OpenType for E
 268     //
 269     // Data Mapping:
 270     //   E[] <-> openTypeData(E)[]
 271     //
 272     static class ArrayMXBeanType extends MappedMXBeanType {
 273         final Class<?> arrayClass;
 274         protected MappedMXBeanType componentType;
 275         protected MappedMXBeanType baseElementType;
 276 
 277         ArrayMXBeanType(Class<?> c) throws OpenDataException {
 278             this.arrayClass = c;
 279             this.componentType = getMappedType(c.getComponentType());
 280 
 281             StringBuilder className = new StringBuilder();
 282             Class<?> et = c;
 283             int dim;
 284             for (dim = 0; et.isArray(); dim++) {
 285                 className.append('[');
 286                 et = et.getComponentType();
 287             }
 288             baseElementType = getMappedType(et);
 289             if (et.isPrimitive()) {
 290                 className = new StringBuilder(c.getName());
 291             } else {
 292                 className.append('L').append(baseElementType.getTypeName()).append(';');
 293             }
 294             try {
 295                 mappedTypeClass = Class.forName(className.toString());
 296             } catch (ClassNotFoundException e) {
 297                 final OpenDataException ode =
 298                     new OpenDataException("Cannot obtain array class");
 299                 ode.initCause(e);
 300                 throw ode;
 301             }
 302 
 303             openType = new ArrayType<>(dim, baseElementType.getOpenType());
 304         }
 305 
 306         protected ArrayMXBeanType() {
 307             arrayClass = null;
 308         };
 309 
 310         Type getJavaType() {
 311             return arrayClass;
 312         }
 313 
 314         String getName() {
 315             return arrayClass.getName();
 316         }
 317 
 318         public Object toOpenTypeData(Object data) throws OpenDataException {
 319             // If the base element type is a basic type
 320             // return the data as no conversion is needed.
 321             // Primitive types are not converted to wrappers.
 322             if (baseElementType.isBasicType()) {
 323                 return data;
 324             }
 325 
 326             final Object[] array = (Object[]) data;
 327             final Object[] openArray = (Object[])
 328                 Array.newInstance(componentType.getMappedTypeClass(),
 329                                   array.length);
 330             int i = 0;
 331             for (Object o : array) {
 332                 if (o == null) {
 333                     openArray[i] = null;
 334                 } else {
 335                     openArray[i] = componentType.toOpenTypeData(o);
 336                 }
 337                 i++;
 338             }
 339             return openArray;
 340         }
 341 
 342 
 343         public Object toJavaTypeData(Object data)
 344             throws OpenDataException, InvalidObjectException {
 345 
 346             // If the base element type is a basic type
 347             // return the data as no conversion is needed.
 348             if (baseElementType.isBasicType()) {
 349                 return data;
 350             }
 351 
 352             final Object[] openArray = (Object[]) data;
 353             final Object[] array = (Object[])
 354                 Array.newInstance((Class) componentType.getJavaType(),
 355                                   openArray.length);
 356             int i = 0;
 357             for (Object o : openArray) {
 358                 if (o == null) {
 359                     array[i] = null;
 360                 } else {
 361                     array[i] = componentType.toJavaTypeData(o);
 362                 }
 363                 i++;
 364             }
 365             return array;
 366         }
 367 
 368     }
 369 
 370     static class GenericArrayMXBeanType extends ArrayMXBeanType {
 371         final GenericArrayType gtype;
 372         GenericArrayMXBeanType(GenericArrayType gat) throws OpenDataException {
 373             this.gtype = gat;
 374             this.componentType = getMappedType(gat.getGenericComponentType());
 375 
 376             StringBuilder className = new StringBuilder();
 377             Type elementType = gat;
 378             int dim;
 379             for (dim = 0; elementType instanceof GenericArrayType; dim++) {
 380                 className.append('[');
 381                 GenericArrayType et = (GenericArrayType) elementType;
 382                 elementType = et.getGenericComponentType();
 383             }
 384             baseElementType = getMappedType(elementType);
 385             if (elementType instanceof Class && ((Class) elementType).isPrimitive()) {
 386                 className = new StringBuilder(gat.toString());
 387             } else {
 388                 className.append('L').append(baseElementType.getTypeName()).append(';');
 389             }
 390             try {
 391                 mappedTypeClass = Class.forName(className.toString());
 392             } catch (ClassNotFoundException e) {
 393                 final OpenDataException ode =
 394                     new OpenDataException("Cannot obtain array class");
 395                 ode.initCause(e);
 396                 throw ode;
 397             }
 398 
 399             openType = new ArrayType<>(dim, baseElementType.getOpenType());
 400         }
 401 
 402         Type getJavaType() {
 403             return gtype;
 404         }
 405 
 406         String getName() {
 407             return gtype.toString();
 408         }
 409     }
 410 
 411     // List<E>
 412     //   Mapped open type - Array with element of OpenType for E
 413     //
 414     // Data Mapping:
 415     //   List<E> <-> openTypeData(E)[]
 416     //
 417     static class ListMXBeanType extends MappedMXBeanType {
 418         final ParameterizedType javaType;
 419         final MappedMXBeanType paramType;
 420         final String typeName;
 421 
 422         ListMXBeanType(ParameterizedType pt) throws OpenDataException {
 423             this.javaType = pt;
 424 
 425             final Type[] argTypes = pt.getActualTypeArguments();
 426             assert(argTypes.length == 1);
 427 
 428             if (!(argTypes[0] instanceof Class)) {
 429                 throw new OpenDataException("Element Type for " + pt +
 430                    " not supported");
 431             }
 432             final Class<?> et = (Class<?>) argTypes[0];
 433             if (et.isArray()) {
 434                 throw new OpenDataException("Element Type for " + pt +
 435                    " not supported");
 436             }
 437             paramType = getMappedType(et);
 438             typeName = "List<" + paramType.getName() + ">";
 439 
 440             try {
 441                 mappedTypeClass = Class.forName(
 442                     "[L" + paramType.getTypeName() + ";");
 443             } catch (ClassNotFoundException e) {
 444                 final OpenDataException ode =
 445                     new OpenDataException("Array class not found");
 446                 ode.initCause(e);
 447                 throw ode;
 448             }
 449             openType = new ArrayType<>(1, paramType.getOpenType());
 450         }
 451 
 452         Type getJavaType() {
 453             return javaType;
 454         }
 455 
 456         String getName() {
 457             return typeName;
 458         }
 459 
 460         public Object toOpenTypeData(Object data) throws OpenDataException {
 461             final List<Object> list = (List<Object>) data;
 462 
 463             final Object[] openArray = (Object[])
 464                 Array.newInstance(paramType.getMappedTypeClass(),
 465                                   list.size());
 466             int i = 0;
 467             for (Object o : list) {
 468                 openArray[i++] = paramType.toOpenTypeData(o);
 469             }
 470             return openArray;
 471         }
 472 
 473         public Object toJavaTypeData(Object data)
 474             throws OpenDataException, InvalidObjectException {
 475 
 476             final Object[] openArray = (Object[]) data;
 477             List<Object> result = new ArrayList<>(openArray.length);
 478             for (Object o : openArray) {
 479                 result.add(paramType.toJavaTypeData(o));
 480             }
 481             return result;
 482         }
 483     }
 484 
 485     private static final String KEY   = "key";
 486     private static final String VALUE = "value";
 487     private static final String[] mapIndexNames = {KEY};
 488     private static final String[] mapItemNames = {KEY, VALUE};
 489 
 490     // Map<K,V>
 491     //   Mapped open type - TabularType with row type:
 492     //                        CompositeType:
 493     //                          "key"   of openDataType(K)
 494     //                          "value" of openDataType(V)
 495     //                        "key" is the index name
 496     //
 497     // Data Mapping:
 498     //   Map<K,V> <-> TabularData
 499     //
 500     static class MapMXBeanType extends MappedMXBeanType {
 501         final ParameterizedType javaType;
 502         final MappedMXBeanType keyType;
 503         final MappedMXBeanType valueType;
 504         final String typeName;
 505 
 506         MapMXBeanType(ParameterizedType pt) throws OpenDataException {
 507             this.javaType = pt;
 508 
 509             final Type[] argTypes = pt.getActualTypeArguments();
 510             assert(argTypes.length == 2);
 511             this.keyType = getMappedType(argTypes[0]);
 512             this.valueType = getMappedType(argTypes[1]);
 513 
 514 
 515             // FIXME: generate typeName for generic
 516             typeName = "Map<" + keyType.getName() + "," +
 517                                 valueType.getName() + ">";
 518             final OpenType<?>[] mapItemTypes = new OpenType<?>[] {
 519                                                 keyType.getOpenType(),
 520                                                 valueType.getOpenType(),
 521                                             };
 522             final CompositeType rowType =
 523                 new CompositeType(typeName,
 524                                   typeName,
 525                                   mapItemNames,
 526                                   mapItemNames,
 527                                   mapItemTypes);
 528 
 529             openType = new TabularType(typeName, typeName, rowType, mapIndexNames);
 530             mappedTypeClass = javax.management.openmbean.TabularData.class;
 531         }
 532 
 533         Type getJavaType() {
 534             return javaType;
 535         }
 536 
 537         String getName() {
 538             return typeName;
 539         }
 540 
 541         public Object toOpenTypeData(Object data) throws OpenDataException {
 542             final Map<Object,Object> map = (Map<Object,Object>) data;
 543             final TabularType tabularType = (TabularType) openType;
 544             final TabularData table = new TabularDataSupport(tabularType);
 545             final CompositeType rowType = tabularType.getRowType();
 546 
 547             for (Map.Entry<Object, Object> entry : map.entrySet()) {
 548                 final Object key = keyType.toOpenTypeData(entry.getKey());
 549                 final Object value = valueType.toOpenTypeData(entry.getValue());
 550                 final CompositeData row =
 551                     new CompositeDataSupport(rowType,
 552                                              mapItemNames,
 553                                              new Object[] {key, value});
 554                 table.put(row);
 555             }
 556             return table;
 557         }
 558 
 559         public Object toJavaTypeData(Object data)
 560             throws OpenDataException, InvalidObjectException {
 561 
 562             final TabularData td = (TabularData) data;
 563 
 564             Map<Object, Object> result = new HashMap<>();
 565             for (CompositeData row : (Collection<CompositeData>) td.values()) {
 566                 Object key = keyType.toJavaTypeData(row.get(KEY));
 567                 Object value = valueType.toJavaTypeData(row.get(VALUE));
 568                 result.put(key, value);
 569             }
 570             return result;
 571         }
 572     }
 573 
 574     private static final Class<?> COMPOSITE_DATA_CLASS =
 575         javax.management.openmbean.CompositeData.class;
 576 
 577     // Classes that have a static from method
 578     //   Mapped open type - CompositeData
 579     //
 580     // Data Mapping:
 581     //   Classes <-> CompositeData
 582     //
 583     // The name and type of items for a class are identified from
 584     // the getter methods. For example, a class defines a method:
 585     //
 586     //    public FooType getFoo();
 587     //
 588     // The composite data view for this class will contain one
 589     // item entry for a "foo" attribute and the item type is
 590     // one of the open types defined in the OpenType class that
 591     // can be determined in the following manner:
 592     // o If FooType is a primitive type, the item type a wrapper
 593     //   class for the corresponding primitive type (such as
 594     //   Integer, Long, Boolean, etc).
 595     // o If FooType is of type CompositeData or TabularData,
 596     //   the item type is FooType.
 597     // o If FooType is an Enum, the item type is a String and
 598     //   the value is the name of the enum constant.
 599     // o If FooType is a class or an interface other than the above,
 600     //   the item type is CompositeData. The same convention
 601     //   can be recursively applied to the FooType class when
 602     //   constructing the composite data for the "foo" attribute.
 603     // o If FooType is an array, the item type is an array and
 604     //   its element type is determined as described above.
 605     //
 606     static class CompositeDataMXBeanType extends MappedMXBeanType {
 607         final Class<?> javaClass;
 608         boolean isCompositeData = false;
 609         Method fromMethod = null;
 610         Method toMethod = null;
 611 
 612         CompositeDataMXBeanType(Class<?> c) throws OpenDataException {
 613             this.javaClass = c;
 614             this.mappedTypeClass = COMPOSITE_DATA_CLASS;
 615 
 616             // check if a static from method exists
 617             try {
 618                 fromMethod = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
 619                         public Method run() throws NoSuchMethodException {
 620                             return javaClass.getMethod("from", COMPOSITE_DATA_CLASS);
 621                         }
 622                     });
 623             } catch (PrivilegedActionException e) {
 624                 // ignore NoSuchMethodException since we allow classes
 625                 // that has no from method to be embeded in another class.
 626             }
 627 
 628             if (COMPOSITE_DATA_CLASS.isAssignableFrom(c)) {
 629                 // c implements CompositeData - set openType to null
 630                 // defer generating the CompositeType
 631                 // until the object is constructed
 632                 this.isCompositeData = true;
 633                 this.openType = null;
 634             } else {
 635                 this.isCompositeData = false;
 636 
 637                 // Make a CompositeData containing all the getters
 638                 final Method[] methods =
 639                     AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
 640                             public Method[] run() {
 641                                 return javaClass.getMethods();
 642                             }
 643                         });
 644                 final List<String> names = new ArrayList<>();
 645                 final List<OpenType<?>> types = new ArrayList<>();
 646 
 647                 /* Select public methods that look like "T getX()" or "boolean
 648                  isX()", where T is not void and X is not the empty
 649                  string.  Exclude "Class getClass()" inherited from Object.  */
 650                 for (int i = 0; i < methods.length; i++) {
 651                     final Method method = methods[i];
 652                     final String name = method.getName();
 653                     final Type type = method.getGenericReturnType();
 654                     final String rest;
 655                     if (name.startsWith("get")) {
 656                         rest = name.substring(3);
 657                     } else if (name.startsWith("is") &&
 658                                type instanceof Class &&
 659                                ((Class) type) == boolean.class) {
 660                         rest = name.substring(2);
 661                     } else {
 662                         // ignore non-getter methods
 663                         continue;
 664                     }
 665 
 666                     if (rest.isEmpty() ||
 667                         method.getParameterTypes().length > 0 ||
 668                         type == void.class ||
 669                         rest.equals("Class")) {
 670 
 671                         // ignore non-getter methods
 672                         continue;
 673                     }
 674                     names.add(decapitalize(rest));
 675                     types.add(toOpenType(type));
 676                 }
 677 
 678                 final String[] nameArray = names.toArray(new String[0]);
 679                 openType = new CompositeType(c.getName(),
 680                         c.getName(),
 681                         nameArray, // field names
 682                         nameArray, // field descriptions
 683                         types.toArray(new OpenType<?>[0]));
 684             }
 685         }
 686 
 687         Type getJavaType() {
 688             return javaClass;
 689         }
 690 
 691         String getName() {
 692             return javaClass.getName();
 693         }
 694 
 695         public Object toOpenTypeData(Object data) throws OpenDataException {
 696             if (toMethod != null) {
 697                 try {
 698                     return toMethod.invoke(null, data);
 699                 } catch (IllegalAccessException e) {
 700                     // should never reach here
 701                     throw new AssertionError(e);
 702                 } catch (InvocationTargetException e) {
 703                     final OpenDataException ode
 704                             = new OpenDataException("Failed to invoke "
 705                                     + toMethod.getName() + " to convert " + javaClass.getName()
 706                                     + " to CompositeData");
 707                     ode.initCause(e);
 708                     throw ode;
 709                 }
 710             }
 711 
 712             if (data instanceof MemoryUsage) {
 713                 return MemoryUsageCompositeData.toCompositeData((MemoryUsage) data);
 714             }
 715 
 716             if (data instanceof ThreadInfo) {
 717                 return ThreadInfoCompositeData.toCompositeData((ThreadInfo) data);
 718             }
 719 
 720             if (data instanceof LockInfo) {
 721                 if (data instanceof java.lang.management.MonitorInfo) {
 722                     return MonitorInfoCompositeData.toCompositeData((MonitorInfo) data);
 723                 }
 724                 return LockInfoCompositeData.toCompositeData((LockInfo) data);
 725             }
 726 
 727             if (data instanceof MemoryNotificationInfo) {
 728                 return MemoryNotifInfoCompositeData.
 729                     toCompositeData((MemoryNotificationInfo) data);
 730             }
 731 
 732             if (isCompositeData) {
 733                 // Classes that implement CompositeData
 734                 //
 735                 // construct a new CompositeDataSupport object
 736                 // so that no other classes are sent over the wire
 737                 CompositeData cd = (CompositeData) data;
 738                 CompositeType ct = cd.getCompositeType();
 739                 String[] itemNames = ct.keySet().toArray(new String[0]);
 740                 Object[] itemValues = cd.getAll(itemNames);
 741                 return new CompositeDataSupport(ct, itemNames, itemValues);
 742             }
 743 
 744             throw new OpenDataException(javaClass.getName() +
 745                 " is not supported for platform MXBeans");
 746         }
 747 
 748         public Object toJavaTypeData(Object data)
 749             throws OpenDataException, InvalidObjectException {
 750 
 751             if (fromMethod == null) {
 752                 throw new AssertionError("Does not support data conversion");
 753             }
 754 
 755             try {
 756                 return fromMethod.invoke(null, data);
 757             } catch (IllegalAccessException e) {
 758                 // should never reach here
 759                 throw new AssertionError(e);
 760             } catch (InvocationTargetException e) {
 761                 final OpenDataException ode =
 762                     new OpenDataException("Failed to invoke " +
 763                         fromMethod.getName() + " to convert CompositeData " +
 764                         " to " + javaClass.getName());
 765                 ode.initCause(e);
 766                 throw ode;
 767             }
 768         }
 769     }
 770 
 771     private static class InProgress<T> extends OpenType<T> {
 772         private static final String description =
 773                   "Marker to detect recursive type use -- internal use only!";
 774 
 775         InProgress() throws OpenDataException {
 776             super("java.lang.String", "java.lang.String", description);
 777         }
 778 
 779         public String toString() {
 780             return description;
 781         }
 782 
 783         public int hashCode() {
 784             return 0;
 785         }
 786 
 787         public boolean equals(Object o) {
 788             return false;
 789         }
 790 
 791         public boolean isValue(Object o) {
 792             return false;
 793         }
 794         private static final long serialVersionUID = -3413063475064374490L;
 795     }
 796     private static final OpenType<?> inProgress;
 797     static {
 798         OpenType<?> t;
 799         try {
 800             t = new InProgress<>();
 801         } catch (OpenDataException e) {
 802             // Should not reach here
 803             throw new AssertionError(e);
 804         }
 805         inProgress = t;
 806     }
 807 
 808     private static final OpenType<?>[] simpleTypes = {
 809         BIGDECIMAL, BIGINTEGER, BOOLEAN, BYTE, CHARACTER, DATE,
 810         DOUBLE, FLOAT, INTEGER, LONG, OBJECTNAME, SHORT, STRING,
 811         VOID,
 812     };
 813     static {
 814         try {
 815             for (int i = 0; i < simpleTypes.length; i++) {
 816                 final OpenType<?> t = simpleTypes[i];
 817                 Class<?> c;
 818                 try {
 819                     c = Class.forName(t.getClassName(), false,
 820                                       MappedMXBeanType.class.getClassLoader());
 821                     MappedMXBeanType.newBasicType(c, t);
 822                 } catch (ClassNotFoundException e) {
 823                     // the classes that these predefined types declare
 824                     // must exist!
 825                     throw new AssertionError(e);
 826                 } catch (OpenDataException e) {
 827                     throw new AssertionError(e);
 828                 }
 829 
 830                 if (c.getName().startsWith("java.lang.")) {
 831                     try {
 832                         final Field typeField = c.getField("TYPE");
 833                         final Class<?> primitiveType = (Class<?>) typeField.get(null);
 834                         MappedMXBeanType.newBasicType(primitiveType, t);
 835                     } catch (NoSuchFieldException e) {
 836                         // OK: must not be a primitive wrapper
 837                     } catch (IllegalAccessException e) {
 838                         // Should not reach here
 839                        throw new AssertionError(e);
 840                     }
 841                 }
 842             }
 843         } catch (OpenDataException e) {
 844             throw new AssertionError(e);
 845         }
 846     }
 847 
 848     /**
 849      * Utility method to take a string and convert it to normal Java variable
 850      * name capitalization.  This normally means converting the first
 851      * character from upper case to lower case, but in the (unusual) special
 852      * case when there is more than one character and both the first and
 853      * second characters are upper case, we leave it alone.
 854      * <p>
 855      * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
 856      * as "URL".
 857      *
 858      * @param  name The string to be decapitalized.
 859      * @return  The decapitalized version of the string.
 860      */
 861     private static String decapitalize(String name) {
 862         if (name == null || name.length() == 0) {
 863             return name;
 864         }
 865         if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
 866                         Character.isUpperCase(name.charAt(0))){
 867             return name;
 868         }
 869         char chars[] = name.toCharArray();
 870         chars[0] = Character.toLowerCase(chars[0]);
 871         return new String(chars);
 872     }
 873 
 874 }