6882376: Add internal support for JRE implementation to eliminate the dependency on logging
authormchung
Thu, 17 Sep 2009 14:24:55 -0700
changeset 3861 a98a057ec335
parent 3854 b2a90c48e69f
child 3862 60a2c695ffb3
6882376: Add internal support for JRE implementation to eliminate the dependency on logging Summary: Added sun.util.logging.PlatformLogger for JRE implementation to log messages. Reviewed-by: alanb, naoto
jdk/make/java/logging/Makefile
jdk/src/share/classes/java/util/Currency.java
jdk/src/share/classes/java/util/jar/Attributes.java
jdk/src/share/classes/java/util/logging/LogManager.java
jdk/src/share/classes/java/util/logging/LogRecord.java
jdk/src/share/classes/sun/util/LocaleServiceProviderPool.java
jdk/src/share/classes/sun/util/logging/PlatformLogger.java
jdk/src/windows/classes/java/util/prefs/WindowsPreferences.java
jdk/test/sun/util/logging/PlatformLoggerTest.java
--- a/jdk/make/java/logging/Makefile	Wed Sep 16 09:23:50 2009 -0700
+++ b/jdk/make/java/logging/Makefile	Thu Sep 17 14:24:55 2009 -0700
@@ -31,7 +31,7 @@
 #
 # Files to compile.
 #
-AUTO_FILES_JAVA_DIRS = java/util/logging
+AUTO_FILES_JAVA_DIRS = java/util/logging sun/util/logging
 
 #
 # Resources
@@ -46,7 +46,6 @@
 include $(BUILDDIR)/common/Classes.gmk
 
 properties: $(LIBDIR)/logging.properties
-
 $(LIBDIR)/logging.properties: $(SHARE_SRC)/lib/logging.properties
 	$(install-file)
 
--- a/jdk/src/share/classes/java/util/Currency.java	Wed Sep 16 09:23:50 2009 -0700
+++ b/jdk/src/share/classes/java/util/Currency.java	Thu Sep 17 14:24:55 2009 -0700
@@ -35,12 +35,12 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.logging.Level;
-import java.util.logging.Logger;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 import java.util.spi.CurrencyNameProvider;
 import java.util.spi.LocaleServiceProvider;
 import sun.util.LocaleServiceProviderPool;
+import sun.util.logging.PlatformLogger;
 import sun.util.resources.LocaleData;
 import sun.util.resources.OpenListResourceBundle;
 
@@ -244,7 +244,7 @@
                         }
                     }
                 } catch (IOException e) {
-                    log(Level.INFO, "currency.properties is ignored because of an IOException", e);
+                    info("currency.properties is ignored because of an IOException", e);
                 }
                 return null;
             }
@@ -686,7 +686,7 @@
                 .append("The entry in currency.properties for ")
                 .append(ctry).append(" is ignored because of the invalid country code.")
                 .toString();
-            log(Level.INFO, message, null);
+            info(message, null);
             return;
         }
 
@@ -698,7 +698,7 @@
                 .append(ctry)
                 .append(" is ignored because the value format is not recognized.")
                 .toString();
-            log(Level.INFO, message, null);
+            info(message, null);
             return;
         }
 
@@ -726,13 +726,13 @@
         setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry);
     }
 
