8139982: Re-examine java.management dependency on java.util.logging.LoggingMXBean

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
This commit is contained in:
Daniel Fuchs 2016-05-17 19:24:46 +02:00
parent 8d9cf355fd
commit a96b7f7d25
7 changed files with 202 additions and 104 deletions
jdk
src
java.logging/share/classes/java/util/logging
java.management/share/classes
test/java/lang/management/PlatformLoggingMXBean

@ -2506,15 +2506,12 @@ public class LogManager {
}
}
// 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 @@ public class LogManager {
/**
* 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();
}
/**

@ -44,16 +44,18 @@ import java.util.ArrayList;
* @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 @@ class Logging implements LoggingMXBean {
}
private static String EMPTY_STRING = "";
@Override
public String getLoggerLevel(String loggerName) {
Logger l = logManager.getLogger(loggerName);
if (l == null) {
@ -79,6 +82,7 @@ class Logging implements LoggingMXBean {
}
}
@Override
public void setLoggerLevel(String loggerName, String levelName) {
if (loggerName == null) {
throw new NullPointerException("loggerName is null");
@ -102,6 +106,7 @@ class Logging implements LoggingMXBean {
logger.setLevel(level);
}
@Override
public String getParentLoggerName( String loggerName ) {
Logger l = logManager.getLogger( loggerName );
if (l == null) {
@ -116,4 +121,11 @@ class Logging implements LoggingMXBean {
return p.getName();
}
}
static Logging getInstance() {
return INSTANCE;
}
private static final Logging INSTANCE = new Logging();
}

@ -27,30 +27,23 @@ package java.util.logging;
/**
* 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 @@ package java.util.logging;
*
* @see java.lang.management.PlatformLoggingMXBean
*/
@Deprecated(since="9")
public interface LoggingMXBean {
/**

@ -598,9 +598,8 @@ public class ManagementFactory {
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 @@ public class ManagementFactory {
}
}
// 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

@ -44,10 +44,6 @@ package java.lang.management;
* {@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 {

@ -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 jdk.internal.misc.SharedSecrets;
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 @@ public class ManagementFactoryHelper {
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 class ManagementFactoryHelper {
}
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 @@ public class ManagementFactoryHelper {
@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;

@ -270,8 +270,7 @@ public class PlatformLoggingMXBeanTest
// 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");
}