1 /*
   2  * Copyright (c) 2004, 2008, 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         final Class enumClass;
 231         EnumMXBeanType(Class<?> c) {
 232             this.enumClass = c;
 233             this.openType = STRING;
 234             this.mappedTypeClass = String.class;
 235         }
 236 
 237         Type getJavaType() {
 238             return enumClass;
 239         }
 240 
 241         String getName() {
 242             return enumClass.getName();
 243         }
 244 
 245         Object toOpenTypeData(Object data) throws OpenDataException {
 246             return ((Enum) data).name();
 247         }
 248 
 249         Object toJavaTypeData(Object data)
 250             throws OpenDataException, InvalidObjectException {
 251 
 252             try {
 253                 return Enum.valueOf(enumClass, (String) data);
 254             } catch (IllegalArgumentException e) {
 255                 // missing enum constants
 256                 final InvalidObjectException ioe =
 257                     new InvalidObjectException("Enum constant named " +
 258                     (String) data + " is missing");
 259                 ioe.initCause(e);
 260                 throw ioe;
 261             }
 262         }
 263     }
 264 
 265     // Array E[]
 266     //   Mapped open type - Array with element of OpenType for E
 267     //
 268     // Data Mapping:
 269     //   E[] <-> openTypeData(E)[]
 270     //
 271     static class ArrayMXBeanType extends MappedMXBeanType {
 272         final Class<?> arrayClass;
 273         protected MappedMXBeanType componentType;
 274         protected MappedMXBeanType baseElementType;
 275 
 276         ArrayMXBeanType(Class<?> c) throws OpenDataException {
 277             this.arrayClass = c;
 278             this.componentType = getMappedType(c.getComponentType());
 279 
 280             StringBuilder className = new StringBuilder();
 281             Class<?> et = c;
 282             int dim;
 283             for (dim = 0; et.isArray(); dim++) {
 284                 className.append('[');
 285                 et = et.getComponentType();
 286             }
 287             baseElementType = getMappedType(et);
 288             if (et.isPrimitive()) {
 289                 className = new StringBuilder(c.getName());
 290             } else {
 291                 className.append("L" + baseElementType.getTypeName() + ";");
 292             }
 293             try {
 294                 mappedTypeClass = Class.forName(className.toString());
 295             } catch (ClassNotFoundException e) {
 296                 final OpenDataException ode =
 297                     new OpenDataException("Cannot obtain array class");
 298                 ode.initCause(e);
 299                 throw ode;
 300             }
 301 
 302             openType = new ArrayType<>(dim, baseElementType.getOpenType());
 303         }
 304 
 305         protected ArrayMXBeanType() {
 306             arrayClass = null;
 307         };
 308 
 309         Type getJavaType() {
 310             return arrayClass;
 311         }
 312 
 313         String getName() {
 314             return arrayClass.getName();
 315         }
 316 
 317         Object toOpenTypeData(Object data) throws OpenDataException {
 318             // If the base element type is a basic type
 319             // return the data as no conversion is needed.
 320             // Primitive types are not converted to wrappers.
 321             if (baseElementType.isBasicType()) {
 322                 return data;
 323             }
 324 
 325             final Object[] array = (Object[]) data;
 326             final Object[] openArray = (Object[])
 327                 Array.newInstance(componentType.getMappedTypeClass(),
 328                                   array.length);
 329             int i = 0;
 330             for (Object o : array) {
 331                 if (o == null) {
 332                     openArray[i] = null;
 333                 } else {
 334                     openArray[i] = componentType.toOpenTypeData(o);
 335                 }
 336                 i++;
 337             }
 338             return openArray;
 339         }
 340 
 341 
 342         Object toJavaTypeData(Object data)
 343             throws OpenDataException, InvalidObjectException {
 344 
 345             // If the base element type is a basic type
 346             // return the data as no conversion is needed.
 347             if (baseElementType.isBasicType()) {
 348                 return data;
 349             }
 350 
 351             final Object[] openArray = (Object[]) data;
 352             final Object[] array = (Object[])
 353                 Array.newInstance((Class) componentType.getJavaType(),
 354                                   openArray.length);
 355             int i = 0;
 356             for (Object o : openArray) {
 357                 if (o == null) {
 358                     array[i] = null;
 359                 } else {
 360                     array[i] = componentType.toJavaTypeData(o);
 361                 }
 362                 i++;
 363             }
 364             return array;
 365         }
 366 
 367     }
 368 
 369     static class GenericArrayMXBeanType extends ArrayMXBeanType {
 370         final GenericArrayType gtype;
 371         GenericArrayMXBeanType(GenericArrayType gat) throws OpenDataException {
 372             this.gtype = gat;
 373             this.componentType = getMappedType(gat.getGenericComponentType());
 374 
 375             StringBuilder className = new StringBuilder();
 376             Type elementType = gat;
 377             int dim;
 378             for (dim = 0; elementType instanceof GenericArrayType; dim++) {
 379                 className.append('[');
 380                 GenericArrayType et = (GenericArrayType) elementType;
 381                 elementType = et.getGenericComponentType();
 382             }
 383             baseElementType = getMappedType(elementType);
 384             if (elementType instanceof Class && ((Class) elementType).isPrimitive()) {
 385                 className = new StringBuilder(gat.toString());
 386             } else {
 387                 className.append("L" + baseElementType.getTypeName() + ";");
 388             }
 389             try {
 390                 mappedTypeClass = Class.forName(className.toString());
 391             } catch (ClassNotFoundException e) {
 392                 final OpenDataException ode =
 393                     new OpenDataException("Cannot obtain array class");
 394                 ode.initCause(e);
 395                 throw ode;
 396             }
 397 
 398             openType = new ArrayType<>(dim, baseElementType.getOpenType());
 399         }
 400 
 401         Type getJavaType() {
 402             return gtype;
 403         }
 404 
 405         String getName() {
 406             return gtype.toString();
 407         }
 408     }
 409 
 410     // List<E>
 411     //   Mapped open type - Array with element of OpenType for E
 412     //
 413     // Data Mapping:
 414     //   List<E> <-> openTypeData(E)[]
 415     //
 416     static class ListMXBeanType extends MappedMXBeanType {
 417         final ParameterizedType javaType;
 418         final MappedMXBeanType paramType;
 419         final String typeName;
 420 
 421         ListMXBeanType(ParameterizedType pt) throws OpenDataException {
 422             this.javaType = pt;
 423 
 424             final Type[] argTypes = pt.getActualTypeArguments();
 425             assert(argTypes.length == 1);
 426 
 427             if (!(argTypes[0] instanceof Class)) {
 428                 throw new OpenDataException("Element Type for " + pt +
 429                    " not supported");
 430             }
 431             final Class<?> et = (Class<?>) argTypes[0];
 432             if (et.isArray()) {
 433                 throw new OpenDataException("Element Type for " + pt +
 434                    " not supported");
 435             }
 436             paramType = getMappedType(et);
 437             typeName = "List<" + paramType.getName() + ">";
 438 
 439             try {
 440                 mappedTypeClass = Class.forName(
 441                     "[L" + paramType.getTypeName() + ";");
 442             } catch (ClassNotFoundException e) {
 443                 final OpenDataException ode =
 444                     new OpenDataException("Array class not found");
 445                 ode.initCause(e);
 446                 throw ode;
 447             }
 448             openType = new ArrayType<>(1, paramType.getOpenType());
 449         }
 450 
 451         Type getJavaType() {
 452             return javaType;
 453         }
 454 
 455         String getName() {
 456             return typeName;
 457         }
 458 
 459         Object toOpenTypeData(Object data) throws OpenDataException {
 460             final List<Object> list = (List<Object>) data;
 461 
 462             final Object[] openArray = (Object[])
 463                 Array.newInstance(paramType.getMappedTypeClass(),
 464                                   list.size());
 465             int i = 0;
 466             for (Object o : list) {
 467                 openArray[i++] = paramType.toOpenTypeData(o);
 468             }
 469             return openArray;
 470         }
 471 
 472         Object toJavaTypeData(Object data)
 473             throws OpenDataException, InvalidObjectException {
 474 
 475             final Object[] openArray = (Object[]) data;
 476             List<Object> result = new ArrayList<>(openArray.length);
 477             for (Object o : openArray) {
 478                 result.add(paramType.toJavaTypeData(o));
 479             }
 480             return result;
 481         }
 482     }
 483 
 484     private static final String KEY   = "key";
 485     private static final String VALUE = "value";
 486     private static final String[] mapIndexNames = {KEY};
 487     private static final String[] mapItemNames = {KEY, VALUE};
 488 
 489     // Map<K,V>
 490     //   Mapped open type - TabularType with row type:
 491     //                        CompositeType:
 492     //                          "key"   of openDataType(K)
 493     //                          "value" of openDataType(V)
 494     //                        "key" is the index name
 495     //
 496     // Data Mapping:
 497     //   Map<K,V> <-> TabularData
 498     //
 499     static class MapMXBeanType extends MappedMXBeanType {
 500         final ParameterizedType javaType;
 501         final MappedMXBeanType keyType;
 502         final MappedMXBeanType valueType;
 503         final String typeName;
 504 
 505         MapMXBeanType(ParameterizedType pt) throws OpenDataException {
 506             this.javaType = pt;
 507 
 508             final Type[] argTypes = pt.getActualTypeArguments();
 509             assert(argTypes.length == 2);
 510             this.keyType = getMappedType(argTypes[0]);
 511             this.valueType = getMappedType(argTypes[1]);
 512 
 513 
 514             // FIXME: generate typeName for generic
 515             typeName = "Map<" + keyType.getName() + "," +
 516                                 valueType.getName() + ">";
 517             final OpenType<?>[] mapItemTypes = new OpenType<?>[] {
 518                                                 keyType.getOpenType(),
 519                                                 valueType.getOpenType(),
 520                                             };
 521             final CompositeType rowType =
 522                 new CompositeType(typeName,
 523                                   typeName,
 524                                   mapItemNames,
 525                                   mapItemNames,
 526                                   mapItemTypes);
 527 
 528             openType = new TabularType(typeName, typeName, rowType, mapIndexNames);
 529             mappedTypeClass = javax.management.openmbean.TabularData.class;
 530         }
 531 
 532         Type getJavaType() {
 533             return javaType;
 534         }
 535 
 536         String getName() {
 537             return typeName;
 538         }
 539 
 540         Object toOpenTypeData(Object data) throws OpenDataException {
 541             final Map<Object,Object> map = (Map<Object,Object>) data;
 542             final TabularType tabularType = (TabularType) openType;
 543             final TabularData table = new TabularDataSupport(tabularType);
 544             final CompositeType rowType = tabularType.getRowType();
 545 
 546             for (Map.Entry<Object, Object> entry : map.entrySet()) {
 547                 final Object key = keyType.toOpenTypeData(entry.getKey());
 548                 final Object value = valueType.toOpenTypeData(entry.getValue());
 549                 final CompositeData row =
 550                     new CompositeDataSupport(rowType,
 551                                              mapItemNames,
 552                                              new Object[] {key, value});
 553                 table.put(row);
 554             }
 555             return table;
 556         }
 557 
 558         Object toJavaTypeData(Object data)
 559             throws OpenDataException, InvalidObjectException {
 560 
 561             final TabularData td = (TabularData) data;
 562 
 563             Map<Object, Object> result = new HashMap<>();
 564             for (CompositeData row : (Collection<CompositeData>) td.values()) {
 565                 Object key = keyType.toJavaTypeData(row.get(KEY));
 566                 Object value = valueType.toJavaTypeData(row.get(VALUE));
 567                 result.put(key, value);
 568             }
 569             return result;
 570         }
 571     }
 572 
 573     private static final Class<?> COMPOSITE_DATA_CLASS =
 574         javax.management.openmbean.CompositeData.class;
 575 
 576     // Classes that have a static from method
 577     //   Mapped open type - CompositeData
 578     //
 579     // Data Mapping:
 580     //   Classes <-> CompositeData
 581     //
 582     // The name and type of items for a class are identified from
 583     // the getter methods. For example, a class defines a method:
 584     //
 585     //    public FooType getFoo();
 586     //
 587     // The composite data view for this class will contain one
 588     // item entry for a "foo" attribute and the item type is
 589     // one of the open types defined in the OpenType class that
 590     // can be determined in the following manner:
 591     // o If FooType is a primitive type, the item type a wrapper
 592     //   class for the corresponding primitive type (such as
 593     //   Integer, Long, Boolean, etc).
 594     // o If FooType is of type CompositeData or TabularData,
 595     //   the item type is FooType.
 596     // o If FooType is an Enum, the item type is a String and
 597     //   the value is the name of the enum constant.
 598     // o If FooType is a class or an interface other than the above,
 599     //   the item type is CompositeData. The same convention
 600     //   can be recursively applied to the FooType class when
 601     //   constructing the composite data for the "foo" attribute.
 602     // o If FooType is an array, the item type is an array and
 603     //   its element type is determined as described above.
 604     //
 605     static class CompositeDataMXBeanType extends MappedMXBeanType {
 606         final Class<?> javaClass;
 607         final boolean isCompositeData;
 608         Method fromMethod = 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             if (COMPOSITE_DATA_CLASS.isAssignableFrom(c)) {
 627                 // c implements CompositeData - set openType to null
 628                 // defer generating the CompositeType
 629                 // until the object is constructed
 630                 this.isCompositeData = true;
 631                 this.openType = null;
 632             } else {
 633                 this.isCompositeData = false;
 634 
 635                 // Make a CompositeData containing all the getters
 636                 final Method[] methods =
 637                     AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
 638                         public Method[] run() {
 639                             return javaClass.getMethods();
 640                         }
 641                     });
 642                 final List<String> names = new ArrayList<>();
 643                 final List<OpenType<?>> types = new ArrayList<>();
 644 
 645                 /* Select public methods that look like "T getX()" or "boolean
 646                    isX()", where T is not void and X is not the empty
 647                    string.  Exclude "Class getClass()" inherited from Object.  */
 648                 for (int i = 0; i < methods.length; i++) {
 649                     final Method method = methods[i];
 650                     final String name = method.getName();
 651                     final Type type = method.getGenericReturnType();
 652                     final String rest;
 653                     if (name.startsWith("get")) {
 654                         rest = name.substring(3);
 655                     } else if (name.startsWith("is") &&
 656                                type instanceof Class &&
 657                                ((Class) type) == boolean.class) {
 658                         rest = name.substring(2);
 659                     } else {
 660                         // ignore non-getter methods
 661                         continue;
 662                     }
 663 
 664                     if (rest.equals("") ||
 665                         method.getParameterTypes().length > 0 ||
 666                         type == void.class ||
 667                         rest.equals("Class")) {
 668 
 669                         // ignore non-getter methods
 670                         continue;
 671                     }
 672                     names.add(decapitalize(rest));
 673                     types.add(toOpenType(type));
 674                 }
 675 
 676                 final String[] nameArray = names.toArray(new String[0]);
 677                 openType = new CompositeType(c.getName(),
 678                                              c.getName(),
 679                                              nameArray, // field names
 680                                              nameArray, // field descriptions
 681                                              types.toArray(new OpenType<?>[0]));
 682             }
 683         }
 684 
 685         Type getJavaType() {
 686             return javaClass;
 687         }
 688 
 689         String getName() {
 690             return javaClass.getName();
 691         }
 692 
 693         Object toOpenTypeData(Object data) throws OpenDataException {
 694             if (data instanceof MemoryUsage) {
 695                 return MemoryUsageCompositeData.toCompositeData((MemoryUsage) data);
 696             }
 697 
 698             if (data instanceof ThreadInfo) {
 699                 return ThreadInfoCompositeData.toCompositeData((ThreadInfo) data);
 700             }
 701 
 702             if (data instanceof LockInfo) {
 703                 if (data instanceof java.lang.management.MonitorInfo) {
 704                     return MonitorInfoCompositeData.toCompositeData((MonitorInfo) data);
 705                 }
 706                 return LockDataConverter.toLockInfoCompositeData((LockInfo) data);
 707             }
 708 
 709             if (data instanceof MemoryNotificationInfo) {
 710                 return MemoryNotifInfoCompositeData.
 711                     toCompositeData((MemoryNotificationInfo) data);
 712             }
 713 
 714             if (data instanceof VMOption) {
 715                 return VMOptionCompositeData.toCompositeData((VMOption) data);
 716             }
 717 
 718             if (isCompositeData) {
 719                 // Classes that implement CompositeData
 720                 //
 721                 // construct a new CompositeDataSupport object
 722                 // so that no other classes are sent over the wire
 723                 CompositeData cd = (CompositeData) data;
 724                 CompositeType ct = cd.getCompositeType();
 725                 String[] itemNames = ct.keySet().toArray(new String[0]);
 726                 Object[] itemValues = cd.getAll(itemNames);
 727                 return new CompositeDataSupport(ct, itemNames, itemValues);
 728             }
 729 
 730             throw new OpenDataException(javaClass.getName() +
 731                 " is not supported for platform MXBeans");
 732         }
 733 
 734         Object toJavaTypeData(Object data)
 735             throws OpenDataException, InvalidObjectException {
 736 
 737             if (fromMethod == null) {
 738                 throw new AssertionError("Does not support data conversion");
 739             }
 740 
 741             try {
 742                 return fromMethod.invoke(null, data);
 743             } catch (IllegalAccessException e) {
 744                 // should never reach here
 745                 throw new AssertionError(e);
 746             } catch (InvocationTargetException e) {
 747                 final OpenDataException ode =
 748                     new OpenDataException("Failed to invoke " +
 749                         fromMethod.getName() + " to convert CompositeData " +
 750                         " to " + javaClass.getName());
 751                 ode.initCause(e);
 752                 throw ode;
 753             }
 754         }
 755     }
 756 
 757     private static class InProgress extends OpenType {
 758         private static final String description =
 759                   "Marker to detect recursive type use -- internal use only!";
 760 
 761         InProgress() throws OpenDataException {
 762             super("java.lang.String", "java.lang.String", description);
 763         }
 764 
 765         public String toString() {
 766             return description;
 767         }
 768 
 769         public int hashCode() {
 770             return 0;
 771         }
 772 
 773         public boolean equals(Object o) {
 774             return false;
 775         }
 776 
 777         public boolean isValue(Object o) {
 778             return false;
 779         }
 780         private static final long serialVersionUID = -3413063475064374490L;
 781     }
 782     private static final OpenType<?> inProgress;
 783     static {
 784         OpenType<?> t;
 785         try {
 786             t = new InProgress();
 787         } catch (OpenDataException e) {
 788             // Should not reach here
 789             throw new AssertionError(e);
 790         }
 791         inProgress = t;
 792     }
 793 
 794     private static final OpenType[] simpleTypes = {
 795         BIGDECIMAL, BIGINTEGER, BOOLEAN, BYTE, CHARACTER, DATE,
 796         DOUBLE, FLOAT, INTEGER, LONG, OBJECTNAME, SHORT, STRING,
 797         VOID,
 798     };
 799     static {
 800         try {
 801             for (int i = 0; i < simpleTypes.length; i++) {
 802                 final OpenType<?> t = simpleTypes[i];
 803                 Class<?> c;
 804                 try {
 805                     c = Class.forName(t.getClassName(), false,
 806                                       MappedMXBeanType.class.getClassLoader());
 807                     MappedMXBeanType.newBasicType(c, t);
 808                 } catch (ClassNotFoundException e) {
 809                     // the classes that these predefined types declare
 810                     // must exist!
 811                     throw new AssertionError(e);
 812                 } catch (OpenDataException e) {
 813                     throw new AssertionError(e);
 814                 }
 815 
 816                 if (c.getName().startsWith("java.lang.")) {
 817                     try {
 818                         final Field typeField = c.getField("TYPE");
 819                         final Class<?> primitiveType = (Class<?>) typeField.get(null);
 820                         MappedMXBeanType.newBasicType(primitiveType, t);
 821                     } catch (NoSuchFieldException e) {
 822                         // OK: must not be a primitive wrapper
 823                     } catch (IllegalAccessException e) {
 824                         // Should not reach here
 825                        throw new AssertionError(e);
 826                     }
 827                 }
 828             }
 829         } catch (OpenDataException e) {
 830             throw new AssertionError(e);
 831         }
 832     }
 833 
 834     /**
 835      * Utility method to take a string and convert it to normal Java variable
 836      * name capitalization.  This normally means converting the first
 837      * character from upper case to lower case, but in the (unusual) special
 838      * case when there is more than one character and both the first and
 839      * second characters are upper case, we leave it alone.
 840      * <p>
 841      * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
 842      * as "URL".
 843      *
 844      * @param  name The string to be decapitalized.
 845      * @return  The decapitalized version of the string.
 846      */
 847     private static String decapitalize(String name) {
 848         if (name == null || name.length() == 0) {
 849             return name;
 850         }
 851         if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
 852                         Character.isUpperCase(name.charAt(0))){
 853             return name;
 854         }
 855         char chars[] = name.toCharArray();
 856         chars[0] = Character.toLowerCase(chars[0]);
 857         return new String(chars);
 858     }
 859 
 860 }