8170772: ResourceBundle improper caching causes tools/javadoc tests intermittently
authormchung
Fri, 09 Dec 2016 16:38:34 -0800
changeset 42467 46018567e1a6
parent 42466 321b4a747cec
child 42468 7a9555a7e080
8170772: ResourceBundle improper caching causes tools/javadoc tests intermittently Reviewed-by: dfuchs, naoto
jdk/src/java.base/share/classes/java/util/ResourceBundle.java
jdk/test/java/util/ResourceBundle/modules/cache/CacheTest.java
jdk/test/java/util/ResourceBundle/modules/cache/src/mainbundles/jdk/test/resources/MyResources.properties
jdk/test/java/util/ResourceBundle/modules/cache/src/mainbundles/jdk/test/util/Bundles.java
jdk/test/java/util/ResourceBundle/modules/cache/src/mainbundles/module-info.java
jdk/test/java/util/ResourceBundle/modules/cache/src/test/jdk/test/Main.java
jdk/test/java/util/ResourceBundle/modules/cache/src/test/module-info.java
jdk/test/java/util/ResourceBundle/modules/security/src/m1/module-info.java
jdk/test/java/util/ResourceBundle/modules/security/src/test/jdk/test/Main.java
--- a/jdk/src/java.base/share/classes/java/util/ResourceBundle.java	Fri Dec 09 21:25:54 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/ResourceBundle.java	Fri Dec 09 16:38:34 2016 -0800
@@ -650,6 +650,7 @@
         private Locale locale;
         private KeyElementReference<ClassLoader> loaderRef;
         private KeyElementReference<Module> moduleRef;
+        private KeyElementReference<Module> callerRef;
 
 
         // bundle format which is necessary for calling
@@ -680,7 +681,7 @@
         // Boolean.TRUE if the factory method caller provides a ResourceBundleProvier.
         private Boolean callerHasProvider;
 
-        CacheKey(String baseName, Locale locale, ClassLoader loader, Module module) {
+        CacheKey(String baseName, Locale locale, ClassLoader loader, Module module, Module caller) {
             Objects.requireNonNull(module);
 
             this.name = baseName;
@@ -691,6 +692,8 @@
                 this.loaderRef = new KeyElementReference<>(loader, referenceQueue, this);
             }
             this.moduleRef = new KeyElementReference<>(module, referenceQueue, this);
+            this.callerRef = new KeyElementReference<>(caller, referenceQueue, this);
+
             calculateHashCode();
         }
 
@@ -726,6 +729,10 @@
             return moduleRef.get();
         }
 
+        Module getCallerModule() {
+            return callerRef.get();
+        }
+
         ServiceLoader<ResourceBundleProvider> getProviders() {
             if (!providersChecked) {
                 providers = getServiceLoader(getModule(), name);
@@ -767,6 +774,8 @@
                 }
                 ClassLoader loader = getLoader();
                 Module module = getModule();
+                Module caller = getCallerModule();
+
                 return (otherEntry.loaderRef != null)
                         // with a null reference we can no longer find
                         // out which class loader or module was referenced; so
@@ -774,7 +783,9 @@
                         && (loader != null)
                         && (loader == otherEntry.getLoader())
                         && (module != null)
-                        && (module.equals(otherEntry.getModule()));
+                        && (module.equals(otherEntry.getModule()))
+                        && (caller != null)
+                        && (caller.equals(otherEntry.getCallerModule()));
             } catch (NullPointerException | ClassCastException e) {
             }
             return false;
@@ -796,6 +807,10 @@
             if (module != null) {
                 hashCodeCache ^= module.hashCode();
             }
+            Module caller = getCallerModule();
+            if (caller != null) {
+                hashCodeCache ^= caller.hashCode();
+            }
         }
 
         @Override
@@ -808,6 +823,9 @@
                 }
                 clone.moduleRef = new KeyElementReference<>(getModule(),
                                                             referenceQueue, clone);
+                clone.callerRef = new KeyElementReference<>(getCallerModule(),
+                                                            referenceQueue, clone);
+
                 // Clear the reference to ResourceBundleProviders and the flag
                 clone.providers = null;
                 clone.providersChecked = false;
@@ -1665,7 +1683,7 @@
         // loader, and module will never change during the bundle loading
         // process. We have to make sure that the locale is set before
         // using it as a cache key.
