View Javadoc
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         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 LockInfoCompositeData.toCompositeData((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 }