View Javadoc
1   /*
2    * Copyright (c) 2007, 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 com.sun.jmx.mbeanserver;
27  
28  import java.io.InvalidObjectException;
29  import java.lang.reflect.Type;
30  import javax.management.openmbean.OpenDataException;
31  import javax.management.openmbean.OpenType;
32  
33  /**
34   * <p>A custom mapping between Java types and Open types for use in MXBeans.
35   * To define such a mapping, subclass this class and define at least the
36   * {@link #fromOpenValue fromOpenValue} and {@link #toOpenValue toOpenValue}
37   * methods, and optionally the {@link #checkReconstructible} method.
38   * Then either use an {@link MXBeanMappingClass} annotation on your custom
39   * Java types, or include this MXBeanMapping in an
40   * {@link MXBeanMappingFactory}.</p>
41   *
42   * <p>For example, suppose we have a class {@code MyLinkedList}, which looks
43   * like this:</p>
44   *
45   * <pre>
46   * public class MyLinkedList {
47   *     public MyLinkedList(String name, MyLinkedList next) {...}
48   *     public String getName() {...}
49   *     public MyLinkedList getNext() {...}
50   * }
51   * </pre>
52   *
53   * <p>This is not a valid type for MXBeans, because it contains a
54   * self-referential property "next" defined by the {@code getNext()}
55   * method.  MXBeans do not support recursive types.  So we would like
56   * to specify a mapping for {@code MyLinkedList} explicitly. When an
57   * MXBean interface contains {@code MyLinkedList}, that will be mapped
58   * into a {@code String[]}, which is a valid Open Type.</p>
59   *
60   * <p>To define this mapping, we first subclass {@code MXBeanMapping}:</p>
61   *
62   * <pre>
63   * public class MyLinkedListMapping extends MXBeanMapping {
64   *     public MyLinkedListMapping(Type type) throws OpenDataException {
65   *         super(MyLinkedList.class, ArrayType.getArrayType(SimpleType.STRING));
66   *         if (type != MyLinkedList.class)
67   *             throw new OpenDataException("Mapping only valid for MyLinkedList");
68   *     }
69   *
70   *     {@literal @Override}
71   *     public Object fromOpenValue(Object openValue) throws InvalidObjectException {
72   *         String[] array = (String[]) openValue;
73   *         MyLinkedList list = null;
74   *         for (int i = array.length - 1; i &gt;= 0; i--)
75   *             list = new MyLinkedList(array[i], list);
76   *         return list;
77   *     }
78   *
79   *     {@literal @Override}
80   *     public Object toOpenValue(Object javaValue) throws OpenDataException {
81   *         ArrayList&lt;String&gt; array = new ArrayList&lt;String&gt;();
82   *         for (MyLinkedList list = (MyLinkedList) javaValue; list != null;
83   *              list = list.getNext())
84   *             array.add(list.getName());
85   *         return array.toArray(new String[0]);
86   *     }
87   * }
88   * </pre>
89   *
90   * <p>The call to the superclass constructor specifies what the
91   * original Java type is ({@code MyLinkedList.class}) and what Open
92   * Type it is mapped to ({@code
93   * ArrayType.getArrayType(SimpleType.STRING)}). The {@code
94   * fromOpenValue} method says how we go from the Open Type ({@code
95   * String[]}) to the Java type ({@code MyLinkedList}), and the {@code
96   * toOpenValue} method says how we go from the Java type to the Open
97   * Type.</p>
98   *
99   * <p>With this mapping defined, we can annotate the {@code MyLinkedList}
100  * class appropriately:</p>
101  *
102  * <pre>
103  * {@literal @MXBeanMappingClass}(MyLinkedListMapping.class)
104  * public class MyLinkedList {...}
105  * </pre>
106  *
107  * <p>Now we can use {@code MyLinkedList} in an MXBean interface and it
108  * will work.</p>
109  *
110  * <p>If we are unable to modify the {@code MyLinkedList} class,
111  * we can define an {@link MXBeanMappingFactory}.  See the documentation
112  * of that class for further details.</p>
113  *
114  * @see <a href="../MXBean.html#custom">MXBean specification, section
115  * "Custom MXBean type mappings"</a>
116  */
117 public abstract class MXBeanMapping {
118     private final Type javaType;
119     private final OpenType<?> openType;
120     private final Class<?> openClass;
121 
122     /**
123      * <p>Construct a mapping between the given Java type and the given
124      * Open Type.</p>
125      *
126      * @param javaType the Java type (for example, {@code MyLinkedList}).
127      * @param openType the Open Type (for example, {@code
128      * ArrayType.getArrayType(SimpleType.STRING)})
129      *
130      * @throws NullPointerException if either argument is null.
131      */
132     protected MXBeanMapping(Type javaType, OpenType<?> openType) {
133         if (javaType == null || openType == null)
134             throw new NullPointerException("Null argument");
135         this.javaType = javaType;
136         this.openType = openType;
137         this.openClass = makeOpenClass(javaType, openType);
138     }
139 
140     /**
141      * <p>The Java type that was supplied to the constructor.</p>
142      * @return the Java type that was supplied to the constructor.
143      */
144     public final Type getJavaType() {
145         return javaType;
146     }
147 
148     /**
149      * <p>The Open Type that was supplied to the constructor.</p>
150      * @return the Open Type that was supplied to the constructor.
151      */
152     public final OpenType<?> getOpenType() {
153         return openType;
154     }
155 
156     /**
157      * <p>The Java class that corresponds to instances of the
158      * {@linkplain #getOpenType() Open Type} for this mapping.</p>
159      * @return the Java class that corresponds to instances of the
160      * Open Type for this mapping.
161      * @see OpenType#getClassName
162      */
163     public final Class<?> getOpenClass() {
164         return openClass;
165     }
166 
167     private static Class<?> makeOpenClass(Type javaType, OpenType<?> openType) {
168         if (javaType instanceof Class<?> && ((Class<?>) javaType).isPrimitive())
169             return (Class<?>) javaType;
170         try {
171             String className = openType.getClassName();
172             return Class.forName(className, false, MXBeanMapping.class.getClassLoader());
173         } catch (ClassNotFoundException e) {
174             throw new RuntimeException(e);  // should not happen
175         }
176     }
177 
178     /**
179      * <p>Convert an instance of the Open Type into the Java type.
180      * @param openValue the value to be converted.
181      * @return the converted value.
182      * @throws InvalidObjectException if the value cannot be converted.
183      */
184     public abstract Object fromOpenValue(Object openValue)
185     throws InvalidObjectException;
186 
187     /**
188      * <p>Convert an instance of the Java type into the Open Type.
189      * @param javaValue the value to be converted.
190      * @return the converted value.
191      * @throws OpenDataException if the value cannot be converted.
192      */
193     public abstract Object toOpenValue(Object javaValue)
194     throws OpenDataException;
195 
196 
197     /**
198      * <p>Throw an appropriate InvalidObjectException if we will not
199      * be able to convert back from the open data to the original Java
200      * object.  The {@link #fromOpenValue fromOpenValue} throws an
201      * exception if a given open data value cannot be converted.  This
202      * method throws an exception if <em>no</em> open data values can
203      * be converted.  The default implementation of this method never
204      * throws an exception.  Subclasses can override it as
205      * appropriate.</p>
206      * @throws InvalidObjectException if {@code fromOpenValue} will throw
207      * an exception no matter what its argument is.
208      */
209     public void checkReconstructible() throws InvalidObjectException {}
210 }