blob: b71b4294372dd82f6549b3c077b2d8574f8800a0 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved.
3 * 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 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
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() {
434 this.network = false;
435 }
436 public SimpleLoader(boolean simulateNetworkLoad) {
437 this.network = simulateNetworkLoad;
438 }
439 public Class loadClass(final String className, final boolean resolveIt)
440 throws ClassNotFoundException {
441 Class result;
442 synchronized (this) {
443 result = findLoadedClass(className);
444 if (result == null) {
445 if (network) {
446 try {
447 Thread.sleep(100);
448 } catch (java.lang.InterruptedException e) {
449 }
450 }
451 result = super.findSystemClass(className);
452 if ((result != null) && resolveIt) {
453 resolveClass(result);
454 }
455 }
456 }
457 return result;
458 }
459 }
460
461 private final class Loader extends ClassLoader {
462 public final Vector loadedClasses = new Vector();
463 private String[] classesToLoad;
464 private String[] classesToWaitFor;
465
466 public Loader() {
467 classesToLoad = new String[0];
468 classesToWaitFor = new String[0];
469 }
470
471 public Loader(final String[] classesToLoadIn, final String[] classesToWaitForIn) {
472 classesToLoad = classesToLoadIn;
473 classesToWaitFor = classesToWaitForIn;
474 }
475
476 /**
477 * Load a class. Files we can load take preference over ones the system
478 * can load.
479 */
480 private byte[] getClassData(final String className) {
481 boolean shouldLoad = false;
482 for (int i = classesToLoad.length-1; i >= 0; --i) {
483 if (className.equals(classesToLoad[i])) {
484 shouldLoad = true;
485 break;
486 }
487 }
488
489 if (shouldLoad) {
490 final String name = CLASS_PREFIX+className+CLASS_SUFFIX;
491 try {
492 final InputStream fi = this.getClass().getClassLoader().getResourceAsStream(name);
493 final byte[] result = new byte[fi.available()];
494 fi.read(result);
495 return result;
496 } catch (Exception e) {
497 logln("Error loading test class: "+name);
498 logln(e.toString());
499 return null;
500 }
501 } else {
502 return null;
503 }
504 }
505
506 /**
507 * Load a class. Files we can load take preference over ones the system
508 * can load.
509 */
510 public Class loadClass(final String className, final boolean resolveIt)
511 throws ClassNotFoundException {
512 Class result;
513 synchronized (this) {
514 logln(">>"+threadName()+">load "+className);
515 loadedClasses.addElement(className);
516
517 result = findLoadedClass(className);
518 if (result == null) {
519 final byte[] classData = getClassData(className);
520 if (classData == null) {
521 //we don't have a local copy of this one
522 logln("Loading system class: "+className);
523 result = loadFromSystem(className);
524 } else {
525 result = defineClass(classData, 0, classData.length);
526 if (result == null) {
527 //there was an error defining the class
528 result = loadFromSystem(className);
529 }
530 }
531 if ((result != null) && resolveIt) {
532 resolveClass(result);
533 }
534 }
535 }
536 for (int i = classesToWaitFor.length-1; i >= 0; --i) {
537 if (className.equals(classesToWaitFor[i])) {
538 rendezvous();
539 break;
540 }
541 }
542 logln("<<"+threadName()+"<load "+className);
543 return result;
544 }
545
546 /**
547 * Delegate loading to the system loader
548 */
549 private Class loadFromSystem(String className) throws ClassNotFoundException {
550 return super.findSystemClass(className);
551 }
552
553 public void logClasses(String title) {
554 logln(title);
555 for (int i = 0; i < loadedClasses.size(); i++) {
556 logln(" "+loadedClasses.elementAt(i));
557 }
558 logln("");
559 }
560
561 public int notifyCount = 0;
562 public int waitForNotify(int count) {
563 return waitForNotify(count, 0);
564 }
565 public synchronized int waitForNotify(int count, long time) {
566 logln(">>"+threadName()+">waitForNotify");
567 if (count > notifyCount) {
568 try {
569 wait(time);
570 } catch (InterruptedException e) {
571 }
572 } else {
573 logln(" count("+count+") > notifyCount("+notifyCount+")");
574 }
575 logln("<<"+threadName()+"<waitForNotify");
576 return notifyCount;
577 }
578 private synchronized void notifyEveryone() {
579 logln(">>"+threadName()+">notifyEveryone");
580 notifyCount++;
581 notifyAll();
582 logln("<<"+threadName()+"<notifyEveryone");
583 }
584 private void rendezvous() {
585 final Thread current = Thread.currentThread();
586 if (current instanceof ConcurrentLoadingThread) {
587 ((ConcurrentLoadingThread)current).waitUntilPinged();
588 }
589 }
590 }
591
592 private static String threadName() {
593 return threadName(Thread.currentThread());
594 }
595
596 private static String threadName(Thread t) {
597 String temp = t.toString();
598 int ndx = temp.indexOf("Thread[");
599 temp = temp.substring(ndx + "Thread[".length());
600 ndx = temp.indexOf(',');
601 temp = temp.substring(0, ndx);
602 return temp;
603 }
604
605 /** Fill memory to force all SoftReferences to be GCed */
606 private void causeResourceBundleCacheFlush() {
607 logln("Filling memory...");
608 int allocationSize = 1024;
609 Vector memoryHog = new Vector();
610 try {
611 while (true) {
612 memoryHog.addElement(new byte[allocationSize]);
613 allocationSize *= 2;
614 }
615 } catch (Throwable e) {
616 logln("Caught "+e+" filling memory");
617 } finally{
618 memoryHog = null;
619 System.gc();
620 }
621 logln("last allocation size: " + allocationSize);
622 }
623
624 /**
625 * NOTE: this problem is not externally testable and can only be
626 * verified through code inspection unless special code to force
627 * a task switch is inserted into ResourceBundle.
628 * The class Bug4168625Resource_sp exists. It's parent bundle
629 * (Bug4168625Resource) contains a resource string with the tag
630 * "language" but Bug4168625Resource_sp does not.
631 * Assume two threads are executing, ThreadA and ThreadB and they both
632 * load a resource Bug4168625Resource with from sp locale.
633 * ResourceBundle.getBundle adds a bundle to the bundle cache (in
634 * findBundle) before it sets the bundle's parent (in getBundle after
635 * returning from findBundle).
636 * <P>
637 * <pre>
638 * ThreadA.getBundle("Bug4168625Resource", new Locale("sp"));
639 * A-->load Bug4168625Resource_sp
640 * A-->find cached Bug4168625Resource
641 * A-->cache Bug4168625Resource_sp as Bug4168625Resource_sp
642 * ThreadB.getBundle("Bug4168625Resource", new Locale("sp"));
643 * B-->find cached Bug4168625Resource_sp
644 * B-->return Bug4168625Resource_sp
645 * ThreadB.bundle.getString("language");
646 * B-->try to find "language" in Bug4168625Resource_sp
647 * B-->Bug4168625Resource_sp does not have a parent, so return null;
648 * ThreadB.System.out.println("Some unknown country");
649 * A-->set parent of Bug4168625Resource_sp to Bug4168625Resource
650 * A-->return Bug4168625Resource_sp (the same bundle ThreadB got)
651 * ThreadA.bundle.getString("language");
652 * A-->try to find "language" in Bug4168625Resource_sp
653 * A-->try to find "language" in Bug4168625Resource (parent of Bug4168625Resource_sp)
654 * A-->return the string
655 * ThreadA.System.out.println("Langauge = "+country);
656 * ThreadB.bundle.getString("language");
657 * B-->try to find "language" in Bug4168625Resource_sp
658 * B-->try to find "language" in Bug4168625Resource (parent of Bug4168625Resource_sp)
659 * B-->return the string
660 * ThreadB.System.out.println("Langauge = "+country);
661 * </pre>
662 * <P>
663 * Note that the first call to getString() by ThreadB returns null, but the second
664 * returns a value. Thus to ThreadB, the bundle appears to change. ThreadA gets
665 * the expected results right away.
666 */
667}