jdk/src/share/classes/java/util/logging/Logger.java
changeset 17487 77566e5979d1
parent 16906 44dfee24cb71
child 18595 c6f81d76027a
--- a/jdk/src/share/classes/java/util/logging/Logger.java	Thu May 16 04:30:45 2013 -0700
+++ b/jdk/src/share/classes/java/util/logging/Logger.java	Thu May 16 11:19:00 2013 -0400
@@ -191,8 +191,6 @@
  *
  * @since 1.4
  */
-
-
 public class Logger {
     private static final Handler emptyHandlers[] = new Handler[0];
     private static final int offValue = Level.OFF.intValue();
@@ -218,6 +216,7 @@
     private ArrayList<LogManager.LoggerWeakRef> kids;   // WeakReferences to loggers that have us as parent
     private volatile Level levelObject;
     private volatile int levelValue;  // current effective level value
+    private WeakReference<ClassLoader> callersClassLoaderRef;
 
     /**
      * GLOBAL_LOGGER_NAME is a name for the global logger.
@@ -278,18 +277,31 @@
      *             no corresponding resource can be found.
      */
     protected Logger(String name, String resourceBundleName) {
+        this(name, resourceBundleName, null);
+    }
+
+    Logger(String name, String resourceBundleName, Class<?> caller) {
         this.manager = LogManager.getLogManager();
-        if (resourceBundleName != null) {
-            // MissingResourceException or IllegalArgumentException can
-            // be thrown by setupResourceInfo(). Since this is the Logger
-            // constructor, the resourceBundleName field is null so
-            // IllegalArgumentException cannot happen here.
-            setupResourceInfo(resourceBundleName);
-        }
+        setupResourceInfo(resourceBundleName, caller);
         this.name = name;
         levelValue = Level.INFO.intValue();
     }
 
+    private void setCallersClassLoaderRef(Class<?> caller) {
+        ClassLoader callersClassLoader = ((caller != null)
+                                         ? caller.getClassLoader()
+                                         : null);
+        if (callersClassLoader != null) {
+            this.callersClassLoaderRef = new WeakReference(callersClassLoader);
+        }
+    }
+
+    private ClassLoader getCallersClassLoader() {
+        return (callersClassLoaderRef != null)
+                ? callersClassLoaderRef.get()
+                : null;
+    }
+
     // This constructor is used only to create the global Logger.
     // It is needed to break a cyclic dependence between the LogManager
     // and Logger static initializers causing deadlocks.
@@ -343,7 +355,9 @@
                 return manager.demandSystemLogger(name, resourceBundleName);
             }
         }
-        return manager.demandLogger(name, resourceBundleName);
+        return manager.demandLogger(name, resourceBundleName, caller);
+        // ends up calling new Logger(name, resourceBundleName, caller)
+        // iff the logger doesn't exist already
     }
 
     /**
@@ -436,11 +450,19 @@
     // adding a new Logger object is handled by LogManager.addLogger().
     @CallerSensitive
     public static Logger getLogger(String name, String resourceBundleName) {
-        Logger result = demandLogger(name, resourceBundleName, Reflection.getCallerClass());
+        Class<?> callerClass = Reflection.getCallerClass();
+        Logger result = demandLogger(name, resourceBundleName, callerClass);
 
         // MissingResourceException or IllegalArgumentException can be
         // thrown by setupResourceInfo().
-        result.setupResourceInfo(resourceBundleName);
+        // We have to set the callers ClassLoader here in case demandLogger
+        // above found a previously created Logger.  This can happen, for
+        // example, if Logger.getLogger(name) is called and subsequently
+        // Logger.getLogger(name, resourceBundleName) is called.  In this case
+        // we won't necessarily have the correct classloader saved away, so
+        // we need to set it here, too.
+
+        result.setupResourceInfo(resourceBundleName, callerClass);
         return result;
     }
 
@@ -507,11 +529,13 @@
 
     // Synchronization is not required here. All synchronization for
     // adding a new anonymous Logger object is handled by doSetParent().
+    @CallerSensitive
     public static Logger getAnonymousLogger(String resourceBundleName) {
         LogManager manager = LogManager.getLogManager();
         // cleanup some Loggers that have been GC'ed
         manager.drainLoggerRefQueueBounded();
-        Logger result = new Logger(null, resourceBundleName);
+        Logger result = new Logger(null, resourceBundleName,
+                                   Reflection.getCallerClass());
         result.anonymous = true;
         Logger root = manager.getLogger("");
         result.doSetParent(root);
@@ -527,7 +551,7 @@
      * @return localization bundle (may be null)
      */
     public ResourceBundle getResourceBundle() {
-        return findResourceBundle(getResourceBundleName());
+        return findResourceBundle(getResourceBundleName(), true);
     }
 
     /**
@@ -609,7 +633,7 @@
         String ebname = getEffectiveResourceBundleName();
         if (ebname != null && !ebname.equals(SYSTEM_LOGGER_RB_NAME)) {
             lr.setResourceBundleName(ebname);
-            lr.setResourceBundle(findResourceBundle(ebname));
+            lr.setResourceBundle(findResourceBundle(ebname, true));
         }
         log(lr);
     }
@@ -936,7 +960,7 @@
         lr.setLoggerName(name);
         if (rbname != null) {
             lr.setResourceBundleName(rbname);
-            lr.setResourceBundle(findResourceBundle(rbname));
+            lr.setResourceBundle(findResourceBundle(rbname, false));
         }
         log(lr);
     }
@@ -960,7 +984,6 @@
      *                         can be null
      * @param   msg     The string message (or a key in the message catalog)
      */
