feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
497
jdkSrc/jdk8/javax/management/MBeanServerInvocationHandler.java
Normal file
497
jdkSrc/jdk8/javax/management/MBeanServerInvocationHandler.java
Normal file
@@ -0,0 +1,497 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2013, 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 javax.management;
|
||||
|
||||
import com.sun.jmx.mbeanserver.MXBeanProxy;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* <p>{@link InvocationHandler} that forwards methods in an MBean's
|
||||
* management interface through the MBean server to the MBean.</p>
|
||||
*
|
||||
* <p>Given an {@link MBeanServerConnection}, the {@link ObjectName}
|
||||
* of an MBean within that MBean server, and a Java interface
|
||||
* <code>Intf</code> that describes the management interface of the
|
||||
* MBean using the patterns for a Standard MBean or an MXBean, this
|
||||
* class can be used to construct a proxy for the MBean. The proxy
|
||||
* implements the interface <code>Intf</code> such that all of its
|
||||
* methods are forwarded through the MBean server to the MBean.</p>
|
||||
*
|
||||
* <p>If the {@code InvocationHandler} is for an MXBean, then the parameters of
|
||||
* a method are converted from the type declared in the MXBean
|
||||
* interface into the corresponding mapped type, and the return value
|
||||
* is converted from the mapped type into the declared type. For
|
||||
* example, with the method<br>
|
||||
|
||||
* {@code public List<String> reverse(List<String> list);}<br>
|
||||
|
||||
* and given that the mapped type for {@code List<String>} is {@code
|
||||
* String[]}, a call to {@code proxy.reverse(someList)} will convert
|
||||
* {@code someList} from a {@code List<String>} to a {@code String[]},
|
||||
* call the MBean operation {@code reverse}, then convert the returned
|
||||
* {@code String[]} into a {@code List<String>}.</p>
|
||||
*
|
||||
* <p>The method Object.toString(), Object.hashCode(), or
|
||||
* Object.equals(Object), when invoked on a proxy using this
|
||||
* invocation handler, is forwarded to the MBean server as a method on
|
||||
* the proxied MBean only if it appears in one of the proxy's
|
||||
* interfaces. For a proxy created with {@link
|
||||
* JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class)
|
||||
* JMX.newMBeanProxy} or {@link
|
||||
* JMX#newMXBeanProxy(MBeanServerConnection, ObjectName, Class)
|
||||
* JMX.newMXBeanProxy}, this means that the method must appear in the
|
||||
* Standard MBean or MXBean interface. Otherwise these methods have
|
||||
* the following behavior:
|
||||
* <ul>
|
||||
* <li>toString() returns a string representation of the proxy
|
||||
* <li>hashCode() returns a hash code for the proxy such
|
||||
* that two equal proxies have the same hash code
|
||||
* <li>equals(Object)
|
||||
* returns true if and only if the Object argument is of the same
|
||||
* proxy class as this proxy, with an MBeanServerInvocationHandler
|
||||
* that has the same MBeanServerConnection and ObjectName; if one
|
||||
* of the {@code MBeanServerInvocationHandler}s was constructed with
|
||||
* a {@code Class} argument then the other must have been constructed
|
||||
* with the same {@code Class} for {@code equals} to return true.
|
||||
* </ul>
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
public class MBeanServerInvocationHandler implements InvocationHandler {
|
||||
/**
|
||||
* <p>Invocation handler that forwards methods through an MBean
|
||||
* server to a Standard MBean. This constructor may be called
|
||||
* instead of relying on {@link
|
||||
* JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class)
|
||||
* JMX.newMBeanProxy}, for instance if you need to supply a
|
||||
* different {@link ClassLoader} to {@link Proxy#newProxyInstance
|
||||
* Proxy.newProxyInstance}.</p>
|
||||
*
|
||||
* <p>This constructor is not appropriate for an MXBean. Use
|
||||
* {@link #MBeanServerInvocationHandler(MBeanServerConnection,
|
||||
* ObjectName, boolean)} for that. This constructor is equivalent
|
||||
* to {@code new MBeanServerInvocationHandler(connection,
|
||||
* objectName, false)}.</p>
|
||||
*
|
||||
* @param connection the MBean server connection through which all
|
||||
* methods of a proxy using this handler will be forwarded.
|
||||
*
|
||||
* @param objectName the name of the MBean within the MBean server
|
||||
* to which methods will be forwarded.
|
||||
*/
|
||||
public MBeanServerInvocationHandler(MBeanServerConnection connection,
|
||||
ObjectName objectName) {
|
||||
|
||||
this(connection, objectName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Invocation handler that can forward methods through an MBean
|
||||
* server to a Standard MBean or MXBean. This constructor may be called
|
||||
* instead of relying on {@link
|
||||
* JMX#newMXBeanProxy(MBeanServerConnection, ObjectName, Class)
|
||||
* JMX.newMXBeanProxy}, for instance if you need to supply a
|
||||
* different {@link ClassLoader} to {@link Proxy#newProxyInstance
|
||||
* Proxy.newProxyInstance}.</p>
|
||||
*
|
||||
* @param connection the MBean server connection through which all
|
||||
* methods of a proxy using this handler will be forwarded.
|
||||
*
|
||||
* @param objectName the name of the MBean within the MBean server
|
||||
* to which methods will be forwarded.
|
||||
*
|
||||
* @param isMXBean if true, the proxy is for an {@link MXBean}, and
|
||||
* appropriate mappings will be applied to method parameters and return
|
||||
* values.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public MBeanServerInvocationHandler(MBeanServerConnection connection,
|
||||
ObjectName objectName,
|
||||
boolean isMXBean) {
|
||||
if (connection == null) {
|
||||
throw new IllegalArgumentException("Null connection");
|
||||
}
|
||||
if (Proxy.isProxyClass(connection.getClass())) {
|
||||
if (MBeanServerInvocationHandler.class.isAssignableFrom(
|
||||
Proxy.getInvocationHandler(connection).getClass())) {
|
||||
throw new IllegalArgumentException("Wrapping MBeanServerInvocationHandler");
|
||||
}
|
||||
}
|
||||
if (objectName == null) {
|
||||
throw new IllegalArgumentException("Null object name");
|
||||
}
|
||||
this.connection = connection;
|
||||
this.objectName = objectName;
|
||||
this.isMXBean = isMXBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>The MBean server connection through which the methods of
|
||||
* a proxy using this handler are forwarded.</p>
|
||||
*
|
||||
* @return the MBean server connection.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public MBeanServerConnection getMBeanServerConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>The name of the MBean within the MBean server to which methods
|
||||
* are forwarded.
|
||||
*
|
||||
* @return the object name.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public ObjectName getObjectName() {
|
||||
return objectName;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>If true, the proxy is for an MXBean, and appropriate mappings
|
||||
* are applied to method parameters and return values.
|
||||
*
|
||||
* @return whether the proxy is for an MXBean.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public boolean isMXBean() {
|
||||
return isMXBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Return a proxy that implements the given interface by
|
||||
* forwarding its methods through the given MBean server to the
|
||||
* named MBean. As of 1.6, the methods {@link
|
||||
* JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class)} and
|
||||
* {@link JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class,
|
||||
* boolean)} are preferred to this method.</p>
|
||||
*
|
||||
* <p>This method is equivalent to {@link Proxy#newProxyInstance
|
||||
* Proxy.newProxyInstance}<code>(interfaceClass.getClassLoader(),
|
||||
* interfaces, handler)</code>. Here <code>handler</code> is the
|
||||
* result of {@link #MBeanServerInvocationHandler new
|
||||
* MBeanServerInvocationHandler(connection, objectName)}, and
|
||||
* <code>interfaces</code> is an array that has one element if
|
||||
* <code>notificationBroadcaster</code> is false and two if it is
|
||||
* true. The first element of <code>interfaces</code> is
|
||||
* <code>interfaceClass</code> and the second, if present, is
|
||||
* <code>NotificationEmitter.class</code>.
|
||||
*
|
||||
* @param connection the MBean server to forward to.
|
||||
* @param objectName the name of the MBean within
|
||||
* <code>connection</code> to forward to.
|
||||
* @param interfaceClass the management interface that the MBean
|
||||
* exports, which will also be implemented by the returned proxy.
|
||||
* @param notificationBroadcaster make the returned proxy
|
||||
* implement {@link NotificationEmitter} by forwarding its methods
|
||||
* via <code>connection</code>. A call to {@link
|
||||
* NotificationBroadcaster#addNotificationListener} on the proxy will
|
||||
* result in a call to {@link
|
||||
* MBeanServerConnection#addNotificationListener(ObjectName,
|
||||
* NotificationListener, NotificationFilter, Object)}, and likewise
|
||||
* for the other methods of {@link NotificationBroadcaster} and {@link
|
||||
* NotificationEmitter}.
|
||||
*
|
||||
* @param <T> allows the compiler to know that if the {@code
|
||||
* interfaceClass} parameter is {@code MyMBean.class}, for example,
|
||||
* then the return type is {@code MyMBean}.
|
||||
*
|
||||
* @return the new proxy instance.
|
||||
*
|
||||
* @see JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class, boolean)
|
||||
*/
|
||||
public static <T> T newProxyInstance(MBeanServerConnection connection,
|
||||
ObjectName objectName,
|
||||
Class<T> interfaceClass,
|
||||
boolean notificationBroadcaster) {
|
||||
return JMX.newMBeanProxy(connection, objectName, interfaceClass, notificationBroadcaster);
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
final Class<?> methodClass = method.getDeclaringClass();
|
||||
|
||||
if (methodClass.equals(NotificationBroadcaster.class)
|
||||
|| methodClass.equals(NotificationEmitter.class))
|
||||
return invokeBroadcasterMethod(proxy, method, args);
|
||||
|
||||
// local or not: equals, toString, hashCode
|
||||
if (shouldDoLocally(proxy, method))
|
||||
return doLocally(proxy, method, args);
|
||||
|
||||
try {
|
||||
if (isMXBean()) {
|
||||
MXBeanProxy p = findMXBeanProxy(methodClass);
|
||||
return p.invoke(connection, objectName, method, args);
|
||||
} else {
|
||||
final String methodName = method.getName();
|
||||
final Class<?>[] paramTypes = method.getParameterTypes();
|
||||
final Class<?> returnType = method.getReturnType();
|
||||
|
||||
/* Inexplicably, InvocationHandler specifies that args is null
|
||||
when the method takes no arguments rather than a
|
||||
zero-length array. */
|
||||
final int nargs = (args == null) ? 0 : args.length;
|
||||
|
||||
if (methodName.startsWith("get")
|
||||
&& methodName.length() > 3
|
||||
&& nargs == 0
|
||||
&& !returnType.equals(Void.TYPE)) {
|
||||
return connection.getAttribute(objectName,
|
||||
methodName.substring(3));
|
||||
}
|
||||
|
||||
if (methodName.startsWith("is")
|
||||
&& methodName.length() > 2
|
||||
&& nargs == 0
|
||||
&& (returnType.equals(Boolean.TYPE)
|
||||
|| returnType.equals(Boolean.class))) {
|
||||
return connection.getAttribute(objectName,
|
||||
methodName.substring(2));
|
||||
}
|
||||
|
||||
if (methodName.startsWith("set")
|
||||
&& methodName.length() > 3
|
||||
&& nargs == 1
|
||||
&& returnType.equals(Void.TYPE)) {
|
||||
Attribute attr = new Attribute(methodName.substring(3), args[0]);
|
||||
connection.setAttribute(objectName, attr);
|
||||
return null;
|
||||
}
|
||||
|
||||
final String[] signature = new String[paramTypes.length];
|
||||
for (int i = 0; i < paramTypes.length; i++)
|
||||
signature[i] = paramTypes[i].getName();
|
||||
return connection.invoke(objectName, methodName,
|
||||
args, signature);
|
||||
}
|
||||
} catch (MBeanException e) {
|
||||
throw e.getTargetException();
|
||||
} catch (RuntimeMBeanException re) {
|
||||
throw re.getTargetException();
|
||||
} catch (RuntimeErrorException rre) {
|
||||
throw rre.getTargetError();
|
||||
}
|
||||
/* The invoke may fail because it can't get to the MBean, with
|
||||
one of the these exceptions declared by
|
||||
MBeanServerConnection.invoke:
|
||||
- RemoteException: can't talk to MBeanServer;
|
||||
- InstanceNotFoundException: objectName is not registered;
|
||||
- ReflectionException: objectName is registered but does not
|
||||
have the method being invoked.
|
||||
In all of these cases, the exception will be wrapped by the
|
||||
proxy mechanism in an UndeclaredThrowableException unless
|
||||
it happens to be declared in the "throws" clause of the
|
||||
method being invoked on the proxy.
|
||||
*/
|
||||
}
|
||||
|
||||
private static MXBeanProxy findMXBeanProxy(Class<?> mxbeanInterface) {
|
||||
synchronized (mxbeanProxies) {
|
||||
WeakReference<MXBeanProxy> proxyRef =
|
||||
mxbeanProxies.get(mxbeanInterface);
|
||||
MXBeanProxy p = (proxyRef == null) ? null : proxyRef.get();
|
||||
if (p == null) {
|
||||
try {
|
||||
p = new MXBeanProxy(mxbeanInterface);
|
||||
} catch (IllegalArgumentException e) {
|
||||
String msg = "Cannot make MXBean proxy for " +
|
||||
mxbeanInterface.getName() + ": " + e.getMessage();
|
||||
IllegalArgumentException iae =
|
||||
new IllegalArgumentException(msg, e.getCause());
|
||||
iae.setStackTrace(e.getStackTrace());
|
||||
throw iae;
|
||||
}
|
||||
mxbeanProxies.put(mxbeanInterface,
|
||||
new WeakReference<MXBeanProxy>(p));
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
private static final WeakHashMap<Class<?>, WeakReference<MXBeanProxy>>
|
||||
mxbeanProxies = new WeakHashMap<Class<?>, WeakReference<MXBeanProxy>>();
|
||||
|
||||
private Object invokeBroadcasterMethod(Object proxy, Method method,
|
||||
Object[] args) throws Exception {
|
||||
final String methodName = method.getName();
|
||||
final int nargs = (args == null) ? 0 : args.length;
|
||||
|
||||
if (methodName.equals("addNotificationListener")) {
|
||||
/* The various throws of IllegalArgumentException here
|
||||
should not happen, since we know what the methods in
|
||||
NotificationBroadcaster and NotificationEmitter
|
||||
are. */
|
||||
if (nargs != 3) {
|
||||
final String msg =
|
||||
"Bad arg count to addNotificationListener: " + nargs;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
/* Other inconsistencies will produce ClassCastException
|
||||
below. */
|
||||
|
||||
NotificationListener listener = (NotificationListener) args[0];
|
||||
NotificationFilter filter = (NotificationFilter) args[1];
|
||||
Object handback = args[2];
|
||||
connection.addNotificationListener(objectName,
|
||||
listener,
|
||||
filter,
|
||||
handback);
|
||||
return null;
|
||||
|
||||
} else if (methodName.equals("removeNotificationListener")) {
|
||||
|
||||
/* NullPointerException if method with no args, but that
|
||||
shouldn't happen because removeNL does have args. */
|
||||
NotificationListener listener = (NotificationListener) args[0];
|
||||
|
||||
switch (nargs) {
|
||||
case 1:
|
||||
connection.removeNotificationListener(objectName, listener);
|
||||
return null;
|
||||
|
||||
case 3:
|
||||
NotificationFilter filter = (NotificationFilter) args[1];
|
||||
Object handback = args[2];
|
||||
connection.removeNotificationListener(objectName,
|
||||
listener,
|
||||
filter,
|
||||
handback);
|
||||
return null;
|
||||
|
||||
default:
|
||||
final String msg =
|
||||
"Bad arg count to removeNotificationListener: " + nargs;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
} else if (methodName.equals("getNotificationInfo")) {
|
||||
|
||||
if (args != null) {
|
||||
throw new IllegalArgumentException("getNotificationInfo has " +
|
||||
"args");
|
||||
}
|
||||
|
||||
MBeanInfo info = connection.getMBeanInfo(objectName);
|
||||
return info.getNotifications();
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad method name: " +
|
||||
methodName);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldDoLocally(Object proxy, Method method) {
|
||||
final String methodName = method.getName();
|
||||
if ((methodName.equals("hashCode") || methodName.equals("toString"))
|
||||
&& method.getParameterTypes().length == 0
|
||||
&& isLocal(proxy, method))
|
||||
return true;
|
||||
if (methodName.equals("equals")
|
||||
&& Arrays.equals(method.getParameterTypes(),
|
||||
new Class<?>[] {Object.class})
|
||||
&& isLocal(proxy, method))
|
||||
return true;
|
||||
if (methodName.equals("finalize")
|
||||
&& method.getParameterTypes().length == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Object doLocally(Object proxy, Method method, Object[] args) {
|
||||
final String methodName = method.getName();
|
||||
|
||||
if (methodName.equals("equals")) {
|
||||
|
||||
if (this == args[0]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(args[0] instanceof Proxy)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final InvocationHandler ihandler =
|
||||
Proxy.getInvocationHandler(args[0]);
|
||||
|
||||
if (ihandler == null ||
|
||||
!(ihandler instanceof MBeanServerInvocationHandler)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final MBeanServerInvocationHandler handler =
|
||||
(MBeanServerInvocationHandler)ihandler;
|
||||
|
||||
return connection.equals(handler.connection) &&
|
||||
objectName.equals(handler.objectName) &&
|
||||
proxy.getClass().equals(args[0].getClass());
|
||||
} else if (methodName.equals("toString")) {
|
||||
return (isMXBean() ? "MX" : "M") + "BeanProxy(" +
|
||||
connection + "[" + objectName + "])";
|
||||
} else if (methodName.equals("hashCode")) {
|
||||
return objectName.hashCode()+connection.hashCode();
|
||||
} else if (methodName.equals("finalize")) {
|
||||
// ignore the finalizer invocation via proxy
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unexpected method name: " + methodName);
|
||||
}
|
||||
|
||||
private static boolean isLocal(Object proxy, Method method) {
|
||||
final Class<?>[] interfaces = proxy.getClass().getInterfaces();
|
||||
if(interfaces == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final String methodName = method.getName();
|
||||
final Class<?>[] params = method.getParameterTypes();
|
||||
for (Class<?> intf : interfaces) {
|
||||
try {
|
||||
intf.getMethod(methodName, params);
|
||||
return false; // found method in one of our interfaces
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
// OK.
|
||||
}
|
||||
}
|
||||
|
||||
return true; // did not find in any interface
|
||||
}
|
||||
|
||||
private final MBeanServerConnection connection;
|
||||
private final ObjectName objectName;
|
||||
private final boolean isMXBean;
|
||||
}
|
||||
Reference in New Issue
Block a user