-    private static void log(Level level, String message, Throwable t) {
-        Logger logger = Logger.getLogger("java.util.Currency");
-        if (logger.isLoggable(level)) {
+    private static void info(String message, Throwable t) {
+        PlatformLogger logger = PlatformLogger.getLogger("java.util.Currency");
+        if (logger.isLoggable(PlatformLogger.INFO)) {
             if (t != null) {
-                logger.log(level, message, t);
+                logger.info(message, t);
             } else {
-                logger.log(level, message);
+                logger.info(message);
             }
         }
     }
--- a/jdk/src/share/classes/java/util/jar/Attributes.java	Wed Sep 16 09:23:50 2009 -0700
+++ b/jdk/src/share/classes/java/util/jar/Attributes.java	Thu Sep 17 14:24:55 2009 -0700
@@ -34,7 +34,7 @@
 import java.util.Collection;
 import java.util.AbstractSet;
 import java.util.Iterator;
-import java.util.logging.Logger;
+import sun.util.logging.PlatformLogger;
 import java.util.Comparator;
 import sun.misc.ASCIICaseInsensitiveComparator;
 
@@ -419,7 +419,7 @@
             }
             try {
                 if ((putValue(name, value) != null) && (!lineContinued)) {
-                    Logger.getLogger("java.util.jar").warning(
+                    PlatformLogger.getLogger("java.util.jar").warning(
                                      "Duplicate name in Manifest: " + name
                                      + ".\n"
                                      + "Ensure that the manifest does not "
--- a/jdk/src/share/classes/java/util/logging/LogManager.java	Wed Sep 16 09:23:50 2009 -0700
+++ b/jdk/src/share/classes/java/util/logging/LogManager.java	Thu Sep 17 14:24:55 2009 -0700
@@ -283,6 +283,10 @@
                         AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                                 public Object run() throws Exception {
                                     readConfiguration();
+
+                                    // Platform loggers begin to delegate to java.util.logging.Logger
+                                    sun.util.logging.PlatformLogger.redirectPlatformLoggers();
+
                                     return null;
                                 }
                             });
--- a/jdk/src/share/classes/java/util/logging/LogRecord.java	Wed Sep 16 09:23:50 2009 -0700
+++ b/jdk/src/share/classes/java/util/logging/LogRecord.java	Thu Sep 17 14:24:55 2009 -0700
@@ -530,6 +530,7 @@
         int depth = access.getStackTraceDepth(throwable);
 
         String logClassName = "java.util.logging.Logger";
+        String plogClassName = "sun.util.logging.PlatformLogger";
         boolean lookingForLogger = true;
         for (int ix = 0; ix < depth; ix++) {
             // Calling getStackTraceElement directly prevents the VM
@@ -539,15 +540,18 @@
             String cname = frame.getClassName();
             if (lookingForLogger) {
                 // Skip all frames until we have found the first logger frame.
-                if (cname.equals(logClassName)) {
+                if (cname.equals(logClassName) || cname.startsWith(plogClassName)) {
                     lookingForLogger = false;
                 }
             } else {
-                if (!cname.equals(logClassName)) {
-                    // We've found the relevant frame.
-                    setSourceClassName(cname);
-                    setSourceMethodName(frame.getMethodName());
-                    return;
+                if (!cname.equals(logClassName) && !cname.startsWith(plogClassName)) {
+                    // skip reflection call
+                    if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
+                       // We've found the relevant frame.
+                       setSourceClassName(cname);
+                       setSourceMethodName(frame.getMethodName());
+                       return;
+                    }
                 }
             }
         }
--- a/jdk/src/share/classes/sun/util/LocaleServiceProviderPool.java	Wed Sep 16 09:23:50 2009 -0700
+++ b/jdk/src/share/classes/sun/util/LocaleServiceProviderPool.java	Thu Sep 17 14:24:55 2009 -0700
@@ -39,8 +39,8 @@
 import java.util.ServiceConfigurationError;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Logger;
 import java.util.spi.LocaleServiceProvider;
+import sun.util.logging.PlatformLogger;
 import sun.util.resources.LocaleData;
 import sun.util.resources.OpenListResourceBundle;
 
@@ -122,10 +122,15 @@
                 }
             });
         }  catch (PrivilegedActionException e) {
-            Logger.getLogger("sun.util.LocaleServiceProviderPool").config(e.toString());
+            config(e.toString());
         }
     }
 
