6664509: Add logging context
authormchung
Mon, 26 Nov 2012 22:49:06 -0800
changeset 16098 9001e536ab4e
parent 16097 6277cf02aeac
child 16099 fb978a77b3e7
6664509: Add logging context 6664528: Find log level matching its name or value given at construction time Reviewed-by: alanb, ahgross, jgish, hawtin
jdk/src/share/classes/java/util/logging/Level.java
jdk/src/share/classes/java/util/logging/LogManager.java
jdk/src/share/classes/java/util/logging/Logger.java
jdk/src/share/classes/java/util/logging/Logging.java
jdk/src/share/classes/java/util/logging/LoggingProxyImpl.java
jdk/src/share/classes/java/util/logging/SimpleFormatter.java
jdk/src/share/classes/sun/awt/AppContext.java
jdk/src/share/classes/sun/misc/JavaAWTAccess.java
jdk/src/share/lib/security/java.security
--- 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.,\