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
26package javax.management;
27
28import com.sun.jmx.mbeanserver.MXBeanProxy;
29
30import java.lang.ref.WeakReference;
31import java.lang.reflect.InvocationHandler;
32import java.lang.reflect.Method;
33import java.lang.reflect.Proxy;
34import java.util.Arrays;
35import 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 */
89public 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}