+    private static void config(String message) {
+        PlatformLogger logger = PlatformLogger.getLogger("sun.util.LocaleServiceProviderPool");
+        logger.config(message);
+    }
+
     /**
      * Lazy loaded set of available locales.
      * Loading all locales is a very long operation.
@@ -337,7 +342,7 @@
                     if (providersObj != null) {
                         return providersObj;
                     } else if (isObjectProvider) {
-                        Logger.getLogger("sun.util.LocaleServiceProviderPool").config(
+                        config(
                             "A locale sensitive service provider returned null for a localized objects,  which should not happen.  provider: " + lsp + " locale: " + requested);
                     }
                 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/util/logging/PlatformLogger.java	Thu Sep 17 14:24:55 2009 -0700
@@ -0,0 +1,629 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+
+package sun.util.logging;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.io.File;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.MessageFormat;
+import java.util.logging.Logger;
+import java.util.*;
+import sun.misc.JavaLangAccess;
+import sun.misc.SharedSecrets;
+
+/**
+ * Platform logger provides an API for the JRE components to log
+ * messages.  This enables the runtime components to eliminate the
+ * static dependency of the logging facility and also defers the
+ * java.util.logging initialization until it is enabled.
+ * In addition, the PlatformLogger API can be used if the logging
+ * module does not exist.
+ *
+ * If the logging facility is not enabled, the platform loggers
+ * will output log messages per the default logging configuration
+ * (see below). In this implementation, it does not log the
+ * the stack frame information issuing the log message.
+ *
+ * When the logging facility is enabled (at startup or runtime),
+ * the java.util.logging.Logger will be created for each platform
+ * logger and all log messages will be forwarded to the Logger
+ * to handle.
+ *
+ * Logging facility is "enabled" when one of the following
+ * conditions is met:
+ * 1) a system property "java.util.logging.config.class" or
+ *    "java.util.logging.config.file" is set
+ * 2) java.util.logging.LogManager or java.util.logging.Logger
+ *    is referenced that will trigger the logging initialization.
+ *
+ * Default logging configuration:
+ *   global logging level = INFO
+ *   handlers = java.util.logging.ConsoleHandler
+ *   java.util.logging.ConsoleHandler.level = INFO
+ *   java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+ *
+ * Limitation:
+ * <JAVA_HOME>/lib/logging.properties is the system-wide logging
+ * configuration defined in the specification and read in the
+ * default case to configure any java.util.logging.Logger instances.
+ * Platform loggers will not detect if <JAVA_HOME>/lib/logging.properties
+ * is modified. In other words, unless the java.util.logging API
+ * is used at runtime or the logging system properties is set,
+ * the platform loggers will use the default setting described above.
+ * The platform loggers are designed for JDK developers use and
+ * this limitation can be workaround with setting
+ * -Djava.util.logging.config.file system property.
+ *
+ * @since 1.7
+ */
+public class PlatformLogger {
+    // Same values as java.util.logging.Level for easy mapping
+    public static final int OFF     = Integer.MAX_VALUE;
+    public static final int SEVERE  = 1000;
+    public static final int WARNING = 900;
+    public static final int INFO    = 800;
+    public static final int CONFIG  = 700;
+    public static final int FINE    = 500;
+    public static final int FINER   = 400;
+    public static final int FINEST  = 300;
+    public static final int ALL     = Integer.MIN_VALUE;
+
+    private static final int defaultLevel = INFO;
+    private static boolean loggingEnabled;
+    static {
+        loggingEnabled = AccessController.doPrivileged(
+            new PrivilegedAction<Boolean>() {
+                public Boolean run() {
+                    String cname = System.getProperty("java.util.logging.config.class");
+                    String fname = System.getProperty("java.util.logging.config.file");
+                    return (cname != null || fname != null);
+                }
+            });
+    }
+
+    // Table of known loggers.  Maps names to PlatformLoggers.
+    private static Map<String,WeakReference<PlatformLogger>> loggers =
+        new HashMap<String,WeakReference<PlatformLogger>>();
+
+    /**
+     * Returns a PlatformLogger of a given name.
+     */
+    public static synchronized PlatformLogger getLogger(String name) {
+        PlatformLogger log = null;
+        WeakReference<PlatformLogger> ref = loggers.get(name);
+        if (ref != null) {
+            log = ref.get();
+        }
+        if (log == null) {
+            log = new PlatformLogger(name);
+            loggers.put(name, new WeakReference<PlatformLogger>(log));
+        }
+        return log;
+    }
+
+    /**
+     * Initialize java.util.logging.Logger objects for all platform loggers.
+     * This method is called from LogManager.readPrimordialConfiguration().
+     */
+    public static synchronized void redirectPlatformLoggers() {
+        if (loggingEnabled || !JavaLogger.supported) return;
+
+        loggingEnabled = true;
+        for (Map.Entry<String, WeakReference<PlatformLogger>> entry : loggers.entrySet()) {
+            WeakReference<PlatformLogger> ref = entry.getValue();
+            PlatformLogger plog = ref.get();
+            if (plog != null) {
+                plog.newJavaLogger();
+            }
+        }
+    }
+
+    /**
+     * Creates a new JavaLogger that the platform logger uses
+     */
+    private void newJavaLogger() {
+        logger = new JavaLogger(logger.name, logger.effectiveLevel);
+    }
+
+    // logger may be replaced with a JavaLogger object
+    // when the logging facility is enabled
+    private volatile LoggerProxy logger;
+
+    private PlatformLogger(String name) {
+        if (loggingEnabled) {
+            this.logger = new JavaLogger(name);
+        } else {
+            this.logger = new LoggerProxy(name);
+        }
+    }
+
+    /**
+     * A convenience method to test if the logger is turned off.
+     * (i.e. its level is OFF).
+     */
+    public boolean isEnabled() {
+        return logger.isEnabled();
+    }
+
+    /**
+     * Gets the name for this platform logger.
+     */
+    public String getName() {
+        return logger.name;
+    }
+
+    /**
+     * Returns true if a message of the given level would actually
+     * be logged by this logger.
+     */
+    public boolean isLoggable(int level) {
+        return logger.isLoggable(level);
+    }
+
+    /**
+     * Gets the current log level.  Returns 0 if the current effective level
+     * is not set (equivalent to Logger.getLevel() returns null).
+     */
+    public int getLevel() {
+        return logger.getLevel();
+    }
+
+    /**
+     * Sets the log level.
+     */
+    public void setLevel(int newLevel) {
+        logger.setLevel(newLevel);
+    }
+
+    /**
+     * Logs a SEVERE message.
+     */
+    public void severe(String msg) {
+        logger.doLog(SEVERE, msg);
+    }
+
+    public void severe(String msg, Throwable t) {
+        logger.doLog(SEVERE, msg, t);
+    }
+
+    public void severe(String msg, Object... params) {
+        logger.doLog(SEVERE, msg, params);
+    }
+
+    /**
+     * Logs a WARNING message.
+     */
+    public void warning(String msg) {
+        logger.doLog(WARNING, msg);
+    }
+
+    public void warning(String msg, Throwable t) {
+        logger.doLog(WARNING, msg, t);
+    }
+
+    public void warning(String msg, Object... params) {
+        logger.doLog(WARNING, msg, params);
+    }
+
+    /**
+     * Logs an INFO message.
+     */
+    public void info(String msg) {
+        logger.doLog(INFO, msg);
+    }
+
+    public void info(String msg, Throwable t) {
+        logger.doLog(INFO, msg, t);
+    }
+
+    public void info(String msg, Object... params) {
+        logger.doLog(INFO, msg, params);
+    }
+
+    /**
+     * Logs a CONFIG message.
+     */
+    public void config(String msg) {
+        logger.doLog(CONFIG, msg);
+    }
+
+    public void config(String msg, Throwable t) {
+        logger.doLog(CONFIG, msg, t);
+    }
+
+    public void config(String msg, Object... params) {
+        logger.doLog(CONFIG, msg, params);
+    }
+
+    /**
+     * Logs a FINE message.
+     */
+    public void fine(String msg) {
+        logger.doLog(FINE, msg);
+    }
+
+    public void fine(String msg, Throwable t) {
+        logger.doLog(FINE, msg, t);
+    }
+
+    public void fine(String msg, Object... params) {
+        logger.doLog(FINE, msg, params);
+    }
+
+    /**
+     * Logs a FINER message.
+     */
+    public void finer(String msg) {
+        logger.doLog(FINER, msg);
+    }
+
+    public void finer(String msg, Throwable t) {
+        logger.doLog(FINER, msg, t);
+    }
+
+    public void finer(String msg, Object... params) {
+        logger.doLog(FINER, msg, params);
+    }
+
+    /**
+     * Logs a FINEST message.
+     */
+    public void finest(String msg) {
+        logger.doLog(FINEST, msg);
+    }
+
+    public void finest(String msg, Throwable t) {
+        logger.doLog(FINEST, msg, t);
+    }
+
+    public void finest(String msg, Object... params) {
+        logger.doLog(FINEST, msg, params);
+    }
+
+    /**
+     * Default platform logging support - output messages to
+     * System.err - equivalent to ConsoleHandler with SimpleFormatter.
+     */
+    static class LoggerProxy {
+        private static final PrintStream defaultStream = System.err;
+        private static final String lineSeparator = AccessController.doPrivileged(
+            new PrivilegedAction<String>() {
+                public String run() {
+                    return System.getProperty("line.separator");
+                }
+            });
+
+        final String name;
+        volatile int levelValue;
+        volatile int effectiveLevel = 0; // current effective level value
+
+        LoggerProxy(String name) {
+            this(name, defaultLevel);
+        }
+
+        LoggerProxy(String name, int level) {
+            this.name = name;
+            this.levelValue = level == 0 ? defaultLevel : level;
+        }
+
+        boolean isEnabled() {
+            return levelValue != OFF;
+        }
+
+        int getLevel() {
+            return effectiveLevel;
+        }
+
+        void setLevel(int newLevel) {
+            levelValue = newLevel;
+            effectiveLevel = newLevel;
+        }
+
+        void doLog(int level, String msg) {
+            if (level < levelValue || levelValue == OFF) {
+                return;
+            }
+            defaultStream.println(format(level, msg, null));
+        }
+
+        void doLog(int level, String msg, Throwable thrown) {
+            if (level < levelValue || levelValue == OFF) {
+                return;
+            }
+            defaultStream.println(format(level, msg, thrown));
+        }
+
+        void doLog(int level, String msg, Object... params) {
+            if (level < levelValue || levelValue == OFF) {
+                return;
+            }
+            String newMsg = formatMessage(msg, params);
+            defaultStream.println(format(level, newMsg, null));
+        }
+
+        public boolean isLoggable(int level) {
+            if (level < levelValue || levelValue == OFF) {
+                return false;
+            }
+            return true;
+        }
+
+        private static final String format = "{0,date} {0,time}";
+
+        private Object args[] = new Object[1];
+        private MessageFormat formatter;
+        private Date dat;
+
+        // Copied from java.util.logging.Formatter.formatMessage
+        private String formatMessage(String format, Object... parameters) {
+            // Do the formatting.
+            try {
+                if (parameters == null || parameters.length == 0) {
+                    // No parameters.  Just return format string.
+                    return format;
+                }
+                // Is it a java.text style format?
+                // Ideally we could match with
+                // Pattern.compile("\\{\\d").matcher(format).find())
+                // However the cost is 14% higher, so we cheaply check for
+                // 1 of the first 4 parameters
+                if (format.indexOf("{0") >= 0 || format.indexOf("{1") >=0 ||
+                            format.indexOf("{2") >=0|| format.indexOf("{3") >=0) {
+                    return java.text.MessageFormat.format(format, parameters);
+                }
+                return format;
+            } catch (Exception ex) {
+                // Formatting failed: use format string.
+                return format;
+            }
+        }
+
+        private synchronized String format(int level, String msg, Throwable thrown) {
+            StringBuffer sb = new StringBuffer();
+            // Minimize memory allocations here.
+            if (dat == null) {
+                dat = new Date();
+                formatter = new MessageFormat(format);
+            }
+            dat.setTime(System.currentTimeMillis());
+            args[0] = dat;
+            StringBuffer text = new StringBuffer();
+            formatter.format(args, text, null);
+            sb.append(text);
+            sb.append(" ");
+            sb.append(getCallerInfo());
+            sb.append(lineSeparator);
+            sb.append(PlatformLogger.getLevelName(level));
+            sb.append(": ");
+            sb.append(msg);
+            if (thrown != null) {
+                try {
+                    StringWriter sw = new StringWriter();
+                    PrintWriter pw = new PrintWriter(sw);
+                    thrown.printStackTrace(pw);
+                    pw.close();
+                    sb.append(sw.toString());
+                } catch (Exception ex) {
+                    throw new AssertionError(ex);
+                }
+            }
+
+            return sb.toString();
+        }
+
+        // Returns the caller's class and method's name; best effort
+        // if cannot infer, return the logger's name.
+        private String getCallerInfo() {
+            String sourceClassName = null;
+            String sourceMethodName = null;
+
+            JavaLangAccess access = SharedSecrets.getJavaLangAccess();
+            Throwable throwable = new Throwable();
+            int depth = access.getStackTraceDepth(throwable);
+
+            String logClassName = "sun.util.logging.PlatformLogger";
+            boolean lookingForLogger = true;
+            for (int ix = 0; ix < depth; ix++) {
+                // Calling getStackTraceElement directly prevents the VM
+                // from paying the cost of building the entire stack frame.
+                StackTraceElement frame =
+                    access.getStackTraceElement(throwable, ix);
+                String cname = frame.getClassName();
+                if (lookingForLogger) {
+                    // Skip all frames until we have found the first logger frame.
+                    if (cname.equals(logClassName)) {
+                        lookingForLogger = false;
+                    }
+                } else {
+                    if (!cname.equals(logClassName)) {
+                        // We've found the relevant frame.
+                        sourceClassName = cname;
+                        sourceMethodName = frame.getMethodName();
+                        break;
+                    }
+                }
+            }
+
+            if (sourceClassName != null) {
+                return sourceClassName + " " + sourceMethodName;
+            } else {
+                return name;
+            }
+        }
+    }
+
+    /**
+     * JavaLogger forwards all the calls to its corresponding
+     * java.util.logging.Logger object.
+     */
+    static class JavaLogger extends LoggerProxy {
+        private static final boolean supported;
+        private static final Class<?> loggerClass;
+        private static final Class<?> levelClass;
+        private static final Method getLoggerMethod;
+        private static final Method setLevelMethod;
+        private static final Method getLevelMethod;
+        private static final Method logMethod;
+        private static final Method logThrowMethod;
+        private static final Method logParamsMethod;
+        private static final Map<Integer, Object> levelObjects =
+            new HashMap<Integer, Object>();
+
+        static {
+            loggerClass = getClass("java.util.logging.Logger");
+            levelClass = getClass("java.util.logging.Level");
+            getLoggerMethod = getMethod(loggerClass, "getLogger", String.class);
+            setLevelMethod = getMethod(loggerClass, "setLevel", levelClass);
+            getLevelMethod = getMethod(loggerClass, "getLevel");
+            logMethod = getMethod(loggerClass, "log", levelClass, String.class);
+            logThrowMethod = getMethod(loggerClass, "log", levelClass, String.class, Throwable.class);
+            logParamsMethod = getMethod(loggerClass, "log", levelClass, String.class, Object[].class);
+            supported = (loggerClass != null && levelClass != null && getLoggerMethod != null &&
+                         getLevelMethod != null && setLevelMethod != null &&
+                         logMethod != null && logThrowMethod != null && logParamsMethod != null);
+            if (supported) {
+                // initialize the map to Level objects
+                getLevelObjects();
+            }
+        }
+
+        private static Class<?> getClass(String name) {
+            try {
+                return Class.forName(name, true, null);
+            } catch (ClassNotFoundException e) {
+                return null;
+            }
+        }
+
+        private static Method getMethod(Class<?> cls, String name, Class<?>... parameterTypes) {
+            if (cls == null) return null;
+
+            try {
+                return cls.getMethod(name, parameterTypes);
+            } catch (NoSuchMethodException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        private static Object invoke(Method m, Object obj, Object... params) {
+            try {
+                return m.invoke(obj, params);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError(e);
+            } catch (InvocationTargetException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        private static void getLevelObjects() {
+            // get all java.util.logging.Level objects
+            Method parseLevelMethod = getMethod(levelClass, "parse", String.class);
+            int[] levelArray = new int[] {OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL};
+            for (int l : levelArray) {
+                Object o = invoke(parseLevelMethod, null, getLevelName(l));
+                levelObjects.put(l, o);
+            }
+        }
+
+        private final Object javaLogger;
+        JavaLogger(String name) {
+            this(name, 0);
+        }
+
+        JavaLogger(String name, int level) {
+            super(name, level);
+            this.javaLogger = invoke(getLoggerMethod, null, name);
+            if (level != 0) {
+                // level has been updated and so set the Logger's level
+                invoke(setLevelMethod, javaLogger, levelObjects.get(level));
+            }
+        }
+
+       /**
+        * Let Logger.log() do the filtering since if the level of a
+        * platform logger is altered directly from
+        * java.util.logging.Logger.setLevel(), the levelValue will
+        * not be updated.
+        */
+        void doLog(int level, String msg) {
+            invoke(logMethod, javaLogger, levelObjects.get(level), msg);
+        }
+
+        void doLog(int level, String msg, Throwable t) {
+            invoke(logThrowMethod, javaLogger, levelObjects.get(level), msg, t);
+        }
+
+        void doLog(int level, String msg, Object... params) {
+            invoke(logParamsMethod, javaLogger, levelObjects.get(level), msg, params);
+        }
+
+        boolean isEnabled() {
+            Object level = invoke(getLevelMethod, javaLogger);
+            return level == null || level.equals(levelObjects.get(OFF)) == false;
+        }
+
+        int getLevel() {
+            Object level = invoke(getLevelMethod, javaLogger);
+            if (level != null) {
+                for (Map.Entry<Integer, Object> l : levelObjects.entrySet()) {
+                    if (level == l.getValue()) {
+                        return l.getKey();
+                    }
+                }
+            }
+            return 0;
+        }
+
+        void setLevel(int newLevel) {
+            levelValue = newLevel;
+            invoke(setLevelMethod, javaLogger, levelObjects.get(newLevel));
+        }
+    }
+
+
+    private static String getLevelName(int level) {
+        switch (level) {
+            case OFF     : return "OFF";
+            case SEVERE  : return "SEVERE";
+            case WARNING : return "WARNING";
+            case INFO    : return "INFO";
+            case CONFIG  : return "CONFIG";
+            case FINE    : return "FINE";
+            case FINER   : return "FINER";
+            case FINEST  : return "FINEST";
+            case ALL     : return "ALL";
+            default      : return "UNKNOWN";
+        }
+    }
+
+}
--- a/jdk/src/windows/classes/java/util/prefs/WindowsPreferences.java	Wed Sep 16 09:23:50 2009 -0700
+++ b/jdk/src/windows/classes/java/util/prefs/WindowsPreferences.java	Thu Sep 17 14:24:55 2009 -0700
@@ -29,7 +29,7 @@
 import java.util.TreeMap;
 import java.util.StringTokenizer;
 import java.io.ByteArrayOutputStream;
-import java.util.logging.Logger;
+import sun.util.logging.PlatformLogger;
 
 /**
  * Windows registry based implementation of  <tt>Preferences</tt>.
@@ -48,7 +48,7 @@
     /**
      * Logger for error messages
      */
-    private static Logger logger;
+    private static PlatformLogger logger;
 
     /**
      * Windows registry path to <tt>Preferences</tt>'s root nodes.
@@ -1102,9 +1102,9 @@
         // assert false;
     }
 
-    private static synchronized Logger logger() {
+    private static synchronized PlatformLogger logger() {
         if (logger == null) {
-            logger = Logger.getLogger("java.util.prefs");
+            logger = PlatformLogger.getLogger("java.util.prefs");
         }
         return logger;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/util/logging/PlatformLoggerTest.java	Thu Sep 17 14:24:55 2009 -0700
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug     6882376
+ * @summary Test if java.util.logging.Logger is created before and after
+ *          logging is enabled.  Also validate some basic PlatformLogger
+ *          operations.
+ *
+ * @build PlatformLoggerTest
+ * @run main PlatformLoggerTest
+ */
+
+import java.util.logging.*;
+import sun.util.logging.PlatformLogger;
+
+public class PlatformLoggerTest {
+    private static final int defaultEffectiveLevel = 0;
+    public static void main(String[] args) throws Exception {
+        final String FOO_PLATFORM_LOGGER = "test.platformlogger.foo";
+        final String BAR_PLATFORM_LOGGER = "test.platformlogger.bar";
+        final String GOO_PLATFORM_LOGGER = "test.platformlogger.goo";
+        final String BAR_LOGGER = "test.logger.bar";
+        PlatformLogger goo = PlatformLogger.getLogger(GOO_PLATFORM_LOGGER);
+
+        // Create a platform logger using the default
+        PlatformLogger foo = PlatformLogger.getLogger(FOO_PLATFORM_LOGGER);
+        checkPlatformLogger(foo, FOO_PLATFORM_LOGGER);
+
+        // create a java.util.logging.Logger
+        // now java.util.logging.Logger should be created for each platform logger
+        Logger logger = Logger.getLogger(BAR_LOGGER);
+        logger.setLevel(Level.WARNING);
+
+        PlatformLogger bar = PlatformLogger.getLogger(BAR_PLATFORM_LOGGER);
+        checkPlatformLogger(bar, BAR_PLATFORM_LOGGER);
+
+        checkLogger(FOO_PLATFORM_LOGGER, Level.FINER);
+        checkLogger(BAR_PLATFORM_LOGGER, Level.FINER);
+
+        checkLogger(GOO_PLATFORM_LOGGER, null);
+        checkLogger(BAR_LOGGER, Level.WARNING);
+
+        foo.setLevel(PlatformLogger.SEVERE);
+        checkLogger(FOO_PLATFORM_LOGGER, Level.SEVERE);
+    }
+
+    private static void checkPlatformLogger(PlatformLogger logger, String name) {
+        if (!logger.getName().equals(name)) {
+            throw new RuntimeException("Invalid logger's name " +
+                logger.getName() + " but expected " + name);
+        }
+
+        if (logger.getLevel() != defaultEffectiveLevel) {
+            throw new RuntimeException("Invalid default level for logger " +
+                logger.getName());
+        }
+
+        if (logger.isLoggable(PlatformLogger.FINE) != false) {
+            throw new RuntimeException("isLoggerable(FINE) returns true for logger " +
+                logger.getName() + " but expected false");
+        }
+
+        logger.setLevel(PlatformLogger.FINER);
+        if (logger.getLevel() != Level.FINER.intValue()) {
+            throw new RuntimeException("Invalid level for logger " +
+                logger.getName() + " " + logger.getLevel());
+        }
+
+        if (logger.isLoggable(PlatformLogger.FINE) != true) {
+            throw new RuntimeException("isLoggerable(FINE) returns false for logger " +
+                logger.getName() + " but expected true");
+        }
+
+        logger.info("OK: Testing log message");
+    }
+
+    private static void checkLogger(String name, Level level) {
+        Logger logger = LogManager.getLogManager().getLogger(name);
+        if (logger == null) {
+            throw new RuntimeException("Logger " + name +
+                " does not exist");
+        }
+
+        if (logger.getLevel() != level) {
+            throw new RuntimeException("Invalid level for logger " +
+                logger.getName() + " " + logger.getLevel());
+        }
+    }
+}