changeset 8460:2ef43f3a901c

8013839: Enhance Logger API for handling of resource bundles 4814565: (rb) add method to get basename from a ResourceBundle Summary: adds Logger.setResourceBundle(ResourceBundle) and ResourceBundle.getBaseBundleName() Reviewed-by: mchung, naoto
author dfuchs
date Wed, 16 Oct 2013 20:47:30 +0200
parents 445667b19e32
children cf9cb3d241a3
files src/share/classes/java/util/ResourceBundle.java src/share/classes/java/util/logging/Logger.java test/java/util/ResourceBundle/getBaseBundleName/TestGetBaseBundleName.java test/java/util/ResourceBundle/getBaseBundleName/resources/ListBundle.java test/java/util/ResourceBundle/getBaseBundleName/resources/ListBundle_fr.java test/java/util/ResourceBundle/getBaseBundleName/resources/PropertyBundle.properties test/java/util/ResourceBundle/getBaseBundleName/resources/PropertyBundle_fr.properties test/java/util/logging/Logger/logrb/TestLogrbResourceBundle.java test/java/util/logging/Logger/logrb/resources/ListBundle.java test/java/util/logging/Logger/logrb/resources/ListBundle_fr.java test/java/util/logging/Logger/logrb/resources/PropertyBundle.properties test/java/util/logging/Logger/logrb/resources/PropertyBundle_fr.properties test/java/util/logging/Logger/setResourceBundle/TestSetResourceBundle.java test/java/util/logging/Logger/setResourceBundle/resources/ListBundle.java test/java/util/logging/Logger/setResourceBundle/resources/ListBundle_fr.java test/java/util/logging/Logger/setResourceBundle/resources/PropertyBundle.properties test/java/util/logging/Logger/setResourceBundle/resources/PropertyBundle_fr.properties
diffstat 17 files changed, 1560 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/util/ResourceBundle.java	Wed Oct 16 17:19:09 2013 +0200
+++ b/src/share/classes/java/util/ResourceBundle.java	Wed Oct 16 20:47:30 2013 +0200
@@ -311,6 +311,24 @@
     private static final ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
 
     /**
+     * Returns the base name of this bundle, if known, or {@code null} if unknown.
+     *
+     * If not null, then this is the value of the {@code baseName} parameter
+     * that was passed to the {@code ResourceBundle.getBundle(...)} method
+     * when the resource bundle was loaded.
+     *
+     * @return The base name of the resource bundle, as provided to and expected
+     * by the {@code ResourceBundle.getBundle(...)} methods.
+     *
+     * @see #getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader)
+     *
+     * @since 1.8
+     */
+    public String getBaseBundleName() {
+        return name;
+    }
+
+    /**
      * The parent bundle of this bundle.
      * The parent bundle is searched by {@link #getObject getObject}
      * when this bundle does not contain a particular resource.
--- a/src/share/classes/java/util/logging/Logger.java	Wed Oct 16 17:19:09 2013 +0200
+++ b/src/share/classes/java/util/logging/Logger.java	Wed Oct 16 20:47:30 2013 +0200
@@ -88,17 +88,21 @@
  * the LogRecord to its output Handlers.  By default, loggers also
  * publish to their parent's Handlers, recursively up the tree.
  * <p>
- * Each Logger may have a ResourceBundle name associated with it.
- * The named bundle will be used for localizing logging messages.
- * If a Logger does not have its own ResourceBundle name, then
- * it will inherit the ResourceBundle name from its parent,
- * recursively up the tree.
+ * Each Logger may have a {@code ResourceBundle} associated with it.
+ * The {@code ResourceBundle} may be specified by name, using the
+ * {@link #getLogger(java.lang.String, java.lang.String)} factory
+ * method, or by value - using the {@link
+ * #setResourceBundle(java.util.ResourceBundle) setResourceBundle} method.
+ * This bundle will be used for localizing logging messages.
+ * If a Logger does not have its own {@code ResourceBundle} or resource bundle
+ * name, then it will inherit the {@code ResourceBundle} or resource bundle name
+ * from its parent, recursively up the tree.
  * <p>
  * Most of the logger output methods take a "msg" argument.  This
  * msg argument may be either a raw value or a localization key.
  * During formatting, if the logger has (or inherits) a localization
- * ResourceBundle and if the ResourceBundle has a mapping for the msg
- * string, then the msg string is replaced by the localized value.
+ * {@code ResourceBundle} and if the {@code ResourceBundle} has a mapping for
+ * the msg string, then the msg string is replaced by the localized value.
  * Otherwise the original msg string is used.  Typically, formatters use
  * java.text.MessageFormat style formatting to format parameters, so
  * for example a format string "{0} {1}" would format two parameters
@@ -131,10 +135,30 @@
    logger.log(Level.FINER, DiagnosisMessages::systemHealthStatus);
 </code></pre>
  * <p>
- * When mapping ResourceBundle names to ResourceBundles, the Logger
- * will first try to use the Thread's ContextClassLoader.  If that
- * is null it will try the
- * {@linkplain java.lang.ClassLoader#getSystemClassLoader() system ClassLoader} instead.
+ * When looking for a {@code ResourceBundle}, the logger will first look at
+ * whether a bundle was specified using {@link
+ * #setResourceBundle(java.util.ResourceBundle) setResourceBundle}, and then
+ * only whether a resource bundle name was specified through the {@link
+ * #getLogger(java.lang.String, java.lang.String) getLogger} factory method.
+ * If no {@code ResourceBundle} or no resource bundle name is found,
+ * then it will use the nearest {@code ResourceBundle} or resource bundle
+ * name inherited from its parent tree.<br>
+ * When a {@code ResourceBundle} was inherited or specified through the
+ * {@link
+ * #setResourceBundle(java.util.ResourceBundle) setResourceBundle} method, then
+ * that {@code ResourceBundle} will be used. Otherwise if the logger only
+ * has or inherited a resource bundle name, then that resource bundle name
+ * will be mapped to a {@code ResourceBundle} object, using the default Locale
+ * at the time of logging.
+ * <br><a name="ResourceBundleMapping"/>When mapping resource bundle names to
+ * {@code ResourceBundle} objects, the logger will first try to use the
+ * Thread's {@linkplain java.lang.Thread#getContextClassLoader() context class
+ * loader} to map the given resource bundle name to a {@code ResourceBundle}.
+ * If the thread context class loader is {@code null}, it will try the
+ * {@linkplain java.lang.ClassLoader#getSystemClassLoader() system class loader}
+ * instead.  If the {@code ResourceBundle} is still not found, it will use the
+ * class loader of the first caller of the {@link
+ * #getLogger(java.lang.String, java.lang.String) getLogger} factory method.
  * <p>
  * Formatting (including localization) is the responsibility of
  * the output Handler, which will typically call a Formatter.
@@ -154,7 +178,7 @@
  * <li><p>
  *     There are a set of "logrb" method (for "log with resource bundle")
  *     that are like the "logp" method, but also take an explicit resource
- *     bundle name for use in localizing the log message.
+ *     bundle object for use in localizing the log message.
  * <li><p>
  *     There are convenience methods for tracing method entries (the
  *     "entering" methods), method returns (the "exiting" methods) and
@@ -198,18 +222,20 @@
     private String name;
     private final CopyOnWriteArrayList<Handler> handlers =
         new CopyOnWriteArrayList<>();
-    private String resourceBundleName;
+    private String resourceBundleName;  // Base name of the bundle.
+    private ResourceBundle userBundle;  // Bundle set through setResourceBundle.
     private volatile boolean useParentHandlers = true;
     private volatile Filter filter;
     private boolean anonymous;
 
+    // Cache to speed up behavior of findResourceBundle:
     private ResourceBundle catalog;     // Cached resource bundle
     private String catalogName;         // name associated with catalog
     private Locale catalogLocale;       // locale associated with catalog
 
     // The fields relating to parent-child relationships and levels
     // are managed under a separate lock, the treeLock.
-    private static Object treeLock = new Object();
+    private static final Object treeLock = new Object();
     // We keep weak references from parents to children, but strong
     // references from children to parents.
     private volatile Logger parent;    // our nearest parent.
@@ -372,6 +398,7 @@
         static boolean disableCallerCheck = getBooleanProperty("sun.util.logging.disableCallerCheck");
         private static boolean getBooleanProperty(final String key) {
             String s = AccessController.doPrivileged(new PrivilegedAction<String>() {
+                @Override
                 public String run() {
                     return System.getProperty(key);
                 }
@@ -579,11 +606,18 @@
 
     /**
      * Retrieve the localization resource bundle for this
-     * logger for the current default locale.  Note that if
-     * the result is null, then the Logger will use a resource
-     * bundle inherited from its parent.
+     * logger.
+     * This method will return a {@code ResourceBundle} that was either
+     * set by the {@link
+     * #setResourceBundle(java.util.ResourceBundle) setResourceBundle} method or
+     * <a href="#ResourceBundleMapping">mapped from the
+     * the resource bundle name</a> set via the {@link
+     * Logger#getLogger(java.lang.String, java.lang.String) getLogger} factory
+     * method for the current default locale.
+     * <br>Note that if the result is {@code null}, then the Logger will use a resource
+     * bundle or resource bundle name inherited from its parent.
      *
-     * @return localization bundle (may be null)
+     * @return localization bundle (may be {@code null})
      */
     public ResourceBundle getResourceBundle() {
         return findResourceBundle(getResourceBundleName(), true);
@@ -591,10 +625,16 @@
 
     /**
      * Retrieve the localization resource bundle name for this
-     * logger.  Note that if the result is null, then the Logger
-     * will use a resource bundle name inherited from its parent.
+     * logger.
+     * This is either the name specified through the {@link
+     * #getLogger(java.lang.String, java.lang.String) getLogger} factory method,
+     * or the {@linkplain ResourceBundle#getBaseBundleName() base name} of the
+     * ResourceBundle set through {@link
+     * #setResourceBundle(java.util.ResourceBundle) setResourceBundle} method.
+     * <br>Note that if the result is {@code null}, then the Logger will use a resource
+     * bundle or resource bundle name inherited from its parent.
      *
-     * @return localization bundle name (may be null)
+     * @return localization bundle name (may be {@code null})
      */
     public String getResourceBundleName() {
         return resourceBundleName;
@@ -665,10 +705,11 @@
     // resource bundle and then call "void log(LogRecord)".
     private void doLog(LogRecord lr) {
         lr.setLoggerName(name);
-        String ebname = getEffectiveResourceBundleName();
-        if (ebname != null && !ebname.equals(SYSTEM_LOGGER_RB_NAME)) {
+        final ResourceBundle bundle = getEffectiveResourceBundle();
+        final String ebname = getEffectiveResourceBundleName();
+        if (ebname != null && bundle != null) {
             lr.setResourceBundleName(ebname);
-            lr.setResourceBundle(findResourceBundle(ebname, true));
+            lr.setResourceBundle(bundle);
         }
         log(lr);
     }
@@ -1000,6 +1041,16 @@
         log(lr);
     }
 
+    // Private support method for logging for "logrb" methods.
+    private void doLog(LogRecord lr, ResourceBundle rb) {
+        lr.setLoggerName(name);
+        if (rb != null) {
+            lr.setResourceBundleName(rb.getBaseBundleName());
+            lr.setResourceBundle(rb);
+        }
+        log(lr);
+    }
+
     /**
      * Log a message, specifying source class, method, and resource bundle name
      * with no arguments.
@@ -1018,7 +1069,11 @@
      * @param   bundleName     name of resource bundle to localize msg,
      *                         can be null
      * @param   msg     The string message (or a key in the message catalog)
+     * @deprecated Use {@link #logrb(java.util.logging.Level, java.lang.String,
+     * java.lang.String, java.util.ResourceBundle, java.lang.String,
+     * java.lang.Object...)} instead.
      */
+    @Deprecated
     public void logrb(Level level, String sourceClass, String sourceMethod,
                                 String bundleName, String msg) {
         if (!isLoggable(level)) {
@@ -1049,7 +1104,11 @@
      *                         can be null
      * @param   msg      The string message (or a key in the message catalog)
      * @param   param1    Parameter to the log message.
+     * @deprecated Use {@link #logrb(java.util.logging.Level, java.lang.String,
+     *   java.lang.String, java.util.ResourceBundle, java.lang.String,
+     *   java.lang.Object...)} instead
      */
+    @Deprecated
     public void logrb(Level level, String sourceClass, String sourceMethod,
                                 String bundleName, String msg, Object param1) {
         if (!isLoggable(level)) {
@@ -1082,7 +1141,11 @@
      *                         can be null.
      * @param   msg     The string message (or a key in the message catalog)
      * @param   params  Array of parameters to the message
+     * @deprecated Use {@link #logrb(java.util.logging.Level, java.lang.String,
+     *      java.lang.String, java.util.ResourceBundle, java.lang.String,
+     *      java.lang.Object...)} instead.
      */
+    @Deprecated
     public void logrb(Level level, String sourceClass, String sourceMethod,
                                 String bundleName, String msg, Object params[]) {
         if (!isLoggable(level)) {
@@ -1096,6 +1159,41 @@
     }
 
     /**
+     * Log a message, specifying source class, method, and resource bundle,
+     * with an optional list of message parameters.
+     * <p>
+     * If the logger is currently enabled for the given message
+     * level then a corresponding LogRecord is created and forwarded
+     * to all the registered output Handler objects.
+     * <p>
+     * The {@code msg} string is localized using the given resource bundle.
+     * If the resource bundle is {@code null}, then the {@code msg} string is not
+     * localized.
+     * <p>
+     * @param   level   One of the message level identifiers, e.g., SEVERE
+     * @param   sourceClass    Name of the class that issued the logging request
+     * @param   sourceMethod   Name of the method that issued the logging request
+     * @param   bundle         Resource bundle to localize {@code msg},
+     *                         can be {@code null}.
+     * @param   msg     The string message (or a key in the message catalog)
+     * @param   params  Parameters to the message (optional, may be none).
+     * @since 1.8
+     */
+    public void logrb(Level level, String sourceClass, String sourceMethod,
+                      ResourceBundle bundle, String msg, Object... params) {
+        if (!isLoggable(level)) {
+            return;
+        }
+        LogRecord lr = new LogRecord(level, msg);
+        lr.setSourceClassName(sourceClass);
+        lr.setSourceMethodName(sourceMethod);
+        if (params != null && params.length != 0) {
+            lr.setParameters(params);
+        }
+        doLog(lr, bundle);
+    }
+
+    /**
      * Log a message, specifying source class, method, and resource bundle name,
      * with associated Throwable information.
      * <p>
@@ -1119,7 +1217,11 @@
      *                         can be null
      * @param   msg     The string message (or a key in the message catalog)
      * @param   thrown  Throwable associated with log message.
+     * @deprecated Use {@link #logrb(java.util.logging.Level, java.lang.String,
+     *     java.lang.String, java.util.ResourceBundle, java.lang.String,
+     *     java.lang.Throwable)} instead.
      */
+    @Deprecated
     public void logrb(Level level, String sourceClass, String sourceMethod,
                                         String bundleName, String msg, Throwable thrown) {
         if (!isLoggable(level)) {
@@ -1132,6 +1234,43 @@
         doLog(lr, bundleName);
     }
 
+    /**
+     * Log a message, specifying source class, method, and resource bundle,
+     * with associated Throwable information.
+     * <p>
+     * If the logger is currently enabled for the given message
+     * level then the given arguments are stored in a LogRecord
+     * which is forwarded to all registered output handlers.
+     * <p>
+     * The {@code msg} string is localized using the given resource bundle.
+     * If the resource bundle is {@code null}, then the {@code msg} string is not
+     * localized.
+     * <p>
+     * Note that the thrown argument is stored in the LogRecord thrown
+     * property, rather than the LogRecord parameters property.  Thus it is
+     * processed specially by output Formatters and is not treated
+     * as a formatting parameter to the LogRecord message property.
+     * <p>
+     * @param   level   One of the message level identifiers, e.g., SEVERE
+     * @param   sourceClass    Name of the class that issued the logging request
+     * @param   sourceMethod   Name of the method that issued the logging request
+     * @param   bundle         Resource bundle to localize {@code msg},
+     *                         can be {@code null}
+     * @param   msg     The string message (or a key in the message catalog)
+     * @param   thrown  Throwable associated with the log message.
+     * @since 1.8
+     */
+    public void logrb(Level level, String sourceClass, String sourceMethod,
+                      ResourceBundle bundle, String msg, Throwable thrown) {
+        if (!isLoggable(level)) {
+            return;
+        }
+        LogRecord lr = new LogRecord(level, msg);
+        lr.setSourceClassName(sourceClass);
+        lr.setSourceMethodName(sourceMethod);
+        lr.setThrown(thrown);
+        doLog(lr, bundle);
+    }
 
     //======================================================================
     // Start of convenience methods for logging method entries and returns.
@@ -1610,6 +1749,7 @@
     private static ResourceBundle findSystemResourceBundle(final Locale locale) {
         // the resource bundle is in a restricted package
         return AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() {
+            @Override
             public ResourceBundle run() {
                 try {
                     return ResourceBundle.getBundle(SYSTEM_LOGGER_RB_NAME,
@@ -1650,7 +1790,10 @@
         Locale currentLocale = Locale.getDefault();
 
         // Normally we should hit on our simple one entry cache.
-        if (catalog != null && currentLocale.equals(catalogLocale)
+        if (userBundle != null &&
+                name.equals(resourceBundleName)) {
+            return userBundle;
+        } else if (catalog != null && currentLocale.equals(catalogLocale)
                 && name.equals(catalogName)) {
             return catalog;
         }
@@ -1738,6 +1881,45 @@
     }
 
     /**
+     * Sets a resource bundle on this logger.
+     * All messages will be logged using the given resource bundle for its
+     * specific {@linkplain ResourceBundle#getLocale locale}.
+     * @param bundle The resource bundle that this logger shall use.
+     * @throws NullPointerException if the given bundle is {@code null}.
+     * @throws IllegalArgumentException if the given bundle doesn't have a
+     *         {@linkplain ResourceBundle#getBaseBundleName base name},
+     *         or if this logger already has a resource bundle set but
+     *         the given bundle has a different base name.
+     * @throws SecurityException  if a security manager exists and if
+     *         the caller does not have LoggingPermission("control").
+     * @since 1.8
+     */
+    public void setResourceBundle(ResourceBundle bundle) {
+        checkPermission();
+
+        // Will throw NPE if bundle is null.
+        final String baseName = bundle.getBaseBundleName();
+
+        // bundle must have a name
+        if (baseName == null || baseName.isEmpty()) {
+            throw new IllegalArgumentException("resource bundle must have a name");
+        }
+
+        synchronized (this) {
+            final boolean canReplaceResourceBundle = resourceBundleName == null
+                    || resourceBundleName.equals(baseName);
+
+            if (!canReplaceResourceBundle) {
+                throw new IllegalArgumentException("can't replace resource bundle");
+            }
+
+
+            userBundle = bundle;
+            resourceBundleName = baseName;
+        }
+    }
+
+    /**
      * Return the parent for this Logger.
      * <p>
      * This method returns the nearest extant parent in the namespace.
@@ -1813,7 +1995,7 @@
                 // we didn't have a previous parent
                 ref = manager.new LoggerWeakRef(this);
             }
-            ref.setParentRef(new WeakReference<Logger>(parent));
+            ref.setParentRef(new WeakReference<>(parent));
             parent.kids.add(ref);
 
             // As a result of the reparenting, the effective level
@@ -1895,4 +2077,30 @@
     }
 
 
+    private ResourceBundle getEffectiveResourceBundle() {
+        Logger target = this;
+        if (SYSTEM_LOGGER_RB_NAME.equals(resourceBundleName)) return null;
+        ResourceBundle localRB = getResourceBundle();
+        if (localRB != null) {
+            return localRB;
+        }
+
+        while (target != null) {
+            final ResourceBundle rb = target.userBundle;
+            if (rb != null) {
+                return rb;
+            }
+            final String rbn = target.getResourceBundleName();
+            if (rbn != null) {
+                if (!SYSTEM_LOGGER_RB_NAME.equals(rbn)) {
+                    return findResourceBundle(rbn, true);
+                } else {
+                    return null;
+                }
+            }
+            target = target.getParent();
+        }
+        return null;
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/ResourceBundle/getBaseBundleName/TestGetBaseBundleName.java	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.PropertyResourceBundle;
+import java.util.ResourceBundle;
+
+/**
+ * @test
+ * @bug 4814565
+ * @summary tests ResourceBundle.getBaseBundleName();
+ * @build TestGetBaseBundleName resources.ListBundle resources.ListBundle_fr
+ * @run main TestGetBaseBundleName
+ * @author danielfuchs
+ */
+public class TestGetBaseBundleName {
+
+    final static String PROPERTY_BUNDLE_NAME = "resources/PropertyBundle";
+    final static String LIST_BUNDLE_NAME = "resources.ListBundle";
+
+    public static String getBaseName(ResourceBundle bundle) {
+        return bundle == null ? null : bundle.getBaseBundleName();
+    }
+
+    public static void main(String... args) throws Exception {
+
+        Locale defaultLocale = Locale.getDefault();
+        System.out.println("Default locale is: " + defaultLocale);
+        for (String baseName : new String[] {
+                    PROPERTY_BUNDLE_NAME,
+                    LIST_BUNDLE_NAME
+        }) {
+            ResourceBundle bundle = ResourceBundle.getBundle(baseName);
+            System.out.println(getBaseName(bundle));
+            if (!Locale.ROOT.equals(bundle.getLocale())) {
+                throw new RuntimeException("Unexpected locale: "
+                        + bundle.getLocale());
+            }
+            if (!baseName.equals(getBaseName(bundle))) {
+                throw new RuntimeException("Unexpected base name: "
+                        + getBaseName(bundle));
+            }
+            Locale.setDefault(Locale.FRENCH);
+            try {
+                ResourceBundle bundle_fr = ResourceBundle.getBundle(baseName);
+                if (!Locale.FRENCH.equals(bundle_fr.getLocale())) {
+                    throw new RuntimeException("Unexpected locale: "
+                            + bundle_fr.getLocale());
+                }
+                if (!baseName.equals(getBaseName(bundle_fr))) {
+                    throw new RuntimeException("Unexpected base name: "
+                            + getBaseName(bundle_fr));
+                }
+            } finally {
+                Locale.setDefault(defaultLocale);
+            }
+        }
+
+        final ResourceBundle bundle = new ResourceBundle() {
+            @Override
+            protected Object handleGetObject(String key) {
+                if ("dummy".equals(key)) return "foo";
+                throw new MissingResourceException("Missing key",
+                        this.getClass().getName(), key);
+            }
+            @Override
+            public Enumeration<String> getKeys() {
+                return Collections.enumeration(java.util.Arrays.asList(
+                        new String[] {"dummy"}));
+            }
+        };
+
+        if (getBaseName(bundle) != null) {
+            throw new RuntimeException("Expected null baseName, got "
+                    + getBaseName(bundle));
+        }
+
+        final ResourceBundle bundle2 = new ResourceBundle() {
+            @Override
+            protected Object handleGetObject(String key) {
+                if ("dummy".equals(key)) return "foo";
+                throw new MissingResourceException("Missing key",
+                        this.getClass().getName(), key);
+            }
+            @Override
+            public Enumeration<String> getKeys() {
+                return Collections.enumeration(java.util.Arrays.asList(
+                        new String[] {"dummy"}));
+            }
+
+            @Override
+            public String getBaseBundleName() {
+                return this.getClass().getName();
+            }
+
+
+        };
+
+        if (!bundle2.getClass().getName().equals(getBaseName(bundle2))) {
+            throw new RuntimeException("Expected "
+                    + bundle2.getClass().getName() + ", got "
+                    + getBaseName(bundle2));
+        }
+
+        ResourceBundle propertyBundle = new PropertyResourceBundle(
+                TestGetBaseBundleName.class.getResourceAsStream(
+                    PROPERTY_BUNDLE_NAME+".properties"));
+
+        if (getBaseName(propertyBundle) != null) {
+            throw new RuntimeException("Expected null baseName, got "
+                    + getBaseName(propertyBundle));
+        }
+
+        ResourceBundle listBundle = new resources.ListBundle_fr();
+        if (getBaseName(listBundle) != null) {
+            throw new RuntimeException("Expected null baseName, got "
+                    + getBaseName(listBundle));
+        }
+
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/ResourceBundle/getBaseBundleName/resources/ListBundle.java	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package resources;
+
+import java.util.ListResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public class ListBundle extends ListResourceBundle {
+
+    @Override
+    protected Object[][] getContents() {
+        return new Object[][] {
+            { "dummy", "foo" },
+        };
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/ResourceBundle/getBaseBundleName/resources/ListBundle_fr.java	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package resources;
+
+import java.util.ListResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public class ListBundle_fr extends ListResourceBundle {
+
+    @Override
+    protected Object[][] getContents() {
+        return new Object[][] {
+            { "dummy", "toto" },
+        };
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/ResourceBundle/getBaseBundleName/resources/PropertyBundle.properties	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 20013, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+dummy=foo
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/ResourceBundle/getBaseBundleName/resources/PropertyBundle_fr.properties	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+dummy=toto
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/Logger/logrb/TestLogrbResourceBundle.java	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import resources.ListBundle;
+
+/**
+ * @test
+ * @bug 8013839
+ * @summary tests Logger.logrb(..., ResourceBundle);
+ * @build TestLogrbResourceBundle resources.ListBundle resources.ListBundle_fr
+ * @run main TestLogrbResourceBundle
+ * @author danielfuchs
+ */
+public class TestLogrbResourceBundle {
+
+    final static String LIST_BUNDLE_NAME = "resources.ListBundle";
+    final static String PROPERTY_BUNDLE_NAME = "resources.PropertyBundle";
+
+    /**
+     * A dummy handler class that we can use to check the bundle/bundle name
+     * that was present in the last LogRecord instance published.
+     */
+    static final class TestHandler extends Handler {
+        ResourceBundle lastBundle = null;
+        String lastBundleName = null;
+        Object[] lastParams = null;
+        Throwable lastThrown = null;
+        String lastMessage = null;
+        @Override
+        public void publish(LogRecord record) {
+            lastBundle = record.getResourceBundle();
+            lastBundleName = record.getResourceBundleName();
+            lastParams = record.getParameters();
+            lastThrown = record.getThrown();
+            lastMessage = record.getMessage();
+        }
+
+        @Override
+        public void flush() {
+        }
+
+        @Override
+        public void close() throws SecurityException {
+        }
+    }
+
+    /**
+     * We're going to do the same test with each of the different new logrb
+     * forms.
+     * <ul>
+     *    <li> LOGRB_NO_ARGS: calling logrb with no message argument.
+     *    <li> LOGRB_SINGLE_ARG: calling logrb with a single message argument.
+     *    <li> LOGRB_ARG_ARRAY: calling logrb with an array of message arguments.
+     *    <li> LOGRB_VARARGS: calling logrb with a variable list of message arguments.
+     *    <li> LOGRB_THROWABLE: calling logrb with an exception.
+     * </ul>
+     */
+    private static enum TestCase {
+        LOGRB_NO_ARGS, LOGRB_SINGLE_ARG, LOGRB_ARG_ARRAY, LOGRB_VARARGS, LOGRB_THROWABLE;
+
+        public void logrb(Logger logger, ResourceBundle bundle) {
+            switch(this) {
+                case LOGRB_NO_ARGS:
+                    logger.logrb(Level.CONFIG,
+                            TestLogrbResourceBundle.class.getName(),
+                            "main", bundle, "dummy");
+                    break;
+                case LOGRB_SINGLE_ARG:
+                    logger.logrb(Level.CONFIG,
+                            TestLogrbResourceBundle.class.getName(),
+                            "main", bundle, "dummy", "bar");
+                    break;
+                case LOGRB_ARG_ARRAY:
+                    logger.logrb(Level.CONFIG,
+                            TestLogrbResourceBundle.class.getName(),
+                            "main", bundle, "dummy",
+                            new Object[] { "bar", "baz"} );
+                    break;
+                case LOGRB_VARARGS:
+                    logger.logrb(Level.CONFIG,
+                            TestLogrbResourceBundle.class.getName(),
+                            "main", bundle, "dummy",
+                            "bar", "baz" );
+                    break;
+                case LOGRB_THROWABLE:
+                    logger.logrb(Level.CONFIG,
+                            TestLogrbResourceBundle.class.getName(),
+                            "main", bundle, "dummy",
+                            new Exception("dummy exception") );
+                    break;
+                default:
+            }
+        }
+
+        /**
+         * Checks that the last published logged record had the expected data.
+         * @param handler the TestHandler through which the record was published.
+         */
+        public void checkLogged(TestHandler handler) {
+            checkLogged(handler.lastMessage, handler.lastParams, handler.lastThrown);
+        }
+
+        private void checkLogged(String message, Object[] parameters, Throwable thrown) {
+            switch(this) {
+                case LOGRB_NO_ARGS:
+                    if ("dummy".equals(message) && thrown == null
+                            && (parameters == null || parameters.length == 0)) {
+                        return; // OK: all was as expected.
+                    }
+                    break;
+                case LOGRB_SINGLE_ARG:
+                    if ("dummy".equals(message) && thrown == null
+                            && parameters != null
+                            && parameters.length == 1
+                            && "bar".equals(parameters[0])) {
+                        return; // OK: all was as expected.
+                    }
+                    break;
+                case LOGRB_VARARGS:
+                case LOGRB_ARG_ARRAY:
+                    if ("dummy".equals(message) && thrown == null
+                            && parameters != null
+                            && parameters.length > 1
+                            && Arrays.deepEquals(new Object[] { "bar", "baz"},
+                                    parameters)) {
+                        return; // OK: all was as expected.
+                    }
+                    break;
+                case LOGRB_THROWABLE:
+                    if ("dummy".equals(message) && thrown != null
+                            && thrown.getClass() == Exception.class
+                            && "dummy exception".equals(thrown.getMessage())) {
+                        return; // OK: all was as expected.
+                    }
+                    break;
+                default:
+            }
+
+            // We had some unexpected stuff: throw exception.
+            throw new RuntimeException(this + ": "
+                    + "Unexpected content in last published log record: "
+                    + "\n\tmessage=\"" + message + "\""
+                    + "\n\tparameters=" + Arrays.toString(parameters)
+                    + "\n\tthrown=" + thrown);
+        }
+    }
+
+    static String getBaseName(ResourceBundle bundle) {
+        return bundle == null ? null : bundle.getBaseBundleName();
+    }
+
+    public static void main(String... args) throws Exception {
+
+        Locale defaultLocale = Locale.getDefault();
+
+        final ResourceBundle bundle = ResourceBundle.getBundle(LIST_BUNDLE_NAME);
+        final ResourceBundle bundle_fr =
+                    ResourceBundle.getBundle(LIST_BUNDLE_NAME, Locale.FRENCH);
+        final ResourceBundle propertyBundle = ResourceBundle.getBundle(PROPERTY_BUNDLE_NAME);
+        final ResourceBundle propertyBundle_fr =
+                    ResourceBundle.getBundle(PROPERTY_BUNDLE_NAME, Locale.FRENCH);
+        Logger foobar = Logger.getLogger("foo.bar");
+        final TestHandler handler = new TestHandler();
+        foobar.addHandler(handler);
+        foobar.setLevel(Level.CONFIG);
+
+        final ResourceBundle anonBundle = new ListBundle();
+        try {
+            // First we're going to call logrb on a logger that
+            // has no bundle set...
+
+            // For each possible logrb form...
+            for (TestCase test : TestCase.values()) {
+                // For various resource bundles
+                for (ResourceBundle b : new ResourceBundle[] {
+                    anonBundle, bundle, bundle_fr, propertyBundle,
+                    anonBundle, null, propertyBundle_fr,
+                }) {
+                    // Prints the resource bundle base name (can be null,
+                    //   we don't enforce non-null names in logrb.
+                    final String baseName = getBaseName(b);
+                    System.out.println("Testing " + test + " with " + baseName);
+
+                    // log in the 'foobar' logger using bundle 'b'
+                    test.logrb(foobar, b);
+
+                    // check that the correct bundle was set in the published
+                    // LogRecord
+                    if (handler.lastBundle != b) {
+                        throw new RuntimeException("Unexpected bundle: "
+                                + handler.lastBundle);
+                    }
+
+                    // check that the correct bundle name was set in the published
+                    // LogRecord
+                    if (!Objects.equals(handler.lastBundleName, baseName)) {
+                        throw new RuntimeException("Unexpected bundle name: "
+                                + handler.lastBundleName);
+                    }
+
+                    // check that calling foobar.logrb() had no side effect on
+                    // the bundle used by foobar. foobar should still have no
+                    // bundle set.
+                    if (foobar.getResourceBundle() != null) {
+                        throw new RuntimeException("Unexpected bundle: "
+                            + foobar.getResourceBundle());
+                    }
+                    if (foobar.getResourceBundleName() != null) {
+                        throw new RuntimeException("Unexpected bundle: "
+                            + foobar.getResourceBundleName());
+                    }
+
+                    // Test that the last published log record had all the
+                    // data that this test case had logged (message, parameters,
+                    // thrown...
+                    test.checkLogged(handler);
+                }
+            }
+
+            // No we're going to set a resource bundle on the foobar logger
+            // and do it all again...
+
+            // For the same bundle in two different locales
+            for (ResourceBundle propBundle : new ResourceBundle[] {
+                propertyBundle, propertyBundle_fr,
+            }) {
+
+                // set the bundle on foobar...
+                foobar.setResourceBundle(propBundle);
+
+                // check the bundle was correctly set...
+                if (!propBundle.equals(foobar.getResourceBundle())) {
+                    throw new RuntimeException("Unexpected bundle: "
+                            + foobar.getResourceBundle());
+                }
+                if (!Objects.equals(getBaseName(propBundle), foobar.getResourceBundleName())) {
+                    throw new RuntimeException("Unexpected bundle name: "
+                            + foobar.getResourceBundleName());
+                }
+
+                System.out.println("Configuring " + foobar.getName() + " with "
+                        + propBundle);
+
+                // for each possible logrb form...
+                for (TestCase test : TestCase.values()) {
+
+                    // for various resource bundles
+                    for (ResourceBundle b : new ResourceBundle[] {
+                        anonBundle, bundle, null, bundle_fr, propertyBundle,
+                        anonBundle, propertyBundle_fr,
+                    }) {
+
+                        final String baseName = getBaseName(b);
+                        System.out.println("Testing " + test + " with " + baseName);
+
+                        // call foobar.logrb
+                        test.logrb(foobar, b);
+
+                        // check which resource bundle was used (should be
+                        // the one passed to logrb)
+                        if (handler.lastBundle != b) {
+                            throw new RuntimeException("Unexpected bundle: "
+                                    + handler.lastBundle);
+                        }
+                        if (!Objects.equals(handler.lastBundleName, baseName)) {
+                            throw new RuntimeException("Unexpected bundle name: "
+                                    + handler.lastBundleName);
+                        }
+
+                        // Verify there was no side effect on the bundle that
+                        // had been previously set on the logger...
+                        if (foobar.getResourceBundle() != propBundle) {
+                            throw new RuntimeException("Unexpected bundle: "
+                                + foobar.getResourceBundle());
+                        }
+                        if (!Objects.equals(getBaseName(propBundle),
+                                foobar.getResourceBundleName())) {
+                            throw new RuntimeException("Unexpected bundle name: "
+                                + foobar.getResourceBundleName());
+                        }
+
+                        // Checked that the published LogRecord had the
+                        // expected content logged by this test case.
+                        test.checkLogged(handler);
+                    }
+                }
+            }
+
+            // Now we're going to the same thing, but with a logger which
+            // has an inherited resource bundle.
+            Logger foobaz = Logger.getLogger("foo.bar.baz");
+
+            // check that foobaz has no bundle set locally.
+            if (foobaz.getResourceBundle() != null) {
+                throw new RuntimeException("Unexpected bundle: "
+                        + foobaz.getResourceBundle());
+            }
+            if (foobaz.getResourceBundleName() != null) {
+                throw new RuntimeException("Unexpected bundle: "
+                        + foobaz.getResourceBundle());
+            }
+
+            // The current locale should have no effect on logrb.
+            Locale.setDefault(Locale.GERMAN); // shouldn't change anything...
+
+            // for each possible logrb form
+            for (TestCase test : TestCase.values()) {
+
+                // for various resource bundle
+                for (ResourceBundle b : new ResourceBundle[] {
+                    anonBundle, bundle, bundle_fr, propertyBundle, null,
+                     anonBundle, propertyBundle_fr,
+                }) {
+                    final String baseName = getBaseName(b);
+                    System.out.println("Testing " + test + " with "
+                            + foobaz.getName() + " and "
+                            + baseName);
+
+                    // call foobaz.logrb with the bundle
+                    test.logrb(foobaz, b);
+
+                    // check that the bundle passed to logrb was used.
+                    if (handler.lastBundle != b) {
+                        throw new RuntimeException("Unexpected bundle: "
+                                + handler.lastBundle);
+                    }
+                    if (!Objects.equals(handler.lastBundleName, baseName)) {
+                        throw new RuntimeException("Unexpected bundle name: "
+                                + handler.lastBundleName);
+                    }
+
+                    // check that there was no effect on the bundle set
+                    // on foobaz: it should still be null.
+                    if (foobaz.getResourceBundle() != null) {
+                        throw new RuntimeException("Unexpected bundle: "
+                            + foobaz.getResourceBundle());
+                    }
+                    if (foobaz.getResourceBundleName() != null) {
+                        throw new RuntimeException("Unexpected bundle: "
+                            + foobaz.getResourceBundleName());
+                    }
+
+                    // check that the last published log record had all the
+                    // data that was logged by this testcase.
+                    test.checkLogged(handler);
+                }
+            }
+
+        } finally {
+            Locale.setDefault(defaultLocale);
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/Logger/logrb/resources/ListBundle.java	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package resources;
+
+import java.util.ListResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public class ListBundle extends ListResourceBundle {
+
+    @Override
+    protected Object[][] getContents() {
+        return new Object[][] {
+            { "dummy", "foo" },
+        };
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/Logger/logrb/resources/ListBundle_fr.java	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package resources;
+
+import java.util.ListResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public class ListBundle_fr extends ListResourceBundle {
+
+    @Override
+    protected Object[][] getContents() {
+        return new Object[][] {
+            { "dummy", "toto" },
+        };
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/Logger/logrb/resources/PropertyBundle.properties	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 20013, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+dummy=foo
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/Logger/logrb/resources/PropertyBundle_fr.properties	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+dummy=toto
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/Logger/setResourceBundle/TestSetResourceBundle.java	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.Permission;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.PropertyPermission;
+import java.util.ResourceBundle;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.logging.LoggingPermission;
+import resources.ListBundle;
+
+/**
+ * @test
+ * @bug 8013839
+ * @summary tests Logger.setResourceBundle;
+ * @build TestSetResourceBundle resources.ListBundle resources.ListBundle_fr
+ * @run main/othervm TestSetResourceBundle UNSECURE
+ * @run main/othervm TestSetResourceBundle PERMISSION
+ * @run main/othervm TestSetResourceBundle SECURE
+ * @author danielfuchs
+ */
+public class TestSetResourceBundle {
+
+    final static String LIST_BUNDLE_NAME = "resources.ListBundle";
+    final static String PROPERTY_BUNDLE_NAME = "resources.PropertyBundle";
+
+    /**
+     * A dummy handler class that we can use to check the bundle/bundle name
+     * that was present in the last LogRecord instance published.
+     */
+    static final class TestHandler extends Handler {
+        ResourceBundle lastBundle = null;
+        String lastBundleName = null;
+        @Override
+        public void publish(LogRecord record) {
+            lastBundle = record.getResourceBundle();
+            lastBundleName = record.getResourceBundleName();
+        }
+
+        @Override
+        public void flush() {
+        }
+
+        @Override
+        public void close() throws SecurityException {
+        }
+    }
+
+    /**
+     * We will test setResourceBundle() in 3 configurations.
+     * UNSECURE: No security manager.
+     * SECURE: With the security manager present - and the required
+     *         LoggingPermission("control") granted.
+     * PERMISSION: With the security manager present - and the required
+     *         LoggingPermission("control") *not* granted. Here we will
+     *         test that the expected security permission is thrown.
+     */
+    public static enum TestCase {
+        UNSECURE, SECURE, PERMISSION;
+        public void run(String name) throws Exception {
+            System.out.println("Running test case: " + name());
+            switch (this) {
+                case UNSECURE:
+                    testUnsecure(name);
+                    break;
+                case SECURE:
+                    testSecure(name);
+                    break;
+                case PERMISSION:
+                    testPermission(name);
+                    break;
+                default:
+                    throw new Error("Unknown test case: "+this);
+            }
+        }
+        public String loggerName(String name) {
+            return name().toLowerCase(Locale.ROOT) + "." + name;
+        }
+    }
+
+    public static void main(String... args) throws Exception {
+
+        Locale defaultLocale = Locale.getDefault();
+
+        if (args == null || args.length == 0) {
+            args = new String[] {
+                TestCase.UNSECURE.name(),
+                TestCase.SECURE.name()
+            };
+        }
+
+        for (String testName : args) {
+            TestCase test = TestCase.valueOf(testName);
+            try {
+                test.run(test.loggerName("foo.bar"));
+            } finally {
+                Locale.setDefault(defaultLocale);
+            }
+        }
+    }
+
+    /**
+     * Test without security manager.
+     * @param loggerName The logger to use.
+     * @throws Exception if the test fails.
+     */
+    public static void testUnsecure(String loggerName) throws Exception {
+        if (System.getSecurityManager() != null) {
+            throw new Error("Security manager is set");
+        }
+        test(loggerName);
+    }
+
+    /**
+     * Test with security manager.
+     * @param loggerName The logger to use.
+     * @throws Exception if the test fails.
+     */
+    public static void testSecure(String loggerName) throws Exception {
+        if (System.getSecurityManager() != null) {
+            throw new Error("Security manager is already set");
+        }
+        Policy.setPolicy(new SimplePolicy(TestCase.SECURE));
+        System.setSecurityManager(new SecurityManager());
+        test(loggerName);
+    }
+
+    /**
+     * Test the LoggingPermission("control") is required.
+     * @param loggerName The logger to use.
+     */
+    public static void testPermission(String loggerName) {
+        if (System.getSecurityManager() != null) {
+            throw new Error("Security manager is already set");
+        }
+        Policy.setPolicy(new SimplePolicy(TestCase.PERMISSION));
+        System.setSecurityManager(new SecurityManager());
+        final ResourceBundle bundle = ResourceBundle.getBundle(LIST_BUNDLE_NAME);
+        Logger foobar = Logger.getLogger(loggerName);
+        try {
+            foobar.setResourceBundle(bundle);
+            throw new RuntimeException("Permission not checked!");
+        } catch (AccessControlException x) {
+            if (x.getPermission() instanceof LoggingPermission) {
+                if ("control".equals(x.getPermission().getName())) {
+                    System.out.println("Got expected exception: " + x);
+                    return;
+                }
+            }
+            throw new RuntimeException("Unexpected exception: "+x, x);
+        }
+
+    }
+
+    static String getBaseName(ResourceBundle bundle) {
+        return bundle == null ? null : bundle.getBaseBundleName();
+    }
+
+    public static void test(String loggerName) throws Exception {
+
+        final ResourceBundle bundle = ResourceBundle.getBundle(LIST_BUNDLE_NAME);
+        Logger foobar = Logger.getLogger(loggerName);
+
+        // Checks that IAE is thrown if the bundle has a null base name.
+        try {
+            foobar.setResourceBundle(new ListBundle());
+            throw new RuntimeException("Expected exception not raised!");
+        } catch (IllegalArgumentException x) {
+            System.out.println("Got expected exception: " + x);
+        }
+
+        // Verify that resource bundle was not set.
+        if (foobar.getResourceBundle() != null) {
+            throw new RuntimeException("Unexpected bundle: "
+                    + foobar.getResourceBundle());
+        }
+        if (foobar.getResourceBundleName() != null) {
+            throw new RuntimeException("Unexpected bundle: "
+                    + foobar.getResourceBundleName());
+        }
+
+        // Set acceptable resource bundle on logger.
+        foobar.setResourceBundle(bundle);
+
+        // check that the bundle has been set correctly
+        if (bundle != foobar.getResourceBundle()) {
+            throw new RuntimeException("Unexpected bundle: "
+                    + foobar.getResourceBundle());
+        }
+        if (!Objects.equals(getBaseName(bundle), foobar.getResourceBundleName())) {
+            throw new RuntimeException("Unexpected bundle name: "
+                    + foobar.getResourceBundleName());
+        }
+
+        // Check that we can replace the bundle with a bundle of the same name.
+        final ResourceBundle bundle_fr =
+                ResourceBundle.getBundle(LIST_BUNDLE_NAME, Locale.FRENCH);
+        foobar.setResourceBundle(bundle_fr);
+
+        if (bundle_fr != foobar.getResourceBundle()) {
+            throw new RuntimeException("Unexpected bundle: "
+                    + foobar.getResourceBundle());
+        }
+        if (!Objects.equals(getBaseName(bundle_fr), foobar.getResourceBundleName())) {
+            throw new RuntimeException("Unexpected bundle name: "
+                    + foobar.getResourceBundleName());
+        }
+
+        // Create a child logger
+        Logger foobaz = Logger.getLogger(loggerName + ".baz");
+
+        // Check that the child logger does not have a bundle set locally
+        if (foobaz.getResourceBundle() != null) {
+            throw new RuntimeException("Unexpected bundle: "
+                    + foobar.getResourceBundle());
+        }
+        if (foobaz.getResourceBundleName() != null) {
+            throw new RuntimeException("Unexpected bundle: "
+                    + foobar.getResourceBundleName());
+        }
+
+
+        // Add a handler on the child logger.
+        final TestHandler handler = new TestHandler();
+        foobaz.addHandler(handler);
+
+        // log a message on the child logger
+        foobaz.severe("dummy");
+
+        // checks that the message has been logged with the bundle
+        // inherited from the parent logger
+        if (!LIST_BUNDLE_NAME.equals(handler.lastBundleName)) {
+            throw new RuntimeException("Unexpected bundle name: "
+                    + handler.lastBundleName);
+        }
+        if (!bundle_fr.equals(handler.lastBundle)) {
+            throw new RuntimeException("Unexpected bundle: "
+                    + handler.lastBundle);
+        }
+
+        // Check that we can get set a bundle on the child logger
+        // using Logger.getLogger.
+        foobaz = Logger.getLogger(loggerName + ".baz", PROPERTY_BUNDLE_NAME);
+
+        // check that the child logger has the correct bundle.
+        // it should no longer inherit it from its parent.
+        if (!PROPERTY_BUNDLE_NAME.equals(foobaz.getResourceBundleName())) {
+            throw new RuntimeException("Unexpected bundle name: "
+                    + foobaz.getResourceBundleName());
+        }
+        if (!PROPERTY_BUNDLE_NAME.equals(foobaz.getResourceBundle().getBaseBundleName())) {
+            throw new RuntimeException("Unexpected bundle name: "
+                    + foobaz.getResourceBundle().getBaseBundleName());
+        }
+
+        // log a message on the child logger
+        foobaz.severe("dummy");
+
+        // check that the last published log record has the appropriate
+        // bundle.
+        if (!PROPERTY_BUNDLE_NAME.equals(handler.lastBundleName)) {
+            throw new RuntimeException("Unexpected bundle name: "
+                    + handler.lastBundleName);
+        }
+        if (foobaz.getResourceBundle() != handler.lastBundle) {
+            throw new RuntimeException("Unexpected bundle: "
+                    + handler.lastBundle);
+        }
+
+        // try to set a bundle that has a different name, and checks that
+        // it fails in IAE.
+        try {
+            foobaz.setResourceBundle(bundle_fr);
+            throw new RuntimeException("Expected exception not raised!");
+        } catch (IllegalArgumentException x) {
+            System.out.println("Got expected exception: " + x);
+        }
+
+        // Test with a subclass of logger which overrides
+        // getResourceBundle() and getResourceBundleName()
+        Logger customLogger = new Logger(foobar.getName()+".bie", null) {
+            @Override
+            public ResourceBundle getResourceBundle() {
+                return bundle_fr;
+            }
+
+            @Override
+            public String getResourceBundleName() {
+                return PROPERTY_BUNDLE_NAME;
+            }
+        };
+
+        final TestHandler handler2 = new TestHandler();
+        customLogger.addHandler(handler2);
+        customLogger.setLevel(Level.FINE);
+        LogManager.getLogManager().addLogger(customLogger);
+
+        Logger l = Logger.getLogger(customLogger.getName());
+        if (l != customLogger) {
+            throw new RuntimeException("Wrong logger: " + l);
+        }
+
+        // log on the custom logger.
+        customLogger.fine("dummy");
+
+        // check that the log record had the correct bundle.
+        if (! PROPERTY_BUNDLE_NAME.equals(handler2.lastBundleName)) {
+            throw new RuntimeException("Unexpected bundle name: "
+                    + handler2.lastBundleName);
+        }
+        if (! PROPERTY_BUNDLE_NAME.equals(customLogger.getResourceBundleName())) {
+            throw new RuntimeException("Unexpected bundle name: "
+                    + customLogger.getResourceBundleName());
+        }
+        if (bundle_fr != handler2.lastBundle) {
+            throw new RuntimeException("Unexpected bundle: "
+                    + handler2.lastBundle);
+        }
+        if (bundle_fr != customLogger.getResourceBundle()) {
+            throw new RuntimeException("Unexpected bundle: "
+                    + customLogger.getResourceBundle());
+        }
+
+        // Do the same thing again with a child of the custom logger.
+        Logger biebar = Logger.getLogger(customLogger.getName() + ".bar");
+        biebar.fine("dummy");
+
+        // because getResourceBundleName() is called on parent logger
+        //         we will have handler2.lastBundleName = PROPERTY_BUNDLE_NAME
+        if (!PROPERTY_BUNDLE_NAME.equals(handler2.lastBundleName)) {
+            throw new RuntimeException("Unexpected bundle name: "
+                    + handler2.lastBundleName);
+        }
+        // because getResourceBundle() is not called on parent logger
+        //         we will have getBaseName(handler2.lastBundle) = PROPERTY_BUNDLE_NAME
+        //         and not handler2.lastBundle = bundle_fr
+        if (handler2.lastBundle == null) {
+            throw new RuntimeException("Unexpected bundle: "
+                    + handler2.lastBundle);
+        }
+        if (!PROPERTY_BUNDLE_NAME.equals(getBaseName(handler2.lastBundle))) {
+            throw new RuntimeException("Unexpected bundle name: "
+                    + getBaseName(handler2.lastBundle));
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+
+        final Permissions permissions;
+        public SimplePolicy(TestCase test) {
+            permissions = new Permissions();
+            if (test != TestCase.PERMISSION) {
+                permissions.add(new LoggingPermission("control", null));
+            }
+            // required for calling Locale.setDefault in the test.
+            permissions.add(new PropertyPermission("user.language", "write"));
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            return permissions.implies(permission);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/Logger/setResourceBundle/resources/ListBundle.java	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package resources;
+
+import java.util.ListResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public class ListBundle extends ListResourceBundle {
+
+    @Override
+    protected Object[][] getContents() {
+        return new Object[][] {
+            { "dummy", "foo" },
+        };
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/Logger/setResourceBundle/resources/ListBundle_fr.java	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package resources;
+
+import java.util.ListResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public class ListBundle_fr extends ListResourceBundle {
+
+    @Override
+    protected Object[][] getContents() {
+        return new Object[][] {
+            { "dummy", "toto" },
+        };
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/Logger/setResourceBundle/resources/PropertyBundle.properties	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 20013, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+dummy=foo
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/logging/Logger/setResourceBundle/resources/PropertyBundle_fr.properties	Wed Oct 16 20:47:30 2013 +0200
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+dummy=toto
+