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