001    /* ManagementFactory.java - Factory for obtaining system beans.
002       Copyright (C) 2006 Free Software Foundation
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package java.lang.management;
039    
040    import gnu.classpath.SystemProperties;
041    
042    import gnu.java.lang.management.ClassLoadingMXBeanImpl;
043    import gnu.java.lang.management.CompilationMXBeanImpl;
044    import gnu.java.lang.management.GarbageCollectorMXBeanImpl;
045    import gnu.java.lang.management.OperatingSystemMXBeanImpl;
046    import gnu.java.lang.management.MemoryMXBeanImpl;
047    import gnu.java.lang.management.MemoryManagerMXBeanImpl;
048    import gnu.java.lang.management.MemoryPoolMXBeanImpl;
049    import gnu.java.lang.management.RuntimeMXBeanImpl;
050    import gnu.java.lang.management.ThreadMXBeanImpl;
051    
052    import java.io.IOException;
053    
054    import java.lang.reflect.InvocationHandler;
055    import java.lang.reflect.Method;
056    import java.lang.reflect.Proxy;
057    
058    import java.util.ArrayList;
059    import java.util.HashMap;
060    import java.util.Iterator;
061    import java.util.List;
062    import java.util.Map;
063    
064    import java.util.logging.LogManager;
065    
066    import javax.management.Attribute;
067    import javax.management.InstanceAlreadyExistsException;
068    import javax.management.MBeanRegistrationException;
069    import javax.management.MBeanServer;
070    import javax.management.MBeanServerConnection;
071    import javax.management.MBeanServerFactory;
072    import javax.management.MalformedObjectNameException;
073    import javax.management.NotCompliantMBeanException;
074    import javax.management.NotificationEmitter;
075    import javax.management.NotificationFilter;
076    import javax.management.NotificationListener;
077    import javax.management.ObjectName;
078    
079    import javax.management.openmbean.CompositeData;
080    import javax.management.openmbean.TabularData;
081    
082    /**
083     * <p>
084     * Provides access to the system's management beans via a series
085     * of static methods.
086     * </p>
087     * <p>
088     * An instance of a system management bean can be obtained by
089     * using one of the following methods:
090     * </p>
091     * <ol>
092     * <li>Calling the appropriate static method of this factory.
093     * </li>
094     * <li>Using the platform {@link javax.management.MBeanServer}
095     * to access the beans locally, or an
096     * {@link javax.management.MBeanServerConnection} for remote
097     * access.  The attributes and operations use the limited
098     * range of data types specified below.</li>
099     * </ol>
100     * <h2>Open Data Types</h2>
101     * <p>
102     * The data types used by the management beans are restricted
103     * to <emph>open</emph> data types to aid interoperability.  This
104     * allows the beans to be accessed remotely, including from non-Java
105     * clients.  Below is a table which lists the types used by the beans
106     * on the left, and the types they are converted to when returned via
107     * a bean server on the right.  Type information is provided for each
108     * bean by obtaining its instance of {@link javax.management.MBeanInfo}.
109     * </p>
110     * <table>
111     * <th><td>Data Type Used</td><td>Data Type Returned</td></th>
112     * <tr>
113     * <td>Primitive types (<code>int</code>, <code>char</code>, etc.)</td>
114     * <td>Same</td>
115     * </tr><tr>
116     * <td>Wrapper classes ({@link{java.lang.Integer},
117     * @link{java.lang.Character}, etc.)</td>
118     * <td>Same</td>
119     * </tr><tr>
120     * <td>An {@link java.lang.Enum}</td>
121     * <td>The <code>name</code> of the enumeration constant</td>
122     * </tr><tr>
123     * <td>An array of type <code>E</code></td>
124     * <td>An array of the same dimensions with this mapping applied
125     * to <code>E</code>.</td>
126     * </tr><tr>
127     * <td>A class with `getter' methods and a
128     * <code>from({@link javax.management.openmbean.CompositeData})</code>
129     * method.</td>
130     * <td>The equivalent {@link javax.management.openmbean.CompositeData}
131     * instance, specified by the <code>from</code> method.</td>
132     * </tr><tr>
133     * <td>A map with keys of type <code>K</code> and values of
134     * type <code>V</code>.</td>
135     * <td>A {@link javax.management.openmbean.TabularData} instance,
136     * with the row type containing two items, <code>"key"</code> and
137     * <code>"value"</code> with the types <code>K</code> and <code>V</code>
138     * respectively (with translation applied).</td>
139     * </tr><tr>
140     * <td>A list of type <code>E</code>.</td>
141     * <td>An array with this mapping applied to <code>E</code>.</td>
142     * </tr></table>
143     *
144     * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
145     * @since 1.5
146     */
147    public class ManagementFactory
148    {
149    
150      /**
151       * The object name for the class loading bean.
152       */
153      public static final String CLASS_LOADING_MXBEAN_NAME =
154        "java.lang:type=ClassLoading";
155    
156      /**
157       * The object name for the compilation bean.
158       */
159      public static final String COMPILATION_MXBEAN_NAME =
160        "java.lang:type=Compilation";
161    
162      /**
163       * The domain for the garbage collecting beans.
164       */
165      public static final String GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE =
166        "java.lang:type=GarbageCollector";
167    
168      /**
169       * The domain for the memory manager beans.
170       */
171      public static final String MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE =
172        "java.lang:type=MemoryManager";
173    
174      /**
175       * The object name for the memory bean.
176       */
177      public static final String MEMORY_MXBEAN_NAME =
178        "java.lang:type=Memory";
179    
180      /**
181       * The domain for the memory pool beans.
182       */
183      public static final String MEMORY_POOL_MXBEAN_DOMAIN_TYPE =
184        "java.lang:type=MemoryPool";
185    
186      /**
187       * The object name for the operating system bean.
188       */
189      public static final String OPERATING_SYSTEM_MXBEAN_NAME =
190        "java.lang:type=OperatingSystem";
191    
192      /**
193       * The object name for the runtime bean.
194       */
195      public static final String RUNTIME_MXBEAN_NAME =
196        "java.lang:type=Runtime";
197    
198      /**
199       * The object name for the threading bean.
200       */
201      public static final String THREAD_MXBEAN_NAME =
202        "java.lang:type=Threading";
203    
204      /**
205       * The operating system management bean.
206       */
207      private static OperatingSystemMXBean osBean;
208    
209      /**
210       * The runtime management bean.
211       */
212      private static RuntimeMXBean runtimeBean;
213    
214      /**
215       * The class loading management bean.
216       */
217      private static ClassLoadingMXBean classLoadingBean;
218    
219      /**
220       * The thread bean.
221       */
222      private static ThreadMXBean threadBean;
223    
224      /**
225       * The memory bean.
226       */
227      private static MemoryMXBean memoryBean;
228    
229      /**
230       * The compilation bean (may remain null).
231       */
232      private static CompilationMXBean compilationBean;
233    
234      /**
235       * The platform server.
236       */
237      private static MBeanServer platformServer;
238    
239      /**
240       * Private constructor to prevent instance creation.
241       */
242      private ManagementFactory() {}
243    
244      /**
245       * Returns the operating system management bean for the
246       * operating system on which the virtual machine is running.
247       *
248       * @return an instance of {@link OperatingSystemMXBean} for
249       *         the underlying operating system.
250       */
251      public static OperatingSystemMXBean getOperatingSystemMXBean()
252      {
253        if (osBean == null)
254          try
255            {
256              osBean = new OperatingSystemMXBeanImpl();
257            }
258          catch (NotCompliantMBeanException e)
259            {
260              throw new InternalError("The GNU implementation of the " +
261                                      "operating system bean is not a " +
262                                      "compliant management bean.");
263            }
264        return osBean;
265      }
266    
267      /**
268       * Returns the runtime management bean for the
269       * running virtual machine.
270       *
271       * @return an instance of {@link RuntimeMXBean} for
272       *         this virtual machine.
273       */
274      public static RuntimeMXBean getRuntimeMXBean()
275      {
276        if (runtimeBean == null)
277          try
278            {
279              runtimeBean = new RuntimeMXBeanImpl();
280            }
281          catch (NotCompliantMBeanException e)
282            {
283              throw new InternalError("The GNU implementation of the " +
284                                      "runtime bean is not a compliant " +
285                                      "management bean.");
286            }
287        return runtimeBean;
288      }
289    
290      /**
291       * Returns the class loading management bean for the
292       * running virtual machine.
293       *
294       * @return an instance of {@link ClassLoadingMXBean} for
295       *         this virtual machine.
296       */
297      public static ClassLoadingMXBean getClassLoadingMXBean()
298      {
299        if (classLoadingBean == null)
300          try
301            {
302              classLoadingBean = new ClassLoadingMXBeanImpl();
303            }
304          catch (NotCompliantMBeanException e)
305            {
306              throw new InternalError("The GNU implementation of the " +
307                                      "class loading bean is not a " +
308                                      "compliant management bean.");
309            }
310        return classLoadingBean;
311      }
312    
313      /**
314       * Returns the thread management bean for the running
315       * virtual machine.
316       *
317       * @return an instance of {@link ThreadMXBean} for
318       *         this virtual machine.
319       */
320      public static ThreadMXBean getThreadMXBean()
321      {
322        if (threadBean == null)
323          try
324            {
325              threadBean = new ThreadMXBeanImpl();
326            }
327          catch (NotCompliantMBeanException e)
328            {
329              throw new InternalError("The GNU implementation of the " +
330                                      "thread bean is not a compliant " +
331                                      "management bean.");
332            }
333        return threadBean;
334      }
335    
336      /**
337       * Returns the memory management bean for the running
338       * virtual machine.
339       *
340       * @return an instance of {@link MemoryMXBean} for
341       *         this virtual machine.
342       */
343      public static MemoryMXBean getMemoryMXBean()
344      {
345        if (memoryBean == null)
346          try
347            {
348              memoryBean = new MemoryMXBeanImpl();
349            }
350          catch (NotCompliantMBeanException e)
351            {
352              throw new InternalError("The GNU implementation of the " +
353                                      "memory bean is not a compliant " +
354                                      "management bean.");
355            }
356        return memoryBean;
357      }
358    
359      /**
360       * Returns the compilation bean for the running
361       * virtual machine, if supported.  Otherwise,
362       * it returns <code>null</code>.
363       *
364       * @return an instance of {@link CompilationMXBean} for
365       *         this virtual machine, or <code>null</code>
366       *         if the virtual machine doesn't include
367       *         a Just-In-Time (JIT) compiler.
368       */
369      public static CompilationMXBean getCompilationMXBean()
370      {
371        if (compilationBean == null &&
372            SystemProperties.getProperty("gnu.java.compiler.name") != null)
373          try
374            {
375              compilationBean = new CompilationMXBeanImpl();
376            }
377          catch (NotCompliantMBeanException e)
378            {
379              throw new InternalError("The GNU implementation of the " +
380                                      "compilation bean is not a compliant " +
381                                      "management bean.");
382            }
383        return compilationBean;
384      }
385    
386      /**
387       * Returns the memory pool beans for the running
388       * virtual machine.  These may change during the course
389       * of execution.
390       *
391       * @return a list of memory pool beans, one for each pool.
392       */
393      public static List<MemoryPoolMXBean> getMemoryPoolMXBeans()
394      {
395        List<MemoryPoolMXBean> poolBeans =
396          new ArrayList<MemoryPoolMXBean>();
397        String[] names = VMManagementFactory.getMemoryPoolNames();
398        for (int a = 0; a < names.length; ++a)
399          try
400            {
401              poolBeans.add(new MemoryPoolMXBeanImpl(names[a]));
402            }
403          catch (NotCompliantMBeanException e)
404            {
405              throw new InternalError("The GNU implementation of the " +
406                                      "memory pool bean, " + a + ", is " +
407                                      "not a compliant management bean.");
408            }
409        return poolBeans;
410      }
411    
412      /**
413       * Returns the memory manager beans for the running
414       * virtual machine.  These may change during the course
415       * of execution.
416       *
417       * @return a list of memory manager beans, one for each manager.
418       */
419      public static List<MemoryManagerMXBean> getMemoryManagerMXBeans()
420      {
421        List<MemoryManagerMXBean> managerBeans =
422          new ArrayList<MemoryManagerMXBean>();
423        String[] names = VMManagementFactory.getMemoryManagerNames();
424        for (int a = 0; a < names.length; ++a)
425          try
426            {
427              managerBeans.add(new MemoryManagerMXBeanImpl(names[a]));
428            }
429          catch (NotCompliantMBeanException e)
430            {
431              throw new InternalError("The GNU implementation of the " +
432                                      "memory manager bean, " + a + ", is " +
433                                      "not a compliant management bean.");
434            }
435        managerBeans.addAll(getGarbageCollectorMXBeans());
436        return managerBeans;
437      }
438    
439      /**
440       * Returns the garbage collector beans for the running
441       * virtual machine.  These may change during the course
442       * of execution.
443       *
444       * @return a list of garbage collector beans, one for each pool.
445       */
446      public static List<GarbageCollectorMXBean> getGarbageCollectorMXBeans()
447      {
448        List<GarbageCollectorMXBean> gcBeans =
449          new ArrayList<GarbageCollectorMXBean>();
450        String[] names = VMManagementFactory.getGarbageCollectorNames();
451        for (int a = 0; a < names.length; ++a)
452          try
453            {
454              gcBeans.add(new GarbageCollectorMXBeanImpl(names[a]));
455            }
456          catch (NotCompliantMBeanException e)
457            {
458              throw new InternalError("The GNU implementation of the " +
459                                      "garbage collector bean, " + a +
460                                      ", is not a compliant management " +
461                                      "bean.");
462            }
463        return gcBeans;
464      }
465    
466      /**
467       * <p>
468       * Returns the platform {@link javax.management.MBeanServer}.  On the
469       * first call to this method, a server instance is retrieved from
470       * the {@link javax.management.MBeanServerFactory} and each of the
471       * beans are registered with it.  Subsequent calls return the existing
472       * instance.  If the property <code>javax.management.builder.initial</code>
473       * is set, its value will be used as the name of the class which is used
474       * to provide the server instance.
475       * </p>
476       * <p>
477       * It is recommended that the platform server is used for other beans as
478       * well, in order to simplify their discovery and publication.  Name conflicts
479       * should be avoided.
480       * </p>
481       *
482       * @return the platform {@link javax.management.MBeanServer}
483       * @throws SecurityException if a security manager exists and the
484       *                           caller's permissions don't imply {@link
485       *                           MBeanServerPermission(String)}("createMBeanServer")
486       * @see javax.management.MBeanServerFactory
487       * @see javax.management.MBeanServerFactory#createMBeanServer()
488       */
489      public static MBeanServer getPlatformMBeanServer()
490      {
491        if (platformServer == null)
492          {
493            platformServer = MBeanServerFactory.createMBeanServer();
494            try
495              {
496                platformServer.registerMBean(getOperatingSystemMXBean(),
497                                             new ObjectName(OPERATING_SYSTEM_MXBEAN_NAME));
498                platformServer.registerMBean(getRuntimeMXBean(),
499                                             new ObjectName(RUNTIME_MXBEAN_NAME));
500                platformServer.registerMBean(getClassLoadingMXBean(),
501                                             new ObjectName(CLASS_LOADING_MXBEAN_NAME));
502                platformServer.registerMBean(getThreadMXBean(),
503                                             new ObjectName(THREAD_MXBEAN_NAME));
504                platformServer.registerMBean(getMemoryMXBean(),
505                                             new ObjectName(MEMORY_MXBEAN_NAME));
506                CompilationMXBean compBean = getCompilationMXBean();
507                if (compBean != null)
508                  platformServer.registerMBean(compBean,
509                                               new ObjectName(COMPILATION_MXBEAN_NAME));
510                Iterator beans = getMemoryPoolMXBeans().iterator();
511                while (beans.hasNext())
512                  {
513                    MemoryPoolMXBean bean = (MemoryPoolMXBean) beans.next();
514                    platformServer.registerMBean(bean,
515                                                 new ObjectName(MEMORY_POOL_MXBEAN_DOMAIN_TYPE +
516                                                                ",name=" +
517                                                                bean.getName()));
518                  }
519                beans = getMemoryManagerMXBeans().iterator();
520                while (beans.hasNext())
521                  {
522                    MemoryManagerMXBean bean = (MemoryManagerMXBean) beans.next();
523                    platformServer.registerMBean(bean,
524                                                 new ObjectName(MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE +
525                                                                ",name=" +
526                                                                bean.getName()));
527                  }
528                beans = getGarbageCollectorMXBeans().iterator();
529                while (beans.hasNext())
530                  {
531                    GarbageCollectorMXBean bean = (GarbageCollectorMXBean) beans.next();
532                    platformServer.registerMBean(bean,
533                                                 new ObjectName(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE +
534                                                                ",name=" +
535                                                                bean.getName()));
536                  }
537                platformServer.registerMBean(LogManager.getLoggingMXBean(),
538                                             new ObjectName(LogManager.LOGGING_MXBEAN_NAME));
539              }
540            catch (InstanceAlreadyExistsException e)
541              {
542                throw (Error)
543                  (new InternalError("One of the management beans is " +
544                                     "already registered.").initCause(e));
545              }
546            catch (MBeanRegistrationException e)
547              {
548                throw (Error)
549                  (new InternalError("One of the management beans' preRegister " +
550                                     "methods threw an exception.").initCause(e));
551              }
552            catch (NotCompliantMBeanException e)
553              {
554                throw (Error)
555                  (new InternalError("One of the management beans is " +
556                                     "not compliant.").initCause(e));
557              }
558            catch (MalformedObjectNameException e)
559              {
560                throw (Error)
561                  (new InternalError("The object name of a management bean is " +
562                                     "not compliant.").initCause(e));
563              }
564          }
565        return platformServer;
566      }
567    
568      /**
569       * <p>
570       * Returns a proxy for the specified platform bean.  A proxy object is created
571       * using <code>Proxy.newProxyInstance(mxbeanInterface.getClassLoader(),
572       * new Class[] { mxbeanInterface }, handler)</code>.  The
573       * {@link javax.management.NotificationEmitter} class is also added to the
574       * array if the bean provides notifications.  <code>handler</code> refers
575       * to the invocation handler which forwards calls to the connection, and
576       * also provides translation between the Java data types used in the
577       * bean interfaces and the open data types, as specified in the description
578       * of this class.  It is this translation that makes the
579       * usual {@link javax.management.MBeanServerInvocationHandler} inappropriate
580       * for providing such a proxy.
581       * </p>
582       * <p>
583       * <strong>Note</strong>: use of the proxy may result in
584       * {@link java.io.IOException}s from the underlying {@link MBeanServerConnection}
585       * and a {@link java.io.InvalidObjectException} if enum constants
586       * used on the client and the server don't match.
587       * </p>
588       *
589       * @param connection the server connection to use to access the bean.
590       * @param mxbeanName the {@link javax.management.ObjectName} of the
591       *                   bean to provide a proxy for.
592       * @param mxbeanInterface the interface for the bean being proxied.
593       * @return a proxy for the specified bean.
594       * @throws IllegalArgumentException if <code>mxbeanName</code> is not a valid
595       *                                  {@link javax.management.ObjectName},
596       *                                  the interface and name do not match the
597       *                                  same bean, the name does not refer to a
598       *                                  platform bean or the bean is not registered
599       *                                  with the server accessed by <code>connection</code>.
600       * @throws IOException if the connection throws one.
601       */
602      public static <T> T newPlatformMXBeanProxy(MBeanServerConnection connection,
603                                                 String mxbeanName,
604                                                 Class<T> mxbeanInterface)
605        throws IOException
606      {
607        if (!(mxbeanName.equals(CLASS_LOADING_MXBEAN_NAME) ||
608              mxbeanName.equals(COMPILATION_MXBEAN_NAME) ||
609              mxbeanName.startsWith(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE) ||
610              mxbeanName.startsWith(MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE) ||
611              mxbeanName.equals(MEMORY_MXBEAN_NAME) ||
612              mxbeanName.startsWith(MEMORY_POOL_MXBEAN_DOMAIN_TYPE) ||
613              mxbeanName.equals(OPERATING_SYSTEM_MXBEAN_NAME) ||
614              mxbeanName.equals(RUNTIME_MXBEAN_NAME) ||
615              mxbeanName.equals(THREAD_MXBEAN_NAME)))
616          {
617            throw new IllegalArgumentException("The named bean, " + mxbeanName +
618                                               ", is not a platform name.");
619          }
620        if ((mxbeanName.equals(CLASS_LOADING_MXBEAN_NAME) &&
621             mxbeanInterface != ClassLoadingMXBean.class) ||
622            (mxbeanName.equals(COMPILATION_MXBEAN_NAME) &&
623             mxbeanInterface != CompilationMXBean.class) ||
624            (mxbeanName.startsWith(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE) &&
625             mxbeanInterface != GarbageCollectorMXBean.class) ||
626            (mxbeanName.startsWith(MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE) &&
627             mxbeanInterface != MemoryManagerMXBean.class) ||
628            (mxbeanName.equals(MEMORY_MXBEAN_NAME) &&
629             mxbeanInterface != MemoryMXBean.class) ||
630            (mxbeanName.startsWith(MEMORY_POOL_MXBEAN_DOMAIN_TYPE) &&
631             mxbeanInterface != MemoryPoolMXBean.class) ||
632            (mxbeanName.equals(OPERATING_SYSTEM_MXBEAN_NAME) &&
633             mxbeanInterface != OperatingSystemMXBean.class) ||
634            (mxbeanName.equals(RUNTIME_MXBEAN_NAME) &&
635             mxbeanInterface != RuntimeMXBean.class) ||
636            (mxbeanName.equals(THREAD_MXBEAN_NAME) &&
637             mxbeanInterface != ThreadMXBean.class))
638          throw new IllegalArgumentException("The interface, " + mxbeanInterface +
639                                             ", does not match the bean, " + mxbeanName);
640        ObjectName bean;
641        try
642          {
643            bean = new ObjectName(mxbeanName);
644          }
645        catch (MalformedObjectNameException e)
646          {
647            throw new IllegalArgumentException("The named bean is invalid.");
648          }
649        if (!(connection.isRegistered(bean)))
650          throw new IllegalArgumentException("The bean is not registered on this connection.");
651        Class[] interfaces;
652        if (mxbeanName.equals(MEMORY_MXBEAN_NAME))
653          interfaces = new Class[] { mxbeanInterface, NotificationEmitter.class };
654        else
655          interfaces = new Class[] { mxbeanInterface };
656        return (T) Proxy.newProxyInstance(mxbeanInterface.getClassLoader(),
657                                          interfaces,
658                                          new ManagementInvocationHandler(connection, bean));
659      }
660    
661      /**
662       * This invocation handler provides method calls for a platform bean
663       * by forwarding them to a {@link MBeanServerConnection}.  Translation from
664       * Java data types to open data types is performed as specified above.
665       *
666       * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
667       * @since 1.5
668       */
669      private static class ManagementInvocationHandler
670        implements InvocationHandler
671      {
672    
673        /**
674         * The encapsulated connection.
675         */
676        private MBeanServerConnection conn;
677    
678        /**
679         * The bean being proxied.
680         */
681        private ObjectName bean;
682    
683        /**
684         * Constructs a new {@link InvocationHandler} which proxies
685         * for the specified bean using the supplied connection.
686         *
687         * @param conn the connection on which to forward method calls.
688         * @param bean the bean to proxy.
689         */
690        public ManagementInvocationHandler(MBeanServerConnection conn,
691                                           ObjectName bean)
692          throws IOException
693        {
694          this.conn = conn;
695          this.bean = bean;
696        }
697    
698        /**
699         * Called by the proxy class whenever a method is called.  The method
700         * is emulated by retrieving an attribute from, setting an attribute on
701         * or invoking a method on the server connection as required.  Translation
702         * between the Java data types supplied as arguments to the open types used
703         * by the bean is provided, as well as translation of the return value back
704         * in to the appropriate Java type.
705         *
706         * @param proxy the proxy on which the method was called.
707         * @param method the method which was called.
708         * @param args the arguments supplied to the method.
709         * @return the return value from the method.
710         * @throws Throwable if an exception is thrown in performing the
711         *                   method emulation.
712         */
713        public Object invoke(Object proxy, Method method, Object[] args)
714          throws Throwable
715        {
716          String name = method.getName();
717          if (name.equals("toString"))
718            return "Proxy for " + bean + " using " + conn;
719          if (name.equals("addNotificationListener"))
720            {
721              conn.addNotificationListener(bean,
722                                           (NotificationListener) args[0],
723                                           (NotificationFilter) args[1],
724                                           args[2]);
725              return null;
726            }
727          if (name.equals("getNotificationInfo"))
728            return conn.getMBeanInfo(bean).getNotifications();
729          if (name.equals("removeNotificationListener"))
730            {
731              if (args.length == 1)
732                conn.removeNotificationListener(bean,
733                                                (NotificationListener)
734                                                args[0]);
735              else
736                conn.removeNotificationListener(bean,
737                                                (NotificationListener)
738                                                args[0],
739                                                (NotificationFilter)
740                                                args[1], args[2]);
741              return null;
742            }
743          String attrib = null;
744          if (name.startsWith("get"))
745            attrib = name.substring(3);
746          else if (name.startsWith("is"))
747            attrib = name.substring(2);
748          if (attrib != null)
749            return translate(conn.getAttribute(bean, attrib), method);
750          else if (name.startsWith("set"))
751            {
752              conn.setAttribute(bean, new Attribute(name.substring(3),
753                                                    args[0]));
754              return null;
755            }
756          else
757            return translate(conn.invoke(bean, name, args, null), method);
758        }
759    
760        /**
761         * Translates the returned open data type to the value
762         * required by the interface.
763         *
764         * @param otype the open type returned by the method call.
765         * @param method the method that was called.
766         * @return the equivalent return type required by the interface.
767         * @throws Throwable if an exception is thrown in performing the
768         *                   conversion.
769         */
770        private final Object translate(Object otype, Method method)
771          throws Throwable
772        {
773          Class<?> returnType = method.getReturnType();
774          if (returnType.isEnum())
775            {
776              String ename = (String) otype;
777              Enum[] constants = (Enum[]) returnType.getEnumConstants();
778              for (Enum c : constants)
779                if (c.name().equals(ename))
780                  return c;
781            }
782          if (List.class.isAssignableFrom(returnType))
783            {
784              Object[] elems = (Object[]) otype;
785              List l = new ArrayList(elems.length);
786              for (Object elem : elems)
787                l.add(elem);
788              return l;
789            }
790          if (Map.class.isAssignableFrom(returnType))
791            {
792              TabularData data = (TabularData) otype;
793              Map m = new HashMap(data.size());
794              for (Object val : data.values())
795                {
796                  CompositeData vals = (CompositeData) val;
797                  m.put(vals.get("key"), vals.get("value"));
798                }
799              return m;
800            }
801          try
802            {
803              Method m = returnType.getMethod("from",
804                                              new Class[]
805                { CompositeData.class });
806              return m.invoke(null, (CompositeData) otype);
807            }
808          catch (NoSuchMethodException e)
809            {
810              /* Ignored; we expect this if this
811                 isn't a from(CompositeData) class */
812            }
813          return otype;
814        }
815    
816      }
817    }