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