/* * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jmx.mbeanserver; import com.sun.jmx.mbeanserver.MBeanIntrospector.MBeanInfoMap; import com.sun.jmx.mbeanserver.MBeanIntrospector.PerInterfaceMap; import java.lang.annotation.Annotation; import java.lang.reflect.GenericArrayType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import javax.management.Descriptor; import javax.management.ImmutableDescriptor; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.NotCompliantMBeanException; import javax.management.openmbean.OpenMBeanAttributeInfoSupport; import javax.management.openmbean.OpenMBeanOperationInfoSupport; import javax.management.openmbean.OpenMBeanParameterInfo; import javax.management.openmbean.OpenMBeanParameterInfoSupport; import javax.management.openmbean.OpenType; /** * Introspector for MXBeans. There is exactly one instance of this class. * * @since 1.6 */ class MXBeanIntrospector extends MBeanIntrospector<ConvertingMethod> { private static final MXBeanIntrospector instance = new MXBeanIntrospector(); static MXBeanIntrospector getInstance() { return instance; } @Override PerInterfaceMap<ConvertingMethod> getPerInterfaceMap() { return perInterfaceMap; } @Override MBeanInfoMap getMBeanInfoMap() { return mbeanInfoMap; } @Override MBeanAnalyzer<ConvertingMethod> getAnalyzer(Class<?> mbeanInterface) throws NotCompliantMBeanException { return MBeanAnalyzer.analyzer(mbeanInterface, this); } @Override boolean isMXBean() { return true; } @Override ConvertingMethod mFrom(Method m) { return ConvertingMethod.from(m); } @Override String getName(ConvertingMethod m) { return m.getName(); } @Override Type getGenericReturnType(ConvertingMethod m) { return m.getGenericReturnType(); } @Override Type[] getGenericParameterTypes(ConvertingMethod m) { return m.getGenericParameterTypes(); } @Override String[] getSignature(ConvertingMethod m) { return m.getOpenSignature(); } @Override void checkMethod(ConvertingMethod m) { m.checkCallFromOpen(); } @Override Object invokeM2(ConvertingMethod m, Object target, Object[] args, Object cookie) throws InvocationTargetException, IllegalAccessException, MBeanException { return m.invokeWithOpenReturn((MXBeanLookup) cookie, target, args); } @Override boolean validParameter(ConvertingMethod m, Object value, int paramNo, Object cookie) { if (value == null) { // Null is a valid value for all OpenTypes, even though // OpenType.isValue(null) will return false. It can always be // matched to the corresponding Java type, except when that // type is primitive. Type t = m.getGenericParameterTypes()[paramNo]; return (!(t instanceof Class<?>) || !((Class<?>) t).isPrimitive()); } else { Object v; try { v = m.fromOpenParameter((MXBeanLookup) cookie, value, paramNo); } catch (Exception e) { // Ignore the exception and let MBeanIntrospector.invokeSetter() // throw the initial exception. return true; } return isValidParameter(m.getMethod(), v, paramNo); } } @Override MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, ConvertingMethod getter, ConvertingMethod setter) { final boolean isReadable = (getter != null); final boolean isWritable = (setter != null); final boolean isIs = isReadable && getName(getter).startsWith("is"); final String description = attributeName; final OpenType<?> openType; final Type originalType; if (isReadable) { openType = getter.getOpenReturnType(); originalType = getter.getGenericReturnType(); } else { openType = setter.getOpenParameterTypes()[0]; originalType = setter.getGenericParameterTypes()[0]; } Descriptor descriptor = typeDescriptor(openType, originalType); if (isReadable) { descriptor = ImmutableDescriptor.union(descriptor, getter.getDescriptor()); } if (isWritable) { descriptor = ImmutableDescriptor.union(descriptor, setter.getDescriptor()); } final MBeanAttributeInfo ai; if (canUseOpenInfo(originalType)) { ai = new OpenMBeanAttributeInfoSupport(attributeName, description, openType, isReadable, isWritable, isIs, descriptor); } else { ai = new MBeanAttributeInfo(attributeName, originalTypeString(originalType), description, isReadable, isWritable, isIs, descriptor); } // could also consult annotations for defaultValue, // minValue, maxValue, legalValues return ai; } @Override MBeanOperationInfo getMBeanOperationInfo(String operationName, ConvertingMethod operation) { final Method method = operation.getMethod(); final String description = operationName; /* Ideally this would be an empty string, but OMBOperationInfo constructor forbids that. Also, we could consult an annotation to get a useful description. */ final int impact = MBeanOperationInfo.UNKNOWN; final OpenType<?> returnType = operation.getOpenReturnType(); final Type originalReturnType = operation.getGenericReturnType(); final OpenType<?>[] paramTypes = operation.getOpenParameterTypes(); final Type[] originalParamTypes = operation.getGenericParameterTypes(); final MBeanParameterInfo[] params = new MBeanParameterInfo[paramTypes.length]; boolean openReturnType = canUseOpenInfo(originalReturnType); boolean openParameterTypes = true; Annotation[][] annots = method.getParameterAnnotations(); for (int i = 0; i < paramTypes.length; i++) { final String paramName = "p" + i; final String paramDescription = paramName; final OpenType<?> openType = paramTypes[i]; final Type originalType = originalParamTypes[i]; Descriptor descriptor = typeDescriptor(openType, originalType); descriptor = ImmutableDescriptor.union(descriptor, Introspector.descriptorForAnnotations(annots[i])); final MBeanParameterInfo pi; if (canUseOpenInfo(originalType)) { pi = new OpenMBeanParameterInfoSupport(paramName, paramDescription, openType, descriptor); } else { openParameterTypes = false; pi = new MBeanParameterInfo( paramName, originalTypeString(originalType), paramDescription, descriptor); } params[i] = pi; } Descriptor descriptor = typeDescriptor(returnType, originalReturnType); descriptor = ImmutableDescriptor.union(descriptor, Introspector.descriptorForElement(method)); final MBeanOperationInfo oi; if (openReturnType && openParameterTypes) { /* If the return value and all the parameters can be faithfully * represented as OpenType then we return an OpenMBeanOperationInfo. * If any of them is a primitive type, we can't. Compatibility * with JSR 174 means that we must return an MBean*Info where * the getType() is the primitive type, not its wrapped type as * we would get with an OpenMBean*Info. The OpenType is available * in the Descriptor in either case. */ final OpenMBeanParameterInfo[] oparams = new OpenMBeanParameterInfo[params.length]; System.arraycopy(params, 0, oparams, 0, params.length); oi = new OpenMBeanOperationInfoSupport(operationName, description, oparams, returnType, impact, descriptor); } else { oi = new MBeanOperationInfo(operationName, description, params, openReturnType ? returnType.getClassName() : originalTypeString(originalReturnType), impact, descriptor); } return oi; } @Override Descriptor getBasicMBeanDescriptor() { return new ImmutableDescriptor("mxbean=true", "immutableInfo=true"); } @Override Descriptor getMBeanDescriptor(Class<?> resourceClass) { /* We already have immutableInfo=true in the Descriptor * included in the MBeanInfo for the MXBean interface. This * method is being called for the MXBean *class* to add any * new items beyond those in the interface Descriptor, which * currently it does not. */ return ImmutableDescriptor.EMPTY_DESCRIPTOR; } private static Descriptor typeDescriptor(OpenType<?> openType, Type originalType) { return new ImmutableDescriptor( new String[] {"openType", "originalType"}, new Object[] {openType, originalTypeString(originalType)}); } /** * <p>True if this type can be faithfully represented in an * OpenMBean*Info.</p> * * <p>Compatibility with JSR 174 means that primitive types must be * represented by an MBean*Info whose getType() is the primitive type * string, e.g. "int". If we used an OpenMBean*Info then this string * would be the wrapped type, e.g. "java.lang.Integer".</p> * * <p>Compatibility with JMX 1.2 (including J2SE 5.0) means that arrays * of primitive types cannot use an ArrayType representing an array of * primitives, because that didn't exist in JMX 1.2.</p> */ private static boolean canUseOpenInfo(Type type) { if (type instanceof GenericArrayType) { return canUseOpenInfo( ((GenericArrayType) type).getGenericComponentType()); } else if (type instanceof Class<?> && ((Class<?>) type).isArray()) { return canUseOpenInfo( ((Class<?>) type).getComponentType()); } return (!(type instanceof Class<?> && ((Class<?>) type).isPrimitive())); } private static String originalTypeString(Type type) { if (type instanceof Class<?>) return ((Class<?>) type).getName(); else return typeName(type); } static String typeName(Type type) { if (type instanceof Class<?>) { Class<?> c = (Class<?>) type; if (c.isArray()) return typeName(c.getComponentType()) + "[]"; else return c.getName(); } else if (type instanceof GenericArrayType) { GenericArrayType gat = (GenericArrayType) type; return typeName(gat.getGenericComponentType()) + "[]"; } else if (type instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) type; StringBuilder sb = new StringBuilder(); sb.append(typeName(pt.getRawType())).append("<"); String sep = ""; for (Type t : pt.getActualTypeArguments()) { sb.append(sep).append(typeName(t)); sep = ", "; } return sb.append(">").toString(); } else return "???"; } private final PerInterfaceMap<ConvertingMethod> perInterfaceMap = new PerInterfaceMap<ConvertingMethod>(); private static final MBeanInfoMap mbeanInfoMap = new MBeanInfoMap(); }