Initial load
diff --git a/test/java/util/ResourceBundle/ReferencesTest.java b/test/java/util/ResourceBundle/ReferencesTest.java
new file mode 100644
index 0000000..57b3aff
--- /dev/null
+++ b/test/java/util/ResourceBundle/ReferencesTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+/*
+ * @test
+ * @bug 4405807
+ * @run main/othervm -Xms10m ReferencesTest
+ * @summary Verify that references from ResourceBundle cache don't prevent
+ * class loader reclamation.
+ */
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * This test relies on the current behavior of the garbage collector and is
+ * therefore no clear indicator of whether the fix for 4405807 works.
+ * If the test fails, it might indicate a regression, or it might just mean
+ * that a less aggressive garbage collector is used.
+ */
+public class ReferencesTest {
+
+ private static final int CLASS_LOADER_COUNT = 20;
+
+ // These two parallel arrays have references to the same class loaders.
+ // The weakLoaders array lets us track whether class loaders are being
+ // reclaimed after the references in the loaders array are nulled out.
+ private static ClassLoader[] loaders = new ClassLoader[CLASS_LOADER_COUNT];
+ private static WeakReference[] weakLoaders = new WeakReference[CLASS_LOADER_COUNT];
+
+ public static void main(String[] args) throws Exception {
+
+ URL testDirectory = new File(System.getProperty("test.classes", ".")).toURL();
+
+ for (int i = 0; i < loaders.length; i++) {
+ URL[] urls = { testDirectory };
+ loaders[i] = new URLClassLoader(urls);
+ weakLoaders[i] = new WeakReference(loaders[i]);
+ }
+
+ // fill the ResourceBundle cache with entries for half the class loaders
+ loadBundles(0, CLASS_LOADER_COUNT / 2);
+
+ report("After loading resource bundles for first half of class loaders: ");
+
+ // release the first half of the class loaders
+ for (int i = 0; i < CLASS_LOADER_COUNT / 2; i++) {
+ loaders[i] = null;
+ }
+
+ System.gc();
+
+ report("After releasing first half of class loaders: ");
+
+ // fill the ResourceBundle cache with entries for second half the class loaders
+ loadBundles(CLASS_LOADER_COUNT / 2, CLASS_LOADER_COUNT);
+
+ report("After loading resource bundles for second half of class loaders: ");
+
+ // release the second half of the class loaders
+ for (int i = CLASS_LOADER_COUNT / 2; i < CLASS_LOADER_COUNT; i++) {
+ loaders[i] = null;
+ }
+
+ System.gc();
+
+ report("After releasing second half of class loaders: ");
+
+ // The garbage collector in Tiger actually has reclaimed all class
+ // loaders at this point, but in order not to become too dependent
+ // on the current behavior, we only require that the first half
+ // has been reclaimed.
+ if (countLoaders(0, CLASS_LOADER_COUNT / 2) > 0) {
+ throw new RuntimeException("Too many class loaders not reclaimed yet.");
+ }
+ }
+
+ private static void report(String when) throws Exception {
+ int first = countLoaders(0, CLASS_LOADER_COUNT / 2);
+ int second = countLoaders(CLASS_LOADER_COUNT / 2, CLASS_LOADER_COUNT);
+
+ Class clazz = ResourceBundle.class;
+ Field cacheList = clazz.getDeclaredField("cacheList");
+ cacheList.setAccessible(true);
+ int cacheSize = ((Map)cacheList.get(clazz)).size();
+
+ System.out.println(when);
+ System.out.println(" " + first + " loaders alive in first half");
+ System.out.println(" " + second + " loaders alive in second half");
+ System.out.println(" " + cacheSize + " entries in resource bundle cache");
+ }
+
+ private static void loadBundles(int start, int end) throws Exception {
+ for (int i = start; i < end; i++) {
+ // There's no resource bundle for NonExistantBundle - this
+ // let's us test the case where a resource bundle is not found,
+ // which in the past created a SoftReference on the value side
+ // of the cache.
+ try {
+ ResourceBundle.getBundle("NonExistantBundle", Locale.US, loaders[i]);
+ } catch (MissingResourceException e) {
+ }
+ // There's a base resource bundle for ReferencesTestBundle - the
+ // normal case.
+ ResourceBundle.getBundle("ReferencesTestBundle", Locale.US, loaders[i]);
+ }
+ }
+
+ private static int countLoaders(int start, int end) {
+ int count = 0;
+ for (int i = start; i < end; i++) {
+ if (weakLoaders[i].get() != null) {
+ count++;
+ }
+ }
+ return count;
+ }
+}