1 /*
   2  * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.management;
  27 import java.io.FilePermission;
  28 import java.io.IOException;
  29 import javax.management.DynamicMBean;
  30 import javax.management.MBeanServer;
  31 import javax.management.MBeanServerConnection;
  32 import javax.management.MBeanServerFactory;
  33 import javax.management.MBeanServerPermission;
  34 import javax.management.NotificationEmitter;
  35 import javax.management.ObjectName;
  36 import javax.management.InstanceNotFoundException;
  37 import javax.management.MalformedObjectNameException;
  38 import javax.management.StandardEmitterMBean;
  39 import javax.management.StandardMBean;
  40 import java.util.Collections;
  41 import java.util.List;
  42 import java.util.Set;
  43 import java.util.Map;
  44 import java.security.AccessController;
  45 import java.security.Permission;
  46 import java.security.PrivilegedAction;
  47 import java.security.PrivilegedActionException;
  48 import java.security.PrivilegedExceptionAction;
  49 import java.util.ArrayList;
  50 import java.util.Collection;
  51 import java.util.Optional;
  52 import java.util.ServiceLoader;
  53 import java.util.function.Function;
  54 import java.util.stream.Collectors;
  55 import static java.util.stream.Collectors.toMap;
  56 import java.util.stream.Stream;
  57 import javax.management.JMX;
  58 import sun.management.Util;
  59 import sun.management.spi.PlatformMBeanProvider;
  60 import sun.management.spi.PlatformMBeanProvider.PlatformComponent;
  61 
  62 /**
  63  * The {@code ManagementFactory} class is a factory class for getting
  64  * managed beans for the Java platform.
  65  * This class consists of static methods each of which returns
  66  * one or more <i>platform MXBeans</i> representing
  67  * the management interface of a component of the Java virtual
  68  * machine.
  69  *
  70  * <h3><a name="MXBean">Platform MXBeans</a></h3>
  71  * <p>
  72  * A platform MXBean is a <i>managed bean</i> that
  73  * conforms to the <a href="../../../javax/management/package-summary.html">JMX</a>
  74  * Instrumentation Specification and only uses a set of basic data types.
  75  * A JMX management application and the {@linkplain
  76  * #getPlatformMBeanServer platform MBeanServer}
  77  * can interoperate without requiring classes for MXBean specific
  78  * data types.
  79  * The data types being transmitted between the JMX connector
  80  * server and the connector client are
  81  * {@linkplain javax.management.openmbean.OpenType open types}
  82  * and this allows interoperation across versions.
  83  * See <a href="../../../javax/management/MXBean.html#MXBean-spec">
  84  * the specification of MXBeans</a> for details.
  85  *
  86  * <a name="MXBeanNames"></a>
  87  * <p>Each platform MXBean is a {@link PlatformManagedObject}
  88  * and it has a unique
  89  * {@link javax.management.ObjectName ObjectName} for
  90  * registration in the platform {@code MBeanServer} as returned by
  91  * by the {@link PlatformManagedObject#getObjectName getObjectName}
  92  * method.
  93  *
  94  * <p>
  95  * An application can access a platform MXBean in the following ways:
  96  * <h4>1. Direct access to an MXBean interface</h4>
  97  * <blockquote>
  98  * <ul>
  99  *     <li>Get an MXBean instance by calling the
 100  *         {@link #getPlatformMXBean(Class) getPlatformMXBean} or
 101  *         {@link #getPlatformMXBeans(Class) getPlatformMXBeans} method
 102  *         and access the MXBean locally in the running
 103  *         virtual machine.
 104  *         </li>
 105  *     <li>Construct an MXBean proxy instance that forwards the
 106  *         method calls to a given {@link MBeanServer MBeanServer} by calling
 107  *         the {@link #getPlatformMXBean(MBeanServerConnection, Class)} or
 108  *         {@link #getPlatformMXBeans(MBeanServerConnection, Class)} method.
 109  *         The {@link #newPlatformMXBeanProxy newPlatformMXBeanProxy} method
 110  *         can also be used to construct an MXBean proxy instance of
 111  *         a given {@code ObjectName}.
 112  *         A proxy is typically constructed to remotely access
 113  *         an MXBean of another running virtual machine.
 114  *         </li>
 115  * </ul>
 116  * <h4>2. Indirect access to an MXBean interface via MBeanServer</h4>
 117  * <ul>
 118  *     <li>Go through the platform {@code MBeanServer} to access MXBeans
 119  *         locally or a specific {@code MBeanServerConnection} to access
 120  *         MXBeans remotely.
 121  *         The attributes and operations of an MXBean use only
 122  *         <em>JMX open types</em> which include basic data types,
 123  *         {@link javax.management.openmbean.CompositeData CompositeData},
 124  *         and {@link javax.management.openmbean.TabularData TabularData}
 125  *         defined in
 126  *         {@link javax.management.openmbean.OpenType OpenType}.
 127  *         The mapping is specified in
 128  *         the {@linkplain javax.management.MXBean MXBean} specification
 129  *         for details.
 130  *        </li>
 131  * </ul>
 132  * </blockquote>
 133  *
 134  * <p>
 135  * The {@link #getPlatformManagementInterfaces getPlatformManagementInterfaces}
 136  * method returns all management interfaces supported in the Java virtual machine
 137  * including the standard management interfaces listed in the tables
 138  * below as well as the management interfaces extended by the JDK implementation.
 139  * <p>
 140  * A Java virtual machine has a single instance of the following management
 141  * interfaces:
 142  *
 143  * <blockquote>
 144  * <table border summary="The list of Management Interfaces and their single instances">
 145  * <tr>
 146  * <th>Management Interface</th>
 147  * <th>ObjectName</th>
 148  * </tr>
 149  * <tr>
 150  * <td> {@link ClassLoadingMXBean} </td>
 151  * <td> {@link #CLASS_LOADING_MXBEAN_NAME
 152  *             java.lang:type=ClassLoading}</td>
 153  * </tr>
 154  * <tr>
 155  * <td> {@link MemoryMXBean} </td>
 156  * <td> {@link #MEMORY_MXBEAN_NAME
 157  *             java.lang:type=Memory}</td>
 158  * </tr>
 159  * <tr>
 160  * <td> {@link ThreadMXBean} </td>
 161  * <td> {@link #THREAD_MXBEAN_NAME
 162  *             java.lang:type=Threading}</td>
 163  * </tr>
 164  * <tr>
 165  * <td> {@link RuntimeMXBean} </td>
 166  * <td> {@link #RUNTIME_MXBEAN_NAME
 167  *             java.lang:type=Runtime}</td>
 168  * </tr>
 169  * <tr>
 170  * <td> {@link OperatingSystemMXBean} </td>
 171  * <td> {@link #OPERATING_SYSTEM_MXBEAN_NAME
 172  *             java.lang:type=OperatingSystem}</td>
 173  * </tr>
 174  * <tr>
 175  * <td> {@link PlatformLoggingMXBean} </td>
 176  * <td> {@link java.util.logging.LogManager#LOGGING_MXBEAN_NAME
 177  *             java.util.logging:type=Logging}</td>
 178  * </tr>
 179  * </table>
 180  * </blockquote>
 181  *
 182  * <p>
 183  * A Java virtual machine has zero or a single instance of
 184  * the following management interfaces.
 185  *
 186  * <blockquote>
 187  * <table border summary="The list of Management Interfaces and their single instances">
 188  * <tr>
 189  * <th>Management Interface</th>
 190  * <th>ObjectName</th>
 191  * </tr>
 192  * <tr>
 193  * <td> {@link CompilationMXBean} </td>
 194  * <td> {@link #COMPILATION_MXBEAN_NAME
 195  *             java.lang:type=Compilation}</td>
 196  * </tr>
 197  * </table>
 198  * </blockquote>
 199  *
 200  * <p>
 201  * A Java virtual machine may have one or more instances of the following
 202  * management interfaces.
 203  * <blockquote>
 204  * <table border summary="The list of Management Interfaces and their single instances">
 205  * <tr>
 206  * <th>Management Interface</th>
 207  * <th>ObjectName</th>
 208  * </tr>
 209  * <tr>
 210  * <td> {@link GarbageCollectorMXBean} </td>
 211  * <td> {@link #GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE
 212  *             java.lang:type=GarbageCollector}{@code ,name=}<i>collector's name</i></td>
 213  * </tr>
 214  * <tr>
 215  * <td> {@link MemoryManagerMXBean} </td>
 216  * <td> {@link #MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE
 217  *             java.lang:type=MemoryManager}{@code ,name=}<i>manager's name</i></td>
 218  * </tr>
 219  * <tr>
 220  * <td> {@link MemoryPoolMXBean} </td>
 221  * <td> {@link #MEMORY_POOL_MXBEAN_DOMAIN_TYPE
 222  *             java.lang:type=MemoryPool}{@code ,name=}<i>pool's name</i></td>
 223  * </tr>
 224  * <tr>
 225  * <td> {@link BufferPoolMXBean} </td>
 226  * <td> {@code java.nio:type=BufferPool,name=}<i>pool name</i></td>
 227  * </tr>
 228  * </table>
 229  * </blockquote>
 230  *
 231  * @see <a href="../../../javax/management/package-summary.html">
 232  *      JMX Specification</a>
 233  * @see <a href="package-summary.html#examples">
 234  *      Ways to Access Management Metrics</a>
 235  * @see javax.management.MXBean
 236  *
 237  * @author  Mandy Chung
 238  * @since   1.5
 239  */
 240 public class ManagementFactory {
 241     // A class with only static fields and methods.
 242     private ManagementFactory() {};
 243 
 244     /**
 245      * String representation of the
 246      * {@code ObjectName} for the {@link ClassLoadingMXBean}.
 247      */
 248     public final static String CLASS_LOADING_MXBEAN_NAME =
 249         "java.lang:type=ClassLoading";
 250 
 251     /**
 252      * String representation of the
 253      * {@code ObjectName} for the {@link CompilationMXBean}.
 254      */
 255     public final static String COMPILATION_MXBEAN_NAME =
 256         "java.lang:type=Compilation";
 257 
 258     /**
 259      * String representation of the
 260      * {@code ObjectName} for the {@link MemoryMXBean}.
 261      */
 262     public final static String MEMORY_MXBEAN_NAME =
 263         "java.lang:type=Memory";
 264 
 265     /**
 266      * String representation of the
 267      * {@code ObjectName} for the {@link OperatingSystemMXBean}.
 268      */
 269     public final static String OPERATING_SYSTEM_MXBEAN_NAME =
 270         "java.lang:type=OperatingSystem";
 271 
 272     /**
 273      * String representation of the
 274      * {@code ObjectName} for the {@link RuntimeMXBean}.
 275      */
 276     public final static String RUNTIME_MXBEAN_NAME =
 277         "java.lang:type=Runtime";
 278 
 279     /**
 280      * String representation of the
 281      * {@code ObjectName} for the {@link ThreadMXBean}.
 282      */
 283     public final static String THREAD_MXBEAN_NAME =
 284         "java.lang:type=Threading";
 285 
 286     /**
 287      * The domain name and the type key property in
 288      * the {@code ObjectName} for a {@link GarbageCollectorMXBean}.
 289      * The unique {@code ObjectName} for a {@code GarbageCollectorMXBean}
 290      * can be formed by appending this string with
 291      * "{@code ,name=}<i>collector's name</i>".
 292      */
 293     public final static String GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE =
 294         "java.lang:type=GarbageCollector";
 295 
 296     /**
 297      * The domain name and the type key property in
 298      * the {@code ObjectName} for a {@link MemoryManagerMXBean}.
 299      * The unique {@code ObjectName} for a {@code MemoryManagerMXBean}
 300      * can be formed by appending this string with
 301      * "{@code ,name=}<i>manager's name</i>".
 302      */
 303     public final static String MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE=
 304         "java.lang:type=MemoryManager";
 305 
 306     /**
 307      * The domain name and the type key property in
 308      * the {@code ObjectName} for a {@link MemoryPoolMXBean}.
 309      * The unique {@code ObjectName} for a {@code MemoryPoolMXBean}
 310      * can be formed by appending this string with
 311      * {@code ,name=}<i>pool's name</i>.
 312      */
 313     public final static String MEMORY_POOL_MXBEAN_DOMAIN_TYPE=
 314         "java.lang:type=MemoryPool";
 315 
 316     /**
 317      * Returns the managed bean for the class loading system of
 318      * the Java virtual machine.
 319      *
 320      * @return a {@link ClassLoadingMXBean} object for
 321      * the Java virtual machine.
 322      */
 323     public static ClassLoadingMXBean getClassLoadingMXBean() {
 324         return getPlatformMXBean(ClassLoadingMXBean.class);
 325     }
 326 
 327     /**
 328      * Returns the managed bean for the memory system of
 329      * the Java virtual machine.
 330      *
 331      * @return a {@link MemoryMXBean} object for the Java virtual machine.
 332      */
 333     public static MemoryMXBean getMemoryMXBean() {
 334         return getPlatformMXBean(MemoryMXBean.class);
 335     }
 336 
 337     /**
 338      * Returns the managed bean for the thread system of
 339      * the Java virtual machine.
 340      *
 341      * @return a {@link ThreadMXBean} object for the Java virtual machine.
 342      */
 343     public static ThreadMXBean getThreadMXBean() {
 344         return getPlatformMXBean(ThreadMXBean.class);
 345     }
 346 
 347     /**
 348      * Returns the managed bean for the runtime system of
 349      * the Java virtual machine.
 350      *
 351      * @return a {@link RuntimeMXBean} object for the Java virtual machine.
 352 
 353      */
 354     public static RuntimeMXBean getRuntimeMXBean() {
 355         return getPlatformMXBean(RuntimeMXBean.class);
 356     }
 357 
 358     /**
 359      * Returns the managed bean for the compilation system of
 360      * the Java virtual machine.  This method returns {@code null}
 361      * if the Java virtual machine has no compilation system.
 362      *
 363      * @return a {@link CompilationMXBean} object for the Java virtual
 364      *   machine or {@code null} if the Java virtual machine has
 365      *   no compilation system.
 366      */
 367     public static CompilationMXBean getCompilationMXBean() {
 368         return getPlatformMXBean(CompilationMXBean.class);
 369     }
 370 
 371     /**
 372      * Returns the managed bean for the operating system on which
 373      * the Java virtual machine is running.
 374      *
 375      * @return an {@link OperatingSystemMXBean} object for
 376      * the Java virtual machine.
 377      */
 378     public static OperatingSystemMXBean getOperatingSystemMXBean() {
 379         return getPlatformMXBean(OperatingSystemMXBean.class);
 380     }
 381 
 382     /**
 383      * Returns a list of {@link MemoryPoolMXBean} objects in the
 384      * Java virtual machine.
 385      * The Java virtual machine can have one or more memory pools.
 386      * It may add or remove memory pools during execution.
 387      *
 388      * @return a list of {@code MemoryPoolMXBean} objects.
 389      *
 390      */
 391     public static List<MemoryPoolMXBean> getMemoryPoolMXBeans() {
 392         return getPlatformMXBeans(MemoryPoolMXBean.class);
 393     }
 394 
 395     /**
 396      * Returns a list of {@link MemoryManagerMXBean} objects
 397      * in the Java virtual machine.
 398      * The Java virtual machine can have one or more memory managers.
 399      * It may add or remove memory managers during execution.
 400      *
 401      * @return a list of {@code MemoryManagerMXBean} objects.
 402      *
 403      */
 404     public static List<MemoryManagerMXBean> getMemoryManagerMXBeans() {
 405         return getPlatformMXBeans(MemoryManagerMXBean.class);
 406     }
 407 
 408 
 409     /**
 410      * Returns a list of {@link GarbageCollectorMXBean} objects
 411      * in the Java virtual machine.
 412      * The Java virtual machine may have one or more
 413      * {@code GarbageCollectorMXBean} objects.
 414      * It may add or remove {@code GarbageCollectorMXBean}
 415      * during execution.
 416      *
 417      * @return a list of {@code GarbageCollectorMXBean} objects.
 418      *
 419      */
 420     public static List<GarbageCollectorMXBean> getGarbageCollectorMXBeans() {
 421         return getPlatformMXBeans(GarbageCollectorMXBean.class);
 422     }
 423 
 424     private static MBeanServer platformMBeanServer;
 425     /**
 426      * Returns the platform {@link javax.management.MBeanServer MBeanServer}.
 427      * On the first call to this method, it first creates the platform
 428      * {@code MBeanServer} by calling the
 429      * {@link javax.management.MBeanServerFactory#createMBeanServer
 430      * MBeanServerFactory.createMBeanServer}
 431      * method and registers each platform MXBean in this platform
 432      * {@code MBeanServer} with its
 433      * {@link PlatformManagedObject#getObjectName ObjectName}.
 434      * This method, in subsequent calls, will simply return the
 435      * initially created platform {@code MBeanServer}.
 436      * <p>
 437      * MXBeans that get created and destroyed dynamically, for example,
 438      * memory {@link MemoryPoolMXBean pools} and
 439      * {@link MemoryManagerMXBean managers},
 440      * will automatically be registered and deregistered into the platform
 441      * {@code MBeanServer}.
 442      * <p>
 443      * If the system property {@code javax.management.builder.initial}
 444      * is set, the platform {@code MBeanServer} creation will be done
 445      * by the specified {@link javax.management.MBeanServerBuilder}.
 446      * <p>
 447      * It is recommended that this platform MBeanServer also be used
 448      * to register other application managed beans
 449      * besides the platform MXBeans.
 450      * This will allow all MBeans to be published through the same
 451      * {@code MBeanServer} and hence allow for easier network publishing
 452      * and discovery.
 453      * Name conflicts with the platform MXBeans should be avoided.
 454      *
 455      * @return the platform {@code MBeanServer}; the platform
 456      *         MXBeans are registered into the platform {@code MBeanServer}
 457      *         at the first time this method is called.
 458      *
 459      * @exception SecurityException if there is a security manager
 460      * and the caller does not have the permission required by
 461      * {@link javax.management.MBeanServerFactory#createMBeanServer}.
 462      *
 463      * @see javax.management.MBeanServerFactory
 464      * @see javax.management.MBeanServerFactory#createMBeanServer
 465      */
 466     public static synchronized MBeanServer getPlatformMBeanServer() {
 467         SecurityManager sm = System.getSecurityManager();
 468         if (sm != null) {
 469             Permission perm = new MBeanServerPermission("createMBeanServer");
 470             sm.checkPermission(perm);
 471         }
 472 
 473         if (platformMBeanServer == null) {
 474             platformMBeanServer = MBeanServerFactory.createMBeanServer();
 475             platformComponents()
 476                     .stream()
 477                     .filter(PlatformComponent::shouldRegister)
 478                     .flatMap(pc -> pc.nameToMBeanMap().entrySet().stream())
 479                     .forEach(entry -> addMXBean(platformMBeanServer, entry.getKey(), entry.getValue()));
 480         }
 481         return platformMBeanServer;
 482     }
 483 
 484     /**
 485      * Returns a proxy for a platform MXBean interface of a
 486      * given <a href="#MXBeanNames">MXBean name</a>
 487      * that forwards its method calls through the given
 488      * {@code MBeanServerConnection}.
 489      *
 490      * <p>This method is equivalent to:
 491      * <blockquote>
 492      * {@link java.lang.reflect.Proxy#newProxyInstance
 493      *        Proxy.newProxyInstance}{@code (mxbeanInterface.getClassLoader(),
 494      *        new Class[] { mxbeanInterface }, handler)}
 495      * </blockquote>
 496      *
 497      * where {@code handler} is an {@link java.lang.reflect.InvocationHandler
 498      * InvocationHandler} to which method invocations to the MXBean interface
 499      * are dispatched. This {@code handler} converts an input parameter
 500      * from an MXBean data type to its mapped open type before forwarding
 501      * to the {@code MBeanServer} and converts a return value from
 502      * an MXBean method call through the {@code MBeanServer}
 503      * from an open type to the corresponding return type declared in
 504      * the MXBean interface.
 505      *
 506      * <p>
 507      * If the MXBean is a notification emitter (i.e.,
 508      * it implements
 509      * {@link javax.management.NotificationEmitter NotificationEmitter}),
 510      * both the {@code mxbeanInterface} and {@code NotificationEmitter}
 511      * will be implemented by this proxy.
 512      *
 513      * <p>
 514      * <b>Notes:</b>
 515      * <ol>
 516      * <li>Using an MXBean proxy is a convenience remote access to
 517      * a platform MXBean of a running virtual machine.  All method
 518      * calls to the MXBean proxy are forwarded to an
 519      * {@code MBeanServerConnection} where
 520      * {@link java.io.IOException IOException} may be thrown
 521      * when the communication problem occurs with the connector server.
 522      * If thrown, {@link java.io.IOException IOException} will be wrappped in
 523      * {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException}.
 524      * An application remotely accessing the platform MXBeans using
 525      * proxy should prepare to catch {@code UndeclaredThrowableException} and
 526      * handle its {@linkplain java.lang.reflect.UndeclaredThrowableException#getCause() cause}
 527      * as if that cause had been thrown by the {@code MBeanServerConnection}
 528      * interface.</li>
 529      *
 530      * <li>When a client application is designed to remotely access MXBeans
 531      * for a running virtual machine whose version is different than
 532      * the version on which the application is running,
 533      * it should prepare to catch
 534      * {@link java.io.InvalidObjectException InvalidObjectException}
 535      * which is thrown when an MXBean proxy receives a name of an
 536      * enum constant which is missing in the enum class loaded in
 537      * the client application.   If thrown,
 538      * {@link java.io.InvalidObjectException InvalidObjectException} will be
 539      * wrappped in
 540      * {@link java.lang.reflect.UndeclaredThrowableException UndeclaredThrowableException}.
 541      * </li>
 542      *
 543      * <li>{@link javax.management.MBeanServerInvocationHandler
 544      * MBeanServerInvocationHandler} or its
 545      * {@link javax.management.MBeanServerInvocationHandler#newProxyInstance
 546      * newProxyInstance} method cannot be used to create
 547      * a proxy for a platform MXBean. The proxy object created
 548      * by {@code MBeanServerInvocationHandler} does not handle
 549      * the properties of the platform MXBeans described in
 550      * the <a href="#MXBean">class specification</a>.
 551      *</li>
 552      * </ol>
 553      *
 554      * @param connection the {@code MBeanServerConnection} to forward to.
 555      * @param mxbeanName the name of a platform MXBean within
 556      * {@code connection} to forward to. {@code mxbeanName} must be
 557      * in the format of {@link ObjectName ObjectName}.
 558      * @param mxbeanInterface the MXBean interface to be implemented
 559      * by the proxy.
 560      * @param <T> an {@code mxbeanInterface} type parameter
 561      *
 562      * @return a proxy for a platform MXBean interface of a
 563      * given <a href="#MXBeanNames">MXBean name</a>
 564      * that forwards its method calls through the given
 565      * {@code MBeanServerConnection}, or {@code null} if not exist.
 566      *
 567      * @throws IllegalArgumentException if
 568      * <ul>
 569      * <li>{@code mxbeanName} is not with a valid
 570      *     {@link ObjectName ObjectName} format, or</li>
 571      * <li>the named MXBean in the {@code connection} is
 572      *     not a MXBean provided by the platform, or</li>
 573      * <li>the named MXBean is not registered in the
 574      *     {@code MBeanServerConnection}, or</li>
 575      * <li>the named MXBean is not an instance of the given
 576      *     {@code mxbeanInterface}</li>
 577      * </ul>
 578      *
 579      * @throws java.io.IOException if a communication problem
 580      * occurred when accessing the {@code MBeanServerConnection}.
 581      */
 582     public static <T> T
 583         newPlatformMXBeanProxy(MBeanServerConnection connection,
 584                                String mxbeanName,
 585                                Class<T> mxbeanInterface)
 586             throws java.io.IOException {
 587 
 588         // Only allow MXBean interfaces from rt.jar loaded by the
 589         // bootstrap class loader
 590         final Class<?> cls = mxbeanInterface;
 591         ClassLoader loader =
 592             AccessController.doPrivileged(
 593                 (PrivilegedAction<ClassLoader>) () -> cls.getClassLoader());
 594         if (!jdk.internal.misc.VM.isSystemDomainLoader(loader)) {
 595             throw new IllegalArgumentException(mxbeanName +
 596                 " is not a platform MXBean");
 597         }
 598 
 599         try {
 600             final ObjectName objName = new ObjectName(mxbeanName);
 601             // skip the isInstanceOf check for LoggingMXBean
 602             String intfName = mxbeanInterface.getName();
 603             if (!connection.isInstanceOf(objName, intfName)) {
 604                 throw new IllegalArgumentException(mxbeanName +
 605                     " is not an instance of " + mxbeanInterface);
 606             }
 607 
 608             // check if the registered MBean is a notification emitter
 609             boolean emitter = connection.isInstanceOf(objName, NOTIF_EMITTER);
 610 
 611             // create an MXBean proxy
 612             return JMX.newMXBeanProxy(connection, objName, mxbeanInterface,
 613                                       emitter);
 614         } catch (InstanceNotFoundException|MalformedObjectNameException e) {
 615             throw new IllegalArgumentException(e);
 616         }
 617     }
 618 
 619     /**
 620      * Returns the platform MXBean implementing
 621      * the given {@code mxbeanInterface} which is specified
 622      * to have one single instance in the Java virtual machine.
 623      * This method may return {@code null} if the management interface
 624      * is not implemented in the Java virtual machine (for example,
 625      * a Java virtual machine with no compilation system does not
 626      * implement {@link CompilationMXBean});
 627      * otherwise, this method is equivalent to calling:
 628      * <pre>
 629      *    {@link #getPlatformMXBeans(Class)
 630      *      getPlatformMXBeans(mxbeanInterface)}.get(0);
 631      * </pre>
 632      *
 633      * @param mxbeanInterface a management interface for a platform
 634      *     MXBean with one single instance in the Java virtual machine
 635      *     if implemented.
 636      * @param <T> an {@code mxbeanInterface} type parameter
 637      *
 638      * @return the platform MXBean that implements
 639      * {@code mxbeanInterface}, or {@code null} if not exist.
 640      *
 641      * @throws IllegalArgumentException if {@code mxbeanInterface}
 642      * is not a platform management interface or
 643      * not a singleton platform MXBean.
 644      *
 645      * @since 1.7
 646      */
 647     public static <T extends PlatformManagedObject>
 648             T getPlatformMXBean(Class<T> mxbeanInterface) {
 649         PlatformComponent<?> pc = PlatformMBeanFinder.findSingleton(mxbeanInterface);
 650 
 651         List<? extends T> mbeans = pc.getMBeans(mxbeanInterface);
 652         assert mbeans.isEmpty() || mbeans.size() == 1;
 653         return mbeans.isEmpty() ? null : mbeans.get(0);
 654     }
 655 
 656     /**
 657      * Returns the list of platform MXBeans implementing
 658      * the given {@code mxbeanInterface} in the Java
 659      * virtual machine.
 660      * The returned list may contain zero, one, or more instances.
 661      * The number of instances in the returned list is defined
 662      * in the specification of the given management interface.
 663      * The order is undefined and there is no guarantee that
 664      * the list returned is in the same order as previous invocations.
 665      *
 666      * @param mxbeanInterface a management interface for a platform
 667      *                        MXBean
 668      * @param <T> an {@code mxbeanInterface} type parameter
 669      *
 670      * @return the list of platform MXBeans that implement
 671      * {@code mxbeanInterface}.
 672      *
 673      * @throws IllegalArgumentException if {@code mxbeanInterface}
 674      * is not a platform management interface.
 675      *
 676      * @since 1.7
 677      */
 678     public static <T extends PlatformManagedObject> List<T>
 679             getPlatformMXBeans(Class<T> mxbeanInterface) {
 680         // Validates at first the specified interface by finding at least one
 681         // PlatformComponent whose MXBean implements this interface.
 682         // An interface can be implemented by different MBeans, provided by
 683         // different platform components.
 684         PlatformComponent<?> pc = PlatformMBeanFinder.findFirst(mxbeanInterface);
 685         if (pc == null) {
 686             throw new IllegalArgumentException(mxbeanInterface.getName()
 687                     + " is not a platform management interface");
 688         }
 689 
 690         return platformComponents().stream()
 691                 .flatMap(p -> p.getMBeans(mxbeanInterface).stream())
 692                 .collect(Collectors.toList());
 693     }
 694 
 695     /**
 696      * Returns the platform MXBean proxy for
 697      * {@code mxbeanInterface} which is specified to have one single
 698      * instance in a Java virtual machine and the proxy will
 699      * forward the method calls through the given {@code MBeanServerConnection}.
 700      * This method may return {@code null} if the management interface
 701      * is not implemented in the Java virtual machine being monitored
 702      * (for example, a Java virtual machine with no compilation system
 703      * does not implement {@link CompilationMXBean});
 704      * otherwise, this method is equivalent to calling:
 705      * <pre>
 706      *     {@link #getPlatformMXBeans(MBeanServerConnection, Class)
 707      *        getPlatformMXBeans(connection, mxbeanInterface)}.get(0);
 708      * </pre>
 709      *
 710      * @param connection the {@code MBeanServerConnection} to forward to.
 711      * @param mxbeanInterface a management interface for a platform
 712      *     MXBean with one single instance in the Java virtual machine
 713      *     being monitored, if implemented.
 714      * @param <T> an {@code mxbeanInterface} type parameter
 715      *
 716      * @return the platform MXBean proxy for
 717      * forwarding the method calls of the {@code mxbeanInterface}
 718      * through the given {@code MBeanServerConnection},
 719      * or {@code null} if not exist.
 720      *
 721      * @throws IllegalArgumentException if {@code mxbeanInterface}
 722      * is not a platform management interface or
 723      * not a singleton platform MXBean.
 724      * @throws java.io.IOException if a communication problem
 725      * occurred when accessing the {@code MBeanServerConnection}.
 726      *
 727      * @see #newPlatformMXBeanProxy
 728      * @since 1.7
 729      */
 730     public static <T extends PlatformManagedObject>
 731             T getPlatformMXBean(MBeanServerConnection connection,
 732                                 Class<T> mxbeanInterface)
 733         throws java.io.IOException
 734     {
 735         PlatformComponent<?> pc = PlatformMBeanFinder.findSingleton(mxbeanInterface);
 736         return newPlatformMXBeanProxy(connection, pc.getObjectNamePattern(), mxbeanInterface);
 737     }
 738 
 739     /**
 740      * Returns the list of the platform MXBean proxies for
 741      * forwarding the method calls of the {@code mxbeanInterface}
 742      * through the given {@code MBeanServerConnection}.
 743      * The returned list may contain zero, one, or more instances.
 744      * The number of instances in the returned list is defined
 745      * in the specification of the given management interface.
 746      * The order is undefined and there is no guarantee that
 747      * the list returned is in the same order as previous invocations.
 748      *
 749      * @param connection the {@code MBeanServerConnection} to forward to.
 750      * @param mxbeanInterface a management interface for a platform
 751      *                        MXBean
 752      * @param <T> an {@code mxbeanInterface} type parameter
 753      *
 754      * @return the list of platform MXBean proxies for
 755      * forwarding the method calls of the {@code mxbeanInterface}
 756      * through the given {@code MBeanServerConnection}.
 757      *
 758      * @throws IllegalArgumentException if {@code mxbeanInterface}
 759      * is not a platform management interface.
 760      *
 761      * @throws java.io.IOException if a communication problem
 762      * occurred when accessing the {@code MBeanServerConnection}.
 763      *
 764      * @see #newPlatformMXBeanProxy
 765      * @since 1.7
 766      */
 767     public static <T extends PlatformManagedObject>
 768             List<T> getPlatformMXBeans(MBeanServerConnection connection,
 769                                        Class<T> mxbeanInterface)
 770         throws java.io.IOException
 771     {
 772         // Validates at first the specified interface by finding at least one
 773         // PlatformComponent whose MXBean implements this interface.
 774         // An interface can be implemented by different MBeans, provided by
 775         // different platform components.
 776         PlatformComponent<?> pc = PlatformMBeanFinder.findFirst(mxbeanInterface);
 777         if (pc == null) {
 778             throw new IllegalArgumentException(mxbeanInterface.getName()
 779                     + " is not a platform management interface");
 780         }
 781 
 782         // Collect all names, eliminate duplicates.
 783         Stream<String> names = Stream.empty();
 784         for (PlatformComponent<?> p : platformComponents()) {
 785             names = Stream.concat(names, getProxyNames(p, connection, mxbeanInterface));
 786         }
 787         Set<String> objectNames = names.collect(Collectors.toSet());
 788         if (objectNames.isEmpty()) return Collections.emptyList();
 789 
 790         // Map names on proxies.
 791         List<T> proxies = new ArrayList<>();
 792         for (String name : objectNames) {
 793             proxies.add(newPlatformMXBeanProxy(connection, name, mxbeanInterface));
 794         }
 795         return proxies;
 796     }
 797 
 798     // Returns a stream containing all ObjectNames of the MBeans represented by
 799     // the specified PlatformComponent and implementing the specified interface.
 800     // If the PlatformComponent is a singleton, the name returned by
 801     // PlatformComponent.getObjectNamePattern() will be used, otherwise
 802     // we will query the specified MBeanServerConnection (conn.queryNames)
 803     // with the pattern returned by PlatformComponent.getObjectNamePattern()
 804     // in order to find the names of matching MBeans.
 805     // In case of singleton, we do not check whether the MBean is registered
 806     // in the connection because the caller "getPlatformMXBeans" will do the check
 807     // when creating a proxy.
 808     private static Stream<String> getProxyNames(PlatformComponent<?> pc,
 809                                                 MBeanServerConnection conn,
 810                                                 Class<?> intf)
 811             throws IOException
 812     {
 813         if (pc.mbeanInterfaceNames().contains(intf.getName())) {
 814             if (pc.isSingleton()) {
 815                 return Stream.of(pc.getObjectNamePattern());
 816             } else {
 817                 return conn.queryNames(Util.newObjectName(pc.getObjectNamePattern()), null)
 818                         .stream().map(ObjectName::getCanonicalName);
 819             }
 820         }
 821         return Stream.empty();
 822     }
 823 
 824     /**
 825      * Returns the set of {@code Class} objects, subinterface of
 826      * {@link PlatformManagedObject}, representing
 827      * all management interfaces for
 828      * monitoring and managing the Java platform.
 829      *
 830      * @return the set of {@code Class} objects, subinterface of
 831      * {@link PlatformManagedObject} representing
 832      * the management interfaces for
 833      * monitoring and managing the Java platform.
 834      *
 835      * @since 1.7
 836      */
 837     public static Set<Class<? extends PlatformManagedObject>>
 838            getPlatformManagementInterfaces()
 839     {
 840         return platformComponents()
 841                 .stream()
 842                 .flatMap(pc -> pc.mbeanInterfaces().stream())
 843                 .filter(clazz -> PlatformManagedObject.class.isAssignableFrom(clazz))
 844                 .map(clazz -> clazz.asSubclass(PlatformManagedObject.class))
 845                 .collect(Collectors.toSet());
 846     }
 847 
 848     private static final String NOTIF_EMITTER =
 849         "javax.management.NotificationEmitter";
 850 
 851     private static void addMXBean(final MBeanServer mbs, String name, final Object pmo)
 852     {
 853         try {
 854             ObjectName oname = ObjectName.getInstance(name);
 855             // Make DynamicMBean out of MXBean by wrapping it with a StandardMBean
 856             AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
 857                 final DynamicMBean dmbean;
 858                 if (pmo instanceof DynamicMBean) {
 859                     dmbean = DynamicMBean.class.cast(pmo);
 860                 } else if (pmo instanceof NotificationEmitter) {
 861                     dmbean = new StandardEmitterMBean(pmo, null, true, (NotificationEmitter) pmo);
 862                 } else {
 863                     dmbean = new StandardMBean(pmo, null, true);
 864                 }
 865 
 866                 mbs.registerMBean(dmbean, oname);
 867                 return null;
 868             });
 869         } catch (MalformedObjectNameException mone) {
 870             throw new IllegalArgumentException(mone);
 871         } catch (PrivilegedActionException e) {
 872             throw new RuntimeException(e.getException());
 873         }
 874     }
 875 
 876     private static Collection<PlatformComponent<?>> platformComponents()
 877     {
 878         return PlatformMBeanFinder.getMap().values();
 879     }
 880 
 881     private static class PlatformMBeanFinder
 882     {
 883         private static final Map<String, PlatformComponent<?>> componentMap;
 884         static {
 885             // get all providers
 886             List<PlatformMBeanProvider> providers = AccessController.doPrivileged(
 887                 (PrivilegedAction<List<PlatformMBeanProvider>>) () -> {
 888                      List<PlatformMBeanProvider> all = new ArrayList<>();
 889                      ServiceLoader.loadInstalled(PlatformMBeanProvider.class)
 890                                   .forEach(all::add);
 891                      all.add(new DefaultPlatformMBeanProvider());
 892                      return all;
 893                 }, null, new FilePermission("<<ALL FILES>>", "read"),
 894                          new RuntimePermission("sun.management.spi.PlatformMBeanProvider.subclass"));
 895 
 896             // load all platform components into a map
 897             componentMap = providers.stream()
 898                 .flatMap(p -> toPlatformComponentStream(p))
 899                 // The first one wins if multiple PlatformComponents
 900                 // with same ObjectName pattern,
 901                 .collect(toMap(PlatformComponent::getObjectNamePattern,
 902                                Function.identity(),
 903                               (p1, p2) -> p1));
 904         }
 905 
 906         static Map<String, PlatformComponent<?>> getMap() {
 907             return componentMap;
 908         }
 909 
 910         // Loads all platform components from a provider into a stream
 911         // Ensures that two different components are not declared with the same
 912         // object name pattern. Throws InternalError if the provider incorrectly
 913         // declares two platform components with the same pattern.
 914         private static Stream<PlatformComponent<?>>
 915             toPlatformComponentStream(PlatformMBeanProvider provider)
 916         {
 917             return provider.getPlatformComponentList()
 918                            .stream()
 919                            .collect(toMap(PlatformComponent::getObjectNamePattern,
 920                                           Function.identity(),
 921                                           (p1, p2) -> {
 922                                               throw new InternalError(
 923                                                  p1.getObjectNamePattern() +
 924                                                  " has been used as key for " + p1 +
 925                                                  ", it cannot be reused for " + p2);
 926                                           }))
 927                            .values().stream();
 928         }
 929 
 930         // Finds the first PlatformComponent whose mbeanInterfaceNames() list
 931         // contains the specified class name. An MBean interface can be implemented
 932         // by different MBeans, provided by different platform components.
 933         // For instance the MemoryManagerMXBean interface is implemented both by
 934         // regular memory managers, and garbage collector MXBeans. This method is
 935         // mainly used to verify that there is at least one PlatformComponent
 936         // which provides an implementation of the desired interface.
 937         static PlatformComponent<?> findFirst(Class<?> mbeanIntf)
 938         {
 939             String name = mbeanIntf.getName();
 940             Optional<PlatformComponent<?>> op = getMap().values()
 941                 .stream()
 942                 .filter(pc -> pc.mbeanInterfaceNames().contains(name))
 943                 .findFirst();
 944 
 945             if (op.isPresent()) {
 946                 return op.getWhenPresent();
 947             } else {
 948                 return null;
 949             }
 950         }
 951 
 952         // Finds a PlatformComponent whose mbeanInterface name list contains
 953         // the specified class name, and make sure that one and only one exists.
 954         static PlatformComponent<?> findSingleton(Class<?> mbeanIntf)
 955         {
 956             String name = mbeanIntf.getName();
 957             Optional<PlatformComponent<?>> op = getMap().values()
 958                 .stream()
 959                 .filter(pc -> pc.mbeanInterfaceNames().contains(name))
 960                 .reduce((p1, p2) -> {
 961                     if (p2 != null) {
 962                         throw new IllegalArgumentException(mbeanIntf.getName() +
 963                             " can have more than one instance");
 964                     } else {
 965                         return p1;
 966                     }
 967                 });
 968 
 969             PlatformComponent<?> singleton = op.isPresent() ? op.getWhenPresent() : null;
 970             if (singleton == null) {
 971                 throw new IllegalArgumentException(mbeanIntf.getName() +
 972                     " is not a platform management interface");
 973             }
 974             if (!singleton.isSingleton()) {
 975                 throw new IllegalArgumentException(mbeanIntf.getName() +
 976                     " can have more than one instance");
 977             }
 978             return singleton;
 979         }
 980     }
 981 
 982     static {
 983         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 984             System.loadLibrary("management");
 985             return null;
 986         });
 987     }
 988 }