OSDN Git Service

libjava/classpath/ChangeLog.gcj:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / lang / management / ManagementFactory.java
index 6e7af0f..977b399 100644 (file)
@@ -49,10 +49,35 @@ import gnu.java.lang.management.MemoryPoolMXBeanImpl;
 import gnu.java.lang.management.RuntimeMXBeanImpl;
 import gnu.java.lang.management.ThreadMXBeanImpl;
 
+import java.io.IOException;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+
+import java.util.logging.LogManager;
 
+import javax.management.Attribute;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerFactory;
+import javax.management.MalformedObjectNameException;
 import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
 
 /**
  * <p>
@@ -66,7 +91,55 @@ import javax.management.NotCompliantMBeanException;
  * <ol>
  * <li>Calling the appropriate static method of this factory.
  * </li>
+ * <li>Using the platform {@link javax.management.MBeanServer}
+ * to access the beans locally, or an
+ * {@link javax.management.MBeanServerConnection} for remote
+ * access.  The attributes and operations use the limited
+ * range of data types specified below.</li>
  * </ol>
+ * <h2>Open Data Types</h2>
+ * <p>
+ * The data types used by the management beans are restricted
+ * to <emph>open</emph> data types to aid interoperability.  This
+ * allows the beans to be accessed remotely, including from non-Java
+ * clients.  Below is a table which lists the types used by the beans
+ * on the left, and the types they are converted to when returned via
+ * a bean server on the right.  Type information is provided for each
+ * bean by obtaining its instance of {@link javax.management.MBeanInfo}.
+ * </p>
+ * <table>
+ * <th><td>Data Type Used</td><td>Data Type Returned</td></th>
+ * <tr>
+ * <td>Primitive types (<code>int</code>, <code>char</code>, etc.)</td>
+ * <td>Same</td>
+ * </tr><tr>
+ * <td>Wrapper classes ({@link{java.lang.Integer},
+ * @link{java.lang.Character}, etc.)</td>
+ * <td>Same</td>
+ * </tr><tr>
+ * <td>An {@link java.lang.Enum}</td>
+ * <td>The <code>name</code> of the enumeration constant</td>
+ * </tr><tr>
+ * <td>An array of type <code>E</code></td>
+ * <td>An array of the same dimensions with this mapping applied
+ * to <code>E</code>.</td>
+ * </tr><tr>
+ * <td>A class with `getter' methods and a
+ * <code>from({@link javax.management.openmbean.CompositeData})</code>
+ * method.</td>
+ * <td>The equivalent {@link javax.management.openmbean.CompositeData}
+ * instance, specified by the <code>from</code> method.</td>
+ * </tr><tr>
+ * <td>A map with keys of type <code>K</code> and values of
+ * type <code>V</code>.</td>
+ * <td>A {@link javax.management.openmbean.TabularData} instance,
+ * with the row type containing two items, <code>"key"</code> and
+ * <code>"value"</code> with the types <code>K</code> and <code>V</code>
+ * respectively (with translation applied).</td>
+ * </tr><tr>
+ * <td>A list of type <code>E</code>.</td>
+ * <td>An array with this mapping applied to <code>E</code>.</td>
+ * </tr></table>
  *
  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
  * @since 1.5
@@ -75,6 +148,60 @@ public class ManagementFactory
 {
 
   /**
+   * The object name for the class loading bean.
+   */
+  public static final String CLASS_LOADING_MXBEAN_NAME =
+    "java.lang:type=ClassLoading";
+
+  /**
+   * The object name for the compilation bean.
+   */
+  public static final String COMPILATION_MXBEAN_NAME =
+    "java.lang:type=Compilation";
+
+  /**
+   * The domain for the garbage collecting beans.
+   */
+  public static final String GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE =
+    "java.lang:type=GarbageCollector";
+
+  /**
+   * The domain for the memory manager beans.
+   */
+  public static final String MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE =
+    "java.lang:type=MemoryManager";
+
+  /**
+   * The object name for the memory bean.
+   */
+  public static final String MEMORY_MXBEAN_NAME =
+    "java.lang:type=Memory";
+
+  /**
+   * The domain for the memory pool beans.
+   */
+  public static final String MEMORY_POOL_MXBEAN_DOMAIN_TYPE =
+    "java.lang:type=MemoryPool";
+
+  /**
+   * The object name for the operating system bean.
+   */
+  public static final String OPERATING_SYSTEM_MXBEAN_NAME =
+    "java.lang:type=OperatingSystem";
+
+  /**
+   * The object name for the runtime bean.
+   */
+  public static final String RUNTIME_MXBEAN_NAME =
+    "java.lang:type=Runtime";
+
+  /**
+   * The object name for the threading bean.
+   */
+  public static final String THREAD_MXBEAN_NAME =
+    "java.lang:type=Threading";
+
+  /**
    * The operating system management bean.
    */
   private static OperatingSystemMXBean osBean;
