001/* ManagementFactory.java - Factory for obtaining system beans.
002   Copyright (C) 2006 Free Software Foundation
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package java.lang.management;
039
040import gnu.classpath.SystemProperties;
041
042import gnu.java.lang.management.ClassLoadingMXBeanImpl;
043import gnu.java.lang.management.CompilationMXBeanImpl;
044import gnu.java.lang.management.GarbageCollectorMXBeanImpl;
045import gnu.java.lang.management.OperatingSystemMXBeanImpl;
046import gnu.java.lang.management.MemoryMXBeanImpl;
047import gnu.java.lang.management.MemoryManagerMXBeanImpl;
048import gnu.java.lang.management.MemoryPoolMXBeanImpl;
049import gnu.java.lang.management.RuntimeMXBeanImpl;
050import gnu.java.lang.management.ThreadMXBeanImpl;
051
052import java.io.IOException;
053
054import java.lang.reflect.InvocationHandler;
055import java.lang.reflect.Method;
056import java.lang.reflect.Proxy;
057
058import java.util.ArrayList;
059import java.util.HashMap;
060import java.util.Iterator;
061import java.util.List;
062import java.util.Map;
063
064import java.util.logging.LogManager;
065
066import javax.management.Attribute;
067import javax.management.InstanceAlreadyExistsException;
068import javax.management.MBeanRegistrationException;
069import javax.management.MBeanServer;
070import javax.management.MBeanServerConnection;
071import javax.management.MBeanServerFactory;
072import javax.management.MalformedObjectNameException;
073import javax.management.NotCompliantMBeanException;
074import javax.management.NotificationEmitter;
075import javax.management.NotificationFilter;
076import javax.management.NotificationListener;
077import javax.management.ObjectName;
078
079import javax.management.openmbean.CompositeData;
080import 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 */
147public 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}