-        CacheKey cacheKey = new CacheKey(baseName, locale, loader, module);
+        CacheKey cacheKey = new CacheKey(baseName, locale, loader, module, callerModule);
         ResourceBundle bundle = null;
 
         // Quick lookup of the cache.
@@ -1869,7 +1887,7 @@
                     bundle = loadBundleFromProviders(baseName,
                                                      targetLocale,
                                                      cacheKey.getProviders(),
-                                                      cacheKey);
+                                                     cacheKey);
                 }
             }
 
@@ -2303,8 +2321,9 @@
 
     private static void clearCache(ClassLoader loader, Module module) {
         Set<CacheKey> set = cacheList.keySet();
-        set.stream().filter((key) -> (key.getLoader() == loader && key.getModule() == module))
-                .forEach(set::remove);
+        set.stream()
+           .filter((key) -> (key.getLoader() == loader && key.getModule() == module))
+           .forEach(set::remove);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/ResourceBundle/modules/cache/CacheTest.java	Fri Dec 09 16:38:34 2016 -0800
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+/**
+ * @test
+ * @bug 8170772
+ * @library /lib/testlibrary
+ * @modules jdk.compiler
+ * @build CacheTest CompilerUtils jdk.testlibrary.*
+ * @run testng CacheTest
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import jdk.testlibrary.OutputAnalyzer;
+import static jdk.testlibrary.ProcessTools.*;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+
+@Test
+public class CacheTest {
+
+    private static final String TEST_SRC = System.getProperty("test.src");
+
+    private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+    private static final Path MODS_DIR = Paths.get("mods");
+
+    private static final String TEST_MODULE = "test";
+    private static final String MAIN_BUNDLES_MODULE = "mainbundles";
+
+    private static final String MAIN = "test/jdk.test.Main";
+    private static final String MAIN_CLASS = "jdk.test.Main";
+
+    @BeforeTest
+    public void compileTestModules() throws Exception {
+
+        for (String mn : new String[] {MAIN_BUNDLES_MODULE, TEST_MODULE}) {
+            boolean compiled =
+                CompilerUtils.compile(SRC_DIR.resolve(mn),
+                                      MODS_DIR.resolve(mn),
+                            "--module-path", MODS_DIR.toString());
+            assertTrue(compiled, "module " + mn + " did not compile");
+        }
+
+        Path res = Paths.get("jdk", "test", "resources", "MyResources.properties");
+        Path dest = MODS_DIR.resolve(MAIN_BUNDLES_MODULE).resolve(res);
+        Files.createDirectories(dest.getParent());
+        Files.copy(SRC_DIR.resolve(MAIN_BUNDLES_MODULE).resolve(res), dest);
+    }
+
+    /**
+     * Load resource bundle in cache first.  Verify that the subsequent
+     * loading of ResourceBundle from another module will not get it from cache.
+     */
+    @Test
+    public void loadCacheFirst() throws Exception {
+        assertTrue(executeTestJava("--module-path", MODS_DIR.toString(),
+                                   "-m", MAIN, "cache")
+                        .outputTo(System.out)
+                        .errorTo(System.out)
+                        .getExitValue() == 0);
+
+        assertTrue(executeTestJava("--class-path", MODS_DIR.resolve(TEST_MODULE).toString(),
+                                   "--module-path", MODS_DIR.resolve(MAIN_BUNDLES_MODULE).toString(),
+                                   "--add-modules", MAIN_BUNDLES_MODULE,
+                                   MAIN_CLASS, "cache")
+                        .outputTo(System.out)
+                        .errorTo(System.out)
+                        .getExitValue() == 0);
+    }
+
+    /**
+     * Load non-existent resource bundle in cache first.  Verify that
+     * the subsequent loading of ResourceBundle from another module
+     * can still successfully load the bundle.
+     */
+    @Test
+    public void loadNonExistentBundleInCache() throws Exception {
+        assertTrue(executeTestJava("--module-path", MODS_DIR.toString(),
+                                   "-m", MAIN)
+                        .outputTo(System.out)
+                        .errorTo(System.out)
+                        .getExitValue() == 0);
+
+        assertTrue(executeTestJava("--class-path", MODS_DIR.resolve(TEST_MODULE).toString(),
+                                   "--module-path", MODS_DIR.resolve(MAIN_BUNDLES_MODULE).toString(),
+                                   "--add-modules", MAIN_BUNDLES_MODULE,
+                                   MAIN_CLASS)
+                        .outputTo(System.out)
+                        .errorTo(System.out)
+                        .getExitValue() == 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/ResourceBundle/modules/cache/src/mainbundles/jdk/test/resources/MyResources.properties	Fri Dec 09 16:38:34 2016 -0800
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 2016, 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.
+#
+
+key=root: message
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/ResourceBundle/modules/cache/src/mainbundles/jdk/test/util/Bundles.java	Fri Dec 09 16:38:34 2016 -0800
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, 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 jdk.test.util;
+
+import java.util.ResourceBundle;
+
+public class Bundles {
+    static final String MAIN_BUNDLES_RESOURCE = "jdk.test.resources.MyResources";
+
+    public static ResourceBundle getBundle() {
+        return ResourceBundle.getBundle(MAIN_BUNDLES_RESOURCE);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/ResourceBundle/modules/cache/src/mainbundles/module-info.java	Fri Dec 09 16:38:34 2016 -0800
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+module mainbundles {
+    exports jdk.test.util;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/ResourceBundle/modules/cache/src/test/jdk/test/Main.java	Fri Dec 09 16:38:34 2016 -0800
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015, 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 jdk.test;
+
+import java.lang.reflect.Module;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+public class Main {
+    static final String MAIN_BUNDLES_RESOURCE = "jdk.test.resources.MyResources";
+    public static void main(String[] args) {
+        if (args.length == 1 && args[0].equals("cache")) {
+            // first load resource bundle in the cache
+            jdk.test.util.Bundles.getBundle();
+
+            // fail to load resource bundle that is present in the cache
+            try {
+                Module mainbundles = jdk.test.util.Bundles.class.getModule();
+                ResourceBundle rb = ResourceBundle.getBundle(MAIN_BUNDLES_RESOURCE, mainbundles);
+                throw new RuntimeException("ERROR: test module loads " + rb);
+            } catch (MissingResourceException e) {
+                System.out.println("Expected: " + e.getMessage());
+            }
+        } else {
+            // fail to load resource bundle; NON_EXISTENT_BUNDLE in the cache
+            try {
+                Module mainbundles = jdk.test.util.Bundles.class.getModule();
+                ResourceBundle rb = ResourceBundle.getBundle(MAIN_BUNDLES_RESOURCE, mainbundles);
+                throw new RuntimeException("ERROR: test module loads " + rb);
+            } catch (MissingResourceException e) {
+                System.out.println("Expected: " + e.getMessage());
+            }
+
+            // successfully load the resource bundle
+            jdk.test.util.Bundles.getBundle();
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/ResourceBundle/modules/cache/src/test/module-info.java	Fri Dec 09 16:38:34 2016 -0800
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+module test {
+    requires mainbundles;
+}
--- a/jdk/test/java/util/ResourceBundle/modules/security/src/m1/module-info.java	Fri Dec 09 21:25:54 2016 +0000
+++ b/jdk/test/java/util/ResourceBundle/modules/security/src/m1/module-info.java	Fri Dec 09 16:38:34 2016 -0800
@@ -23,4 +23,5 @@
 
 module m1 {
     exports p1;
+    opens p1.resources to test;
 }
--- a/jdk/test/java/util/ResourceBundle/modules/security/src/test/jdk/test/Main.java	Fri Dec 09 21:25:54 2016 +0000
+++ b/jdk/test/java/util/ResourceBundle/modules/security/src/test/jdk/test/Main.java	Fri Dec 09 16:38:34 2016 -0800
@@ -39,9 +39,11 @@
 
         // resource in another module
         Module m1 = p1.Bundle.class.getModule();
+
+        // bundles loaded with different cache key
         ResourceBundle rb1 = Bundle.getBundle(M1_RESOURCE_BUNDLE_NAME);
         ResourceBundle rb2 = ResourceBundle.getBundle(M1_RESOURCE_BUNDLE_NAME, m1);
-        if (rb1 != rb2) {
+        if (rb1 == rb2) {
             throw new RuntimeException("unexpected resource bundle");
         }