280 lines · 10.5 KB · Java
RawDownload
/* * Copyright (c) 2005, 2006, 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 java.security.AccessController;import java.util.Arrays;import java.util.Collections;import java.util.List;import java.util.Map;import javax.management.AttributeNotFoundException;import javax.management.InvalidAttributeValueException;import javax.management.MBeanException;import javax.management.MBeanInfo;import javax.management.ReflectionException; import static com.sun.jmx.mbeanserver.Util.*; /** * Per-MBean-interface behavior.  A single instance of this class can be shared * by all MBeans of the same kind (Standard MBean or MXBean) that have the same * MBean interface. * * @since 1.6 */final class PerInterface<M> {    PerInterface(Class<?> mbeanInterface, MBeanIntrospector<M> introspector,                 MBeanAnalyzer<M> analyzer, MBeanInfo mbeanInfo) {        this.mbeanInterface = mbeanInterface;        this.introspector = introspector;        this.mbeanInfo = mbeanInfo;        analyzer.visit(new InitMaps());    }     Class<?> getMBeanInterface() {        return mbeanInterface;    }     MBeanInfo getMBeanInfo() {        return mbeanInfo;    }     boolean isMXBean() {        return introspector.isMXBean();    }     Object getAttribute(Object resource, String attribute, Object cookie)            throws AttributeNotFoundException,                   MBeanException,                   ReflectionException {         final M cm = getters.get(attribute);        if (cm == null) {            final String msg;            if (setters.containsKey(attribute))                msg = "Write-only attribute: " + attribute;            else                msg = "No such attribute: " + attribute;            throw new AttributeNotFoundException(msg);        }        return introspector.invokeM(cm, resource, (Object[]) null, cookie);    }     void setAttribute(Object resource, String attribute, Object value,                      Object cookie)            throws AttributeNotFoundException,                   InvalidAttributeValueException,                   MBeanException,                   ReflectionException {         final M cm = setters.get(attribute);        if (cm == null) {            final String msg;            if (getters.containsKey(attribute))                msg = "Read-only attribute: " + attribute;            else                msg = "No such attribute: " + attribute;            throw new AttributeNotFoundException(msg);        }        introspector.invokeSetter(attribute, cm, resource, value, cookie);    }     Object invoke(Object resource, String operation, Object[] params,                  String[] signature, Object cookie)            throws MBeanException, ReflectionException {         final List<MethodAndSig> list = ops.get(operation);        if (list == null) {            final String msg = "No such operation: " + operation;            return noSuchMethod(msg, resource, operation, params, signature,                                cookie);        }        if (signature == null)            signature = new String[0];        MethodAndSig found = null;        for (MethodAndSig mas : list) {            if (Arrays.equals(mas.signature, signature)) {                found = mas;                break;            }        }        if (found == null) {            final String badSig = sigString(signature);            final String msg;            if (list.size() == 1) {  // helpful exception message                msg = "Signature mismatch for operation " + operation +                        ": " + badSig + " should be " +                        sigString(list.get(0).signature);            } else {                msg = "Operation " + operation + " exists but not with " +                        "this signature: " + badSig;            }            return noSuchMethod(msg, resource, operation, params, signature,                                cookie);        }        return introspector.invokeM(found.method, resource, params, cookie);    }     /*     * This method is called when invoke doesn't find the named method.     * Before throwing an exception, we check to see whether the     * jmx.invoke.getters property is set, and if so whether the method     * being invoked might be a getter or a setter.  If so we invoke it     * and return the result.  This is for compatibility     * with code based on JMX RI 1.0 or 1.1 which allowed invoking getters     * and setters.  It is *not* recommended that new code use this feature.     *     * Since this method is either going to throw an exception or use     * functionality that is strongly discouraged, we consider that its     * performance is not very important.     *     * A simpler way to implement the functionality would be to add the getters     * and setters to the operations map when jmx.invoke.getters is set.     * However, that means that the property is consulted when an MBean     * interface is being introspected and not thereafter.  Previously,     * the property was consulted on every invocation.  So this simpler     * implementation could potentially break code that sets and unsets     * the property at different times.     */    private Object noSuchMethod(String msg, Object resource, String operation,                                Object[] params, String[] signature,                                Object cookie)            throws MBeanException, ReflectionException {         // Construct the exception that we will probably throw        final NoSuchMethodException nsme =            new NoSuchMethodException(operation + sigString(signature));        final ReflectionException exception =            new ReflectionException(nsme, msg);         if (introspector.isMXBean())            throw exception; // No compatibility requirement here         // Is the compatibility property set?        GetPropertyAction act = new GetPropertyAction("jmx.invoke.getters");        String invokeGettersS;        try {            invokeGettersS = AccessController.doPrivileged(act);        } catch (Exception e) {            // We don't expect an exception here but if we get one then            // we'll simply assume that the property is not set.            invokeGettersS = null;        }        if (invokeGettersS == null)            throw exception;         int rest = 0;        Map<String, M> methods = null;        if (signature == null || signature.length == 0) {            if (operation.startsWith("get"))                rest = 3;            else if (operation.startsWith("is"))                rest = 2;            if (rest != 0)                methods = getters;        } else if (signature.length == 1 &&                   operation.startsWith("set")) {            rest = 3;            methods = setters;        }         if (rest != 0) {            String attrName = operation.substring(rest);            M method = methods.get(attrName);            if (method != null && introspector.getName(method).equals(operation)) {                String[] msig = introspector.getSignature(method);                if ((signature == null && msig.length == 0) ||                        Arrays.equals(signature, msig)) {                    return introspector.invokeM(method, resource, params, cookie);                }            }        }         throw exception;    }     private String sigString(String[] signature) {        StringBuilder b = new StringBuilder("(");        if (signature != null) {            for (String s : signature) {                if (b.length() > 1)                    b.append(", ");                b.append(s);            }        }        return b.append(")").toString();    }     /**     * Visitor that sets up the method maps (operations, getters, setters).     */    private class InitMaps implements MBeanAnalyzer.MBeanVisitor<M> {        public void visitAttribute(String attributeName,                                   M getter,                                   M setter) {            if (getter != null) {                introspector.checkMethod(getter);                final Object old = getters.put(attributeName, getter);                assert(old == null);            }            if (setter != null) {                introspector.checkMethod(setter);                final Object old = setters.put(attributeName, setter);                assert(old == null);            }        }         public void visitOperation(String operationName,                                   M operation) {            introspector.checkMethod(operation);            final String[] sig = introspector.getSignature(operation);            final MethodAndSig mas = new MethodAndSig();            mas.method = operation;            mas.signature = sig;            List<MethodAndSig> list = ops.get(operationName);            if (list == null)                list = Collections.singletonList(mas);            else {                if (list.size() == 1)                    list = newList(list);                list.add(mas);            }            ops.put(operationName, list);        }    }     private class MethodAndSig {        M method;        String[] signature;    }     private final Class<?> mbeanInterface;    private final MBeanIntrospector<M> introspector;    private final MBeanInfo mbeanInfo;    private final Map<String, M> getters = newMap();    private final Map<String, M> setters = newMap();    private final Map<String, List<MethodAndSig>> ops = newMap();}