You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
9.6 KiB
281 lines
9.6 KiB
/*
|
|
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE 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.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();
|
|
}
|