Path: blob/master/src/java.management/share/classes/com/sun/jmx/mbeanserver/MBeanAnalyzer.java
41161 views
/*1* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.jmx.mbeanserver;2627import static com.sun.jmx.mbeanserver.Util.*;2829import java.lang.reflect.Method;30import java.lang.reflect.Modifier;31import java.security.AccessController;32import java.util.Arrays;33import java.util.Comparator;34import java.util.List;35import java.util.Map;36import java.util.Set;37import javax.management.NotCompliantMBeanException;3839/**40* <p>An analyzer for a given MBean interface. The analyzer can41* be for Standard MBeans or MXBeans, depending on the MBeanIntrospector42* passed at construction.43*44* <p>The analyzer can45* visit the attributes and operations of the interface, calling46* a caller-supplied visitor method for each one.</p>47*48* @param <M> Method or ConvertingMethod according as this is a49* Standard MBean or an MXBean.50*51* @since 1.652*/53class MBeanAnalyzer<M> {54static interface MBeanVisitor<M> {55public void visitAttribute(String attributeName,56M getter,57M setter);58public void visitOperation(String operationName,59M operation);60}6162void visit(MBeanVisitor<M> visitor) {63// visit attributes64for (Map.Entry<String, AttrMethods<M>> entry : attrMap.entrySet()) {65String name = entry.getKey();66AttrMethods<M> am = entry.getValue();67visitor.visitAttribute(name, am.getter, am.setter);68}6970// visit operations71for (Map.Entry<String, List<M>> entry : opMap.entrySet()) {72for (M m : entry.getValue())73visitor.visitOperation(entry.getKey(), m);74}75}7677/* Map op name to method */78private Map<String, List<M>> opMap = newInsertionOrderMap();79/* Map attr name to getter and/or setter */80private Map<String, AttrMethods<M>> attrMap = newInsertionOrderMap();8182private static class AttrMethods<M> {83M getter;84M setter;85}8687/**88* <p>Return an MBeanAnalyzer for the given MBean interface and89* MBeanIntrospector. Calling this method twice with the same90* parameters may return the same object or two different but91* equivalent objects.92*/93// Currently it's two different but equivalent objects. This only94// really impacts proxy generation. For MBean creation, the95// cached PerInterface object for an MBean interface means that96// an analyzer will not be recreated for a second MBean using the97// same interface.98static <M> MBeanAnalyzer<M> analyzer(Class<?> mbeanType,99MBeanIntrospector<M> introspector)100throws NotCompliantMBeanException {101return new MBeanAnalyzer<M>(mbeanType, introspector);102}103104private MBeanAnalyzer(Class<?> mbeanType,105MBeanIntrospector<M> introspector)106throws NotCompliantMBeanException {107if (!mbeanType.isInterface()) {108throw new NotCompliantMBeanException("Not an interface: " +109mbeanType.getName());110} else if (!Modifier.isPublic(mbeanType.getModifiers()) &&111!Introspector.ALLOW_NONPUBLIC_MBEAN) {112throw new NotCompliantMBeanException("Interface is not public: " +113mbeanType.getName());114}115116try {117initMaps(mbeanType, introspector);118} catch (Exception x) {119throw Introspector.throwException(mbeanType,x);120}121}122123// Introspect the mbeanInterface and initialize this object's maps.124//125private void initMaps(Class<?> mbeanType,126MBeanIntrospector<M> introspector) throws Exception {127final List<Method> methods1 = introspector.getMethods(mbeanType);128final List<Method> methods = eliminateCovariantMethods(methods1);129130/* Run through the methods to detect inconsistencies and to enable131us to give getter and setter together to visitAttribute. */132for (Method m : methods) {133final String name = m.getName();134final int nParams = m.getParameterTypes().length;135136final M cm = introspector.mFrom(m);137138String attrName = "";139if (name.startsWith("get"))140attrName = name.substring(3);141else if (name.startsWith("is")142&& m.getReturnType() == boolean.class)143attrName = name.substring(2);144145if (attrName.length() != 0 && nParams == 0146&& m.getReturnType() != void.class) {147// It's a getter148// Check we don't have both isX and getX149AttrMethods<M> am = attrMap.get(attrName);150if (am == null)151am = new AttrMethods<M>();152else {153if (am.getter != null) {154final String msg = "Attribute " + attrName +155" has more than one getter";156throw new NotCompliantMBeanException(msg);157}158}159am.getter = cm;160attrMap.put(attrName, am);161} else if (name.startsWith("set") && name.length() > 3162&& nParams == 1 &&163m.getReturnType() == void.class) {164// It's a setter165attrName = name.substring(3);166AttrMethods<M> am = attrMap.get(attrName);167if (am == null)168am = new AttrMethods<M>();169else if (am.setter != null) {170final String msg = "Attribute " + attrName +171" has more than one setter";172throw new NotCompliantMBeanException(msg);173}174am.setter = cm;175attrMap.put(attrName, am);176} else {177// It's an operation178List<M> cms = opMap.get(name);179if (cms == null)180cms = newList();181cms.add(cm);182opMap.put(name, cms);183}184}185/* Check that getters and setters are consistent. */186for (Map.Entry<String, AttrMethods<M>> entry : attrMap.entrySet()) {187AttrMethods<M> am = entry.getValue();188if (!introspector.consistent(am.getter, am.setter)) {189final String msg = "Getter and setter for " + entry.getKey() +190" have inconsistent types";191throw new NotCompliantMBeanException(msg);192}193}194}195196/**197* A comparator that defines a total order so that methods have the198* same name and identical signatures appear next to each others.199* The methods are sorted in such a way that methods which200* override each other will sit next to each other, with the201* overridden method first - e.g. Object getFoo() is placed before202* Integer getFoo(). This makes it possible to determine whether203* a method overrides another one simply by looking at the method(s)204* that precedes it in the list. (see eliminateCovariantMethods).205**/206private static class MethodOrder implements Comparator<Method> {207public int compare(Method a, Method b) {208final int cmp = a.getName().compareTo(b.getName());209if (cmp != 0) return cmp;210final Class<?>[] aparams = a.getParameterTypes();211final Class<?>[] bparams = b.getParameterTypes();212if (aparams.length != bparams.length)213return aparams.length - bparams.length;214if (!Arrays.equals(aparams, bparams)) {215return Arrays.toString(aparams).216compareTo(Arrays.toString(bparams));217}218final Class<?> aret = a.getReturnType();219final Class<?> bret = b.getReturnType();220if (aret == bret) return 0;221222// Super type comes first: Object, Number, Integer223if (aret.isAssignableFrom(bret))224return -1;225return +1; // could assert bret.isAssignableFrom(aret)226}227public static final MethodOrder instance = new MethodOrder();228}229230231/* Eliminate methods that are overridden with a covariant return type.232Reflection will return both the original and the overriding method233but only the overriding one is of interest. We return the methods234in the same order they arrived in. This isn't required by the spec235but existing code may depend on it and users may be used to seeing236operations or attributes appear in a particular order.237238Because of the way this method works, if the same Method appears239more than once in the given List then it will be completely deleted!240So don't do that. */241static List<Method>242eliminateCovariantMethods(List<Method> startMethods) {243// We are assuming that you never have very many methods with the244// same name, so it is OK to use algorithms that are quadratic245// in the number of methods with the same name.246247final int len = startMethods.size();248final Method[] sorted = startMethods.toArray(new Method[len]);249Arrays.sort(sorted,MethodOrder.instance);250final Set<Method> overridden = newSet();251for (int i=1;i<len;i++) {252final Method m0 = sorted[i-1];253final Method m1 = sorted[i];254255// Methods that don't have the same name can't override each other256if (!m0.getName().equals(m1.getName())) continue;257258// Methods that have the same name and same signature override259// each other. In that case, the second method overrides the first,260// due to the way we have sorted them in MethodOrder.261if (Arrays.equals(m0.getParameterTypes(),262m1.getParameterTypes())) {263if (!overridden.add(m0))264throw new RuntimeException("Internal error: duplicate Method");265}266}267268final List<Method> methods = newList(startMethods);269methods.removeAll(overridden);270return methods;271}272273274}275276277