8005615: Java Logger fails to load tomcat logger implementation (JULI)
Reviewed-by: alanb, ahgross
--- a/jdk/src/share/classes/java/util/logging/LogManager.java Tue Dec 18 13:48:48 2012 -0500
+++ b/jdk/src/share/classes/java/util/logging/LogManager.java Thu Jan 10 19:43:36 2013 -0800
@@ -158,7 +158,7 @@
// LoggerContext for system loggers and user loggers
private final LoggerContext systemContext = new SystemLoggerContext();
- private final LoggerContext userContext = new UserLoggerContext();
+ private final LoggerContext userContext = new LoggerContext();
private Logger rootLogger;
// Have we done the primordial reading of the configuration file?
@@ -196,13 +196,13 @@
// Create and retain Logger for the root of the namespace.
manager.rootLogger = manager.new RootLogger();
- manager.systemContext.addLogger(manager.rootLogger);
- manager.userContext.addLogger(manager.rootLogger);
+ manager.addLogger(manager.rootLogger);
+ manager.systemContext.addLocalLogger(manager.rootLogger);
// Adding the global Logger. Doing so in the Logger.<clinit>
// would deadlock with the LogManager.<clinit>.
- Logger.getGlobal().setLogManager(manager);
- manager.systemContext.addLogger(Logger.getGlobal());
+ Logger.global.setLogManager(manager);
+ manager.addLogger(Logger.global);
// We don't call readConfiguration() here, as we may be running
// very early in the JVM startup sequence. Instead readConfiguration
@@ -373,7 +373,7 @@
// Returns the LoggerContext for the user code (i.e. application or AppContext).
// Loggers are isolated from each AppContext.
- LoggerContext getUserContext() {
+ private LoggerContext getUserContext() {
LoggerContext context = null;
SecurityManager sm = System.getSecurityManager();
@@ -394,8 +394,8 @@
if (javaAwtAccess.isMainAppContext()) {
context = userContext;
} else {
- context = new UserLoggerContext();
- context.addLogger(manager.rootLogger);
+ context = new LoggerContext();
+ context.addLocalLogger(manager.rootLogger);
}
javaAwtAccess.put(ecx, LoggerContext.class, context);
}
@@ -406,10 +406,6 @@
return context;
}
- LoggerContext getSystemContext() {
- return systemContext;
- }
-
private List<LoggerContext> contexts() {
List<LoggerContext> cxs = new ArrayList<>();
cxs.add(systemContext);
@@ -417,6 +413,58 @@
return cxs;
}
+ // Find or create a specified logger instance. If a logger has
+ // already been created with the given name it is returned.
+ // Otherwise a new logger instance is created and registered
+ // in the LogManager global namespace.
+ // This method will always return a non-null Logger object.
+ // Synchronization is not required here. All synchronization for
+ // adding a new Logger object is handled by addLogger().
+ //
+ // This method must delegate to the LogManager implementation to
+ // add a new Logger or return the one that has been added previously
+ // as a LogManager subclass may override the addLogger, getLogger,
+ // readConfiguration, and other methods.
+ Logger demandLogger(String name, String resourceBundleName) {
+ Logger result = getLogger(name);
+ if (result == null) {
+ // only allocate the new logger once
+ Logger newLogger = new Logger(name, resourceBundleName);
+ do {
+ if (addLogger(newLogger)) {
+ // We successfully added the new Logger that we
+ // created above so return it without refetching.
+ return newLogger;
+ }
+
+ // We didn't add the new Logger that we created above
+ // because another thread added a Logger with the same
+ // name after our null check above and before our call
+ // to addLogger(). We have to refetch the Logger because
+ // addLogger() returns a boolean instead of the Logger
+ // reference itself. However, if the thread that created
+ // the other Logger is not holding a strong reference to
+ // the other Logger, then it is possible for the other
+ // Logger to be GC'ed after we saw it in addLogger() and
+ // before we can refetch it. If it has been GC'ed then
+ // we'll just loop around and try again.
+ result = getLogger(name);
+ } while (result == null);
+ }
+ return result;
+ }
+
+ Logger demandSystemLogger(String name, String resourceBundleName) {
+ return systemContext.demandLogger(name, resourceBundleName);
+ }
+
+ // LoggerContext maintains the logger namespace per context.
+ // The default LogManager implementation has one system context and user
+ // context. The system context is used to maintain the namespace for
+ // all system loggers and is queried by the system code. If a system logger
+ // doesn't exist in the user context, it'll also be added to the user context.
+ // The user context is queried by the user code and all other loggers are
+ // added in the user context.
static class LoggerContext {
// Table of named Loggers that maps names to Loggers.
private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
@@ -427,6 +475,12 @@
this.root = new LogNode(null, this);
}
+ Logger demandLogger(String name, String resourceBundleName) {
+ // a LogManager subclass may have its own implementation to add and
+ // get a Logger. So delegate to the LogManager to do the work.
+ return manager.demandLogger(name, resourceBundleName);
+ }
+
synchronized Logger findLogger(String name) {
LoggerWeakRef ref = namedLoggers.get(name);
if (ref == null) {
@@ -441,7 +495,9 @@
return logger;
}
- synchronized boolean addLogger(Logger logger) {
+ // Add a logger to this context. This method will only set its level
+ // and process parent loggers. It doesn't set its handlers.
+ synchronized boolean addLocalLogger(Logger logger) {
final String name = logger.getName();
if (name == null) {
throw new NullPointerException();
@@ -474,9 +530,9 @@
doSetLevel(logger, level);
}
- // Do we have a per logger handler too?
- // Note: this will add a 200ms penalty
- manager.loadLoggerHandlers(logger, name, name + ".handlers");
+ // instantiation of the handler is done in the LogManager.addLogger
+ // implementation as a handler class may be only visible to LogManager
+ // subclass for the custom log manager case
processParentHandlers(logger, name);
// Find the new node and its parent.
@@ -513,50 +569,21 @@
return namedLoggers.keys();
}
- Logger demandLogger(String name) {
- return demandLogger(name, null);
- }
-
- // Find or create a specified logger instance. If a logger has
- // already been created with the given name it is returned.
- // Otherwise a new logger instance is created and registered
- // in the LogManager global namespace.
-
- // This method will always return a non-null Logger object.
- // Synchronization is not required here. All synchronization for
- // adding a new Logger object is handled by addLogger().
- Logger demandLogger(String name, String resourceBundleName) {
- Logger result = findLogger(name);
- if (result == null) {
- // only allocate the new logger once
- Logger newLogger = new Logger(name, resourceBundleName);
- do {
- if (addLogger(newLogger)) {
- // We successfully added the new Logger that we
- // created above so return it without refetching.
- return newLogger;
- }
-
- // We didn't add the new Logger that we created above
- // because another thread added a Logger with the same
- // name after our null check above and before our call
- // to addLogger(). We have to refetch the Logger because
- // addLogger() returns a boolean instead of the Logger
- // reference itself. However, if the thread that created
- // the other Logger is not holding a strong reference to
- // the other Logger, then it is possible for the other
- // Logger to be GC'ed after we saw it in addLogger() and
- // before we can refetch it. If it has been GC'ed then
- // we'll just loop around and try again.
- result = findLogger(name);
- } while (result == null);
- }
- return result;
- }
-
// If logger.getUseParentHandlers() returns 'true' and any of the logger's
// parents have levels or handlers defined, make sure they are instantiated.
- private void processParentHandlers(Logger logger, String name) {
+ private void processParentHandlers(final Logger logger, final String name) {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ if (logger != manager.rootLogger) {
+ boolean useParent = manager.getBooleanProperty(name + ".useParentHandlers", true);
+ if (!useParent) {
+ logger.setUseParentHandlers(false);
+ }
+ }
+ return null;
+ }
+ });
+
int ix = 1;
for (;;) {
int ix2 = name.indexOf(".", ix);
@@ -564,12 +591,11 @@
break;
}
String pname = name.substring(0, ix2);
-
if (manager.getProperty(pname + ".level") != null ||
manager.getProperty(pname + ".handlers") != null) {
// This pname has a level/handlers definition.
// Make sure it exists.
- demandLogger(pname);
+ demandLogger(pname, null);
}
ix = ix2+1;
}
@@ -607,53 +633,51 @@
}
static class SystemLoggerContext extends LoggerContext {
- // Default resource bundle for all system loggers
- Logger demandLogger(String name) {
- // default to use the system logger's resource bundle
- return super.demandLogger(name, Logger.SYSTEM_LOGGER_RB_NAME);
- }
- }
-
- static class UserLoggerContext extends LoggerContext {
- /**
- * Returns a Logger of the given name if there is one registered
- * in this context. Otherwise, it will return the one registered
- * in the system context if there is one. The returned Logger
- * instance may be initialized with a different resourceBundleName.
- * If no such logger exists, a new Logger instance will be created
- * and registered in this context.
- */
+ // Add a system logger in the system context's namespace as well as
+ // in the LogManager's namespace if not exist so that there is only
+ // one single logger of the given name. System loggers are visible
+ // to applications unless a logger of the same name has been added.
Logger demandLogger(String name, String resourceBundleName) {
Logger result = findLogger(name);
if (result == null) {
- // use the system logger if exists; or allocate a new logger.
- // The system logger is added to the app logger context so that
- // any child logger created in the app logger context can have
- // a system logger as its parent if already exist.
- Logger logger = manager.systemContext.findLogger(name);
- Logger newLogger =
- logger != null ? logger : new Logger(name, resourceBundleName);
+ // only allocate the new system logger once
+ Logger newLogger = new Logger(name, resourceBundleName);
do {
- if (addLogger(newLogger)) {
+ if (addLocalLogger(newLogger)) {
// We successfully added the new Logger that we
// created above so return it without refetching.
- return newLogger;
+ result = newLogger;
+ } else {
+ // We didn't add the new Logger that we created above
+ // because another thread added a Logger with the same
+ // name after our null check above and before our call
+ // to addLogger(). We have to refetch the Logger because
+ // addLogger() returns a boolean instead of the Logger
+ // reference itself. However, if the thread that created
+ // the other Logger is not holding a strong reference to
+ // the other Logger, then it is possible for the other
+ // Logger to be GC'ed after we saw it in addLogger() and
+ // before we can refetch it. If it has been GC'ed then
+ // we'll just loop around and try again.
+ result = findLogger(name);
}
-
- // We didn't add the new Logger that we created above
- // because another thread added a Logger with the same
- // name after our null check above and before our call
- // to addLogger(). We have to refetch the Logger because
- // addLogger() returns a boolean instead of the Logger
- // reference itself. However, if the thread that created
- // the other Logger is not holding a strong reference to
- // the other Logger, then it is possible for the other
- // Logger to be GC'ed after we saw it in addLogger() and
- // before we can refetch it. If it has been GC'ed then
- // we'll just loop around and try again.
- result = findLogger(name);
} while (result == null);
}
+ // Add the system logger to the LogManager's namespace if not exists
+ // The LogManager will set its handlers via the LogManager.addLogger method.
+ if (!manager.addLogger(result) && result.getHandlers().length == 0) {
+ // if logger already exists but handlers not set
+ final Logger l = manager.getLogger(name);
+ final Logger logger = result;
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ for (Handler hdl : l.getHandlers()) {
+ logger.addHandler(hdl);
+ }
+ return null;
+ }
+ });
+ }
return result;
}
}
@@ -663,22 +687,16 @@
// be made based on the logging configuration, which can
// only be modified by trusted code.
private void loadLoggerHandlers(final Logger logger, final String name,
- final String handlersPropertyName) {
+ final String handlersPropertyName)
+ {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
- if (logger != rootLogger) {
- boolean useParent = getBooleanProperty(name + ".useParentHandlers", true);
- if (!useParent) {
- logger.setUseParentHandlers(false);
- }
- }
-
String names[] = parseClassNames(handlersPropertyName);
for (int i = 0; i < names.length; i++) {
String word = names[i];
try {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
- Handler hdl = (Handler) clz.newInstance();
+ Handler hdl = (Handler) clz.newInstance();
// Check if there is a property defining the
// this handler's level.
String levs = getProperty(word + ".level");
@@ -700,7 +718,8 @@
}
}
return null;
- }});
+ }
+ });
}
@@ -839,13 +858,17 @@
if (name == null) {
throw new NullPointerException();
}
- if (systemContext.findLogger(name) != null) {
+ LoggerContext cx = getUserContext();
+ if (cx.addLocalLogger(logger)) {
+ // Do we have a per logger handler too?
+ // Note: this will add a 200ms penalty
+ loadLoggerHandlers(logger, name, name + ".handlers");
+ return true;
+ } else {
return false;
}
- return getUserContext().addLogger(logger);
}
-
// Private method to set a level on a logger.
// If necessary, we raise privilege before doing the call.
private static void doSetLevel(final Logger logger, final Level level) {
@@ -864,8 +887,6 @@
}});
}
-
-
// Private method to set a parent on a logger.
// If necessary, we raise privilege before doing the setParent call.
private static void doSetParent(final Logger logger, final Logger parent) {
@@ -900,15 +921,7 @@
* @return matching logger or null if none is found
*/
public Logger getLogger(String name) {
- // return the first logger added
- //
- // once a system logger is added in the system context, no one can
- // adds a logger with the same name in the global context
- // (see LogManager.addLogger). So if there is a logger in the global
- // context with the same name as one in the system context, it must be
- // added before the system logger was created.
- Logger logger = getUserContext().findLogger(name);
- return logger != null ? logger : systemContext.findLogger(name);
+ return getUserContext().findLogger(name);
}
/**
@@ -928,10 +941,7 @@
* @return enumeration of logger name strings
*/
public Enumeration<String> getLoggerNames() {
- // only return unique names
- Set<String> names = new HashSet<>(Collections.list(systemContext.getLoggerNames()));
- names.addAll(Collections.list(getUserContext().getLoggerNames()));
- return Collections.enumeration(names);
+ return getUserContext().getLoggerNames();
}
/**
@@ -1329,7 +1339,6 @@
// that we only instantiate the global handlers when they
// are first needed.
private class RootLogger extends Logger {
-
private RootLogger() {
super("", null);
setLevel(defaultLevel);
--- a/jdk/src/share/classes/java/util/logging/Logger.java Tue Dec 18 13:48:48 2012 -0500
+++ b/jdk/src/share/classes/java/util/logging/Logger.java Thu Jan 10 19:43:36 2013 -0800
@@ -31,7 +31,6 @@
import java.security.*;
import java.lang.ref.WeakReference;
import java.util.function.Supplier;
-import java.util.logging.LogManager.LoggerContext;
/**
* A Logger object is used to log messages for a specific
@@ -321,18 +320,32 @@
//
// As an interim solution, if the immediate caller whose caller loader is
// null, we assume it's a system logger and add it to the system context.
- private static LoggerContext getLoggerContext() {
+ // These system loggers only set the resource bundle to the given
+ // resource bundle name (rather than the default system resource bundle).
+ private static class SystemLoggerHelper {
+ static boolean disableCallerCheck = getBooleanProperty("sun.util.logging.disableCallerCheck");
+ private static boolean getBooleanProperty(final String key) {
+ String s = AccessController.doPrivileged(new PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty(key);
+ }
+ });
+ return Boolean.valueOf(s);
+ }
+ }
+
+ private static Logger demandLogger(String name, String resourceBundleName) {
LogManager manager = LogManager.getLogManager();
SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- // 0: Reflection 1: Logger.getLoggerContext 2: Logger.getLogger 3: caller
+ if (sm != null && !SystemLoggerHelper.disableCallerCheck) {
+ // 0: Reflection 1: Logger.demandLogger 2: Logger.getLogger 3: caller
final int SKIP_FRAMES = 3;
Class<?> caller = sun.reflect.Reflection.getCallerClass(SKIP_FRAMES);
if (caller.getClassLoader() == null) {
- return manager.getSystemContext();
+ return manager.demandSystemLogger(name, resourceBundleName);
}
}
- return manager.getUserContext();
+ return manager.demandLogger(name, resourceBundleName);
}
/**
@@ -376,8 +389,7 @@
// would throw an IllegalArgumentException in the second call
// because the wrapper would result in an attempt to replace
// the existing "resourceBundleForFoo" with null.
- LoggerContext context = getLoggerContext();
- return context.demandLogger(name);
+ return demandLogger(name, null);
}
/**
@@ -424,8 +436,7 @@
// Synchronization is not required here. All synchronization for
// adding a new Logger object is handled by LogManager.addLogger().
public static Logger getLogger(String name, String resourceBundleName) {
- LoggerContext context = getLoggerContext();
- Logger result = context.demandLogger(name, resourceBundleName);
+ Logger result = demandLogger(name, resourceBundleName);
// MissingResourceException or IllegalArgumentException can be
// thrown by setupResourceInfo().
@@ -438,11 +449,10 @@
// i.e. caller of sun.util.logging.PlatformLogger.getLogger
static Logger getPlatformLogger(String name) {
LogManager manager = LogManager.getLogManager();
- LoggerContext context = manager.getSystemContext();
// all loggers in the system context will default to
// the system logger's resource bundle
- Logger result = context.demandLogger(name);
+ Logger result = manager.demandSystemLogger(name, SYSTEM_LOGGER_RB_NAME);
return result;
}
@@ -1588,7 +1598,8 @@
public ResourceBundle run() {
try {
return ResourceBundle.getBundle(SYSTEM_LOGGER_RB_NAME,
- locale);
+ locale,
+ ClassLoader.getSystemClassLoader());
} catch (MissingResourceException e) {
throw new InternalError(e.toString());
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/CustomLogManager.java Thu Jan 10 19:43:36 2013 -0800
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.*;
+import java.util.*;
+import java.util.logging.*;
+
+/*
+ * Custom LogManager implementation to verify that the implementation delegates
+ * to the LogManager subclass to register both system logger and user logger.
+ *
+ * The LogManager implementation is the one configuring the logger's property
+ * such as level, handler, etc.
+ */
+public class CustomLogManager extends LogManager {
+ static LogManager INSTANCE;
+ Map<String,Logger> namedLoggers = new HashMap<>();
+ Properties props = initConfig();
+ public CustomLogManager() {
+ if (INSTANCE != null) {
+ throw new RuntimeException("CustomLogManager already created");
+ }
+ INSTANCE = this;
+ }
+
+ public synchronized boolean addLogger(Logger logger) {
+ String name = logger.getName();
+ if (namedLoggers.containsKey(name)) {
+ return false;
+ }
+ namedLoggers.put(name, logger);
+ // set level
+ if (props.get(name + ".level") != null) {
+ logger.setLevel(Level.parse(props.getProperty(name + ".level")));
+ }
+ // add handlers
+ if (props.get(name + ".handlers") != null && logger.getHandlers().length == 0) {
+ logger.addHandler(new CustomHandler());
+ }
+ // add parent loggers
+ int ix = 1;
+ for (;;) {
+ int ix2 = name.indexOf(".", ix);
+ if (ix2 < 0) {
+ break;
+ }
+ String pname = name.substring(0, ix2);
+ if (props.get(pname + ".level") != null ||
+ props.get(pname + ".handlers") != null) {
+ // This pname has a level/handlers definition.
+ // Make sure it exists.
+ //
+ // The test doesn't set the parent for simplicity.
+ if (!namedLoggers.containsKey(pname)) {
+ Logger.getLogger(pname);
+ }
+ }
+ ix = ix2 + 1;
+ }
+ return true;
+ }
+
+ public synchronized Logger getLogger(String name) {
+ return namedLoggers.get(name);
+ }
+
+ public synchronized Enumeration<String> getLoggerNames() {
+ return Collections.enumeration(namedLoggers.keySet());
+ }
+
+ public String getProperty(String name) {
+ return props.getProperty(name);
+ }
+
+ public void readConfiguration() {
+ // do nothing
+ }
+
+ public void readConfiguration(InputStream ins) {
+ // do nothing
+ }
+
+ private Properties initConfig() {
+ Properties props = new Properties();
+ props.put(".level", "CONFIG");
+ props.put("CustomLogManagerTest.level", "WARNING");
+ props.put("CustomLogManagerTest.handlers", "CustomLogManager$CustomHandler");
+ props.put("SimpleLogManager.level", "INFO");
+ props.put("SimpleLogManager.handlers", "CustomLogManager$CustomHandler");
+ props.put("CustomLogManager$CustomHandler.level", "WARNING");
+ props.put(".handlers", "CustomLogManager$CustomHandler");
+ props.put("org.foo.bar.level", "SEVERE");
+ props.put("org.foo.handlers", "CustomLogManager$CustomHandler");
+ props.put("org.openjdk.level", "SEVERE");
+ props.put("org.openjdk.handlers", "CustomLogManager$CustomHandler");
+ props.put("org.openjdk.core.level", "INFO");
+
+ return props;
+ }
+
+ public static void checkLogger(String name) {
+ checkLogger(name, null);
+ }
+
+ public static void checkLogger(String name, String resourceBundleName) {
+ Logger logger = INSTANCE.getLogger(name);
+ if (logger == null) {
+ throw new RuntimeException("Logger \"" + name + "\" not exist");
+ }
+ System.out.format("Logger \"%s\" level=%s handlers=%s resourcebundle=%s%n",
+ name, logger.getLevel(),
+ Arrays.toString(logger.getHandlers()),
+ logger.getResourceBundleName());
+ String rb = logger.getResourceBundleName();
+ if (rb != resourceBundleName && (rb == null || rb.equals(resourceBundleName))) {
+ throw new RuntimeException("Logger \"" + name +
+ "\" unexpected resource bundle: " + rb);
+ }
+
+ String value = INSTANCE.getProperty(name + ".level");
+ String level = logger.getLevel() != null ? logger.getLevel().getName() : null;
+ if (level != value && (level == null || level.equals(value))) {
+ throw new RuntimeException("Logger \"" + name + "\" unexpected level: " + level);
+ }
+
+ Handler[] handlers = logger.getHandlers();
+ String hdl = INSTANCE.getProperty(name + ".handlers");
+ if ((hdl == null && handlers.length != 0) ||
+ (hdl != null && handlers.length != 1)) {
+ throw new RuntimeException("Logger \"" + name + "\" unexpected handler: " +
+ Arrays.toString(handlers));
+ }
+ checkParents(name);
+ }
+
+ private static void checkParents(String name) {
+ int ix = 1;
+ for (;;) {
+ int ix2 = name.indexOf(".", ix);
+ if (ix2 < 0) {
+ break;
+ }
+ String pname = name.substring(0, ix2);
+ if (INSTANCE.getProperty(pname + ".level") != null ||
+ INSTANCE.getProperty(pname + ".handlers") != null) {
+ // This pname has a level/handlers definition.
+ // Make sure it exists.
+ checkLogger(pname);
+ }
+ ix = ix2 + 1;
+ }
+ }
+
+ // only CustomLogManager can create an instance of CustomHandler
+ private class CustomHandler extends StreamHandler {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/CustomLogManagerTest.java Thu Jan 10 19:43:36 2013 -0800
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.*;
+import java.util.*;
+
+import java.util.logging.*;
+import sun.util.logging.PlatformLogger;
+
+/*
+ * @test
+ * @bug 8005615
+ * @summary Add loggers to custom log manager
+ *
+ * @compile -XDignore.symbol.file CustomLogManagerTest.java CustomLogManager.java
+ * @run main/othervm -Djava.util.logging.manager=CustomLogManager CustomLogManagerTest
+ */
+public class CustomLogManagerTest {
+ private static final String RESOURCE_BUNDLE = "sun.util.logging.resources.logging";
+ public static void main(String[] args) {
+ String mgr = System.getProperty("java.util.logging.manager");
+ if (!mgr.equals("CustomLogManager")) {
+ throw new RuntimeException("java.util.logging.manager not set");
+ }
+
+ Logger.getLogger(CustomLogManagerTest.class.getName());
+ Logger.getLogger("org.foo.Foo");
+ Logger.getLogger("org.foo.bar.Foo", RESOURCE_BUNDLE);
+ // platform logger will be set with the default system resource bundle
+ PlatformLogger.getLogger("org.openjdk.core.logger");
+
+ if (LogManager.getLogManager() != CustomLogManager.INSTANCE) {
+ throw new RuntimeException(LogManager.getLogManager() + " not CustomLogManager");
+ }
+
+ CustomLogManager.checkLogger(CustomLogManagerTest.class.getName());
+ CustomLogManager.checkLogger("org.foo.Foo");
+ CustomLogManager.checkLogger("org.foo.bar.Foo", RESOURCE_BUNDLE);
+ CustomLogManager.checkLogger(Logger.GLOBAL_LOGGER_NAME);
+ CustomLogManager.checkLogger("");
+ CustomLogManager.checkLogger("org.openjdk.core.logger", RESOURCE_BUNDLE);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/logging/SimpleLogManager.java Thu Jan 10 19:43:36 2013 -0800
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.*;
+import java.util.logging.*;
+import sun.util.logging.PlatformLogger;
+
+/*
+ * @test
+ * @bug 8005615
+ * @summary A LogManager subclass overrides its own implementation of named
+ * logger (see the subclassing information in the Logger class specification)
+ *
+ * @compile -XDignore.symbol.file CustomLogManager.java SimpleLogManager.java
+ * @run main/othervm -Djava.util.logging.manager=SimpleLogManager SimpleLogManager
+ */
+public class SimpleLogManager extends CustomLogManager {
+ public static void main(String[] args) {
+ String classname = System.getProperty("java.util.logging.manager");
+ if (!classname.equals("SimpleLogManager")) {
+ throw new RuntimeException("java.util.logging.manager not set");
+ }
+
+ Logger logger = Logger.getLogger(SimpleLogManager.class.getName());
+ Logger.getLogger("org.foo.bar.Foo");
+
+ // a platform logger used by the system code is just a Logger instance.
+ PlatformLogger.getLogger("org.openjdk.core.logger");
+
+ LogManager mgr = LogManager.getLogManager();
+ if (mgr != CustomLogManager.INSTANCE || !(mgr instanceof SimpleLogManager)) {
+ throw new RuntimeException(LogManager.getLogManager() + " not SimpleLogManager");
+ }
+
+ checkCustomLogger(SimpleLogManager.class.getName(), null);
+ checkCustomLogger("org.foo.bar.Foo", null);
+ checkCustomLogger("org.openjdk.core.logger", "sun.util.logging.resources.logging");
+
+ // ## The LogManager.demandLogger method does not handle custom log manager
+ // ## that overrides the getLogger method to return a custom logger
+ // ## (see the test case in 8005640). Logger.getLogger may return
+ // ## a Logger instance but LogManager overrides it with a custom Logger
+ // ## instance like this case.
+ //
+ // However, the specification of LogManager and Logger subclassing is
+ // not clear whether this is supported or not. The following check
+ // just captures the current behavior.
+ if (logger instanceof CustomLogger) {
+ throw new RuntimeException(logger + " not CustomLogger");
+ }
+ }
+
+ private static void checkCustomLogger(String name, String resourceBundleName) {
+ CustomLogManager.checkLogger(name, resourceBundleName);
+ Logger logger1 = Logger.getLogger(name);
+ Logger logger2 = LogManager.getLogManager().getLogger(name);
+ if (logger1 != logger2) {
+ throw new RuntimeException(logger1 + " != " + logger2);
+ }
+ if (!(logger1 instanceof CustomLogger)) {
+ throw new RuntimeException(logger1 + " not CustomLogger");
+ }
+ }
+
+ /*
+ * This SimpleLogManager overrides the addLogger method to replace
+ * the given logger with a custom logger.
+ *
+ * It's unclear what the recommended way to use custom logger is.
+ * A LogManager subclass might override the getLogger method to return
+ * a custom Logger and create a new custom logger if not exist so that
+ * Logger.getLogger() can return a custom Logger instance but that violates
+ * the LogManager.getLogger() spec which should return null if not found.
+ */
+ public synchronized boolean addLogger(Logger logger) {
+ String name = logger.getName();
+ if (namedLoggers.containsKey(name)) {
+ return false;
+ }
+ CustomLogger newLogger = new CustomLogger(logger);
+ super.addLogger(newLogger);
+ return true;
+ }
+
+ public class CustomLogger extends Logger {
+ CustomLogger(Logger logger) {
+ super(logger.getName(), logger.getResourceBundleName());
+ }
+ CustomLogger(String name) {
+ super(name, null);
+ }
+ }
+}