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
26package sun.management;
27
28import java.lang.management.*;
29import java.lang.reflect.InvocationTargetException;
30import java.lang.reflect.Method;
31import javax.management.InstanceAlreadyExistsException;
32import javax.management.InstanceNotFoundException;
33import javax.management.MBeanServer;
34import javax.management.MBeanRegistrationException;
35import javax.management.NotCompliantMBeanException;
36import javax.management.ObjectName;
37import javax.management.RuntimeOperationsException;
38import java.security.AccessController;
39import java.security.PrivilegedActionException;
40import java.security.PrivilegedExceptionAction;
41
42import jdk.internal.misc.JavaNioAccess;
43import jdk.internal.misc.SharedSecrets;
44
45import java.util.ArrayList;
46import java.util.List;
47
48import java.lang.reflect.UndeclaredThrowableException;
49import java.security.PrivilegedAction;
50import java.util.Collections;
51import java.util.HashMap;
52import java.util.Map;
53import java.util.Optional;
54
55/**
56 * ManagementFactoryHelper provides static factory methods to create
57 * instances of the management interface.
58 */
59public class ManagementFactoryHelper {
60    static {
61        // make sure that the management lib is loaded within
62        // java.lang.management.ManagementFactory
63        jdk.internal.misc.Unsafe.getUnsafe().ensureClassInitialized(ManagementFactory.class);
64    }
65
66    private static final VMManagement jvm = new VMManagementImpl();
67
68    private ManagementFactoryHelper() {};
69
70    public static VMManagement getVMManagement() {
71        return jvm;
72    }
73
74    static final String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
75    private static ClassLoadingImpl    classMBean = null;
76    private static MemoryImpl          memoryMBean = null;
77    private static ThreadImpl          threadMBean = null;
78    private static RuntimeImpl         runtimeMBean = null;
79    private static CompilationImpl     compileMBean = null;
80    private static BaseOperatingSystemImpl osMBean = null;
81
82    public static synchronized ClassLoadingMXBean getClassLoadingMXBean() {
83        if (classMBean == null) {
84            classMBean = new ClassLoadingImpl(jvm);
85        }
86        return classMBean;
87    }
88
89    public static synchronized MemoryMXBean getMemoryMXBean() {
90        if (memoryMBean == null) {
91            memoryMBean = new MemoryImpl(jvm);
92        }
93        return memoryMBean;
94    }
95
96    public static synchronized ThreadMXBean getThreadMXBean() {
97        if (threadMBean == null) {
98            threadMBean = new ThreadImpl(jvm);
99        }
100        return threadMBean;
101    }
102
103    public static synchronized RuntimeMXBean getRuntimeMXBean() {
104        if (runtimeMBean == null) {
105            runtimeMBean = new RuntimeImpl(jvm);
106        }
107        return runtimeMBean;
108    }
109
110    public static synchronized CompilationMXBean getCompilationMXBean() {
111        if (compileMBean == null && jvm.getCompilerName() != null) {
112            compileMBean = new CompilationImpl(jvm);
113        }
114        return compileMBean;
115    }
116
117    public static synchronized OperatingSystemMXBean getOperatingSystemMXBean() {
118        if (osMBean == null) {
119            osMBean = new BaseOperatingSystemImpl(jvm);
120        }
121        return osMBean;
122    }
123
124    public static List<MemoryPoolMXBean> getMemoryPoolMXBeans() {
125        MemoryPoolMXBean[] pools = MemoryImpl.getMemoryPools();
126        List<MemoryPoolMXBean> list = new ArrayList<>(pools.length);
127        for (MemoryPoolMXBean p : pools) {
128            list.add(p);
129        }
130        return list;
131    }
132
133    public static List<MemoryManagerMXBean> getMemoryManagerMXBeans() {
134        MemoryManagerMXBean[]  mgrs = MemoryImpl.getMemoryManagers();
135        List<MemoryManagerMXBean> result = new ArrayList<>(mgrs.length);
136        for (MemoryManagerMXBean m : mgrs) {
137            result.add(m);
138        }
139        return result;
140    }
141
142     public static List<GarbageCollectorMXBean> getGarbageCollectorMXBeans() {
143        MemoryManagerMXBean[]  mgrs = MemoryImpl.getMemoryManagers();
144        List<GarbageCollectorMXBean> result = new ArrayList<>(mgrs.length);
145        for (MemoryManagerMXBean m : mgrs) {
146            if (GarbageCollectorMXBean.class.isInstance(m)) {
147                 result.add(GarbageCollectorMXBean.class.cast(m));
148            }
149        }
150        return result;
151    }
152
153    public static PlatformLoggingMXBean getPlatformLoggingMXBean() {
154        if (LoggingMXBeanAccess.isAvailable()) {
155            return PlatformLoggingImpl.MBEAN;
156        } else {
157            return null;
158        }
159    }
160
161    public static boolean isPlatformLoggingMXBeanAvailable() {
162        return LoggingMXBeanAccess.isAvailable();
163    }
164
165    // The LoggingMXBeanAccess class uses reflection to determine
166    // whether java.util.logging is present, and load the actual LoggingMXBean
167    // implementation.
168    //
169    static final class LoggingMXBeanAccess {
170
171        final static String LOG_MANAGER_CLASS_NAME = "java.util.logging.LogManager";
172        final static String LOGGING_MXBEAN_CLASS_NAME = "java.util.logging.LoggingMXBean";
173        final static Class<?> LOG_MANAGER_CLASS = loadLoggingClass(LOG_MANAGER_CLASS_NAME);
174
175        static boolean isAvailable() {
176            return LOG_MANAGER_CLASS != null;
177        }
178
179        private static Class<?> loadLoggingClass(String className) {
180            return AccessController.doPrivileged(new PrivilegedAction<>() {
181                @Override
182                public Class<?> run() {
183                    Optional<Module> logging = ModuleLayer.boot().findModule("java.logging");
184                    if (logging.isPresent()) {
185                        return Class.forName(logging.get(), className);
186                    }
187                    return null;
188                }
189            });
190        }
191
192        private Map<String, Method> initMethodMap(Object impl) {
193            if (impl == null) {
194                return Collections.emptyMap();
195            }
196            Class<?> intfClass = loadLoggingClass(LOGGING_MXBEAN_CLASS_NAME);
197            final Map<String, Method> methodsMap = new HashMap<>();
198            for (Method m : intfClass.getMethods()) {
199                try {
200                    // Sanity checking: all public methods present in
201                    // java.util.logging.LoggingMXBean should
202                    // also be in PlatformLoggingMXBean
203                    Method specMethod = PlatformLoggingMXBean.class
204                             .getMethod(m.getName(), m.getParameterTypes());
205                    if (specMethod.getReturnType().isAssignableFrom(m.getReturnType())) {
206                        if (methodsMap.putIfAbsent(m.getName(), m) != null) {
207                            throw new RuntimeException("unexpected polymorphic method: "
208                                     + m.getName());
209                        }
210                    }
211                } catch (NoSuchMethodException x) {
212                    // All methods in java.util.logging.LoggingMXBean should
213                    // also be in PlatformLoggingMXBean
214                    throw new InternalError(x);
215                }
216            }
217            return Collections.unmodifiableMap(methodsMap);
218        }
219
220        private static Object getMXBeanImplementation() {
221            if (!isAvailable()) {
222                // should not happen
223                throw new NoClassDefFoundError(LOG_MANAGER_CLASS_NAME);
224            }
225            try {
226                final Method m = LOG_MANAGER_CLASS.getMethod("getLoggingMXBean");
227                return m.invoke(null);
228            } catch (NoSuchMethodException
229                    | IllegalAccessException
230                    | InvocationTargetException x) {
231                throw new ExceptionInInitializerError(x);
232            }
233         }
234
235        // The implementation object, which will be invoked through
236        // reflection. The implementation does not need to implement
237        // PlatformLoggingMXBean, but must declare the same methods
238        // with same signatures, and they must be public, with one
239        // exception:
240        // getObjectName will not be called on the implementation object,
241        // so the implementation object does not need to declare such
242        // a method.
243        final Object impl = getMXBeanImplementation();
244        final Map<String, Method> methods = initMethodMap(impl);
245
246        LoggingMXBeanAccess() {
247        }
248
249        <T> T invoke(String methodName, Object... args) {
250            Method m = methods.get(methodName);
251            if (m == null) {
252                throw new UnsupportedOperationException(methodName);
253            }
254            try {
255                @SuppressWarnings("unchecked")
256                T result = (T) m.invoke(impl, args);
257                return result;
258            } catch (IllegalAccessException ex) {
259                throw new UnsupportedOperationException(ex);
260            } catch (InvocationTargetException ex) {
261                throw unwrap(ex);
262            }
263        }
264
265        private static RuntimeException unwrap(InvocationTargetException x) {
266            Throwable t = x.getCause();
267            if (t instanceof RuntimeException) {
268                return (RuntimeException)t;
269            }
270            if (t instanceof Error) {
271                throw (Error)t;
272            }
273            return new UndeclaredThrowableException(t == null ? x : t);
274        }
275
276
277    }
278
279    static final class PlatformLoggingImpl implements PlatformLoggingMXBean {
280
281        private final LoggingMXBeanAccess loggingAccess;
282        private PlatformLoggingImpl(LoggingMXBeanAccess loggingAccess) {
283            this.loggingAccess = loggingAccess;
284        }
285
286        private volatile ObjectName objname;  // created lazily
287        @Override
288        public ObjectName getObjectName() {
289            ObjectName result = objname;
290            if (result == null) {
291                synchronized (this) {
292                    result = objname;
293                    if (result == null) {
294                        result = Util.newObjectName(LOGGING_MXBEAN_NAME);
295                        objname = result;
296                    }
297                }
298            }
299            return result;
300        }
301
302        @Override
303        public java.util.List<String> getLoggerNames() {
304            return loggingAccess.invoke("getLoggerNames");
305        }
306
307        @Override
308        public String getLoggerLevel(String loggerName) {
309            return loggingAccess.invoke("getLoggerLevel", loggerName);
310        }
311
312        @Override
313        public void setLoggerLevel(String loggerName, String levelName) {
314            loggingAccess.invoke("setLoggerLevel", loggerName, levelName);
315        }
316
317        @Override
318        public String getParentLoggerName(String loggerName) {
319            return loggingAccess.invoke("getParentLoggerName", loggerName);
320        }
321
322        private static PlatformLoggingImpl getInstance() {
323            return new PlatformLoggingImpl(new LoggingMXBeanAccess());
324         }
325
326        static final PlatformLoggingMXBean MBEAN = getInstance();
327    }
328
329    private static List<BufferPoolMXBean> bufferPools = null;
330    public static synchronized List<BufferPoolMXBean> getBufferPoolMXBeans() {
331        if (bufferPools == null) {
332            bufferPools = new ArrayList<>(2);
333            bufferPools.add(createBufferPoolMXBean(SharedSecrets.getJavaNioAccess()
334                .getDirectBufferPool()));
335            bufferPools.add(createBufferPoolMXBean(sun.nio.ch.FileChannelImpl
336                .getMappedBufferPool()));
337        }
338        return bufferPools;
339    }
340
341    private final static String BUFFER_POOL_MXBEAN_NAME = "java.nio:type=BufferPool";
342
343    /**
344     * Creates management interface for the given buffer pool.
345     */
346    private static BufferPoolMXBean
347        createBufferPoolMXBean(final JavaNioAccess.BufferPool pool)
348    {
349        return new BufferPoolMXBean() {
350            private volatile ObjectName objname;  // created lazily
351            @Override
352            public ObjectName getObjectName() {
353                ObjectName result = objname;
354                if (result == null) {
355                    synchronized (this) {
356                        result = objname;
357                        if (result == null) {
358                            result = Util.newObjectName(BUFFER_POOL_MXBEAN_NAME +
359                                ",name=" + pool.getName());
360                            objname = result;
361                        }
362                    }
363                }
364                return result;
365            }
366            @Override
367            public String getName() {
368                return pool.getName();
369            }
370            @Override
371            public long getCount() {
372                return pool.getCount();
373            }
374            @Override
375            public long getTotalCapacity() {
376                return pool.getTotalCapacity();
377            }
378            @Override
379            public long getMemoryUsed() {
380                return pool.getMemoryUsed();
381            }
382        };
383    }
384
385    private static HotspotRuntime hsRuntimeMBean = null;
386    private static HotspotClassLoading hsClassMBean = null;
387    private static HotspotThread hsThreadMBean = null;
388    private static HotspotCompilation hsCompileMBean = null;
389    private static HotspotMemory hsMemoryMBean = null;
390
391    /**
392     * This method is for testing only.
393     */
394    public static synchronized HotspotRuntimeMBean getHotspotRuntimeMBean() {
395        if (hsRuntimeMBean == null) {
396            hsRuntimeMBean = new HotspotRuntime(jvm);
397        }
398        return hsRuntimeMBean;
399    }
400
401    /**
402     * This method is for testing only.
403     */
404    public static synchronized HotspotClassLoadingMBean getHotspotClassLoadingMBean() {
405        if (hsClassMBean == null) {
406            hsClassMBean = new HotspotClassLoading(jvm);
407        }
408        return hsClassMBean;
409    }
410
411    /**
412     * This method is for testing only.
413     */
414    public static synchronized HotspotThreadMBean getHotspotThreadMBean() {
415        if (hsThreadMBean == null) {
416            hsThreadMBean = new HotspotThread(jvm);
417        }
418        return hsThreadMBean;
419    }
420
421    /**
422     * This method is for testing only.
423     */
424    public static synchronized HotspotMemoryMBean getHotspotMemoryMBean() {
425        if (hsMemoryMBean == null) {
426            hsMemoryMBean = new HotspotMemory(jvm);
427        }
428        return hsMemoryMBean;
429    }
430
431    /**
432     * This method is for testing only.
433     */
434    public static synchronized HotspotCompilationMBean getHotspotCompilationMBean() {
435        if (hsCompileMBean == null) {
436            hsCompileMBean = new HotspotCompilation(jvm);
437        }
438        return hsCompileMBean;
439    }
440
441    /**
442     * Registers a given MBean if not registered in the MBeanServer;
443     * otherwise, just return.
444     */
445    private static void addMBean(MBeanServer mbs, Object mbean, String mbeanName) {
446        try {
447            final ObjectName objName = Util.newObjectName(mbeanName);
448
449            // inner class requires these fields to be final
450            final MBeanServer mbs0 = mbs;
451            final Object mbean0 = mbean;
452            AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
453                public Void run() throws MBeanRegistrationException,
454                                         NotCompliantMBeanException {
455                    try {
456                        mbs0.registerMBean(mbean0, objName);
457                        return null;
458                    } catch (InstanceAlreadyExistsException e) {
459                        // if an instance with the object name exists in
460                        // the MBeanServer ignore the exception
461                    }
462                    return null;
463                }
464            });
465        } catch (PrivilegedActionException e) {
466            throw Util.newException(e.getException());
467        }
468    }
469
470    private final static String HOTSPOT_CLASS_LOADING_MBEAN_NAME =
471        "sun.management:type=HotspotClassLoading";
472
473    private final static String HOTSPOT_COMPILATION_MBEAN_NAME =
474        "sun.management:type=HotspotCompilation";
475
476    private final static String HOTSPOT_MEMORY_MBEAN_NAME =
477        "sun.management:type=HotspotMemory";
478
479    private static final String HOTSPOT_RUNTIME_MBEAN_NAME =
480        "sun.management:type=HotspotRuntime";
481
482    private final static String HOTSPOT_THREAD_MBEAN_NAME =
483        "sun.management:type=HotspotThreading";
484
485    static void registerInternalMBeans(MBeanServer mbs) {
486        // register all internal MBeans if not registered
487        // No exception is thrown if a MBean with that object name
488        // already registered
489        addMBean(mbs, getHotspotClassLoadingMBean(),
490            HOTSPOT_CLASS_LOADING_MBEAN_NAME);
491        addMBean(mbs, getHotspotMemoryMBean(),
492            HOTSPOT_MEMORY_MBEAN_NAME);
493        addMBean(mbs, getHotspotRuntimeMBean(),
494            HOTSPOT_RUNTIME_MBEAN_NAME);
495        addMBean(mbs, getHotspotThreadMBean(),
496            HOTSPOT_THREAD_MBEAN_NAME);
497
498        // CompilationMBean may not exist
499        if (getCompilationMXBean() != null) {
500            addMBean(mbs, getHotspotCompilationMBean(),
501                HOTSPOT_COMPILATION_MBEAN_NAME);
502        }
503    }
504
505    private static void unregisterMBean(MBeanServer mbs, String mbeanName) {
506        try {
507            final ObjectName objName = Util.newObjectName(mbeanName);
508
509            // inner class requires these fields to be final
510            final MBeanServer mbs0 = mbs;
511            AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
512                public Void run() throws MBeanRegistrationException,
513                                           RuntimeOperationsException  {
514                    try {
515                        mbs0.unregisterMBean(objName);
516                    } catch (InstanceNotFoundException e) {
517                        // ignore exception if not found
518                    }
519                    return null;
520                }
521            });
522        } catch (PrivilegedActionException e) {
523            throw Util.newException(e.getException());
524        }
525    }
526
527    static void unregisterInternalMBeans(MBeanServer mbs) {
528        // unregister all internal MBeans
529        unregisterMBean(mbs, HOTSPOT_CLASS_LOADING_MBEAN_NAME);
530        unregisterMBean(mbs, HOTSPOT_MEMORY_MBEAN_NAME);
531        unregisterMBean(mbs, HOTSPOT_RUNTIME_MBEAN_NAME);
532        unregisterMBean(mbs, HOTSPOT_THREAD_MBEAN_NAME);
533
534        // CompilationMBean may not exist
535        if (getCompilationMXBean() != null) {
536            unregisterMBean(mbs, HOTSPOT_COMPILATION_MBEAN_NAME);
537        }
538    }
539
540    public static boolean isThreadSuspended(int state) {
541        return ((state & JMM_THREAD_STATE_FLAG_SUSPENDED) != 0);
542    }
543
544    public static boolean isThreadRunningNative(int state) {
545        return ((state & JMM_THREAD_STATE_FLAG_NATIVE) != 0);
546    }
547
548    public static Thread.State toThreadState(int state) {
549        // suspended and native bits may be set in state
550        int threadStatus = state & ~JMM_THREAD_STATE_FLAG_MASK;
551        return jdk.internal.misc.VM.toThreadState(threadStatus);
552    }
553
554    // These values are defined in jmm.h
555    private static final int JMM_THREAD_STATE_FLAG_MASK = 0xFFF00000;
556    private static final int JMM_THREAD_STATE_FLAG_SUSPENDED = 0x00100000;
557    private static final int JMM_THREAD_STATE_FLAG_NATIVE = 0x00400000;
558
559    // Invoked by the VM
560    private static MemoryPoolMXBean createMemoryPool
561        (String name, boolean isHeap, long uThreshold, long gcThreshold) {
562        return new MemoryPoolImpl(name, isHeap, uThreshold, gcThreshold);
563    }
564
565    private static MemoryManagerMXBean createMemoryManager(String name) {
566        return new MemoryManagerImpl(name);
567    }
568
569    private static GarbageCollectorMXBean
570        createGarbageCollector(String name, String type) {
571
572        // ignore type parameter which is for future extension
573        return new GarbageCollectorImpl(name);
574    }
575}
576