@@ -105,6 +232,11 @@ public class ManagementFactory
   private static CompilationMXBean compilationBean;
 
   /**
+   * The platform server.
+   */
+  private static MBeanServer platformServer;
+
+  /**
    * Private constructor to prevent instance creation.
    */
   private ManagementFactory() {}
@@ -258,9 +390,10 @@ public class ManagementFactory
    *
    * @return a list of memory pool beans, one for each pool.
    */
-  public static List getMemoryPoolMXBeans()
+  public static List<MemoryPoolMXBean> getMemoryPoolMXBeans()
   {
-    List poolBeans = new ArrayList();
+    List<MemoryPoolMXBean> poolBeans =
+      new ArrayList<MemoryPoolMXBean>();
     String[] names = VMManagementFactory.getMemoryPoolNames();
     for (int a = 0; a < names.length; ++a)
       try
@@ -283,9 +416,10 @@ public class ManagementFactory
    *
    * @return a list of memory manager beans, one for each manager.
    */
-  public static List getMemoryManagerMXBeans()
+  public static List<MemoryManagerMXBean> getMemoryManagerMXBeans()
   {
-    List managerBeans = new ArrayList();
+    List<MemoryManagerMXBean> managerBeans =
+      new ArrayList<MemoryManagerMXBean>();
     String[] names = VMManagementFactory.getMemoryManagerNames();
     for (int a = 0; a < names.length; ++a)
       try
@@ -309,9 +443,10 @@ public class ManagementFactory
    *
    * @return a list of garbage collector beans, one for each pool.
    */
-  public static List getGarbageCollectorMXBeans()
+  public static List<GarbageCollectorMXBean> getGarbageCollectorMXBeans()
   {
-    List gcBeans = new ArrayList();
+    List<GarbageCollectorMXBean> gcBeans =
+      new ArrayList<GarbageCollectorMXBean>();
     String[] names = VMManagementFactory.getGarbageCollectorNames();
     for (int a = 0; a < names.length; ++a)
       try
@@ -328,4 +463,355 @@ public class ManagementFactory
     return gcBeans;
   }
 
+  /**
+   * <p>
+   * Returns the platform {@link javax.management.MBeanServer}.  On the
+   * first call to this method, a server instance is retrieved from
+   * the {@link javax.management.MBeanServerFactory} and each of the
+   * beans are registered with it.  Subsequent calls return the existing
+   * instance.  If the property <code>javax.management.builder.initial</code>
+   * is set, its value will be used as the name of the class which is used
+   * to provide the server instance.
+   * </p>
+   * <p>
+   * It is recommended that the platform server is used for other beans as
+   * well, in order to simplify their discovery and publication.  Name conflicts
+   * should be avoided.
+   * </p>
+   *
+   * @return the platform {@link javax.management.MBeanServer}
+   * @throws SecurityException if a security manager exists and the
+   *                           caller's permissions don't imply {@link
+   *                           MBeanServerPermission(String)}("createMBeanServer")
+   * @see javax.management.MBeanServerFactory
+   * @see javax.management.MBeanServerFactory#createMBeanServer()
+   */
+  public static MBeanServer getPlatformMBeanServer()
+  {
+    if (platformServer == null)
+      {
+       platformServer = MBeanServerFactory.createMBeanServer();
+       try
+         {
+           platformServer.registerMBean(getOperatingSystemMXBean(),
+                                        new ObjectName(OPERATING_SYSTEM_MXBEAN_NAME));
+           platformServer.registerMBean(getRuntimeMXBean(),
+                                        new ObjectName(RUNTIME_MXBEAN_NAME));
+           platformServer.registerMBean(getClassLoadingMXBean(),
+                                        new ObjectName(CLASS_LOADING_MXBEAN_NAME));
+           platformServer.registerMBean(getThreadMXBean(),
+                                        new ObjectName(THREAD_MXBEAN_NAME));
+           platformServer.registerMBean(getMemoryMXBean(),
+                                        new ObjectName(MEMORY_MXBEAN_NAME));
+           CompilationMXBean compBean = getCompilationMXBean();
+           if (compBean != null)
+             platformServer.registerMBean(compBean,
+                                          new ObjectName(COMPILATION_MXBEAN_NAME));
+           Iterator beans = getMemoryPoolMXBeans().iterator();
+           while (beans.hasNext())
+             {
+               MemoryPoolMXBean bean = (MemoryPoolMXBean) beans.next();
+               platformServer.registerMBean(bean,
+                                            new ObjectName(MEMORY_POOL_MXBEAN_DOMAIN_TYPE +
+                                                           ",name=" +
+                                                           bean.getName()));
+             }
+           beans = getMemoryManagerMXBeans().iterator();
+           while (beans.hasNext())
+             {
+               MemoryManagerMXBean bean = (MemoryManagerMXBean) beans.next();
+               platformServer.registerMBean(bean,
+                                            new ObjectName(MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE +
+                                                           ",name=" +
+                                                           bean.getName()));
+             }
+           beans = getGarbageCollectorMXBeans().iterator();
+           while (beans.hasNext())
+             {
+               GarbageCollectorMXBean bean = (GarbageCollectorMXBean) beans.next();
+               platformServer.registerMBean(bean,
+                                            new ObjectName(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE +
+                                                           ",name=" +
+                                                           bean.getName()));
+             }
+           platformServer.registerMBean(LogManager.getLoggingMXBean(),
+                                        new ObjectName(LogManager.LOGGING_MXBEAN_NAME));
+         }
+       catch (InstanceAlreadyExistsException e)
+         {
+           throw (Error) 
+             (new InternalError("One of the management beans is " +
+                                "already registered.").initCause(e));
+         }
+       catch (MBeanRegistrationException e)
+         {
+           throw (Error) 
+             (new InternalError("One of the management beans' preRegister " +
+                                "methods threw an exception.").initCause(e));
+         }
+       catch (NotCompliantMBeanException e)
+         {
+           throw (Error) 
+             (new InternalError("One of the management beans is " +
+                                "not compliant.").initCause(e));
+         }
+       catch (MalformedObjectNameException e)
+         {
+           throw (Error) 
+             (new InternalError("The object name of a management bean is " +
+                                "not compliant.").initCause(e));
+         }
+      }
+    return platformServer;
+  }
+
+  /**
+   * <p>
+   * Returns a proxy for the specified platform bean.  A proxy object is created
+   * using <code>Proxy.newProxyInstance(mxbeanInterface.getClassLoader(),
+   * new Class[] { mxbeanInterface }, handler)</code>.  The
+   * {@link javax.management.NotificationEmitter} class is also added to the
+   * array if the bean provides notifications.  <code>handler</code> refers
+   * to the invocation handler which forwards calls to the connection, and
+   * also provides translation between the Java data types used in the
+   * bean interfaces and the open data types, as specified in the description
+   * of this class.  It is this translation that makes the
+   * usual {@link javax.management.MBeanServerInvocationHandler} inappropriate
+   * for providing such a proxy.
+   * </p>
+   * <p>
+   * <strong>Note</strong>: use of the proxy may result in
+   * {@link java.io.IOException}s from the underlying {@link MBeanServerConnection}
+   * and a {@link java.io.InvalidObjectException} if enum constants
+   * used on the client and the server don't match.
+   * </p>
+   *
+   * @param connection the server connection to use to access the bean.
+   * @param mxbeanName the {@link javax.management.ObjectName} of the
+   *                   bean to provide a proxy for.
+   * @param mxbeanInterface the interface for the bean being proxied.
+   * @return a proxy for the specified bean.
+   * @throws IllegalArgumentException if <code>mxbeanName</code> is not a valid
+   *                                  {@link javax.management.ObjectName},
+   *                                  the interface and name do not match the
+   *                                  same bean, the name does not refer to a
+   *                                  platform bean or the bean is not registered
+   *                                  with the server accessed by <code>connection</code>.
+   * @throws IOException if the connection throws one.
+   */
+  public static <T> T newPlatformMXBeanProxy(MBeanServerConnection connection,
+                                            String mxbeanName,
+                                            Class<T> mxbeanInterface)
+    throws IOException
+  {
+    if (!(mxbeanName.equals(CLASS_LOADING_MXBEAN_NAME) ||
+         mxbeanName.equals(COMPILATION_MXBEAN_NAME) ||
+         mxbeanName.startsWith(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE) ||
+         mxbeanName.startsWith(MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE) ||
+         mxbeanName.equals(MEMORY_MXBEAN_NAME) ||
+         mxbeanName.startsWith(MEMORY_POOL_MXBEAN_DOMAIN_TYPE) ||
+         mxbeanName.equals(OPERATING_SYSTEM_MXBEAN_NAME) ||
+         mxbeanName.equals(RUNTIME_MXBEAN_NAME) ||
+         mxbeanName.equals(THREAD_MXBEAN_NAME)))
+      {
+       throw new IllegalArgumentException("The named bean, " + mxbeanName +
+                                          ", is not a platform name.");
+      }
+    if ((mxbeanName.equals(CLASS_LOADING_MXBEAN_NAME) &&
+        mxbeanInterface != ClassLoadingMXBean.class) ||
+       (mxbeanName.equals(COMPILATION_MXBEAN_NAME) &&
+        mxbeanInterface != CompilationMXBean.class) ||
+       (mxbeanName.startsWith(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE) &&
+        mxbeanInterface != GarbageCollectorMXBean.class) ||
+       (mxbeanName.startsWith(MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE) &&
+        mxbeanInterface != MemoryManagerMXBean.class) ||
+       (mxbeanName.equals(MEMORY_MXBEAN_NAME) &&
+        mxbeanInterface != MemoryMXBean.class) ||
+       (mxbeanName.startsWith(MEMORY_POOL_MXBEAN_DOMAIN_TYPE) &&
+        mxbeanInterface != MemoryPoolMXBean.class) ||
+       (mxbeanName.equals(OPERATING_SYSTEM_MXBEAN_NAME) &&
+        mxbeanInterface != OperatingSystemMXBean.class) ||
+       (mxbeanName.equals(RUNTIME_MXBEAN_NAME) &&
+        mxbeanInterface != RuntimeMXBean.class) ||
+       (mxbeanName.equals(THREAD_MXBEAN_NAME) &&
+        mxbeanInterface != ThreadMXBean.class))
+      throw new IllegalArgumentException("The interface, " + mxbeanInterface +
+                                        ", does not match the bean, " + mxbeanName);
+    ObjectName bean;
+    try
+      {
+       bean = new ObjectName(mxbeanName);
+      }
+    catch (MalformedObjectNameException e)
+      {
+       throw new IllegalArgumentException("The named bean is invalid.");
+      }
+    if (!(connection.isRegistered(bean)))
+      throw new IllegalArgumentException("The bean is not registered on this connection.");
+    Class[] interfaces;
+    if (mxbeanName.equals(MEMORY_MXBEAN_NAME))
+      interfaces = new Class[] { mxbeanInterface, NotificationEmitter.class };
+    else
+      interfaces = new Class[] { mxbeanInterface };
+    return (T) Proxy.newProxyInstance(mxbeanInterface.getClassLoader(),
+                                     interfaces,
+                                     new ManagementInvocationHandler(connection, bean));
+  }
+
+  /**
+   * This invocation handler provides method calls for a platform bean
+   * by forwarding them to a {@link MBeanServerConnection}.  Translation from
+   * Java data types to open data types is performed as specified above.
+   *
+   * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+   * @since 1.5
+   */
+  private static class ManagementInvocationHandler
+    implements InvocationHandler
+  {
+
+    /**
+     * The encapsulated connection.
+     */
+    private MBeanServerConnection conn;
+
+    /**
+     * The bean being proxied.
+     */
+    private ObjectName bean;
+
+    /**
+     * Constructs a new {@link InvocationHandler} which proxies
+     * for the specified bean using the supplied connection.
+     *
+     * @param conn the connection on which to forward method calls.
+     * @param bean the bean to proxy.
+     */
+    public ManagementInvocationHandler(MBeanServerConnection conn,
+                                      ObjectName bean)
+      throws IOException
+    {
+      this.conn = conn;
+      this.bean = bean;
+    }
+
+    /**
+     * Called by the proxy class whenever a method is called.  The method
+     * is emulated by retrieving an attribute from, setting an attribute on
+     * or invoking a method on the server connection as required.  Translation
+     * between the Java data types supplied as arguments to the open types used
+     * by the bean is provided, as well as translation of the return value back
+     * in to the appropriate Java type.
+     *
+     * @param proxy the proxy on which the method was called.
+     * @param method the method which was called.
+     * @param args the arguments supplied to the method.
+     * @return the return value from the method.
+     * @throws Throwable if an exception is thrown in performing the
+     *                   method emulation.
+     */
+    public Object invoke(Object proxy, Method method, Object[] args)
+      throws Throwable
+    {
+      String name = method.getName();
+      if (name.equals("toString"))
+       return "Proxy for " + bean + " using " + conn;
+      if (name.equals("addNotificationListener"))
+       {
+         conn.addNotificationListener(bean,
+                                      (NotificationListener) args[0],
+                                      (NotificationFilter) args[1],
+                                      args[2]);
+         return null;
+       }
+      if (name.equals("getNotificationInfo"))
+       return conn.getMBeanInfo(bean).getNotifications();
+      if (name.equals("removeNotificationListener"))
+       {
+         if (args.length == 1)
+           conn.removeNotificationListener(bean, 
+                                           (NotificationListener)
+                                           args[0]);
+         else
+           conn.removeNotificationListener(bean, 
+                                           (NotificationListener)
+                                           args[0],
+                                           (NotificationFilter)
+                                           args[1], args[2]);
+         return null;
+       }
+      String attrib = null;
+      if (name.startsWith("get"))
+       attrib = name.substring(3);
+      else if (name.startsWith("is"))
+       attrib = name.substring(2);
+      if (attrib != null)
+       return translate(conn.getAttribute(bean, attrib), method);
+      else if (name.startsWith("set"))
+       {
+         conn.setAttribute(bean, new Attribute(name.substring(3),
+                                               args[0]));
+         return null;
+       }
+      else
+       return translate(conn.invoke(bean, name, args, null), method);
+    }
+
+    /**
+     * Translates the returned open data type to the value
+     * required by the interface.
+     *
+     * @param otype the open type returned by the method call.
+     * @param method the method that was called.
+     * @return the equivalent return type required by the interface.
+     * @throws Throwable if an exception is thrown in performing the
+     *                   conversion.
+     */
+    private final Object translate(Object otype, Method method)
+      throws Throwable
+    {
+      Class<?> returnType = method.getReturnType();
+      if (returnType.isEnum())
+       {
+         String ename = (String) otype;
+         Enum[] constants = (Enum[]) returnType.getEnumConstants();
+         for (Enum c : constants)
+           if (c.name().equals(ename))
+             return c;
+       }
+      if (List.class.isAssignableFrom(returnType))
+       {
+         Object[] elems = (Object[]) otype;
+         List l = new ArrayList(elems.length);
+         for (Object elem : elems)
+           l.add(elem);
+         return l;
+       }
+      if (Map.class.isAssignableFrom(returnType))
+       {
+         TabularData data = (TabularData) otype;
+         Map m = new HashMap(data.size());
+         for (Object val : data.values())
+           {
+             CompositeData vals = (CompositeData) val;
+             m.put(vals.get("key"), vals.get("value"));
+           }
+         return m;
+       }
+      try
+       {
+         Method m = returnType.getMethod("from",
+                                         new Class[]
+           { CompositeData.class });
+         return m.invoke(null, (CompositeData) otype);
+       }
+      catch (NoSuchMethodException e)
+       {
+         /* Ignored; we expect this if this
+            isn't a from(CompositeData) class */
+       }
+      return otype;
+    }
+
+  }
 }