1 /*
2  * Copyright (c) 2005, 2013, 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 import java.io.InvalidObjectException;
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Method;
30 import java.lang.reflect.Type;
31 
32 import javax.management.Descriptor;
33 import javax.management.MBeanException;
34 import javax.management.openmbean.OpenDataException;
35 import javax.management.openmbean.OpenType;
36 import sun.reflect.misc.MethodUtil;
37 
38 final class ConvertingMethod {
from(Method m)39     static ConvertingMethod from(Method m) {
40         try {
41             return new ConvertingMethod(m);
42         } catch (OpenDataException ode) {
43             final String msg = "Method " + m.getDeclaringClass().getName() +
44                 "." + m.getName() + " has parameter or return type that " +
45                 "cannot be translated into an open type";
46             throw new IllegalArgumentException(msg, ode);
47         }
48     }
49 
getMethod()50     Method getMethod() {
51         return method;
52     }
53 
getDescriptor()54     Descriptor getDescriptor() {
55         return Introspector.descriptorForElement(method);
56     }
57 
getGenericReturnType()58     Type getGenericReturnType() {
59         return method.getGenericReturnType();
60     }
61 
getGenericParameterTypes()62     Type[] getGenericParameterTypes() {
63         return method.getGenericParameterTypes();
64     }
65 
getName()66     String getName() {
67         return method.getName();
68     }
69 
getOpenReturnType()70     OpenType<?> getOpenReturnType() {
71         return returnMapping.getOpenType();
72     }
73 
getOpenParameterTypes()74     OpenType<?>[] getOpenParameterTypes() {
75         final OpenType<?>[] types = new OpenType<?>[paramMappings.length];
76         for (int i = 0; i < paramMappings.length; i++)
77             types[i] = paramMappings[i].getOpenType();
78         return types;
79     }
80 
81     /* Check that this method will be callable when we are going from
82      * open types to Java types, for example when we are going from
83      * an MXBean wrapper to the underlying resource.
84      * The parameters will be converted to
85      * Java types, so they must be "reconstructible".  The return
86      * value will be converted to an Open Type, so if it is convertible
87      * at all there is no further check needed.
88      */
checkCallFromOpen()89     void checkCallFromOpen() {
90         try {
91             for (MXBeanMapping paramConverter : paramMappings)
92                 paramConverter.checkReconstructible();
93         } catch (InvalidObjectException e) {
94             throw new IllegalArgumentException(e);
95         }
96     }
97 
98     /* Check that this method will be callable when we are going from
99      * Java types to open types, for example when we are going from
100      * an MXBean proxy to the open types that it will be mapped to.
101      * The return type will be converted back to a Java type, so it
102      * must be "reconstructible".  The parameters will be converted to
103      * open types, so if it is convertible at all there is no further
104      * check needed.
105      */
checkCallToOpen()106     void checkCallToOpen() {
107         try {
108             returnMapping.checkReconstructible();
109         } catch (InvalidObjectException e) {
110             throw new IllegalArgumentException(e);
111         }
112     }
113 
getOpenSignature()114     String[] getOpenSignature() {
115         if (paramMappings.length == 0)
116             return noStrings;
117 
118         String[] sig = new String[paramMappings.length];
119         for (int i = 0; i < paramMappings.length; i++)
120             sig[i] = paramMappings[i].getOpenClass().getName();
121         return sig;
122     }
123 
toOpenReturnValue(MXBeanLookup lookup, Object ret)124     final Object toOpenReturnValue(MXBeanLookup lookup, Object ret)
125             throws OpenDataException {
126         return returnMapping.toOpenValue(ret);
127     }
128 
fromOpenReturnValue(MXBeanLookup lookup, Object ret)129     final Object fromOpenReturnValue(MXBeanLookup lookup, Object ret)
130             throws InvalidObjectException {
131         return returnMapping.fromOpenValue(ret);
132     }
133 
toOpenParameters(MXBeanLookup lookup, Object[] params)134     final Object[] toOpenParameters(MXBeanLookup lookup, Object[] params)
135             throws OpenDataException {
136         if (paramConversionIsIdentity || params == null)
137             return params;
138         final Object[] oparams = new Object[params.length];
139         for (int i = 0; i < params.length; i++)
140             oparams[i] = paramMappings[i].toOpenValue(params[i]);
141         return oparams;
142     }
143 
fromOpenParameters(Object[] params)144     final Object[] fromOpenParameters(Object[] params)
145             throws InvalidObjectException {
146         if (paramConversionIsIdentity || params == null)
147             return params;
148         final Object[] jparams = new Object[params.length];
149         for (int i = 0; i < params.length; i++)
150             jparams[i] = paramMappings[i].fromOpenValue(params[i]);
151         return jparams;
152     }
153 
toOpenParameter(MXBeanLookup lookup, Object param, int paramNo)154     final Object toOpenParameter(MXBeanLookup lookup,
155                                  Object param,
156                                  int paramNo)
157         throws OpenDataException {
158         return paramMappings[paramNo].toOpenValue(param);
159     }
160 
fromOpenParameter(MXBeanLookup lookup, Object param, int paramNo)161     final Object fromOpenParameter(MXBeanLookup lookup,
162                                    Object param,
163                                    int paramNo)
164         throws InvalidObjectException {
165         return paramMappings[paramNo].fromOpenValue(param);
166     }
167 
invokeWithOpenReturn(MXBeanLookup lookup, Object obj, Object[] params)168     Object invokeWithOpenReturn(MXBeanLookup lookup,
169                                 Object obj, Object[] params)
170             throws MBeanException, IllegalAccessException,
171                    InvocationTargetException {
172         MXBeanLookup old = MXBeanLookup.getLookup();
173         try {
174             MXBeanLookup.setLookup(lookup);
175             return invokeWithOpenReturn(obj, params);
176         } finally {
177             MXBeanLookup.setLookup(old);
178         }
179     }
180 
invokeWithOpenReturn(Object obj, Object[] params)181     private Object invokeWithOpenReturn(Object obj, Object[] params)
182             throws MBeanException, IllegalAccessException,
183                    InvocationTargetException {
184         final Object[] javaParams;
185         try {
186             javaParams = fromOpenParameters(params);
187         } catch (InvalidObjectException e) {
188             // probably can't happen
189             final String msg = methodName() + ": cannot convert parameters " +
190                 "from open values: " + e;
191             throw new MBeanException(e, msg);
192         }
193         final Object javaReturn = MethodUtil.invoke(method, obj, javaParams);
194         try {
195             return returnMapping.toOpenValue(javaReturn);
196         } catch (OpenDataException e) {
197             // probably can't happen
198             final String msg = methodName() + ": cannot convert return " +
199                 "value to open value: " + e;
200             throw new MBeanException(e, msg);
201         }
202     }
203 
methodName()204     private String methodName() {
205         return method.getDeclaringClass() + "." + method.getName();
206     }
207 
ConvertingMethod(Method m)208     private ConvertingMethod(Method m) throws OpenDataException {
209         this.method = m;
210         MXBeanMappingFactory mappingFactory = MXBeanMappingFactory.DEFAULT;
211         returnMapping =
212                 mappingFactory.mappingForType(m.getGenericReturnType(), mappingFactory);
213         Type[] params = m.getGenericParameterTypes();
214         paramMappings = new MXBeanMapping[params.length];
215         boolean identity = true;
216         for (int i = 0; i < params.length; i++) {
217             paramMappings[i] = mappingFactory.mappingForType(params[i], mappingFactory);
218             identity &= DefaultMXBeanMappingFactory.isIdentity(paramMappings[i]);
219         }
220         paramConversionIsIdentity = identity;
221     }
222 
223     private static final String[] noStrings = new String[0];
224 
225     private final Method method;
226     private final MXBeanMapping returnMapping;
227     private final MXBeanMapping[] paramMappings;
228     private final boolean paramConversionIsIdentity;
229 }
230