1 /*
   2  * Copyright (c) 2004, 2012, 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.*;
  42 import javax.management.openmbean.*;
  43 import static javax.management.openmbean.SimpleType.*;
  44 import com.sun.management.VMOption;
  45 
  46 /**
  47  * A mapped mxbean type maps a Java type to an open type.
  48  * Only the following Java types are mappable
  49  * (currently required by the platform MXBeans):
  50  *   1. Primitive types
  51  *   2. Wrapper classes such java.lang.Integer, etc
  52  *   3. Classes with only getter methods and with a static "from" method
  53  *      that takes a CompositeData argument.
  54  *   4. E[] where E is a type of 1-4 (can be multi-dimensional array)
  55  *   5. List<E> where E is a type of 1-3
  56  *   6. Map<K, V> where K and V are a type of 1-4
  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     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     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     abstract Object toOpenTypeData(Object javaTypeData)
 181         throws OpenDataException;
 182 
 183     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         Object toOpenTypeData(Object data) throws OpenDataException {
 212             return data;
 213         }
 214 
 215         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         Object toOpenTypeData(Object data) throws OpenDataException {
 247             return ((Enum) data).name();
 248         }
 249 
 250         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" + baseElementType.getTypeName() + ";");
 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         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         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" + baseElementType.getTypeName() + ";");
 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         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         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         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         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         final boolean isCompositeData;
 609         Method fromMethod = null;
 610 
 611         CompositeDataMXBeanType(Class<?> c) throws OpenDataException {
 612             this.javaClass = c;
 613             this.mappedTypeClass = COMPOSITE_DATA_CLASS;
 614 
 615             // check if a static from method exists
 616             try {
 617                 fromMethod = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
 618                         public Method run() throws NoSuchMethodException {
 619                             return javaClass.getMethod("from", COMPOSITE_DATA_CLASS);
 620                         }
 621                     });
 622             } catch (PrivilegedActionException e) {
 623                 // ignore NoSuchMethodException since we allow classes
 624                 // that has no from method to be embeded in another class.
 625             }
 626 
 627             if (COMPOSITE_DATA_CLASS.isAssignableFrom(c)) {
 628                 // c implements CompositeData - set openType to null
 629                 // defer generating the CompositeType
 630                 // until the object is constructed
 631                 this.isCompositeData = true;
 632                 this.openType = null;
 633             } else {
 634                 this.isCompositeData = false;
 635 
 636                 // Make a CompositeData containing all the getters
 637                 final Method[] methods =
 638                     AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
 639                         public Method[] run() {
 640                             return javaClass.getMethods();
 641                         }
 642                     });
 643                 final List<String> names = new ArrayList<>();
 644                 final List<OpenType<?>> types = new ArrayList<>();
 645 
 646                 /* Select public methods that look like "T getX()" or "boolean
 647                    isX()", where T is not void and X is not the empty
 648                    string.  Exclude "Class getClass()" inherited from Object.  */
 649                 for (int i = 0; i < methods.length; i++) {
 650                     final Method method = methods[i];
 651                     final String name = method.getName();
 652                     final Type type = method.getGenericReturnType();
 653                     final String rest;
 654                     if (name.startsWith("get")) {
 655                         rest = name.substring(3);
 656                     } else if (name.startsWith("is") &&
 657                                type instanceof Class &&
 658                                ((Class) type) == boolean.class) {
 659                         rest = name.substring(2);
 660                     } else {
 661                         // ignore non-getter methods
 662                         continue;
 663                     }
 664 
 665                     if (rest.equals("") ||
 666                         method.getParameterTypes().length > 0 ||
 667                         type == void.class ||
 668                         rest.equals("Class")) {
 669 
 670                         // ignore non-getter methods
 671                         continue;
 672                     }
 673                     names.add(decapitalize(rest));
 674                     types.add(toOpenType(type));
 675                 }
 676 
 677                 final String[] nameArray = names.toArray(new String[0]);
 678                 openType = new CompositeType(c.getName(),
 679                                              c.getName(),
 680                                              nameArray, // field names
 681                                              nameArray, // field descriptions
 682                                              types.toArray(new OpenType<?>[0]));
 683             }
 684         }
 685 
 686         Type getJavaType() {
 687             return javaClass;
 688         }
 689 
 690         String getName() {
 691             return javaClass.getName();
 692         }
 693 
 694         Object toOpenTypeData(Object data) throws OpenDataException {
 695             if (data instanceof MemoryUsage) {
 696                 return MemoryUsageCompositeData.toCompositeData((MemoryUsage) data);
 697             }
 698 
 699             if (data instanceof ThreadInfo) {
 700                 return ThreadInfoCompositeData.toCompositeData((ThreadInfo) data);
 701             }
 702 
 703             if (data instanceof LockInfo) {
 704                 if (data instanceof java.lang.management.MonitorInfo) {
 705                     return MonitorInfoCompositeData.toCompositeData((MonitorInfo) data);
 706                 }
 707                 return LockInfoCompositeData.toCompositeData((LockInfo) data);
 708             }
 709 
 710             if (data instanceof MemoryNotificationInfo) {
 711                 return MemoryNotifInfoCompositeData.
 712                     toCompositeData((MemoryNotificationInfo) data);
 713             }
 714 
 715             if (data instanceof VMOption) {
 716                 return VMOptionCompositeData.toCompositeData((VMOption) data);
 717             }
 718 
 719             if (isCompositeData) {
 720                 // Classes that implement CompositeData
 721                 //
 722                 // construct a new CompositeDataSupport object
 723                 // so that no other classes are sent over the wire
 724                 CompositeData cd = (CompositeData) data;
 725                 CompositeType ct = cd.getCompositeType();
 726                 String[] itemNames = ct.keySet().toArray(new String[0]);
 727                 Object[] itemValues = cd.getAll(itemNames);
 728                 return new CompositeDataSupport(ct, itemNames, itemValues);
 729             }
 730 
 731             throw new OpenDataException(javaClass.getName() +
 732                 " is not supported for platform MXBeans");
 733         }
 734 
 735         Object toJavaTypeData(Object data)
 736             throws OpenDataException, InvalidObjectException {
 737 
 738             if (fromMethod == null) {
 739                 throw new AssertionError("Does not support data conversion");
 740             }
 741 
 742             try {
 743                 return fromMethod.invoke(null, data);
 744             } catch (IllegalAccessException e) {
 745                 // should never reach here
 746                 throw new AssertionError(e);
 747             } catch (InvocationTargetException e) {
 748                 final OpenDataException ode =
 749                     new OpenDataException("Failed to invoke " +
 750                         fromMethod.getName() + " to convert CompositeData " +
 751                         " to " + javaClass.getName());
 752                 ode.initCause(e);
 753                 throw ode;
 754             }
 755         }
 756     }
 757 
 758     private static class InProgress<T> extends OpenType<T> {
 759         private static final String description =
 760                   "Marker to detect recursive type use -- internal use only!";
 761 
 762         InProgress() throws OpenDataException {
 763             super("java.lang.String", "java.lang.String", description);
 764         }
 765 
 766         public String toString() {
 767             return description;
 768         }
 769 
 770         public int hashCode() {
 771             return 0;
 772         }
 773 
 774         public boolean equals(Object o) {
 775             return false;
 776         }
 777 
 778         public boolean isValue(Object o) {
 779             return false;
 780         }
 781         private static final long serialVersionUID = -3413063475064374490L;
 782     }
 783     private static final OpenType<?> inProgress;
 784     static {
 785         OpenType<?> t;
 786         try {
 787             t = new InProgress<>();
 788         } catch (OpenDataException e) {
 789             // Should not reach here
 790             throw new AssertionError(e);
 791         }
 792         inProgress = t;
 793     }
 794 
 795     private static final OpenType<?>[] simpleTypes = {
 796         BIGDECIMAL, BIGINTEGER, BOOLEAN, BYTE, CHARACTER, DATE,
 797         DOUBLE, FLOAT, INTEGER, LONG, OBJECTNAME, SHORT, STRING,
 798         VOID,
 799     };
 800     static {
 801         try {
 802             for (int i = 0; i < simpleTypes.length; i++) {
 803                 final OpenType<?> t = simpleTypes[i];
 804                 Class<?> c;
 805                 try {
 806                     c = Class.forName(t.getClassName(), false,
 807                                       MappedMXBeanType.class.getClassLoader());
 808                     MappedMXBeanType.newBasicType(c, t);
 809                 } catch (ClassNotFoundException e) {
 810                     // the classes that these predefined types declare
 811                     // must exist!
 812                     throw new AssertionError(e);
 813                 } catch (OpenDataException e) {
 814                     throw new AssertionError(e);
 815                 }
 816 
 817                 if (c.getName().startsWith("java.lang.")) {
 818                     try {
 819                         final Field typeField = c.getField("TYPE");
 820                         final Class<?> primitiveType = (Class<?>) typeField.get(null);
 821                         MappedMXBeanType.newBasicType(primitiveType, t);
 822                     } catch (NoSuchFieldException e) {
 823                         // OK: must not be a primitive wrapper
 824                     } catch (IllegalAccessException e) {
 825                         // Should not reach here
 826                        throw new AssertionError(e);
 827                     }
 828                 }
 829             }
 830         } catch (OpenDataException e) {
 831             throw new AssertionError(e);
 832         }
 833     }
 834 
 835     /**
 836      * Utility method to take a string and convert it to normal Java variable
 837      * name capitalization.  This normally means converting the first
 838      * character from upper case to lower case, but in the (unusual) special
 839      * case when there is more than one character and both the first and
 840      * second characters are upper case, we leave it alone.
 841      * <p>
 842      * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
 843      * as "URL".
 844      *
 845      * @param  name The string to be decapitalized.
 846      * @return  The decapitalized version of the string.
 847      */
 848     private static String decapitalize(String name) {
 849         if (name == null || name.length() == 0) {
 850             return name;
 851         }
 852         if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
 853                         Character.isUpperCase(name.charAt(0))){
 854             return name;
 855         }
 856         char chars[] = name.toCharArray();
 857         chars[0] = Character.toLowerCase(chars[0]);
 858         return new String(chars);
 859     }
 860 
 861 }