Source Code Cross Referenced for MBeanServerInvocationHandler.java in  » 6.0-JDK-Core » management » javax » management » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » management » javax.management 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001        /*
002         * Copyright 2002-2007 Sun Microsystems, Inc.  All Rights Reserved.
003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004         *
005         * This code is free software; you can redistribute it and/or modify it
006         * under the terms of the GNU General Public License version 2 only, as
007         * published by the Free Software Foundation.  Sun designates this
008         * particular file as subject to the "Classpath" exception as provided
009         * by Sun in the LICENSE file that accompanied this code.
010         *
011         * This code is distributed in the hope that it will be useful, but WITHOUT
012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014         * version 2 for more details (a copy is included in the LICENSE file that
015         * accompanied this code).
016         *
017         * You should have received a copy of the GNU General Public License version
018         * 2 along with this work; if not, write to the Free Software Foundation,
019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020         *
021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022         * CA 95054 USA or visit www.sun.com if you need additional information or
023         * have any questions.
024         */
025
026        package javax.management;
027
028        import com.sun.jmx.mbeanserver.MXBeanProxy;
029
030        import java.lang.ref.WeakReference;
031        import java.lang.reflect.InvocationHandler;
032        import java.lang.reflect.Method;
033        import java.lang.reflect.Proxy;
034        import java.util.Arrays;
035        import java.util.WeakHashMap;
036
037        /**
038         * <p>{@link InvocationHandler} that forwards methods in an MBean's
039         * management interface through the MBean server to the MBean.</p>
040         *
041         * <p>Given an {@link MBeanServerConnection}, the {@link ObjectName}
042         * of an MBean within that MBean server, and a Java interface
043         * <code>Intf</code> that describes the management interface of the
044         * MBean using the patterns for a Standard MBean or an MXBean, this
045         * class can be used to construct a proxy for the MBean.  The proxy
046         * implements the interface <code>Intf</code> such that all of its
047         * methods are forwarded through the MBean server to the MBean.</p>
048         *
049         * <p>If the {@code InvocationHandler} is for an MXBean, then the parameters of
050         * a method are converted from the type declared in the MXBean
051         * interface into the corresponding mapped type, and the return value
052         * is converted from the mapped type into the declared type.  For
053         * example, with the method<br>
054
055         * {@code public List<String> reverse(List<String> list);}<br>
056
057         * and given that the mapped type for {@code List<String>} is {@code
058         * String[]}, a call to {@code proxy.reverse(someList)} will convert
059         * {@code someList} from a {@code List<String>} to a {@code String[]},
060         * call the MBean operation {@code reverse}, then convert the returned
061         * {@code String[]} into a {@code List<String>}.</p>
062         *
063         * <p>The method Object.toString(), Object.hashCode(), or
064         * Object.equals(Object), when invoked on a proxy using this
065         * invocation handler, is forwarded to the MBean server as a method on
066         * the proxied MBean only if it appears in one of the proxy's
067         * interfaces.  For a proxy created with {@link
068         * JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class)
069         * JMX.newMBeanProxy} or {@link
070         * JMX#newMXBeanProxy(MBeanServerConnection, ObjectName, Class)
071         * JMX.newMXBeanProxy}, this means that the method must appear in the
072         * Standard MBean or MXBean interface.  Otherwise these methods have
073         * the following behavior:
074         * <ul>
075         * <li>toString() returns a string representation of the proxy
076         * <li>hashCode() returns a hash code for the proxy such
077         * that two equal proxies have the same hash code
078         * <li>equals(Object)
079         * returns true if and only if the Object argument is of the same
080         * proxy class as this proxy, with an MBeanServerInvocationHandler
081         * that has the same MBeanServerConnection and ObjectName; if one
082         * of the {@code MBeanServerInvocationHandler}s was constructed with
083         * a {@code Class} argument then the other must have been constructed
084         * with the same {@code Class} for {@code equals} to return true.
085         * </ul>
086         *
087         * @since 1.5
088         */
089        public class MBeanServerInvocationHandler implements  InvocationHandler {
090            /**
091             * <p>Invocation handler that forwards methods through an MBean
092             * server to a Standard MBean.  This constructor may be called
093             * instead of relying on {@link
094             * JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class)
095             * JMX.newMBeanProxy}, for instance if you need to supply a
096             * different {@link ClassLoader} to {@link Proxy#newProxyInstance
097             * Proxy.newProxyInstance}.</p>
098             *
099             * <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(
112                    MBeanServerConnection connection, 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(
139                    MBeanServerConnection connection, 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(
231                    MBeanServerConnection connection, ObjectName objectName,
232                    Class<T> interfaceClass, boolean notificationBroadcaster) {
233                final InvocationHandler handler = new MBeanServerInvocationHandler(
234                        connection, objectName);
235                final Class[] interfaces;
236                if (notificationBroadcaster) {
237                    interfaces = new Class[] { interfaceClass,
238                            NotificationEmitter.class };
239                } else
240                    interfaces = new Class[] { interfaceClass };
241
242                Object proxy = Proxy.newProxyInstance(interfaceClass
243                        .getClassLoader(), interfaces, handler);
244                return interfaceClass.cast(proxy);
245            }
246
247            public Object invoke(Object proxy, Method method, Object[] args)
248                    throws Throwable {
249                final Class methodClass = method.getDeclaringClass();
250
251                if (methodClass.equals(NotificationBroadcaster.class)
252                        || methodClass.equals(NotificationEmitter.class))
253                    return invokeBroadcasterMethod(proxy, method, args);
254
255                // local or not: equals, toString, hashCode
256                if (shouldDoLocally(proxy, method))
257                    return doLocally(proxy, method, args);
258
259                try {
260                    if (isMXBean) {
261                        MXBeanProxy p = findMXBeanProxy(methodClass);
262                        return p.invoke(connection, objectName, method, args);
263                    } else {
264                        final String methodName = method.getName();
265                        final Class[] paramTypes = method.getParameterTypes();
266                        final Class returnType = method.getReturnType();
267
268                        /* Inexplicably, InvocationHandler specifies that args is null
269                           when the method takes no arguments rather than a
270                           zero-length array.  */
271                        final int nargs = (args == null) ? 0 : args.length;
272
273                        if (methodName.startsWith("get")
274                                && methodName.length() > 3 && nargs == 0
275                                && !returnType.equals(Void.TYPE)) {
276                            return connection.getAttribute(objectName,
277                                    methodName.substring(3));
278                        }
279
280                        if (methodName.startsWith("is")
281                                && methodName.length() > 2
282                                && nargs == 0
283                                && (returnType.equals(Boolean.TYPE) || returnType
284                                        .equals(Boolean.class))) {
285                            return connection.getAttribute(objectName,
286                                    methodName.substring(2));
287                        }
288
289                        if (methodName.startsWith("set")
290                                && methodName.length() > 3 && nargs == 1
291                                && returnType.equals(Void.TYPE)) {
292                            Attribute attr = new Attribute(methodName
293                                    .substring(3), args[0]);
294                            connection.setAttribute(objectName, attr);
295                            return null;
296                        }
297
298                        final String[] signature = new String[paramTypes.length];
299                        for (int i = 0; i < paramTypes.length; i++)
300                            signature[i] = paramTypes[i].getName();
301                        return connection.invoke(objectName, methodName, args,
302                                signature);
303                    }
304                } catch (MBeanException e) {
305                    throw e.getTargetException();
306                } catch (RuntimeMBeanException re) {
307                    throw re.getTargetException();
308                } catch (RuntimeErrorException rre) {
309                    throw rre.getTargetError();
310                }
311                /* The invoke may fail because it can't get to the MBean, with
312                   one of the these exceptions declared by
313                   MBeanServerConnection.invoke:
314                   - RemoteException: can't talk to MBeanServer;
315                   - InstanceNotFoundException: objectName is not registered;
316                   - ReflectionException: objectName is registered but does not
317                     have the method being invoked.
318                   In all of these cases, the exception will be wrapped by the
319                   proxy mechanism in an UndeclaredThrowableException unless
320                   it happens to be declared in the "throws" clause of the
321                   method being invoked on the proxy.
322                 */
323            }
324
325            private static MXBeanProxy findMXBeanProxy(Class<?> mxbeanInterface) {
326                synchronized (mxbeanProxies) {
327                    WeakReference<MXBeanProxy> proxyRef = mxbeanProxies
328                            .get(mxbeanInterface);
329                    MXBeanProxy p = (proxyRef == null) ? null : proxyRef.get();
330                    if (p == null) {
331                        p = new MXBeanProxy(mxbeanInterface);
332                        mxbeanProxies.put(mxbeanInterface,
333                                new WeakReference<MXBeanProxy>(p));
334                    }
335                    return p;
336                }
337            }
338
339            private static final WeakHashMap<Class<?>, WeakReference<MXBeanProxy>> mxbeanProxies = new WeakHashMap<Class<?>, WeakReference<MXBeanProxy>>();
340
341            private Object invokeBroadcasterMethod(Object proxy, Method method,
342                    Object[] args) throws Exception {
343                final String methodName = method.getName();
344                final int nargs = (args == null) ? 0 : args.length;
345
346                if (methodName.equals("addNotificationListener")) {
347                    /* The various throws of IllegalArgumentException here
348                       should not happen, since we know what the methods in
349                       NotificationBroadcaster and NotificationEmitter
350                       are.  */
351                    if (nargs != 3) {
352                        final String msg = "Bad arg count to addNotificationListener: "
353                                + nargs;
354                        throw new IllegalArgumentException(msg);
355                    }
356                    /* Other inconsistencies will produce ClassCastException
357                       below.  */
358
359                    NotificationListener listener = (NotificationListener) args[0];
360                    NotificationFilter filter = (NotificationFilter) args[1];
361                    Object handback = args[2];
362                    connection.addNotificationListener(objectName, listener,
363                            filter, handback);
364                    return null;
365
366                } else if (methodName.equals("removeNotificationListener")) {
367
368                    /* NullPointerException if method with no args, but that
369                       shouldn't happen because removeNL does have args.  */
370                    NotificationListener listener = (NotificationListener) args[0];
371
372                    switch (nargs) {
373                    case 1:
374                        connection.removeNotificationListener(objectName,
375                                listener);
376                        return null;
377
378                    case 3:
379                        NotificationFilter filter = (NotificationFilter) args[1];
380                        Object handback = args[2];
381                        connection.removeNotificationListener(objectName,
382                                listener, filter, handback);
383                        return null;
384
385                    default:
386                        final String msg = "Bad arg count to removeNotificationListener: "
387                                + nargs;
388                        throw new IllegalArgumentException(msg);
389                    }
390
391                } else if (methodName.equals("getNotificationInfo")) {
392
393                    if (args != null) {
394                        throw new IllegalArgumentException(
395                                "getNotificationInfo has " + "args");
396                    }
397
398                    MBeanInfo info = connection.getMBeanInfo(objectName);
399                    return info.getNotifications();
400
401                } else {
402                    throw new IllegalArgumentException("Bad method name: "
403                            + methodName);
404                }
405            }
406
407            private boolean shouldDoLocally(Object proxy, Method method) {
408                final String methodName = method.getName();
409                if ((methodName.equals("hashCode") || methodName
410                        .equals("toString"))
411                        && method.getParameterTypes().length == 0
412                        && isLocal(proxy, method))
413                    return true;
414                if (methodName.equals("equals")
415                        && Arrays.equals(method.getParameterTypes(),
416                                new Class[] { Object.class })
417                        && isLocal(proxy, method))
418                    return true;
419                return false;
420            }
421
422            private Object doLocally(Object proxy, Method method, Object[] args) {
423                final String methodName = method.getName();
424
425                if (methodName.equals("equals")) {
426
427                    if (this  == args[0]) {
428                        return true;
429                    }
430
431                    if (!(args[0] instanceof  Proxy)) {
432                        return false;
433                    }
434
435                    final InvocationHandler ihandler = Proxy
436                            .getInvocationHandler(args[0]);
437
438                    if (ihandler == null
439                            || !(ihandler instanceof  MBeanServerInvocationHandler)) {
440                        return false;
441                    }
442
443                    final MBeanServerInvocationHandler handler = (MBeanServerInvocationHandler) ihandler;
444
445                    return connection.equals(handler.connection)
446                            && objectName.equals(handler.objectName)
447                            && proxy.getClass().equals(args[0].getClass());
448                } else if (methodName.equals("toString")) {
449                    return (isMXBean ? "MX" : "M") + "BeanProxy(" + connection
450                            + "[" + objectName + "])";
451                } else if (methodName.equals("hashCode")) {
452                    return objectName.hashCode() + connection.hashCode();
453                }
454
455                throw new RuntimeException("Unexpected method name: "
456                        + methodName);
457            }
458
459            private static boolean isLocal(Object proxy, Method method) {
460                final Class<?>[] interfaces = proxy.getClass().getInterfaces();
461                if (interfaces == null) {
462                    return true;
463                }
464
465                final String methodName = method.getName();
466                final Class<?>[] params = method.getParameterTypes();
467                for (Class<?> intf : interfaces) {
468                    try {
469                        intf.getMethod(methodName, params);
470                        return false; // found method in one of our interfaces
471                    } catch (NoSuchMethodException nsme) {
472                        // OK.
473                    }
474                }
475
476                return true; // did not find in any interface
477            }
478
479            private final MBeanServerConnection connection;
480            private final ObjectName objectName;
481            private final boolean isMXBean;
482        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.