8139982: Re-examine java.management dependency on java.util.logging.LoggingMXBean
Summary: The logging MXBean implementation no longer implements java.util.logging.LoggingMXBean. java.lang.management.PlatformLoggingMXBean is used instead. java.util.logging.LoggingMXBean and java.util.logging.LogManager::getLoggingMXBean are now deprecated. Types and accessors defined in java.lang.management should be used instead.
Reviewed-by: mchung
--- a/jdk/src/java.logging/share/classes/java/util/logging/LogManager.java Tue May 17 07:43:45 2016 -0700
+++ b/jdk/src/java.logging/share/classes/java/util/logging/LogManager.java Tue May 17 19:24:46 2016 +0200
@@ -2506,15 +2506,12 @@
}
}
- // Management Support
- private static LoggingMXBean loggingMXBean = null;
/**
* String representation of the
* {@link javax.management.ObjectName} for the management interface
* for the logging facility.
*
* @see java.lang.management.PlatformLoggingMXBean
- * @see java.util.logging.LoggingMXBean
*
* @since 1.5
*/
@@ -2523,24 +2520,21 @@
/**
* Returns {@code LoggingMXBean} for managing loggers.
- * An alternative way to manage loggers is through the
- * {@link java.lang.management.PlatformLoggingMXBean} interface
- * that can be obtained by calling:
- * <pre>
- * PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
- * ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class);
- * </pre>
*
* @return a {@link LoggingMXBean} object.
*
+ * @deprecated {@code java.util.logging.LoggingMXBean} is deprecated and
+ * replaced with {@code java.lang.management.PlatformLoggingMXBean}. Use
+ * {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
+ * ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class)
+ * instead.
+ *
* @see java.lang.management.PlatformLoggingMXBean
* @since 1.5
*/
+ @Deprecated(since="9")
public static synchronized LoggingMXBean getLoggingMXBean() {
- if (loggingMXBean == null) {
- loggingMXBean = new Logging();
- }
- return loggingMXBean;
+ return Logging.getInstance();
}
/**
--- a/jdk/src/java.logging/share/classes/java/util/logging/Logging.java Tue May 17 07:43:45 2016 -0700
+++ b/jdk/src/java.logging/share/classes/java/util/logging/Logging.java Tue May 17 19:24:46 2016 +0200
@@ -44,16 +44,18 @@
* @see Logger
* @see LogManager
*/
-class Logging implements LoggingMXBean {
+@SuppressWarnings("deprecation") // implements LoggingMXBean
+final class Logging implements LoggingMXBean {
private static LogManager logManager = LogManager.getLogManager();
/** Constructor of Logging which is the implementation class
* of LoggingMXBean.
*/
- Logging() {
+ private Logging() {
}
+ @Override
public List<String> getLoggerNames() {
Enumeration<String> loggers = logManager.getLoggerNames();
ArrayList<String> array = new ArrayList<>();
@@ -65,6 +67,7 @@
}
private static String EMPTY_STRING = "";
+ @Override
public String getLoggerLevel(String loggerName) {
Logger l = logManager.getLogger(loggerName);
if (l == null) {
@@ -79,6 +82,7 @@
}
}
+ @Override
public void setLoggerLevel(String loggerName, String levelName) {
if (loggerName == null) {
throw new NullPointerException("loggerName is null");
@@ -102,6 +106,7 @@
logger.setLevel(level);
}
+ @Override
public String getParentLoggerName( String loggerName ) {
Logger l = logManager.getLogger( loggerName );
if (l == null) {
@@ -116,4 +121,11 @@
return p.getName();
}
}
+
+ static Logging getInstance() {
+ return INSTANCE;
+ }
+
+ private static final Logging INSTANCE = new Logging();
+
}
--- a/jdk/src/java.logging/share/classes/java/util/logging/LoggingMXBean.java Tue May 17 07:43:45 2016 -0700
+++ b/jdk/src/java.logging/share/classes/java/util/logging/LoggingMXBean.java Tue May 17 19:24:46 2016 +0200
@@ -27,30 +27,23 @@
/**
- * The management interface for the logging facility. It is recommended
- * to use the {@link java.lang.management.PlatformLoggingMXBean} management
- * interface that implements all attributes defined in this
- * {@code LoggingMXBean}. The
- * {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
- * ManagementFactory.getPlatformMXBean} method can be used to obtain
- * the {@code PlatformLoggingMXBean} object representing the management
- * interface for logging.
+ * The management interface for the logging facility.
*
- * <p>There is a single global instance of the {@code LoggingMXBean}.
- * This instance is an {@link javax.management.MXBean MXBean} that
- * can be obtained by calling the {@link LogManager#getLoggingMXBean}
- * method or from the
- * {@linkplain java.lang.management.ManagementFactory#getPlatformMBeanServer
+ * {@link java.lang.management.PlatformLoggingMXBean
+ * java.lang.management.PlatformLoggingMXBean} is the management interface
+ * for logging facility registered in the {@link
+ * java.lang.management.ManagementFactory#getPlatformMBeanServer()
* platform MBeanServer}.
- * <p>
- * The {@link javax.management.ObjectName ObjectName} that uniquely identifies
- * the management interface for logging within the {@code MBeanServer} is:
- * <pre>
- * {@link LogManager#LOGGING_MXBEAN_NAME java.util.logging:type=Logging}
- * </pre>
- * <p>
- * The instance registered in the platform {@code MBeanServer}
- * is also a {@link java.lang.management.PlatformLoggingMXBean}.
+ * It is recommended to use the {@code PlatformLoggingMXBean} obtained via
+ * the {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class)
+ * ManagementFactory.getPlatformMXBean(PlatformLoggingMXBean.class)} method.
+ *
+ * @deprecated {@code LoggingMXBean} is no longer a {@link
+ * java.lang.management.PlatformManagedObject platform MXBean} and is replaced
+ * with {@link java.lang.management.PlatformLoggingMXBean}.
+ * It will not register in the platform {@code MBeanServer}.
+ * Use {@code ManagementFactory.getPlatformMXBean(PlatformLoggingMXBean.class)}
+ * instead.
*
* @author Ron Mann
* @author Mandy Chung
@@ -58,6 +51,7 @@
*
* @see java.lang.management.PlatformLoggingMXBean
*/
+@Deprecated(since="9")
public interface LoggingMXBean {
/**
--- a/jdk/src/java.management/share/classes/java/lang/management/ManagementFactory.java Tue May 17 07:43:45 2016 -0700
+++ b/jdk/src/java.management/share/classes/java/lang/management/ManagementFactory.java Tue May 17 19:24:46 2016 +0200
@@ -598,9 +598,8 @@
try {
final ObjectName objName = new ObjectName(mxbeanName);
- // skip the isInstanceOf check for LoggingMXBean
String intfName = mxbeanInterface.getName();
- if (!connection.isInstanceOf(objName, intfName)) {
+ if (!isInstanceOf(connection, objName, intfName)) {
throw new IllegalArgumentException(mxbeanName +
" is not an instance of " + mxbeanInterface);
}
@@ -616,6 +615,33 @@
}
}
+ // This makes it possible to obtain an instance of LoggingMXBean
+ // using newPlatformMXBeanProxy(mbs, on, LoggingMXBean.class)
+ // even though the underlying MXBean no longer implements
+ // java.util.logging.LoggingMXBean.
+ // Altough java.util.logging.LoggingMXBean is deprecated, an application
+ // that uses newPlatformMXBeanProxy(mbs, on, LoggingMXBean.class) will
+ // continue to work.
+ //
+ private static boolean isInstanceOf(MBeanServerConnection connection,
+ ObjectName objName, String intfName)
+ throws InstanceNotFoundException, IOException
+ {
+ // special case for java.util.logging.LoggingMXBean.
+ // java.util.logging.LoggingMXBean is deprecated and
+ // replaced with java.lang.management.PlatformLoggingMXBean,
+ // so we will consider that any MBean implementing
+ // java.lang.management.PlatformLoggingMXBean also implements
+ // java.util.logging.LoggingMXBean.
+ if ("java.util.logging.LoggingMXBean".equals(intfName)) {
+ if (connection.isInstanceOf(objName,
+ PlatformLoggingMXBean.class.getName())) {
+ return true;
+ }
+ }
+ return connection.isInstanceOf(objName, intfName);
+ }
+
/**
* Returns the platform MXBean implementing
* the given {@code mxbeanInterface} which is specified
--- a/jdk/src/java.management/share/classes/java/lang/management/PlatformLoggingMXBean.java Tue May 17 07:43:45 2016 -0700
+++ b/jdk/src/java.management/share/classes/java/lang/management/PlatformLoggingMXBean.java Tue May 17 19:24:46 2016 +0200
@@ -44,10 +44,6 @@
* {@link java.util.logging.LogManager#LOGGING_MXBEAN_NAME java.util.logging:type=Logging}
* </pre>
*
- * <p>The instance registered in the platform {@code MBeanServer} with
- * this {@code ObjectName} implements all attributes defined by
- * {@link java.util.logging.LoggingMXBean}.
- *
* @since 1.7
*/
public interface PlatformLoggingMXBean extends PlatformManagedObject {
--- a/jdk/src/java.management/share/classes/sun/management/ManagementFactoryHelper.java Tue May 17 07:43:45 2016 -0700
+++ b/jdk/src/java.management/share/classes/sun/management/ManagementFactoryHelper.java Tue May 17 19:24:46 2016 +0200
@@ -26,6 +26,8 @@
package sun.management;
import java.lang.management.*;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
@@ -43,9 +45,13 @@
import java.util.ArrayList;
import java.util.List;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Module;
+import java.lang.reflect.UndeclaredThrowableException;
import java.security.PrivilegedAction;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
/**
* ManagementFactoryHelper provides static factory methods to create
@@ -66,6 +72,7 @@
return jvm;
}
+ static final String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
private static ClassLoadingImpl classMBean = null;
private static MemoryImpl memoryMBean = null;
private static ThreadImpl threadMBean = null;
@@ -145,74 +152,138 @@
}
public static PlatformLoggingMXBean getPlatformLoggingMXBean() {
- if (LoggingMXBeanSupport.isAvailable()) {
- return PlatformLoggingImpl.instance;
+ if (LoggingMXBeanAccess.isAvailable()) {
+ return PlatformLoggingImpl.MBEAN;
} else {
return null;
}
}
public static boolean isPlatformLoggingMXBeanAvailable() {
- return LoggingMXBeanSupport.isAvailable();
+ return LoggingMXBeanAccess.isAvailable();
}
- /**
- * The logging MXBean object is an instance of
- * PlatformLoggingMXBean and java.util.logging.LoggingMXBean
- * but it can't directly implement two MXBean interfaces
- * as a compliant MXBean implements exactly one MXBean interface,
- * or if it implements one interface that is a subinterface of
- * all the others; otherwise, it is a non-compliant MXBean
- * and MBeanServer will throw NotCompliantMBeanException.
- * See the Definition of an MXBean section in javax.management.MXBean spec.
- *
- * To create a compliant logging MXBean, define a LoggingMXBean interface
- * that extend PlatformLoggingMXBean and j.u.l.LoggingMXBean
- */
- public interface LoggingMXBean
- extends PlatformLoggingMXBean, java.util.logging.LoggingMXBean {
- }
-
- // This is a trick: if java.util.logging is not present then
- // attempting to access something that implements
- // java.util.logging.LoggingMXBean will trigger a CNFE.
- // So we cannot directly call any static method or access any static field
- // on PlatformLoggingImpl, as we would risk raising a CNFE.
- // Instead we use this intermediate LoggingMXBeanSupport class to determine
+ // The LoggingMXBeanAccess class uses reflection to determine
// whether java.util.logging is present, and load the actual LoggingMXBean
// implementation.
//
- static final class LoggingMXBeanSupport {
- final static Object loggingImpl =
- AccessController.doPrivileged(new PrivilegedAction<Object>() {
- @Override
- public Object run() {
- try {
- // create a LoggingProxyImpl instance when
- // java.util.logging classes exist
- Class<?> c = Class.forName("java.util.logging.Logging", true, null);
- Constructor<?> cons = c.getDeclaredConstructor();
- cons.setAccessible(true);
- return cons.newInstance();
- } catch (ClassNotFoundException cnf) {
- return null;
- } catch (NoSuchMethodException | InstantiationException
- | IllegalAccessException | InvocationTargetException e) {
- throw new AssertionError(e);
- }
- }});
+ static final class LoggingMXBeanAccess {
+
+ final static String LOG_MANAGER_CLASS_NAME = "java.util.logging.LogManager";
+ final static String LOGGING_MXBEAN_CLASS_NAME = "java.util.logging.LoggingMXBean";
+ final static Class<?> LOG_MANAGER_CLASS = loadLoggingClass(LOG_MANAGER_CLASS_NAME);
static boolean isAvailable() {
- return loggingImpl != null;
+ return LOG_MANAGER_CLASS != null;
+ }
+
+ private static Class<?> loadLoggingClass(String className) {
+ return AccessController.doPrivileged(new PrivilegedAction<>() {
+ @Override
+ public Class<?> run() {
+ Optional<Module> logging = java.lang.reflect.Layer.boot()
+ .findModule("java.logging");
+ if (logging.isPresent()) {
+ return Class.forName(logging.get(), className);
+ }
+ return null;
+ }
+ });
+ }
+
+ private Map<String, Method> initMethodMap(Object impl) {
+ if (impl == null) {
+ return Collections.emptyMap();
+ }
+ Class<?> intfClass = loadLoggingClass(LOGGING_MXBEAN_CLASS_NAME);
+ final Map<String, Method> methodsMap = new HashMap<>();
+ for (Method m : intfClass.getMethods()) {
+ try {
+ // Sanity checking: all public methods present in
+ // java.util.logging.LoggingMXBean should
+ // also be in PlatformLoggingMXBean
+ Method specMethod = PlatformLoggingMXBean.class
+ .getMethod(m.getName(), m.getParameterTypes());
+ if (specMethod.getReturnType().isAssignableFrom(m.getReturnType())) {
+ if (methodsMap.putIfAbsent(m.getName(), m) != null) {
+ throw new RuntimeException("unexpected polymorphic method: "
+ + m.getName());
+ }
+ }
+ } catch (NoSuchMethodException x) {
+ // All methods in java.util.logging.LoggingMXBean should
+ // also be in PlatformLoggingMXBean
+ throw new InternalError(x);
+ }
+ }
+ return Collections.unmodifiableMap(methodsMap);
}
+
+ private static Object getMXBeanImplementation() {
+ if (!isAvailable()) {
+ // should not happen
+ throw new NoClassDefFoundError(LOG_MANAGER_CLASS_NAME);
+ }
+ try {
+ final Method m = LOG_MANAGER_CLASS.getMethod("getLoggingMXBean");
+ return m.invoke(null);
+ } catch (NoSuchMethodException
+ | IllegalAccessException
+ | InvocationTargetException x) {
+ throw new ExceptionInInitializerError(x);
+ }
+ }
+
+ // The implementation object, which will be invoked through
+ // reflection. The implementation does not need to implement
+ // PlatformLoggingMXBean, but must declare the same methods
+ // with same signatures, and they must be public, with one
+ // exception:
+ // getObjectName will not be called on the implementation object,
+ // so the implementation object does not need to declare such
+ // a method.
+ final Object impl = getMXBeanImplementation();
+ final Map<String, Method> methods = initMethodMap(impl);
+
+ LoggingMXBeanAccess() {
+ }
+
+ <T> T invoke(String methodName, Object... args) {
+ Method m = methods.get(methodName);
+ if (m == null) {
+ throw new UnsupportedOperationException(methodName);
+ }
+ try {
+ @SuppressWarnings("unchecked")
+ T result = (T) m.invoke(impl, args);
+ return result;
+ } catch (IllegalAccessException ex) {
+ throw new UnsupportedOperationException(ex);
+ } catch (InvocationTargetException ex) {
+ throw unwrap(ex);
+ }
+ }
+
+ private static RuntimeException unwrap(InvocationTargetException x) {
+ Throwable t = x.getCause();
+ if (t instanceof RuntimeException) {
+ return (RuntimeException)t;
+ }
+ if (t instanceof Error) {
+ throw (Error)t;
+ }
+ return new UndeclaredThrowableException(t == null ? x : t);
+ }
+
+
}
- static class PlatformLoggingImpl implements LoggingMXBean
- {
- final static java.util.logging.LoggingMXBean impl =
- (java.util.logging.LoggingMXBean) LoggingMXBeanSupport.loggingImpl;
- final static PlatformLoggingMXBean instance = new PlatformLoggingImpl();
- final static String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
+ static final class PlatformLoggingImpl implements PlatformLoggingMXBean {
+
+ private final LoggingMXBeanAccess loggingAccess;
+ private PlatformLoggingImpl(LoggingMXBeanAccess loggingAccess) {
+ this.loggingAccess = loggingAccess;
+ }
private volatile ObjectName objname; // created lazily
@Override
@@ -232,23 +303,29 @@
@Override
public java.util.List<String> getLoggerNames() {
- return impl.getLoggerNames();
+ return loggingAccess.invoke("getLoggerNames");
}
@Override
public String getLoggerLevel(String loggerName) {
- return impl.getLoggerLevel(loggerName);
+ return loggingAccess.invoke("getLoggerLevel", loggerName);
}
@Override
public void setLoggerLevel(String loggerName, String levelName) {
- impl.setLoggerLevel(loggerName, levelName);
+ loggingAccess.invoke("setLoggerLevel", loggerName, levelName);
}
@Override
public String getParentLoggerName(String loggerName) {
- return impl.getParentLoggerName(loggerName);
+ return loggingAccess.invoke("getParentLoggerName", loggerName);
}
+
+ private static PlatformLoggingImpl getInstance() {
+ return new PlatformLoggingImpl(new LoggingMXBeanAccess());
+ }
+
+ static final PlatformLoggingMXBean MBEAN = getInstance();
}
private static List<BufferPoolMXBean> bufferPools = null;
--- a/jdk/test/java/lang/management/PlatformLoggingMXBean/PlatformLoggingMXBeanTest.java Tue May 17 07:43:45 2016 -0700
+++ b/jdk/test/java/lang/management/PlatformLoggingMXBean/PlatformLoggingMXBeanTest.java Tue May 17 19:24:46 2016 +0200
@@ -270,8 +270,7 @@
// Calling getMBeanInfo will throw exception if not found.
platformMBS.getMBeanInfo(objName);
- if (!platformMBS.isInstanceOf(objName, "java.lang.management.PlatformLoggingMXBean") ||
- !platformMBS.isInstanceOf(objName, "java.util.logging.LoggingMXBean")) {
+ if (!platformMBS.isInstanceOf(objName, "java.lang.management.PlatformLoggingMXBean")) {
throw new RuntimeException(objName + " is of unexpected type");
}