-
     public void logrb(Level level, String sourceClass, String sourceMethod,
                                 String bundleName, String msg) {
         if (level.intValue() < levelValue || levelValue == offValue) {
@@ -1609,9 +1632,18 @@
      * there is no suitable previous cached value.
      *
      * @param name the ResourceBundle to locate
+     * @param userCallersClassLoader if true search using the caller's ClassLoader
      * @return ResourceBundle specified by name or null if not found
      */
-    private synchronized ResourceBundle findResourceBundle(String name) {
+    private synchronized ResourceBundle findResourceBundle(String name,
+                                                           boolean useCallersClassLoader) {
+        // For all lookups, we first check the thread context class loader
+        // if it is set.  If not, we use the system classloader.  If we
+        // still haven't found it we use the callersClassLoaderRef if it
+        // is set and useCallersClassLoader is true.  We set
+        // callersClassLoaderRef initially upon creating the logger with a
+        // non-null resource bundle name.
+
         // Return a null bundle for a null name.
         if (name == null) {
             return null;
@@ -1644,17 +1676,40 @@
             catalogLocale = currentLocale;
             return catalog;
         } catch (MissingResourceException ex) {
+            // We can't find the ResourceBundle in the default
+            // ClassLoader.  Drop through.
+        }
+
+        if (useCallersClassLoader) {
+            // Try with the caller's ClassLoader
+            ClassLoader callersClassLoader = getCallersClassLoader();
+
+            if (callersClassLoader == null || callersClassLoader == cl) {
+                return null;
+            }
+
+            try {
+                catalog = ResourceBundle.getBundle(name, currentLocale,
+                                                   callersClassLoader);
+                catalogName = name;
+                catalogLocale = currentLocale;
+                return catalog;
+            } catch (MissingResourceException ex) {
+                return null; // no luck
+            }
+        } else {
             return null;
         }
     }
 
     // Private utility method to initialize our one entry
-    // resource bundle name cache.
+    // resource bundle name cache and the callers ClassLoader
     // Note: for consistency reasons, we are careful to check
     // that a suitable ResourceBundle exists before setting the
     // resourceBundleName field.
-    // Synchronized to prevent races in setting the field.
-    private synchronized void setupResourceInfo(String name) {
+    // Synchronized to prevent races in setting the fields.
+    private synchronized void setupResourceInfo(String name,
+                                                Class<?> callersClass) {
         if (name == null) {
             return;
         }
@@ -1672,9 +1727,14 @@
                 resourceBundleName + " != " + name);
         }
 
-        if (findResourceBundle(name) == null) {
+        setCallersClassLoaderRef(callersClass);
+        if (findResourceBundle(name, true) == null) {
             // We've failed to find an expected ResourceBundle.
-            throw new MissingResourceException("Can't find " + name + " bundle", name, "");
+            // unset the caller's ClassLoader since we were unable to find the
+            // the bundle using it
+            this.callersClassLoaderRef = null;
+            throw new MissingResourceException("Can't find " + name + " bundle",
+                                                name, "");
         }
         resourceBundleName = name;
     }