History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/management/MBeanServerInvocationHandler.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/management/MBeanServerInvocationHandler.java+497−0
@@ -0,0 +1,497 @@
1+/*
2+ * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package javax.management;
27+
28+import com.sun.jmx.mbeanserver.MXBeanProxy;
29+
30+import java.lang.ref.WeakReference;
31+import java.lang.reflect.InvocationHandler;
32+import java.lang.reflect.Method;
33+import java.lang.reflect.Proxy;
34+import java.util.Arrays;
35+import java.util.WeakHashMap;
36+
37+/**
38+ * <p>{@link InvocationHandler} that forwards methods in an MBean's
39+ * management interface through the MBean server to the MBean.</p>
40+ *
41+ * <p>Given an {@link MBeanServerConnection}, the {@link ObjectName}
42+ * of an MBean within that MBean server, and a Java interface
43+ * <code>Intf</code> that describes the management interface of the
44+ * MBean using the patterns for a Standard MBean or an MXBean, this
45+ * class can be used to construct a proxy for the MBean. The proxy
46+ * implements the interface <code>Intf</code> such that all of its
47+ * methods are forwarded through the MBean server to the MBean.</p>
48+ *
49+ * <p>If the {@code InvocationHandler} is for an MXBean, then the parameters of
50+ * a method are converted from the type declared in the MXBean
51+ * interface into the corresponding mapped type, and the return value
52+ * is converted from the mapped type into the declared type. For
53+ * example, with the method<br>
54+
55+ * {@code public List<String> reverse(List<String> list);}<br>
56+
57+ * and given that the mapped type for {@code List<String>} is {@code
58+ * String[]}, a call to {@code proxy.reverse(someList)} will convert
59+ * {@code someList} from a {@code List<String>} to a {@code String[]},
60+ * call the MBean operation {@code reverse}, then convert the returned
61+ * {@code String[]} into a {@code List<String>}.</p>
62+ *
63+ * <p>The method Object.toString(), Object.hashCode(), or
64+ * Object.equals(Object), when invoked on a proxy using this
65+ * invocation handler, is forwarded to the MBean server as a method on
66+ * the proxied MBean only if it appears in one of the proxy's
67+ * interfaces. For a proxy created with {@link
68+ * JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class)
69+ * JMX.newMBeanProxy} or {@link
70+ * JMX#newMXBeanProxy(MBeanServerConnection, ObjectName, Class)
71+ * JMX.newMXBeanProxy}, this means that the method must appear in the
72+ * Standard MBean or MXBean interface. Otherwise these methods have
73+ * the following behavior:
74+ * <ul>
75+ * <li>toString() returns a string representation of the proxy
76+ * <li>hashCode() returns a hash code for the proxy such
77+ * that two equal proxies have the same hash code
78+ * <li>equals(Object)
79+ * returns true if and only if the Object argument is of the same
80+ * proxy class as this proxy, with an MBeanServerInvocationHandler
81+ * that has the same MBeanServerConnection and ObjectName; if one
82+ * of the {@code MBeanServerInvocationHandler}s was constructed with
83+ * a {@code Class} argument then the other must have been constructed
84+ * with the same {@code Class} for {@code equals} to return true.
85+ * </ul>
86+ *
87+ * @since 1.5
88+ */
89+public class MBeanServerInvocationHandler implements InvocationHandler {
90+ /**
91+ * <p>Invocation handler that forwards methods through an MBean
92+ * server to a Standard MBean. This constructor may be called
93+ * instead of relying on {@link
94+ * JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class)
95+ * JMX.newMBeanProxy}, for instance if you need to supply a
96+ * different {@link ClassLoader} to {@link Proxy#newProxyInstance
97+ * Proxy.newProxyInstance}.</p>
98+ *
99+ * <p>This constructor is not appropriate for an MXBean. Use
100+ * {@link #MBeanServerInvocationHandler(MBeanServerConnection,
101+ * ObjectName, boolean)} for that. This constructor is equivalent
102+ * to {@code new MBeanServerInvocationHandler(connection,
103+ * objectName, false)}.</p>
104+ *
105+ * @param connection the MBean server connection through which all
106+ * methods of a proxy using this handler will be forwarded.
107+ *
108+ * @param objectName the name of the MBean within the MBean server
109+ * to which methods will be forwarded.
110+ */
111+ public MBeanServerInvocationHandler(MBeanServerConnection connection,
112+ ObjectName objectName) {
113+
114+ this(connection, objectName, false);
115+ }
116+
117+ /**
118+ * <p>Invocation handler that can forward methods through an MBean
119+ * server to a Standard MBean or MXBean. This constructor may be called
120+ * instead of relying on {@link
121+ * JMX#newMXBeanProxy(MBeanServerConnection, ObjectName, Class)
122+ * JMX.newMXBeanProxy}, for instance if you need to supply a
123+ * different {@link ClassLoader} to {@link Proxy#newProxyInstance
124+ * Proxy.newProxyInstance}.</p>
125+ *
126+ * @param connection the MBean server connection through which all
127+ * methods of a proxy using this handler will be forwarded.
128+ *
129+ * @param objectName the name of the MBean within the MBean server
130+ * to which methods will be forwarded.
131+ *
132+ * @param isMXBean if true, the proxy is for an {@link MXBean}, and
133+ * appropriate mappings will be applied to method parameters and return
134+ * values.
135+ *
136+ * @since 1.6
137+ */
138+ public MBeanServerInvocationHandler(MBeanServerConnection connection,
139+ ObjectName objectName,
140+ boolean isMXBean) {
141+ if (connection == null) {
142+ throw new IllegalArgumentException("Null connection");
143+ }
144+ if (objectName == null) {
145+ throw new IllegalArgumentException("Null object name");
146+ }
147+ this.connection = connection;
148+ this.objectName = objectName;
149+ this.isMXBean = isMXBean;
150+ }
151+
152+ /**
153+ * <p>The MBean server connection through which the methods of
154+ * a proxy using this handler are forwarded.</p>
155+ *
156+ * @return the MBean server connection.
157+ *
158+ * @since 1.6
159+ */
160+ public MBeanServerConnection getMBeanServerConnection() {
161+ return connection;
162+ }
163+
164+ /**
165+ * <p>The name of the MBean within the MBean server to which methods
166+ * are forwarded.
167+ *
168+ * @return the object name.
169+ *
170+ * @since 1.6
171+ */
172+ public ObjectName getObjectName() {
173+ return objectName;
174+ }
175+
176+ /**
177+ * <p>If true, the proxy is for an MXBean, and appropriate mappings
178+ * are applied to method parameters and return values.
179+ *
180+ * @return whether the proxy is for an MXBean.
181+ *
182+ * @since 1.6
183+ */
184+ public boolean isMXBean() {
185+ return isMXBean;
186+ }
187+
188+ /**
189+ * <p>Return a proxy that implements the given interface by
190+ * forwarding its methods through the given MBean server to the
191+ * named MBean. As of 1.6, the methods {@link
192+ * JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class)} and
193+ * {@link JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class,
194+ * boolean)} are preferred to this method.</p>
195+ *
196+ * <p>This method is equivalent to {@link Proxy#newProxyInstance
197+ * Proxy.newProxyInstance}<code>(interfaceClass.getClassLoader(),
198+ * interfaces, handler)</code>. Here <code>handler</code> is the
199+ * result of {@link #MBeanServerInvocationHandler new
200+ * MBeanServerInvocationHandler(connection, objectName)}, and
201+ * <code>interfaces</code> is an array that has one element if
202+ * <code>notificationBroadcaster</code> is false and two if it is
203+ * true. The first element of <code>interfaces</code> is
204+ * <code>interfaceClass</code> and the second, if present, is
205+ * <code>NotificationEmitter.class</code>.
206+ *
207+ * @param connection the MBean server to forward to.
208+ * @param objectName the name of the MBean within
209+ * <code>connection</code> to forward to.
210+ * @param interfaceClass the management interface that the MBean
211+ * exports, which will also be implemented by the returned proxy.
212+ * @param notificationBroadcaster make the returned proxy
213+ * implement {@link NotificationEmitter} by forwarding its methods
214+ * via <code>connection</code>. A call to {@link
215+ * NotificationBroadcaster#addNotificationListener} on the proxy will
216+ * result in a call to {@link
217+ * MBeanServerConnection#addNotificationListener(ObjectName,
218+ * NotificationListener, NotificationFilter, Object)}, and likewise
219+ * for the other methods of {@link NotificationBroadcaster} and {@link
220+ * NotificationEmitter}.
221+ *
222+ * @param <T> allows the compiler to know that if the {@code
223+ * interfaceClass} parameter is {@code MyMBean.class}, for example,
224+ * then the return type is {@code MyMBean}.
225+ *
226+ * @return the new proxy instance.
227+ *
228+ * @see JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class)
229+ */
230+ public static <T> T newProxyInstance(MBeanServerConnection connection,
231+ ObjectName objectName,
232+ Class<T> interfaceClass,
233+ boolean notificationBroadcaster) {
234+ final InvocationHandler handler =
235+ new MBeanServerInvocationHandler(connection, objectName);
236+ final Class<?>[] interfaces;
237+ if (notificationBroadcaster) {
238+ interfaces =
239+ new Class<?>[] {interfaceClass, NotificationEmitter.class};
240+ } else
241+ interfaces = new Class<?>[] {interfaceClass};
242+
243+ Object proxy =
244+ Proxy.newProxyInstance(interfaceClass.getClassLoader(),
245+ interfaces,
246+ handler);
247+ return interfaceClass.cast(proxy);
248+ }
249+
250+ public Object invoke(Object proxy, Method method, Object[] args)
251+ throws Throwable {
252+ final Class<?> methodClass = method.getDeclaringClass();
253+
254+ if (methodClass.equals(NotificationBroadcaster.class)
255+ || methodClass.equals(NotificationEmitter.class))
256+ return invokeBroadcasterMethod(proxy, method, args);
257+
258+ // local or not: equals, toString, hashCode
259+ if (shouldDoLocally(proxy, method))
260+ return doLocally(proxy, method, args);
261+
262+ try {
263+ if (isMXBean()) {
264+ MXBeanProxy p = findMXBeanProxy(methodClass);
265+ return p.invoke(connection, objectName, method, args);
266+ } else {
267+ final String methodName = method.getName();
268+ final Class<?>[] paramTypes = method.getParameterTypes();
269+ final Class<?> returnType = method.getReturnType();
270+
271+ /* Inexplicably, InvocationHandler specifies that args is null
272+ when the method takes no arguments rather than a
273+ zero-length array. */
274+ final int nargs = (args == null) ? 0 : args.length;
275+
276+ if (methodName.startsWith("get")
277+ && methodName.length() > 3
278+ && nargs == 0
279+ && !returnType.equals(Void.TYPE)) {
280+ return connection.getAttribute(objectName,
281+ methodName.substring(3));
282+ }
283+
284+ if (methodName.startsWith("is")
285+ && methodName.length() > 2
286+ && nargs == 0
287+ && (returnType.equals(Boolean.TYPE)
288+ || returnType.equals(Boolean.class))) {
289+ return connection.getAttribute(objectName,
290+ methodName.substring(2));
291+ }
292+
293+ if (methodName.startsWith("set")
294+ && methodName.length() > 3
295+ && nargs == 1
296+ && returnType.equals(Void.TYPE)) {
297+ Attribute attr = new Attribute(methodName.substring(3), args[0]);
298+ connection.setAttribute(objectName, attr);
299+ return null;
300+ }
301+
302+ final String[] signature = new String[paramTypes.length];
303+ for (int i = 0; i < paramTypes.length; i++)
304+ signature[i] = paramTypes[i].getName();
305+ return connection.invoke(objectName, methodName,
306+ args, signature);
307+ }
308+ } catch (MBeanException e) {
309+ throw e.getTargetException();
310+ } catch (RuntimeMBeanException re) {
311+ throw re.getTargetException();
312+ } catch (RuntimeErrorException rre) {
313+ throw rre.getTargetError();
314+ }
315+ /* The invoke may fail because it can't get to the MBean, with
316+ one of the these exceptions declared by
317+ MBeanServerConnection.invoke:
318+ - RemoteException: can't talk to MBeanServer;
319+ - InstanceNotFoundException: objectName is not registered;
320+ - ReflectionException: objectName is registered but does not
321+ have the method being invoked.
322+ In all of these cases, the exception will be wrapped by the
323+ proxy mechanism in an UndeclaredThrowableException unless
324+ it happens to be declared in the "throws" clause of the
325+ method being invoked on the proxy.
326+ */
327+ }
328+
329+ private static MXBeanProxy findMXBeanProxy(Class<?> mxbeanInterface) {
330+ synchronized (mxbeanProxies) {
331+ WeakReference<MXBeanProxy> proxyRef =
332+ mxbeanProxies.get(mxbeanInterface);
333+ MXBeanProxy p = (proxyRef == null) ? null : proxyRef.get();
334+ if (p == null) {
335+ try {
336+ p = new MXBeanProxy(mxbeanInterface);
337+ } catch (IllegalArgumentException e) {
338+ String msg = "Cannot make MXBean proxy for " +
339+ mxbeanInterface.getName() + ": " + e.getMessage();
340+ IllegalArgumentException iae =
341+ new IllegalArgumentException(msg, e.getCause());
342+ iae.setStackTrace(e.getStackTrace());
343+ throw iae;
344+ }
345+ mxbeanProxies.put(mxbeanInterface,
346+ new WeakReference<MXBeanProxy>(p));
347+ }
348+ return p;
349+ }
350+ }
351+ private static final WeakHashMap<Class<?>, WeakReference<MXBeanProxy>>
352+ mxbeanProxies = new WeakHashMap<Class<?>, WeakReference<MXBeanProxy>>();
353+
354+ private Object invokeBroadcasterMethod(Object proxy, Method method,
355+ Object[] args) throws Exception {
356+ final String methodName = method.getName();
357+ final int nargs = (args == null) ? 0 : args.length;
358+
359+ if (methodName.equals("addNotificationListener")) {
360+ /* The various throws of IllegalArgumentException here
361+ should not happen, since we know what the methods in
362+ NotificationBroadcaster and NotificationEmitter
363+ are. */
364+ if (nargs != 3) {
365+ final String msg =
366+ "Bad arg count to addNotificationListener: " + nargs;
367+ throw new IllegalArgumentException(msg);
368+ }
369+ /* Other inconsistencies will produce ClassCastException
370+ below. */
371+
372+ NotificationListener listener = (NotificationListener) args[0];
373+ NotificationFilter filter = (NotificationFilter) args[1];
374+ Object handback = args[2];
375+ connection.addNotificationListener(objectName,
376+ listener,
377+ filter,
378+ handback);
379+ return null;
380+
381+ } else if (methodName.equals("removeNotificationListener")) {
382+
383+ /* NullPointerException if method with no args, but that
384+ shouldn't happen because removeNL does have args. */
385+ NotificationListener listener = (NotificationListener) args[0];
386+
387+ switch (nargs) {
388+ case 1:
389+ connection.removeNotificationListener(objectName, listener);
390+ return null;
391+
392+ case 3:
393+ NotificationFilter filter = (NotificationFilter) args[1];
394+ Object handback = args[2];
395+ connection.removeNotificationListener(objectName,
396+ listener,
397+ filter,
398+ handback);
399+ return null;
400+
401+ default:
402+ final String msg =
403+ "Bad arg count to removeNotificationListener: " + nargs;
404+ throw new IllegalArgumentException(msg);
405+ }
406+
407+ } else if (methodName.equals("getNotificationInfo")) {
408+
409+ if (args != null) {
410+ throw new IllegalArgumentException("getNotificationInfo has " +
411+ "args");
412+ }
413+
414+ MBeanInfo info = connection.getMBeanInfo(objectName);
415+ return info.getNotifications();
416+
417+ } else {
418+ throw new IllegalArgumentException("Bad method name: " +
419+ methodName);
420+ }
421+ }
422+
423+ private boolean shouldDoLocally(Object proxy, Method method) {
424+ final String methodName = method.getName();
425+ if ((methodName.equals("hashCode") || methodName.equals("toString"))
426+ && method.getParameterTypes().length == 0
427+ && isLocal(proxy, method))
428+ return true;
429+ if (methodName.equals("equals")
430+ && Arrays.equals(method.getParameterTypes(),
431+ new Class<?>[] {Object.class})
432+ && isLocal(proxy, method))
433+ return true;
434+ return false;
435+ }
436+
437+ private Object doLocally(Object proxy, Method method, Object[] args) {
438+ final String methodName = method.getName();
439+
440+ if (methodName.equals("equals")) {
441+
442+ if (this == args[0]) {
443+ return true;
444+ }
445+
446+ if (!(args[0] instanceof Proxy)) {
447+ return false;
448+ }
449+
450+ final InvocationHandler ihandler =
451+ Proxy.getInvocationHandler(args[0]);
452+
453+ if (ihandler == null ||
454+ !(ihandler instanceof MBeanServerInvocationHandler)) {
455+ return false;
456+ }
457+
458+ final MBeanServerInvocationHandler handler =
459+ (MBeanServerInvocationHandler)ihandler;
460+
461+ return connection.equals(handler.connection) &&
462+ objectName.equals(handler.objectName) &&
463+ proxy.getClass().equals(args[0].getClass());
464+ } else if (methodName.equals("toString")) {
465+ return (isMXBean() ? "MX" : "M") + "BeanProxy(" +
466+ connection + "[" + objectName + "])";
467+ } else if (methodName.equals("hashCode")) {
468+ return objectName.hashCode()+connection.hashCode();
469+ }
470+
471+ throw new RuntimeException("Unexpected method name: " + methodName);
472+ }
473+
474+ private static boolean isLocal(Object proxy, Method method) {
475+ final Class<?>[] interfaces = proxy.getClass().getInterfaces();
476+ if(interfaces == null) {
477+ return true;
478+ }
479+
480+ final String methodName = method.getName();
481+ final Class<?>[] params = method.getParameterTypes();
482+ for (Class<?> intf : interfaces) {
483+ try {
484+ intf.getMethod(methodName, params);
485+ return false; // found method in one of our interfaces
486+ } catch (NoSuchMethodException nsme) {
487+ // OK.
488+ }
489+ }
490+
491+ return true; // did not find in any interface
492+ }
493+
494+ private final MBeanServerConnection connection;
495+ private final ObjectName objectName;
496+ private final boolean isMXBean;
497+}