1 /* 2 * Copyright (c) 2005, 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 com.sun.jmx.mbeanserver; 27 28 import com.sun.jmx.mbeanserver.MBeanIntrospector.MBeanInfoMap; 29 import com.sun.jmx.mbeanserver.MBeanIntrospector.PerInterfaceMap; 30 import java.lang.annotation.Annotation; 31 import java.lang.reflect.GenericArrayType; 32 import java.lang.reflect.InvocationTargetException; 33 import java.lang.reflect.Method; 34 import java.lang.reflect.ParameterizedType; 35 import java.lang.reflect.Type; 36 import javax.management.Descriptor; 37 import javax.management.ImmutableDescriptor; 38 import javax.management.MBeanAttributeInfo; 39 import javax.management.MBeanException; 40 import javax.management.MBeanOperationInfo; 41 import javax.management.MBeanParameterInfo; 42 import javax.management.NotCompliantMBeanException; 43 import javax.management.openmbean.OpenMBeanAttributeInfoSupport; 44 import javax.management.openmbean.OpenMBeanOperationInfoSupport; 45 import javax.management.openmbean.OpenMBeanParameterInfo; 46 import javax.management.openmbean.OpenMBeanParameterInfoSupport; 47 import javax.management.openmbean.OpenType; 48 49 /** 50 * Introspector for MXBeans. There is exactly one instance of this class. 51 * 52 * @since 1.6 53 */ 54 class MXBeanIntrospector extends MBeanIntrospector<ConvertingMethod> { 55 private static final MXBeanIntrospector instance = new MXBeanIntrospector(); 56 getInstance()57 static MXBeanIntrospector getInstance() { 58 return instance; 59 } 60 61 @Override getPerInterfaceMap()62 PerInterfaceMap<ConvertingMethod> getPerInterfaceMap() { 63 return perInterfaceMap; 64 } 65 66 @Override getMBeanInfoMap()67 MBeanInfoMap getMBeanInfoMap() { 68 return mbeanInfoMap; 69 } 70 71 @Override getAnalyzer(Class<?> mbeanInterface)72 MBeanAnalyzer<ConvertingMethod> getAnalyzer(Class<?> mbeanInterface) 73 throws NotCompliantMBeanException { 74 return MBeanAnalyzer.analyzer(mbeanInterface, this); 75 } 76 77 @Override isMXBean()78 boolean isMXBean() { 79 return true; 80 } 81 82 @Override mFrom(Method m)83 ConvertingMethod mFrom(Method m) { 84 return ConvertingMethod.from(m); 85 } 86 87 @Override getName(ConvertingMethod m)88 String getName(ConvertingMethod m) { 89 return m.getName(); 90 } 91 92 @Override getGenericReturnType(ConvertingMethod m)93 Type getGenericReturnType(ConvertingMethod m) { 94 return m.getGenericReturnType(); 95 } 96 97 @Override getGenericParameterTypes(ConvertingMethod m)98 Type[] getGenericParameterTypes(ConvertingMethod m) { 99 return m.getGenericParameterTypes(); 100 } 101 102 @Override getSignature(ConvertingMethod m)103 String[] getSignature(ConvertingMethod m) { 104 return m.getOpenSignature(); 105 } 106 107 @Override checkMethod(ConvertingMethod m)108 void checkMethod(ConvertingMethod m) { 109 m.checkCallFromOpen(); 110 } 111 112 @Override invokeM2(ConvertingMethod m, Object target, Object[] args, Object cookie)113 Object invokeM2(ConvertingMethod m, Object target, Object[] args, 114 Object cookie) 115 throws InvocationTargetException, IllegalAccessException, 116 MBeanException { 117 return m.invokeWithOpenReturn((MXBeanLookup) cookie, target, args); 118 } 119 120 @Override validParameter(ConvertingMethod m, Object value, int paramNo, Object cookie)121 boolean validParameter(ConvertingMethod m, Object value, int paramNo, 122 Object cookie) { 123 if (value == null) { 124 // Null is a valid value for all OpenTypes, even though 125 // OpenType.isValue(null) will return false. It can always be 126 // matched to the corresponding Java type, except when that 127 // type is primitive. 128 Type t = m.getGenericParameterTypes()[paramNo]; 129 return (!(t instanceof Class<?>) || !((Class<?>) t).isPrimitive()); 130 } else { 131 Object v; 132 try { 133 v = m.fromOpenParameter((MXBeanLookup) cookie, value, paramNo); 134 } catch (Exception e) { 135 // Ignore the exception and let MBeanIntrospector.invokeSetter() 136 // throw the initial exception. 137 return true; 138 } 139 return isValidParameter(m.getMethod(), v, paramNo); 140 } 141 } 142 143 @Override getMBeanAttributeInfo(String attributeName, ConvertingMethod getter, ConvertingMethod setter)144 MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, 145 ConvertingMethod getter, ConvertingMethod setter) { 146 147 final boolean isReadable = (getter != null); 148 final boolean isWritable = (setter != null); 149 final boolean isIs = isReadable && getName(getter).startsWith("is"); 150 151 final String description = attributeName; 152 153 final OpenType<?> openType; 154 final Type originalType; 155 if (isReadable) { 156 openType = getter.getOpenReturnType(); 157 originalType = getter.getGenericReturnType(); 158 } else { 159 openType = setter.getOpenParameterTypes()[0]; 160 originalType = setter.getGenericParameterTypes()[0]; 161 } 162 Descriptor descriptor = typeDescriptor(openType, originalType); 163 if (isReadable) { 164 descriptor = ImmutableDescriptor.union(descriptor, 165 getter.getDescriptor()); 166 } 167 if (isWritable) { 168 descriptor = ImmutableDescriptor.union(descriptor, 169 setter.getDescriptor()); 170 } 171 172 final MBeanAttributeInfo ai; 173 if (canUseOpenInfo(originalType)) { 174 ai = new OpenMBeanAttributeInfoSupport(attributeName, 175 description, 176 openType, 177 isReadable, 178 isWritable, 179 isIs, 180 descriptor); 181 } else { 182 ai = new MBeanAttributeInfo(attributeName, 183 originalTypeString(originalType), 184 description, 185 isReadable, 186 isWritable, 187 isIs, 188 descriptor); 189 } 190 // could also consult annotations for defaultValue, 191 // minValue, maxValue, legalValues 192 193 return ai; 194 } 195 196 @Override getMBeanOperationInfo(String operationName, ConvertingMethod operation)197 MBeanOperationInfo getMBeanOperationInfo(String operationName, 198 ConvertingMethod operation) { 199 final Method method = operation.getMethod(); 200 final String description = operationName; 201 /* Ideally this would be an empty string, but 202 OMBOperationInfo constructor forbids that. Also, we 203 could consult an annotation to get a useful 204 description. */ 205 206 final int impact = MBeanOperationInfo.UNKNOWN; 207 208 final OpenType<?> returnType = operation.getOpenReturnType(); 209 final Type originalReturnType = operation.getGenericReturnType(); 210 final OpenType<?>[] paramTypes = operation.getOpenParameterTypes(); 211 final Type[] originalParamTypes = operation.getGenericParameterTypes(); 212 final MBeanParameterInfo[] params = 213 new MBeanParameterInfo[paramTypes.length]; 214 boolean openReturnType = canUseOpenInfo(originalReturnType); 215 boolean openParameterTypes = true; 216 Annotation[][] annots = method.getParameterAnnotations(); 217 for (int i = 0; i < paramTypes.length; i++) { 218 final String paramName = "p" + i; 219 final String paramDescription = paramName; 220 final OpenType<?> openType = paramTypes[i]; 221 final Type originalType = originalParamTypes[i]; 222 Descriptor descriptor = 223 typeDescriptor(openType, originalType); 224 descriptor = ImmutableDescriptor.union(descriptor, 225 Introspector.descriptorForAnnotations(annots[i])); 226 final MBeanParameterInfo pi; 227 if (canUseOpenInfo(originalType)) { 228 pi = new OpenMBeanParameterInfoSupport(paramName, 229 paramDescription, 230 openType, 231 descriptor); 232 } else { 233 openParameterTypes = false; 234 pi = new MBeanParameterInfo( 235 paramName, 236 originalTypeString(originalType), 237 paramDescription, 238 descriptor); 239 } 240 params[i] = pi; 241 } 242 243 Descriptor descriptor = 244 typeDescriptor(returnType, originalReturnType); 245 descriptor = ImmutableDescriptor.union(descriptor, 246 Introspector.descriptorForElement(method)); 247 final MBeanOperationInfo oi; 248 if (openReturnType && openParameterTypes) { 249 /* If the return value and all the parameters can be faithfully 250 * represented as OpenType then we return an OpenMBeanOperationInfo. 251 * If any of them is a primitive type, we can't. Compatibility 252 * with JSR 174 means that we must return an MBean*Info where 253 * the getType() is the primitive type, not its wrapped type as 254 * we would get with an OpenMBean*Info. The OpenType is available 255 * in the Descriptor in either case. 256 */ 257 final OpenMBeanParameterInfo[] oparams = 258 new OpenMBeanParameterInfo[params.length]; 259 System.arraycopy(params, 0, oparams, 0, params.length); 260 oi = new OpenMBeanOperationInfoSupport(operationName, 261 description, 262 oparams, 263 returnType, 264 impact, 265 descriptor); 266 } else { 267 oi = new MBeanOperationInfo(operationName, 268 description, 269 params, 270 openReturnType ? 271 returnType.getClassName() : 272 originalTypeString(originalReturnType), 273 impact, 274 descriptor); 275 } 276 277 return oi; 278 } 279 280 @Override getBasicMBeanDescriptor()281 Descriptor getBasicMBeanDescriptor() { 282 return new ImmutableDescriptor("mxbean=true", 283 "immutableInfo=true"); 284 } 285 286 @Override getMBeanDescriptor(Class<?> resourceClass)287 Descriptor getMBeanDescriptor(Class<?> resourceClass) { 288 /* We already have immutableInfo=true in the Descriptor 289 * included in the MBeanInfo for the MXBean interface. This 290 * method is being called for the MXBean *class* to add any 291 * new items beyond those in the interface Descriptor, which 292 * currently it does not. 293 */ 294 return ImmutableDescriptor.EMPTY_DESCRIPTOR; 295 } 296 typeDescriptor(OpenType<?> openType, Type originalType)297 private static Descriptor typeDescriptor(OpenType<?> openType, 298 Type originalType) { 299 return new ImmutableDescriptor( 300 new String[] {"openType", 301 "originalType"}, 302 new Object[] {openType, 303 originalTypeString(originalType)}); 304 } 305 306 /** 307 * <p>True if this type can be faithfully represented in an 308 * OpenMBean*Info.</p> 309 * 310 * <p>Compatibility with JSR 174 means that primitive types must be 311 * represented by an MBean*Info whose getType() is the primitive type 312 * string, e.g. "int". If we used an OpenMBean*Info then this string 313 * would be the wrapped type, e.g. "java.lang.Integer".</p> 314 * 315 * <p>Compatibility with JMX 1.2 (including J2SE 5.0) means that arrays 316 * of primitive types cannot use an ArrayType representing an array of 317 * primitives, because that didn't exist in JMX 1.2.</p> 318 */ canUseOpenInfo(Type type)319 private static boolean canUseOpenInfo(Type type) { 320 if (type instanceof GenericArrayType) { 321 return canUseOpenInfo( 322 ((GenericArrayType) type).getGenericComponentType()); 323 } else if (type instanceof Class<?> && ((Class<?>) type).isArray()) { 324 return canUseOpenInfo( 325 ((Class<?>) type).getComponentType()); 326 } 327 return (!(type instanceof Class<?> && ((Class<?>) type).isPrimitive())); 328 } 329 originalTypeString(Type type)330 private static String originalTypeString(Type type) { 331 if (type instanceof Class<?>) 332 return ((Class<?>) type).getName(); 333 else 334 return typeName(type); 335 } 336 typeName(Type type)337 static String typeName(Type type) { 338 if (type instanceof Class<?>) { 339 Class<?> c = (Class<?>) type; 340 if (c.isArray()) 341 return typeName(c.getComponentType()) + "[]"; 342 else 343 return c.getName(); 344 } else if (type instanceof GenericArrayType) { 345 GenericArrayType gat = (GenericArrayType) type; 346 return typeName(gat.getGenericComponentType()) + "[]"; 347 } else if (type instanceof ParameterizedType) { 348 ParameterizedType pt = (ParameterizedType) type; 349 StringBuilder sb = new StringBuilder(); 350 sb.append(typeName(pt.getRawType())).append("<"); 351 String sep = ""; 352 for (Type t : pt.getActualTypeArguments()) { 353 sb.append(sep).append(typeName(t)); 354 sep = ", "; 355 } 356 return sb.append(">").toString(); 357 } else 358 return "???"; 359 } 360 361 private final PerInterfaceMap<ConvertingMethod> 362 perInterfaceMap = new PerInterfaceMap<ConvertingMethod>(); 363 364 private static final MBeanInfoMap mbeanInfoMap = new MBeanInfoMap(); 365 } 366