6664509: Add logging context
6664528: Find log level matching its name or value given at construction time
Reviewed-by: alanb, ahgross, jgish, hawtin
--- a/jdk/src/share/classes/java/util/logging/Level.java Mon Nov 26 20:49:54 2012 +0400
+++ b/jdk/src/share/classes/java/util/logging/Level.java Mon Nov 26 22:49:06 2012 -0800
@@ -24,6 +24,10 @@
*/
package java.util.logging;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.ResourceBundle;
/**
@@ -59,7 +63,6 @@
*/
public class Level implements java.io.Serializable {
- private static java.util.ArrayList<Level> known = new java.util.ArrayList<>();
private static String defaultBundle = "sun.util.logging.resources.logging";
/**
@@ -77,6 +80,9 @@
*/
private final String resourceBundleName;
+ // localized level name
+ private String localizedLevelName;
+
/**
* OFF is a special level that can be used to turn off logging.
* This level is initialized to <CODE>Integer.MAX_VALUE</CODE>.
@@ -202,9 +208,8 @@
this.name = name;
this.value = value;
this.resourceBundleName = resourceBundleName;
- synchronized (Level.class) {
- known.add(this);
- }
+ this.localizedLevelName = resourceBundleName == null ? name : null;
+ KnownLevel.add(this);
}
/**
@@ -236,12 +241,76 @@
* @return localized name
*/
public String getLocalizedName() {
+ return getLocalizedLevelName();
+ }
+
+ // package-private getLevelName() is used by the implementation
+ // instead of getName() to avoid calling the subclass's version
+ final String getLevelName() {
+ return this.name;
+ }
+
+ final synchronized String getLocalizedLevelName() {
+ if (localizedLevelName != null) {
+ return localizedLevelName;
+ }
+
try {
ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName);
- return rb.getString(name);
+ localizedLevelName = rb.getString(name);
} catch (Exception ex) {
- return name;
+ localizedLevelName = name;
+ }
+ return localizedLevelName;
+ }
+
+ // Returns a mirrored Level object that matches the given name as
+ // specified in the Level.parse method. Returns null if not found.
+ //
+ // It returns the same Level object as the one returned by Level.parse
+ // method if the given name is a non-localized name or integer.
+ //
+ // If the name is a localized name, findLevel and parse method may
+ // return a different level value if there is a custom Level subclass
+ // that overrides Level.getLocalizedName() to return a different string
+ // than what's returned by the default implementation.
+ //
+ static Level findLevel(String name) {
+ if (name == null) {
+ throw new NullPointerException();
}
+
+ KnownLevel level;
+
+ // Look for a known Level with the given non-localized name.
+ level = KnownLevel.findByName(name);
+ if (level != null) {
+ return level.mirroredLevel;
+ }
+
+ // Now, check if the given name is an integer. If so,
+ // first look for a Level with the given value and then
+ // if necessary create one.
+ try {
+ int x = Integer.parseInt(name);
+ level = KnownLevel.findByValue(x);
+ if (level == null) {
+ // add new Level
+ Level levelObject = new Level(name, x);
+ level = KnownLevel.findByValue(x);
+ }
+ return level.mirroredLevel;
+ } catch (NumberFormatException ex) {
+ // Not an integer.
+ // Drop through.
+ }
+
+ level = KnownLevel.findByLocalizedLevelName(name);
+ if (level != null) {
+ return level.mirroredLevel;
+ }
+
+ return null;
}
/**
@@ -268,21 +337,15 @@
// Serialization magic to prevent "doppelgangers".
// This is a performance optimization.
private Object readResolve() {
- synchronized (Level.class) {
- for (int i = 0; i < known.size(); i++) {
- Level other = known.get(i);
- if (this.name.equals(other.name) && this.value == other.value
- && (this.resourceBundleName == other.resourceBundleName ||
- (this.resourceBundleName != null &&
- this.resourceBundleName.equals(other.resourceBundleName)))) {
- return other;
- }
- }
- // Woops. Whoever sent us this object knows
- // about a new log level. Add it to our list.
- known.add(this);
- return this;
+ KnownLevel o = KnownLevel.matches(this);
+ if (o != null) {
+ return o.levelObject;
}
+
+ // Woops. Whoever sent us this object knows
+ // about a new log level. Add it to our list.
+ Level level = new Level(this.name, this.value, this.resourceBundleName);
+ return level;
}
/**
@@ -296,6 +359,7 @@
* <li> "SEVERE"
* <li> "1000"
* </ul>
+ *
* @param name string to be parsed
* @throws NullPointerException if the name is null
* @throws IllegalArgumentException if the value is not valid.
@@ -315,12 +379,12 @@
// Check that name is not null.
name.length();
+ KnownLevel level;
+
// Look for a known Level with the given non-localized name.
- for (int i = 0; i < known.size(); i++) {
- Level l = known.get(i);
- if (name.equals(l.name)) {
- return l;
- }
+ level = KnownLevel.findByName(name);
+ if (level != null) {
+ return level.levelObject;
}
// Now, check if the given name is an integer. If so,
@@ -328,14 +392,13 @@
// if necessary create one.
try {
int x = Integer.parseInt(name);
- for (int i = 0; i < known.size(); i++) {
- Level l = known.get(i);
- if (l.value == x) {
- return l;
- }
+ level = KnownLevel.findByValue(x);
+ if (level == null) {
+ // add new Level
+ Level levelObject = new Level(name, x);
+ level = KnownLevel.findByValue(x);
}
- // Create a new Level.
- return new Level(name, x);
+ return level.levelObject;
} catch (NumberFormatException ex) {
// Not an integer.
// Drop through.
@@ -344,11 +407,9 @@
// Finally, look for a known level with the given localized name,
// in the current default locale.
// This is relatively expensive, but not excessively so.
- for (int i = 0; i < known.size(); i++) {
- Level l = known.get(i);
- if (name.equals(l.getLocalizedName())) {
- return l;
- }
+ level = KnownLevel.findByLocalizedName(name);
+ if (level != null) {
+ return level.levelObject;
}
// OK, we've tried everything and failed
@@ -375,4 +436,124 @@
public int hashCode() {
return this.value;
}
+
+ // KnownLevel class maintains the global list of all known levels.
+ // The API allows multiple custom Level instances of the same name/value
+ // be created. This class provides convenient methods to find a level
+ // by a given name, by a given value, or by a given localized name.
+ //
+ // KnownLevel wraps the following Level objects:
+ // 1. levelObject: standard Level object or custom Level object
+ // 2. mirroredLevel: Level object representing the level specified in the
+ // logging configuration.
+ //
+ // Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
+ // are non-final but the name and resource bundle name are parameters to
+ // the Level constructor. Use the mirroredLevel object instead of the
+ // levelObject to prevent the logging framework to execute foreign code
+ // implemented by untrusted Level subclass.
+ //
+ // Implementation Notes:
+ // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
+ // were final, the following KnownLevel implementation can be removed.
+ // Future API change should take this into consideration.
+ static final class KnownLevel {
+ private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();
+ private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();
+ final Level levelObject; // instance of Level class or Level subclass
+ final Level mirroredLevel; // instance of Level class
+ KnownLevel(Level l) {
+ this.levelObject = l;
+ if (l.getClass() == Level.class) {
+ this.mirroredLevel = l;
+ } else {
+ this.mirroredLevel = new Level(l.name, l.value, l.resourceBundleName);
+ }
+ }
+
+ static synchronized void add(Level l) {
+ // the mirroredLevel object is always added to the list
+ // before the custom Level instance
+ KnownLevel o = new KnownLevel(l);
+ List<KnownLevel> list = nameToLevels.get(l.name);
+ if (list == null) {
+ list = new ArrayList<>();
+ nameToLevels.put(l.name, list);
+ }
+ list.add(o);
+
+ list = intToLevels.get(l.value);
+ if (list == null) {
+ list = new ArrayList<>();
+ intToLevels.put(l.value, list);
+ }
+ list.add(o);
+ }
+
+ // Returns a KnownLevel with the given non-localized name.
+ static synchronized KnownLevel findByName(String name) {
+ List<KnownLevel> list = nameToLevels.get(name);
+ if (list != null) {
+ return list.get(0);
+ }
+ return null;
+ }
+
+ // Returns a KnownLevel with the given value.
+ static synchronized KnownLevel findByValue(int value) {
+ List<KnownLevel> list = intToLevels.get(value);
+ if (list != null) {
+ return list.get(0);
+ }
+ return null;
+ }
+
+ // Returns a KnownLevel with the given localized name matching
+ // by calling the Level.getLocalizedLevelName() method (i.e. found
+ // from the resourceBundle associated with the Level object).
+ // This method does not call Level.getLocalizedName() that may
+ // be overridden in a subclass implementation
+ static synchronized KnownLevel findByLocalizedLevelName(String name) {
+ for (List<KnownLevel> levels : nameToLevels.values()) {
+ for (KnownLevel l : levels) {
+ String lname = l.levelObject.getLocalizedLevelName();
+ if (name.equals(lname)) {
+ return l;
+ }
+ }
+ }
+ return null;
+ }
+
+ // Returns a KnownLevel with the given localized name matching
+ // by calling the Level.getLocalizedName() method
+ static synchronized KnownLevel findByLocalizedName(String name) {
+ for (List<KnownLevel> levels : nameToLevels.values()) {
+ for (KnownLevel l : levels) {
+ String lname = l.levelObject.getLocalizedName();
+ if (name.equals(lname)) {
+ return l;
+ }
+ }
+ }
+ return null;
+ }
+
+ static synchronized KnownLevel matches(Level l) {
+ List<KnownLevel> list = nameToLevels.get(l.name);
+ if (list != null) {
+ for (KnownLevel level : list) {
+ Level other = level.mirroredLevel;
+ if (l.value == other.value &&
+ (l.resourceBundleName == other.resourceBundleName ||
+ (l.resourceBundleName != null &&
+ l.resourceBundleName.equals(other.resourceBundleName)))) {
+ return level;
+ }
+ }
+ }
+ return null;
+ }
+ }
+
}
--- a/jdk/src/share/classes/java/util/logging/LogManager.java Mon Nov 26 20:49:54 2012 +0400
+++ b/jdk/src/share/classes/java/util/logging/LogManager.java Mon Nov 26 22:49:06 2012 -0800
@@ -34,6 +34,8 @@
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.net.URL;
+import sun.misc.JavaAWTAccess;
+import sun.misc.SharedSecrets;
import sun.security.action.GetPropertyAction;
/**
@@ -157,10 +159,9 @@
// count to allow for cases where the same listener is registered many times.
private final Map<PropertyChangeListener,Integer> listenerMap = new HashMap<>();
- // Table of named Loggers that maps names to Loggers.
- private Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
- // Tree of named Loggers
- private LogNode root = new LogNode(null);
+ // LoggerContext for system loggers and user loggers
+ private final LoggerContext systemContext = new SystemLoggerContext();
+ private final LoggerContext userContext = new UserLoggerContext();
private Logger rootLogger;
// Have we done the primordial reading of the configuration file?
@@ -198,12 +199,13 @@
// Create and retain Logger for the root of the namespace.
manager.rootLogger = manager.new RootLogger();
- manager.addLogger(manager.rootLogger);
+ manager.systemContext.addLogger(manager.rootLogger);
+ manager.userContext.addLogger(manager.rootLogger);
// Adding the global Logger. Doing so in the Logger.<clinit>
// would deadlock with the LogManager.<clinit>.
Logger.getGlobal().setLogManager(manager);
- manager.addLogger(Logger.getGlobal());
+ manager.systemContext.addLogger(Logger.getGlobal());
// We don't call readConfiguration() here, as we may be running
// very early in the JVM startup sequence. Instead readConfiguration
@@ -281,14 +283,14 @@
return;
}
readPrimordialConfiguration = true;
+
try {
- AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- public Object run() throws Exception {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+ public Void run() throws Exception {
readConfiguration();
// Platform loggers begin to delegate to java.util.logging.Logger
sun.util.logging.PlatformLogger.redirectPlatformLoggers();
-
return null;
}
});
@@ -358,62 +360,290 @@
}
}
- // Package-level method.
- // 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.
+ // Returns the LoggerContext for the user code (i.e. application or AppContext).
+ // Loggers are isolated from each AppContext.
+ LoggerContext getUserContext() {
+ LoggerContext context = null;
- // 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) {
- Logger result = getLogger(name);
- if (result == null) {
- // only allocate the new logger once
- Logger newLogger = new Logger(name, null);
- do {
- if (addLogger(newLogger)) {
- // We successfully added the new Logger that we
- // created above so return it without refetching.
- return newLogger;
+ SecurityManager sm = System.getSecurityManager();
+ JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
+ if (sm != null && javaAwtAccess != null) {
+ synchronized (javaAwtAccess) {
+ // AppContext.getAppContext() returns the system AppContext if called
+ // from a system thread but Logger.getLogger might be called from
+ // an applet code. Instead, find the AppContext of the applet code
+ // from the execution stack.
+ Object ecx = javaAwtAccess.getExecutionContext();
+ if (ecx == null) {
+ // fall back to AppContext.getAppContext()
+ ecx = javaAwtAccess.getContext();
}
+ context = (LoggerContext)javaAwtAccess.get(ecx, LoggerContext.class);
+ if (context == null) {
+ if (javaAwtAccess.isMainAppContext()) {
+ context = userContext;
+ } else {
+ context = new UserLoggerContext();
+ context.addLogger(manager.rootLogger);
+ }
+ javaAwtAccess.put(ecx, LoggerContext.class, context);
+ }
+ }
+ } else {
+ context = userContext;
+ }
+ return context;
+ }
- // 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;
+ LoggerContext getSystemContext() {
+ return systemContext;
+ }
+
+ private List<LoggerContext> contexts() {
+ List<LoggerContext> cxs = new ArrayList<>();
+ cxs.add(systemContext);
+ cxs.add(getUserContext());
+ return cxs;
}
- // 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) {
- int ix = 1;
- for (;;) {
- int ix2 = name.indexOf(".", ix);
- if (ix2 < 0) {
- break;
+ static class LoggerContext {
+ // Table of named Loggers that maps names to Loggers.
+ private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
+ // Tree of named Loggers
+ private final LogNode root;
+
+ private LoggerContext() {
+ this.root = new LogNode(null, this);
+ }
+
+ synchronized Logger findLogger(String name) {
+ LoggerWeakRef ref = namedLoggers.get(name);
+ if (ref == null) {
+ return null;
+ }
+ Logger logger = ref.get();
+ if (logger == null) {
+ // Hashtable holds stale weak reference
+ // to a logger which has been GC-ed.
+ removeLogger(name);
+ }
+ return logger;
+ }
+
+ synchronized boolean addLogger(Logger logger) {
+ final String name = logger.getName();
+ if (name == null) {
+ throw new NullPointerException();
+ }
+
+ // cleanup some Loggers that have been GC'ed
+ manager.drainLoggerRefQueueBounded();
+
+ LoggerWeakRef ref = namedLoggers.get(name);
+ if (ref != null) {
+ if (ref.get() == null) {
+ // It's possible that the Logger was GC'ed after the
+ // drainLoggerRefQueueBounded() call above so allow
+ // a new one to be registered.
+ removeLogger(name);
+ } else {
+ // We already have a registered logger with the given name.
+ return false;
+ }
+ }
+
+ // We're adding a new logger.
+ // Note that we are creating a weak reference here.
+ ref = manager.new LoggerWeakRef(logger);
+ namedLoggers.put(name, ref);
+
+ // Apply any initial level defined for the new logger.
+ Level level = manager.getLevelProperty(name + ".level", null);
+ if (level != null) {
+ doSetLevel(logger, level);
+ }
+
+ // Do we have a per logger handler too?
+ // Note: this will add a 200ms penalty
+ manager.loadLoggerHandlers(logger, name, name + ".handlers");
+ processParentHandlers(logger, name);
+
+ // Find the new node and its parent.
+ LogNode node = getNode(name);
+ node.loggerRef = ref;
+ Logger parent = null;
+ LogNode nodep = node.parent;
+ while (nodep != null) {
+ LoggerWeakRef nodeRef = nodep.loggerRef;
+ if (nodeRef != null) {
+ parent = nodeRef.get();
+ if (parent != null) {
+ break;
+ }
+ }
+ nodep = nodep.parent;
+ }
+
+ if (parent != null) {
+ doSetParent(logger, parent);
}
- String pname = name.substring(0,ix2);
+ // Walk over the children and tell them we are their new parent.
+ node.walkAndSetParent(logger);
+ // new LogNode is ready so tell the LoggerWeakRef about it
+ ref.setNode(node);
+ return true;
+ }
+
+ void removeLogger(String name) {
+ namedLoggers.remove(name);
+ }
+
+ synchronized Enumeration<String> getLoggerNames() {
+ 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) {
+ int ix = 1;
+ for (;;) {
+ int ix2 = name.indexOf(".", ix);
+ if (ix2 < 0) {
+ 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);
+ }
+ ix = ix2+1;
+ }
+ }
- if (getProperty(pname+".level") != null ||
- getProperty(pname+".handlers") != null) {
- // This pname has a level/handlers definition.
- // Make sure it exists.
- demandLogger(pname);
+ // Gets a node in our tree of logger nodes.
+ // If necessary, create it.
+ LogNode getNode(String name) {
+ if (name == null || name.equals("")) {
+ return root;
+ }
+ LogNode node = root;
+ while (name.length() > 0) {
+ int ix = name.indexOf(".");
+ String head;
+ if (ix > 0) {
+ head = name.substring(0, ix);
+ name = name.substring(ix + 1);
+ } else {
+ head = name;
+ name = "";
+ }
+ if (node.children == null) {
+ node.children = new HashMap<>();
+ }
+ LogNode child = node.children.get(head);
+ if (child == null) {
+ child = new LogNode(node, this);
+ node.children.put(head, child);
+ }
+ node = child;
}
- ix = ix2+1;
+ return node;
+ }
+ }
+
+ 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.
+ */
+ 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);
+ 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;
}
}
@@ -438,16 +668,17 @@
try {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
Handler hdl = (Handler) clz.newInstance();
- try {
- // Check if there is a property defining the
- // this handler's level.
- String levs = getProperty(word + ".level");
- if (levs != null) {
- hdl.setLevel(Level.parse(levs));
+ // Check if there is a property defining the
+ // this handler's level.
+ String levs = getProperty(word + ".level");
+ if (levs != null) {
+ Level l = Level.findLevel(levs);
+ if (l != null) {
+ hdl.setLevel(l);
+ } else {
+ // Probably a bad level. Drop through.
+ System.err.println("Can't set level for " + word);
}
- } catch (Exception ex) {
- System.err.println("Can't set level for " + word);
- // Probably a bad level. Drop through.
}
// Add this Handler to the logger
logger.addHandler(hdl);
@@ -503,7 +734,7 @@
if (node != null) {
// if we have a LogNode, then we were a named Logger
// so clear namedLoggers weak ref to us
- manager.namedLoggers.remove(name);
+ node.context.removeLogger(name);
name = null; // clear our ref to the Logger's name
node.loggerRef = null; // clear LogNode's weak ref to us
@@ -592,70 +823,15 @@
* false if a logger of that name already exists.
* @exception NullPointerException if the logger name is null.
*/
- public synchronized boolean addLogger(Logger logger) {
+ public boolean addLogger(Logger logger) {
final String name = logger.getName();
if (name == null) {
throw new NullPointerException();
}
-
- // cleanup some Loggers that have been GC'ed
- drainLoggerRefQueueBounded();
-
- LoggerWeakRef ref = namedLoggers.get(name);
- if (ref != null) {
- if (ref.get() == null) {
- // It's possible that the Logger was GC'ed after the
- // drainLoggerRefQueueBounded() call above so allow
- // a new one to be registered.
- namedLoggers.remove(name);
- } else {
- // We already have a registered logger with the given name.
- return false;
- }
- }
-
- // We're adding a new logger.
- // Note that we are creating a weak reference here.
- ref = new LoggerWeakRef(logger);
- namedLoggers.put(name, ref);
-
- // Apply any initial level defined for the new logger.
- Level level = getLevelProperty(name+".level", null);
- if (level != null) {
- doSetLevel(logger, level);
+ if (systemContext.findLogger(name) != null) {
+ return false;
}
-
- // Do we have a per logger handler too?
- // Note: this will add a 200ms penalty
- loadLoggerHandlers(logger, name, name+".handlers");
- processParentHandlers(logger, name);
-
- // Find the new node and its parent.
- LogNode node = findNode(name);
- node.loggerRef = ref;
- Logger parent = null;
- LogNode nodep = node.parent;
- while (nodep != null) {
- LoggerWeakRef nodeRef = nodep.loggerRef;
- if (nodeRef != null) {
- parent = nodeRef.get();
- if (parent != null) {
- break;
- }
- }
- nodep = nodep.parent;
- }
-
- if (parent != null) {
- doSetParent(logger, parent);
- }
- // Walk over the children and tell them we are their new parent.
- node.walkAndSetParent(logger);
-
- // new LogNode is ready so tell the LoggerWeakRef about it
- ref.setNode(node);
-
- return true;
+ return getUserContext().addLogger(logger);
}
@@ -697,36 +873,6 @@
}});
}
- // Find a node in our tree of logger nodes.
- // If necessary, create it.
- private LogNode findNode(String name) {
- if (name == null || name.equals("")) {
- return root;
- }
- LogNode node = root;
- while (name.length() > 0) {
- int ix = name.indexOf(".");
- String head;
- if (ix > 0) {
- head = name.substring(0,ix);
- name = name.substring(ix+1);
- } else {
- head = name;
- name = "";
- }
- if (node.children == null) {
- node.children = new HashMap<>();
- }
- LogNode child = node.children.get(head);
- if (child == null) {
- child = new LogNode(node);
- node.children.put(head, child);
- }
- node = child;
- }
- return node;
- }
-
/**
* Method to find a named logger.
* <p>
@@ -742,18 +888,16 @@
* @param name name of the logger
* @return matching logger or null if none is found
*/
- public synchronized Logger getLogger(String name) {
- LoggerWeakRef ref = namedLoggers.get(name);
- if (ref == null) {
- return null;
- }
- Logger logger = ref.get();
- if (logger == null) {
- // Hashtable holds stale weak reference
- // to a logger which has been GC-ed.
- namedLoggers.remove(name);
- }
- return logger;
+ 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);
}
/**
@@ -772,8 +916,11 @@
* <p>
* @return enumeration of logger name strings
*/
- public synchronized Enumeration<String> getLoggerNames() {
- return namedLoggers.keys();
+ 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);
}
/**
@@ -858,20 +1005,20 @@
// the global handlers, if they haven't been initialized yet.
initializedGlobalHandlers = true;
}
- Enumeration<String> enum_ = getLoggerNames();
- while (enum_.hasMoreElements()) {
- String name = enum_.nextElement();
- resetLogger(name);
+ for (LoggerContext cx : contexts()) {
+ Enumeration<String> enum_ = cx.getLoggerNames();
+ while (enum_.hasMoreElements()) {
+ String name = enum_.nextElement();
+ Logger logger = cx.findLogger(name);
+ if (logger != null) {
+ resetLogger(logger);
+ }
+ }
}
}
-
// Private method to reset an individual target logger.
- private void resetLogger(String name) {
- Logger logger = getLogger(name);
- if (logger == null) {
- return;
- }
+ private void resetLogger(Logger logger) {
// Close all the Logger's handlers.
Handler[] targets = logger.getHandlers();
for (int i = 0; i < targets.length; i++) {
@@ -883,6 +1030,7 @@
// Problems closing a handler? Keep going...
}
}
+ String name = logger.getName();
if (name != null && name.equals("")) {
// This is the root logger.
logger.setLevel(defaultLevel);
@@ -1046,11 +1194,8 @@
if (val == null) {
return defaultValue;
}
- try {
- return Level.parse(val.trim());
- } catch (Exception ex) {
- return defaultValue;
- }
+ Level l = Level.findLevel(val.trim());
+ return l != null ? l : defaultValue;
}
// Package private method to get a filter property.
@@ -1140,9 +1285,11 @@
HashMap<String,LogNode> children;
LoggerWeakRef loggerRef;
LogNode parent;
+ final LoggerContext context;
- LogNode(LogNode parent) {
+ LogNode(LogNode parent, LoggerContext context) {
this.parent = parent;
+ this.context = context;
}
// Recursive method to walk the tree below a node and set
@@ -1215,11 +1362,13 @@
System.err.println("Bad level value for property: " + key);
continue;
}
- Logger l = getLogger(name);
- if (l == null) {
- continue;
+ for (LoggerContext cx : contexts()) {
+ Logger l = cx.findLogger(name);
+ if (l == null) {
+ continue;
+ }
+ l.setLevel(level);
}
- l.setLevel(level);
}
}
--- a/jdk/src/share/classes/java/util/logging/Logger.java Mon Nov 26 20:49:54 2012 +0400
+++ b/jdk/src/share/classes/java/util/logging/Logger.java Mon Nov 26 22:49:06 2012 -0800
@@ -30,6 +30,7 @@
import java.util.concurrent.CopyOnWriteArrayList;
import java.security.*;
import java.lang.ref.WeakReference;
+import java.util.logging.LogManager.LoggerContext;
/**
* A Logger object is used to log messages for a specific
@@ -286,6 +287,26 @@
}
}
+ // Until all JDK code converted to call sun.util.logging.PlatformLogger
+ // (see 7054233), we need to determine if Logger.getLogger is to add
+ // a system logger or user logger.
+ //
+ // 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() {
+ LogManager manager = LogManager.getLogManager();
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ // 0: Reflection 1: Logger.getLoggerContext 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.getUserContext();
+ }
+
/**
* Find or create a logger for a named subsystem. If a logger has
* already been created with the given name it is returned. Otherwise
@@ -327,8 +348,8 @@
// would throw an IllegalArgumentException in the second call
// because the wrapper would result in an attempt to replace
// the existing "resourceBundleForFoo" with null.
- LogManager manager = LogManager.getLogManager();
- return manager.demandLogger(name);
+ LoggerContext context = getLoggerContext();
+ return context.demandLogger(name);
}
/**
@@ -375,8 +396,8 @@
// 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) {
- LogManager manager = LogManager.getLogManager();
- Logger result = manager.demandLogger(name);
+ LoggerContext context = getLoggerContext();
+ Logger result = context.demandLogger(name, resourceBundleName);
// MissingResourceException or IllegalArgumentException can be
// thrown by setupResourceInfo().
@@ -384,6 +405,18 @@
return result;
}
+ // package-private
+ // Add a platform logger to the system context.
+ // 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);
+ return result;
+ }
/**
* Create an anonymous Logger. The newly created Logger is not
@@ -536,7 +569,7 @@
private void doLog(LogRecord lr) {
lr.setLoggerName(name);
String ebname = getEffectiveResourceBundleName();
- if (ebname != null) {
+ if (ebname != null && !ebname.equals(SYSTEM_LOGGER_RB_NAME)) {
lr.setResourceBundleName(ebname);
lr.setResourceBundle(findResourceBundle(ebname));
}
@@ -1285,6 +1318,22 @@
// May also return null if we can't find the resource bundle and
// there is no suitable previous cached value.
+ static final String SYSTEM_LOGGER_RB_NAME = "sun.util.logging.resources.logging";
+
+ private static ResourceBundle findSystemResourceBundle(final Locale locale) {
+ // the resource bundle is in a restricted package
+ return AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() {
+ public ResourceBundle run() {
+ try {
+ return ResourceBundle.getBundle(SYSTEM_LOGGER_RB_NAME,
+ locale);
+ } catch (MissingResourceException e) {
+ throw new InternalError(e.toString());
+ }
+ }
+ });
+ }
+
private synchronized ResourceBundle findResourceBundle(String name) {
// Return a null bundle for a null name.
if (name == null) {
@@ -1299,6 +1348,13 @@
return catalog;
}
+ if (name.equals(SYSTEM_LOGGER_RB_NAME)) {
+ catalog = findSystemResourceBundle(currentLocale);
+ catalogName = name;
+ catalogLocale = currentLocale;
+ return catalog;
+ }
+
// Use the thread's context ClassLoader. If there isn't one,
// use the SystemClassloader.
ClassLoader cl = Thread.currentThread().getContextClassLoader();
@@ -1315,7 +1371,6 @@
// ClassLoader. Drop through.
}
-
// Fall back to searching up the call stack and trying each
// calling ClassLoader.
for (int ix = 0; ; ix++) {
--- a/jdk/src/share/classes/java/util/logging/Logging.java Mon Nov 26 20:49:54 2012 +0400
+++ b/jdk/src/share/classes/java/util/logging/Logging.java Mon Nov 26 22:49:06 2012 -0800
@@ -34,7 +34,7 @@
*
* The <tt>LoggingMXBean</tt> interface provides a standard
* method for management access to the individual
- * java.util.Logger objects available at runtime.
+ * {@code Logger} objects available at runtime.
*
* @author Ron Mann
* @author Mandy Chung
@@ -75,7 +75,7 @@
if (level == null) {
return EMPTY_STRING;
} else {
- return level.getName();
+ return level.getLevelName();
}
}
@@ -85,7 +85,6 @@
}
Logger logger = logManager.getLogger(loggerName);
-
if (logger == null) {
throw new IllegalArgumentException("Logger " + loggerName +
"does not exist");
@@ -94,7 +93,10 @@
Level level = null;
if (levelName != null) {
// parse will throw IAE if logLevel is invalid
- level = Level.parse(levelName);
+ level = Level.findLevel(levelName);
+ if (level == null) {
+ throw new IllegalArgumentException("Unknown level \"" + levelName + "\"");
+ }
}
logger.setLevel(level);
--- a/jdk/src/share/classes/java/util/logging/LoggingProxyImpl.java Mon Nov 26 20:49:54 2012 +0400
+++ b/jdk/src/share/classes/java/util/logging/LoggingProxyImpl.java Mon Nov 26 22:49:06 2012 -0800
@@ -37,7 +37,8 @@
@Override
public Object getLogger(String name) {
- return Logger.getLogger(name);
+ // always create a platform logger with the resource bundle name
+ return Logger.getPlatformLogger(name);
}
@Override
@@ -92,12 +93,16 @@
@Override
public Object parseLevel(String levelName) {
- return Level.parse(levelName);
+ Level level = Level.findLevel(levelName);
+ if (level == null) {
+ throw new IllegalArgumentException("Unknown level \"" + levelName + "\"");
+ }
+ return level;
}
@Override
public String getLevelName(Object level) {
- return ((Level) level).getName();
+ return ((Level) level).getLevelName();
}
@Override
--- a/jdk/src/share/classes/java/util/logging/SimpleFormatter.java Mon Nov 26 20:49:54 2012 +0400
+++ b/jdk/src/share/classes/java/util/logging/SimpleFormatter.java Mon Nov 26 22:49:06 2012 -0800
@@ -162,7 +162,7 @@
dat,
source,
record.getLoggerName(),
- record.getLevel().getLocalizedName(),
+ record.getLevel().getLocalizedLevelName(),
message,
throwable);
}
--- a/jdk/src/share/classes/sun/awt/AppContext.java Mon Nov 26 20:49:54 2012 +0400
+++ b/jdk/src/share/classes/sun/awt/AppContext.java Mon Nov 26 22:49:06 2012 -0800
@@ -327,21 +327,27 @@
// Before we return the main "system" AppContext, check to
// see if there's an AWTSecurityManager installed. If so,
// allow it to choose the AppContext to return.
- SecurityManager securityManager = System.getSecurityManager();
- if ((securityManager != null) &&
- (securityManager instanceof AWTSecurityManager))
- {
- AWTSecurityManager awtSecMgr = (AWTSecurityManager)securityManager;
- AppContext secAppContext = awtSecMgr.getAppContext();
- if (secAppContext != null) {
- appContext = secAppContext; // Return what we're told
- }
+ AppContext secAppContext = getExecutionAppContext();
+ if (secAppContext != null) {
+ appContext = secAppContext; // Return what we're told
}
}
return appContext;
}
+ private final static AppContext getExecutionAppContext() {
+ SecurityManager securityManager = System.getSecurityManager();
+ if ((securityManager != null) &&
+ (securityManager instanceof AWTSecurityManager))
+ {
+ AWTSecurityManager awtSecMgr = (AWTSecurityManager) securityManager;
+ AppContext secAppContext = awtSecMgr.getAppContext();
+ return secAppContext; // Return what we're told
+ }
+ return null;
+ }
+
/**
* Returns the main ("system") AppContext.
*
@@ -806,6 +812,21 @@
public boolean isMainAppContext() {
return (numAppContexts.get() == 1);
}
+ public Object getContext() {
+ return getAppContext();
+ }
+ public Object getExecutionContext() {
+ return getExecutionAppContext();
+ }
+ public Object get(Object context, Object key) {
+ return ((AppContext)context).get(key);
+ }
+ public void put(Object context, Object key, Object value) {
+ ((AppContext)context).put(key, value);
+ }
+ public void remove(Object context, Object key) {
+ ((AppContext)context).remove(key);
+ }
});
}
}
--- a/jdk/src/share/classes/sun/misc/JavaAWTAccess.java Mon Nov 26 20:49:54 2012 +0400
+++ b/jdk/src/share/classes/sun/misc/JavaAWTAccess.java Mon Nov 26 22:49:06 2012 -0800
@@ -26,6 +26,14 @@
package sun.misc;
public interface JavaAWTAccess {
+ public Object getContext();
+ public Object getExecutionContext();
+
+ public Object get(Object context, Object key);
+ public void put(Object context, Object key, Object value);
+ public void remove(Object context, Object key);
+
+ // convenience methods whose context is the object returned by getContext()
public Object get(Object key);
public void put(Object key, Object value);
public void remove(Object key);
--- a/jdk/src/share/lib/security/java.security Mon Nov 26 20:49:54 2012 +0400
+++ b/jdk/src/share/lib/security/java.security Mon Nov 26 22:49:06 2012 -0800
@@ -148,6 +148,9 @@
package.access=sun.,\
com.sun.xml.internal.,\
com.sun.imageio.,\
+ com.sun.istack.internal.,\
+ com.sun.jmx.default.,\
+ com.sun.jmx.remote.util.,\
com.sun.org.apache.xerces.internal.utils.,\
com.sun.org.apache.xalan.internal.utils.,\
com.sun.org.glassfish.external.,\
@@ -166,6 +169,9 @@
package.definition=sun.,\
com.sun.xml.internal.,\
com.sun.imageio.,\
+ com.sun.istack.internal.,\
+ com.sun.jmx.default.,\
+ com.sun.jmx.remote.util.,\
com.sun.org.apache.xerces.internal.utils.,\
com.sun.org.apache.xalan.internal.utils.,\
com.sun.org.glassfish.external.,\