FileDocCategorySizeDatePackage
PerInterface.javaAPI DocJava SE 6 API8801Tue Jun 10 00:22:04 BST 2008com.sun.jmx.mbeanserver

PerInterface.java

/*
 * @(#)PerInterface.java	1.5 05/11/17
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

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 != null && 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();
}