blob: 8dc213db170f90f1746f11564dca0803ae9a82b6 [file] [log] [blame]
duke6e45e102007-12-01 00:00:00 +00001/*
ohair2283b9d2010-05-25 15:58:33 -07002 * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
duke6e45e102007-12-01 00:00:00 +00003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
ohair2283b9d2010-05-25 15:58:33 -070019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
duke6e45e102007-12-01 00:00:00 +000022 */
23/*
24 @test
25 @summary test Resource Bundle for bug 4168625
26 @build Bug4168625Class Bug4168625Getter Bug4168625Resource Bug4168625Resource3 Bug4168625Resource3_en Bug4168625Resource3_en_CA Bug4168625Resource3_en_IE Bug4168625Resource3_en_US Bug4168625Resource2_en_US Bug4168625Resource2
27 @run main/timeout=600 Bug4168625Test
28 @bug 4168625
29*/
30/*
31 *
32 *
33 * (C) Copyright IBM Corp. 1999 - All Rights Reserved
34 *
35 * Portions Copyright 2007 by Sun Microsystems, Inc.,
36 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
37 * All rights reserved.
38 *
39 * This software is the confidential and proprietary information
40 * of Sun Microsystems, Inc. ("Confidential Information"). You
41 * shall not disclose such Confidential Information and shall use
42 * it only in accordance with the terms of the license agreement
43 * you entered into with Sun.
44 *
45 * The original version of this source code and documentation is
46 * copyrighted and owned by IBM. These materials are provided
47 * under terms of a License Agreement between IBM and Sun.
48 * This technology is protected by multiple US and International
49 * patents. This notice and attribution to IBM may not be removed.
50 *
51 */
52
53import java.util.*;
54import java.io.*;
55
56/**
57 * This test tries to correct three efficiency problems with the caching
58 * mechanism of ResourceBundle. All tests assume that none of the bundles
59 * have been previously loaded and cached. It also allows concurrent loads
60 * of resource bundles to be performed if the bundles are unrelated (ex. a
61 * load of a local system resource by one thread while another thread is
62 * doing a slow load over a network).
63 */
64public class Bug4168625Test extends RBTestFmwk {
65 public static void main(String[] args) throws Exception {
66 new Bug4168625Test().run(args);
67 }
68
69 /**
70 * Verify that getBundle will do something reasonable when part of the
71 * resource hierarchy is missing.
72 */
73 public void testMissingParent() throws Exception {
74 final Locale oldDefault = Locale.getDefault();
75 Locale.setDefault(new Locale("en", "US"));
76 try {
77 final Locale loc = new Locale("jf", "jf");
78 ResourceBundle bundle = ResourceBundle.getBundle("Bug4168625Resource2", loc);
79 final String s1 = bundle.getString("name");
80 if (!s1.equals("Bug4168625Resource2_en_US")) {
81 errln("getBundle did not find leaf bundle: "+bundle.getClass().getName());
82 }
83 final String s2 = bundle.getString("baseName");
84 if (!s2.equals("Bug4168625Resource2")) {
85 errln("getBundle did not set up proper inheritance chain");
86 }
87 } finally {
88 Locale.setDefault(oldDefault);
89 }
90 }
91
92 /**
93 * Previous versions of ResourceBundle have had the following
94 * caching behavior. Assume the classes
95 * Bug4168625Resource_fr_FR, Bug4168625Resource_fr,
96 * Bug4168625Resource_en_US, and Bug4168625Resource_en don't
97 * exist. The class Bug4168625Resource does. Assume the default
98 * locale is en_US.
99 * <P>
100 * <pre>
101 * getBundle("Bug4168625Resource", new Locale("fr", "FR"));
102 * -->try to load Bug4168625Resource_fr_FR
103 * -->try to load Bug4168625Resource_fr
104 * -->try to load Bug4168625Resource_en_US
105 * -->try to load Bug4168625Resource_en
106 * -->load Bug4168625Resource
107 * -->cache Bug4168625Resource as Bug4168625Resource
108 * -->cache Bug4168625Resource as Bug4168625Resource_en
109 * -->cache Bug4168625Resource as Bug4168625Resource_en_US
110 * -->return Bug4168625Resource
111 * getBundle("Bug4168625Resource", new Locale("fr", "FR"));
112 * -->try to load Bug4168625Resource_fr_FR
113 * -->try to load Bug4168625Resource_fr
114 * -->find cached Bug4168625Resource_en_US
115 * -->return Bug4168625Resource_en_US (which is realy Bug4168625Resource)
116 * </pre>
117 * <P>
118 * The second call causes two loads for Bug4168625Resource_fr_FR and
119 * Bug4168625Resource_en which have already been tried and failed. These
120 * two loads should have been cached as Bug4168625Resource by the first
121 * call.
122 *
123 * The following, more efficient behavior is desired:
124 * <P>
125 * <pre>
126 * getBundle("Bug4168625Resource", new Locale("fr", "FR"));
127 * -->try to load Bug4168625Resource_fr_FR
128 * -->try to load Bug4168625Resource_fr
129 * -->try to load Bug4168625Resource_en_US
130 * -->try to load Bug4168625Resource_en
131 * -->load Bug4168625Resource
132 * -->cache Bug4168625Resource as Bug4168625Resource
133 * -->cache Bug4168625Resource as Bug4168625Resource_en
134 * -->cache Bug4168625Resource as Bug4168625Resource_en_US
135 * -->cache Bug4168625Resource as Bug4168625Resource_fr
136 * -->cache Bug4168625Resource as Bug4168625Resource_fr_FR
137 * -->return Bug4168625Resource
138 * getBundle("Bug4168625Resource", new Locale("fr", "FR"));
139 * -->find cached Bug4168625Resource_fr_FR
140 * -->return Bug4168625Resource_en_US (which is realy Bug4168625Resource)
141 * </pre>
142 * <P>
143 *
144 */
145 public void testCacheFailures() throws Exception {
146 checkResourceLoading("Bug4168625Resource", new Locale("fr", "FR"));
147 }
148
149 /**
150 * Previous versions of ResourceBundle have had the following
151 * caching behavior. Assume the current locale is locale is en_US.
152 * The classes Bug4168625Resource_en_US, and Bug4168625Resource_en don't
153 * exist. The class Bug4168625Resource does.
154 * <P>
155 * <pre>
156 * getBundle("Bug4168625Resource", new Locale("en", "US"));
157 * -->try to load Bug4168625Resource_en_US
158 * -->try to load Bug4168625Resource_en
159 * -->try to load Bug4168625Resource_en_US
160 * -->try to load Bug4168625Resource_en
161 * -->load Bug4168625Resource
162 * -->cache Bug4168625Resource as Bug4168625Resource
163 * -->cache Bug4168625Resource as Bug4168625Resource_en
164 * -->cache Bug4168625Resource as Bug4168625Resource_en_US
165 * -->return Bug4168625Resource
166 * </pre>
167 * <P>
168 * The redundant loads of Bug4168625Resource_en_US and Bug4168625Resource_en
169 * should not occur. The desired behavior is as follows:
170 * <P>
171 * <pre>
172 * getBundle("Bug4168625Resource", new Locale("en", "US"));
173 * -->try to load Bug4168625Resource_en_US
174 * -->try to load Bug4168625Resource_en
175 * -->load Bug4168625Resource
176 * -->cache Bug4168625Resource as Bug4168625Resource
177 * -->cache Bug4168625Resource as Bug4168625Resource_en
178 * -->cache Bug4168625Resource as Bug4168625Resource_en_US
179 * -->return Bug4168625Resource
180 * </pre>
181 * <P>
182 */
183 public void testRedundantLoads() throws Exception {
184 checkResourceLoading("Bug4168625Resource", Locale.getDefault());
185 }
186
187 /**
188 * Ensure that resources are only loaded once and are cached correctly
189 */
190 private void checkResourceLoading(String resName, Locale l) throws Exception {
191 final Loader loader = new Loader( new String[] { "Bug4168625Class" }, new String[] { "Bug4168625Resource3_en_US", "Bug4168625Resource3_en_CA" });
192 final Class c = loader.loadClass("Bug4168625Class");
193 Bug4168625Getter test = (Bug4168625Getter)c.newInstance();
194 final String resClassName;
195 if (l.toString().length() > 0) {
196 resClassName = resName+"_"+l;
197 } else {
198 resClassName = resName;
199 }
200
201 Object bundle = test.getResourceBundle(resName, l);
202 loader.logClasses("Initial lookup of "+resClassName+" generated the following loads:");
203
204 final Vector lastLoad = new Vector(loader.loadedClasses.size());
205 boolean dups = false;
206 for (int i = loader.loadedClasses.size() - 1; i >= 0 ; i--) {
207 final Object item = loader.loadedClasses.elementAt(i);
208 loader.loadedClasses.removeElementAt(i);
209 if (loader.loadedClasses.contains(item)) {
210 logln("Resource loaded more than once: "+item);
211 dups = true;
212 } else {
213 lastLoad.addElement(item);
214 }
215 }
216 if (dups) {
217 errln("ResourceBundle loaded some classes multiple times");
218 }
219
220 loader.loadedClasses.removeAllElements();
221 bundle = test.getResourceBundle(resName, l);
222 loader.logClasses("Second lookup of "+resClassName+" generated the following loads:");
223
224 dups = false;
225 for (int i = 0; i < loader.loadedClasses.size(); i++) {
226 Object item = loader.loadedClasses.elementAt(i);
227 if (lastLoad.contains(item)) {
228 logln("ResourceBundle did not cache "+item+" correctly");
229 dups = true;
230 }
231 }
232 if (dups) {
233 errln("Resource bundle not caching some classes properly");
234 }
235 }
236
237 /**
238 * Previous versions of ResourceBundle exhibited the following caching behavior.
239 * Assume the class Bug4168625Resource_en exists. Bug4168625Resource_en_US does
240 * not. Two threads, ThreadA and ThreadB both try to get the same bundle.
241 * <P>
242 * <pre>
243 * ThreadA.getBundle("Bug4168625Resource", new Locale("en", "US"));
244 * A-->try to load Bug4168625Resource_en_US
245 * ThreadB.getBundle("Bug4168625Resource", new Locale("en", "US"));
246 * B-->try to load Bug4168625Resource_en_US
247 * B-->load Bug4168625Resource_en (#1)
248 * A-->load Bug4168625Resource_en (#2)
249 * A-->cache Bug4168625Resource_en (#2) as Bug4168625Resource_en
250 * A-->cache Bug4168625Resource_en (#2) as Bug4168625Resource_en_US
251 * A-->return Bug4168625Resource_en (#2)
252 * B-->cache Bug4168625Resource_en (#1) as Bug4168625Resource_en
253 * B-->cache Bug4168625Resource_en (#1) as Bug4168625Resource_en_US
254 * B-->return Bug4168625Resource_en (#1)
255 * </pre>
256 * <P>
257 * Both threads try and fail to load Bug4168625Resource_en_US. Both
258 * threads load Bug4168625Resource_en. Both threads get their own copy
259 * of the Bug4168625Resource_en resource.
260 *
261 * The desired behavior is as follows:
262 * <P>
263 * <pre>
264 * ThreadA.getBundle("Bug4168625Resource", new Locale("en", "US"));
265 * A-->try to load Bug4168625Resource_en_US
266 * ThreadB.getBundle("Bug4168625Resource", new Locale("en", "US"));
267 * B-->try to load Bug4168625Resource_en_US
268 * B-->load Bug4168625Resource_en
269 * A-->load Bug4168625Resource_en (block in ResourceBundle.getBundle)
270 * B-->cache Bug4168625Resource_en as Bug4168625Resource_en
271 * B-->cache Bug4168625Resource_en as Bug4168625Resource_en_US
272 * A-->return Bug4168625Resource_en
273 * B-->return Bug4168625Resource_en
274 * </pre>
275 * <P>
276 * Note that both threads return the same bundle object.
277 */
278 public void testConcurrentLoading1() throws Exception {
279 final Loader loader = new Loader( new String[] { "Bug4168625Class" }, new String[] { "Bug4168625Resource3_en_US", "Bug4168625Resource3_en_CA" });
280 final Class c = loader.loadClass("Bug4168625Class");
281 final Bug4168625Getter test = (Bug4168625Getter)c.newInstance();
282
283 //both threads want the same resource
284 ConcurrentLoadingThread thread1 = new ConcurrentLoadingThread(loader, test, new Locale("en", "US"));
285 ConcurrentLoadingThread thread2 = new ConcurrentLoadingThread(loader, test, new Locale("en", "US"));
286
287 thread1.start(); //start thread 1
288 loader.waitForNotify(1); //wait for thread1 to do getBundle & block in loader
289 thread2.start(); //start second thread
290 loader.waitForNotify(2, 1000); //wait until thread2 blocks somewhere in getBundle
291 thread1.ping(); //continue both threads
292 thread2.ping();
293
294 thread1.join(); //wait unitl both threads complete
295 thread2.join();
296
297 //Now, examine the class loads that were done.
298 loader.logClasses("Classes loaded after completion of both threads:");
299
300 boolean dups = false;
301 for (int i = loader.loadedClasses.size() - 1; i >= 0 ; i--) {
302 final Object item = loader.loadedClasses.elementAt(i);
303 loader.loadedClasses.removeElementAt(i);
304 if (loader.loadedClasses.contains(item)) {
305 logln("Resource loaded more than once: "+item);
306 dups = true;
307 }
308 }
309 if (dups) {
310 errln("ResourceBundle loaded some classes multiple times");
311 }
312 }
313
314 private class ConcurrentLoadingThread extends Thread {
315 private Loader loader;
316 public Object bundle;
317 private Bug4168625Getter test;
318 private Locale locale;
319 private String resourceName = "Bug4168625Resource3";
320 public ConcurrentLoadingThread(Loader loader, Bug4168625Getter test, Locale l, String resourceName) {
321 this.loader = loader;
322 this.test = test;
323 this.locale = l;
324 this.resourceName = resourceName;
325 }
326 public ConcurrentLoadingThread(Loader loader, Bug4168625Getter test, Locale l) {
327 this.loader = loader;
328 this.test = test;
329 this.locale = l;
330 }
331 public void run() {
332 try {
333 logln(">>"+threadName()+">run");
334 bundle = test.getResourceBundle(resourceName, locale);
335 } catch (Exception e) {
336 errln("TEST CAUGHT UNEXPECTED EXCEPTION: "+e);
337 } finally {
338 logln("<<"+threadName()+"<run");
339 }
340 }
341 public synchronized void waitUntilPinged() {
342 logln(">>"+threadName()+">waitUntilPinged");
343 loader.notifyEveryone();
344 try {
345 wait(30000); //wait 30 seconds max.
346 } catch (InterruptedException e) {
347 logln("Test deadlocked.");
348 }
349 logln("<<"+threadName()+"<waitUntilPinged");
350 }
351 public synchronized void ping() {
352 logln(">>"+threadName()+">ping "+threadName(this));
353 notifyAll();
354 logln("<<"+threadName()+"<ping "+threadName(this));
355 }
356 };
357
358 /**
359 * This test ensures that multiple resources can be loading at the same
360 * time as long as they don't depend on each other in some way.
361 */
362 public void testConcurrentLoading2() throws Exception {
363 final Loader loader = new Loader( new String[] { "Bug4168625Class" }, new String[] { "Bug4168625Resource3_en_US", "Bug4168625Resource3_en_CA" });
364 final Class c = loader.loadClass("Bug4168625Class");
365 final Bug4168625Getter test = (Bug4168625Getter)c.newInstance();
366
367 ConcurrentLoadingThread thread1 = new ConcurrentLoadingThread(loader, test, new Locale("en", "CA"));
368 ConcurrentLoadingThread thread2 = new ConcurrentLoadingThread(loader, test, new Locale("en", "IE"));
369
370 thread1.start(); //start thread 1
371 loader.waitForNotify(1); //wait for thread1 to do getBundle & block in loader
372 thread2.start(); //start second thread
373 thread2.join(1000); //wait until thread2 blocks somewhere in getBundle
374
375 //Thread1 should be blocked inside getBundle at the class loader
376 //Thread2 should have completed its getBundle call and terminated
377 if (!thread1.isAlive() || thread2.isAlive()) {
378 errln("ResourceBundle.getBundle not allowing legal concurrent loads");
379 }
380
381 thread1.ping(); //continue thread1
382 thread1.join();
383 thread2.join();
384 }
385
386 /**
387 * This test ensures that a resource loads correctly (with all its parents)
388 * when memory is very low (ex. the cache gets purged during a load).
389 */
390 public void testLowMemoryLoad() throws Exception {
391 final String[] classToLoad = { "Bug4168625Class" };
392 final String[] classToWait = { "Bug4168625Resource3_en_US","Bug4168625Resource3_en","Bug4168625Resource3" };
393 final Loader loader = new Loader(classToLoad, classToWait);
394 final Class c = loader.loadClass("Bug4168625Class");
395 final Bug4168625Getter test = (Bug4168625Getter)c.newInstance();
396 causeResourceBundleCacheFlush();
397
398 ConcurrentLoadingThread thread1 = new ConcurrentLoadingThread(loader, test, new Locale("en", "US"));
399 thread1.start(); //start thread 1
400 loader.waitForNotify(1); //wait for thread1 to do getBundle(en_US) & block in loader
401 causeResourceBundleCacheFlush(); //cause a cache flush
402 thread1.ping(); //kick thread 1
403 loader.waitForNotify(2); //wait for thread1 to do getBundle(en) & block in loader
404 causeResourceBundleCacheFlush(); //cause a cache flush
405 thread1.ping(); //kick thread 1
406 loader.waitForNotify(3); //wait for thread1 to do getBundle(en) & block in loader
407 causeResourceBundleCacheFlush(); //cause a cache flush
408 thread1.ping(); //kick thread 1
409 thread1.ping(); //kick thread 1
410 thread1.join(1000); //wait until thread2 blocks somewhere in getBundle
411
412 ResourceBundle bundle = (ResourceBundle)thread1.bundle;
413 String s1 = bundle.getString("Bug4168625Resource3_en_US");
414 String s2 = bundle.getString("Bug4168625Resource3_en");
415 String s3 = bundle.getString("Bug4168625Resource3");
416 if ((s1 == null) || (s2 == null) || (s3 == null)) {
417 errln("Bundle not constructed correctly. The parent chain is incorrect.");
418 }
419 }
420
421 /**
422 * A simple class loader that loads classes from the current
423 * working directory. The loader will block the current thread
424 * of execution before it returns when it tries to load
425 * the class "Bug4168625Resource3_en_US".
426 */
427 private static final String CLASS_PREFIX = "";
428 private static final String CLASS_SUFFIX = ".class";
429
430 private static final class SimpleLoader extends ClassLoader {
431 private boolean network = false;
432
433 public SimpleLoader() {
mchung019ce7d2010-06-15 20:34:49 -0700434 super(SimpleLoader.class.getClassLoader());
duke6e45e102007-12-01 00:00:00 +0000435 this.network = false;
436 }
437 public SimpleLoader(boolean simulateNetworkLoad) {
mchung019ce7d2010-06-15 20:34:49 -0700438 super(SimpleLoader.class.getClassLoader());
duke6e45e102007-12-01 00:00:00 +0000439 this.network = simulateNetworkLoad;
440 }
441 public Class loadClass(final String className, final boolean resolveIt)
442 throws ClassNotFoundException {
443 Class result;
444 synchronized (this) {
445 result = findLoadedClass(className);
446 if (result == null) {
447 if (network) {
448 try {
449 Thread.sleep(100);
450 } catch (java.lang.InterruptedException e) {
451 }
452 }
mchung019ce7d2010-06-15 20:34:49 -0700453 result = getParent().loadClass(className);
duke6e45e102007-12-01 00:00:00 +0000454 if ((result != null) && resolveIt) {
455 resolveClass(result);
456 }
457 }
458 }
459 return result;
460 }
461 }
462
463 private final class Loader extends ClassLoader {
464 public final Vector loadedClasses = new Vector();
465 private String[] classesToLoad;
466 private String[] classesToWaitFor;
467
468 public Loader() {
mchung019ce7d2010-06-15 20:34:49 -0700469 super(Loader.class.getClassLoader());
duke6e45e102007-12-01 00:00:00 +0000470 classesToLoad = new String[0];
471 classesToWaitFor = new String[0];
472 }
473
474 public Loader(final String[] classesToLoadIn, final String[] classesToWaitForIn) {
mchung019ce7d2010-06-15 20:34:49 -0700475 super(Loader.class.getClassLoader());
duke6e45e102007-12-01 00:00:00 +0000476 classesToLoad = classesToLoadIn;
477 classesToWaitFor = classesToWaitForIn;
478 }
479
480 /**
481 * Load a class. Files we can load take preference over ones the system
482 * can load.
483 */
484 private byte[] getClassData(final String className) {
485 boolean shouldLoad = false;
486 for (int i = classesToLoad.length-1; i >= 0; --i) {
487 if (className.equals(classesToLoad[i])) {
488 shouldLoad = true;
489 break;
490 }
491 }
492
493 if (shouldLoad) {
494 final String name = CLASS_PREFIX+className+CLASS_SUFFIX;
495 try {
496 final InputStream fi = this.getClass().getClassLoader().getResourceAsStream(name);
497 final byte[] result = new byte[fi.available()];
498 fi.read(result);
499 return result;
500 } catch (Exception e) {
501 logln("Error loading test class: "+name);
502 logln(e.toString());
503 return null;
504 }
505 } else {
506 return null;
507 }
508 }
509
510 /**
511 * Load a class. Files we can load take preference over ones the system
512 * can load.
513 */
514 public Class loadClass(final String className, final boolean resolveIt)
515 throws ClassNotFoundException {
516 Class result;
517 synchronized (this) {
518 logln(">>"+threadName()+">load "+className);
519 loadedClasses.addElement(className);
520
521 result = findLoadedClass(className);
522 if (result == null) {
523 final byte[] classData = getClassData(className);
524 if (classData == null) {
525 //we don't have a local copy of this one
526 logln("Loading system class: "+className);
527 result = loadFromSystem(className);
528 } else {
529 result = defineClass(classData, 0, classData.length);
530 if (result == null) {
531 //there was an error defining the class
532 result = loadFromSystem(className);
533 }
534 }
535 if ((result != null) && resolveIt) {
536 resolveClass(result);
537 }
538 }
539 }
540 for (int i = classesToWaitFor.length-1; i >= 0; --i) {
541 if (className.equals(classesToWaitFor[i])) {
542 rendezvous();
543 break;
544 }
545 }
546 logln("<<"+threadName()+"<load "+className);
547 return result;
548 }
549
550 /**
mchung019ce7d2010-06-15 20:34:49 -0700551 * Delegate loading to its parent class loader that loads the test classes.
552 * In othervm mode, the parent class loader is the system class loader;
553 * in samevm mode, the parent class loader is the jtreg URLClassLoader.
duke6e45e102007-12-01 00:00:00 +0000554 */
555 private Class loadFromSystem(String className) throws ClassNotFoundException {
mchung019ce7d2010-06-15 20:34:49 -0700556 return getParent().loadClass(className);
duke6e45e102007-12-01 00:00:00 +0000557 }
558
559 public void logClasses(String title) {
560 logln(title);
561 for (int i = 0; i < loadedClasses.size(); i++) {
562 logln(" "+loadedClasses.elementAt(i));
563 }
564 logln("");
565 }
566
567 public int notifyCount = 0;
568 public int waitForNotify(int count) {
569 return waitForNotify(count, 0);
570 }
571 public synchronized int waitForNotify(int count, long time) {
572 logln(">>"+threadName()+">waitForNotify");
573 if (count > notifyCount) {
574 try {
575 wait(time);
576 } catch (InterruptedException e) {
577 }
578 } else {
579 logln(" count("+count+") > notifyCount("+notifyCount+")");
580 }
581 logln("<<"+threadName()+"<waitForNotify");
582 return notifyCount;
583 }
584 private synchronized void notifyEveryone() {
585 logln(">>"+threadName()+">notifyEveryone");
586 notifyCount++;
587 notifyAll();
588 logln("<<"+threadName()+"<notifyEveryone");
589 }
590 private void rendezvous() {
591 final Thread current = Thread.currentThread();
592 if (current instanceof ConcurrentLoadingThread) {
593 ((ConcurrentLoadingThread)current).waitUntilPinged();
594 }
595 }
596 }
597
598 private static String threadName() {
599 return threadName(Thread.currentThread());
600 }
601
602 private static String threadName(Thread t) {
603 String temp = t.toString();
604 int ndx = temp.indexOf("Thread[");
605 temp = temp.substring(ndx + "Thread[".length());
606 ndx = temp.indexOf(',');
607 temp = temp.substring(0, ndx);
608 return temp;
609 }
610
611 /** Fill memory to force all SoftReferences to be GCed */
612 private void causeResourceBundleCacheFlush() {
613 logln("Filling memory...");
614 int allocationSize = 1024;
615 Vector memoryHog = new Vector();
616 try {
617 while (true) {
618 memoryHog.addElement(new byte[allocationSize]);
619 allocationSize *= 2;
620 }
621 } catch (Throwable e) {
622 logln("Caught "+e+" filling memory");
623 } finally{
624 memoryHog = null;
625 System.gc();
626 }
627 logln("last allocation size: " + allocationSize);
628 }
629
630 /**
631 * NOTE: this problem is not externally testable and can only be
632 * verified through code inspection unless special code to force
633 * a task switch is inserted into ResourceBundle.
634 * The class Bug4168625Resource_sp exists. It's parent bundle
635 * (Bug4168625Resource) contains a resource string with the tag
636 * "language" but Bug4168625Resource_sp does not.
637 * Assume two threads are executing, ThreadA and ThreadB and they both
638 * load a resource Bug4168625Resource with from sp locale.
639 * ResourceBundle.getBundle adds a bundle to the bundle cache (in
640 * findBundle) before it sets the bundle's parent (in getBundle after
641 * returning from findBundle).
642 * <P>
643 * <pre>
644 * ThreadA.getBundle("Bug4168625Resource", new Locale("sp"));
645 * A-->load Bug4168625Resource_sp
646 * A-->find cached Bug4168625Resource
647 * A-->cache Bug4168625Resource_sp as Bug4168625Resource_sp
648 * ThreadB.getBundle("Bug4168625Resource", new Locale("sp"));
649 * B-->find cached Bug4168625Resource_sp
650 * B-->return Bug4168625Resource_sp
651 * ThreadB.bundle.getString("language");
652 * B-->try to find "language" in Bug4168625Resource_sp
653 * B-->Bug4168625Resource_sp does not have a parent, so return null;
654 * ThreadB.System.out.println("Some unknown country");
655 * A-->set parent of Bug4168625Resource_sp to Bug4168625Resource
656 * A-->return Bug4168625Resource_sp (the same bundle ThreadB got)
657 * ThreadA.bundle.getString("language");
658 * A-->try to find "language" in Bug4168625Resource_sp
659 * A-->try to find "language" in Bug4168625Resource (parent of Bug4168625Resource_sp)
660 * A-->return the string
661 * ThreadA.System.out.println("Langauge = "+country);
662 * ThreadB.bundle.getString("language");
663 * B-->try to find "language" in Bug4168625Resource_sp
664 * B-->try to find "language" in Bug4168625Resource (parent of Bug4168625Resource_sp)
665 * B-->return the string
666 * ThreadB.System.out.println("Langauge = "+country);
667 * </pre>
668 * <P>
669 * Note that the first call to getString() by ThreadB returns null, but the second
670 * returns a value. Thus to ThreadB, the bundle appears to change. ThreadA gets
671 * the expected results right away.
672 */
673}