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