Path: blob/master/test/jdk/java/util/ResourceBundle/Bug4168625Test.java
41152 views
/*1* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/22/*23@test24@summary test Resource Bundle for bug 416862525@build Bug4168625Class Bug4168625Getter Bug4168625Resource Bug4168625Resource3 Bug4168625Resource3_en Bug4168625Resource3_en_CA Bug4168625Resource3_en_IE Bug4168625Resource3_en_US Bug4168625Resource2_en_US Bug4168625Resource226@run main/timeout=600 Bug4168625Test27@bug 4168625 699333928*/29/*30*31*32* (C) Copyright IBM Corp. 1999 - All Rights Reserved33*34* The original version of this source code and documentation is35* copyrighted and owned by IBM. These materials are provided36* under terms of a License Agreement between IBM and Sun.37* This technology is protected by multiple US and International38* patents. This notice and attribution to IBM may not be removed.39*40*/4142import java.util.*;43import java.io.*;4445/**46* This test tries to correct two efficiency problems with the caching47* mechanism of ResourceBundle. It also allows concurrent loads48* of resource bundles to be performed if the bundles are unrelated (ex. a49* load of a local system resource by one thread while another thread is50* doing a slow load over a network).51*/52public class Bug4168625Test extends RBTestFmwk {53public static void main(String[] args) throws Exception {54new Bug4168625Test().run(args);55}5657/**58* Verify that getBundle will do something reasonable when part of the59* resource hierarchy is missing.60*/61public void testMissingParent() throws Exception {62final Locale oldDefault = Locale.getDefault();63Locale.setDefault(new Locale("en", "US"));64try {65final Locale loc = new Locale("jf", "jf");66ResourceBundle bundle = ResourceBundle.getBundle("Bug4168625Resource2", loc);67final String s1 = bundle.getString("name");68if (!s1.equals("Bug4168625Resource2_en_US")) {69errln("getBundle did not find leaf bundle: "+bundle.getClass().getName());70}71final String s2 = bundle.getString("baseName");72if (!s2.equals("Bug4168625Resource2")) {73errln("getBundle did not set up proper inheritance chain");74}75} finally {76Locale.setDefault(oldDefault);77}78}7980/**81* Previous versions of ResourceBundle have had the following82* caching behavior. Assume the classes83* Bug4168625Resource_fr_FR, Bug4168625Resource_fr,84* Bug4168625Resource_en_US, and Bug4168625Resource_en don't85* exist. The class Bug4168625Resource does. Assume the default86* locale is en_US.87* <P>88* <pre>89* getBundle("Bug4168625Resource", new Locale("fr", "FR"));90* -->try to load Bug4168625Resource_fr_FR91* -->try to load Bug4168625Resource_fr92* -->try to load Bug4168625Resource_en_US93* -->try to load Bug4168625Resource_en94* -->load Bug4168625Resource95* -->cache Bug4168625Resource as Bug4168625Resource96* -->cache Bug4168625Resource as Bug4168625Resource_en97* -->cache Bug4168625Resource as Bug4168625Resource_en_US98* -->return Bug4168625Resource99* getBundle("Bug4168625Resource", new Locale("fr", "FR"));100* -->try to load Bug4168625Resource_fr_FR101* -->try to load Bug4168625Resource_fr102* -->find cached Bug4168625Resource_en_US103* -->return Bug4168625Resource_en_US (which is realy Bug4168625Resource)104* </pre>105* <P>106* The second call causes two loads for Bug4168625Resource_fr_FR and107* Bug4168625Resource_en which have already been tried and failed. These108* two loads should have been cached as Bug4168625Resource by the first109* call.110*111* The following, more efficient behavior is desired:112* <P>113* <pre>114* getBundle("Bug4168625Resource", new Locale("fr", "FR"));115* -->try to load Bug4168625Resource_fr_FR116* -->try to load Bug4168625Resource_fr117* -->try to load Bug4168625Resource_en_US118* -->try to load Bug4168625Resource_en119* -->load Bug4168625Resource120* -->cache Bug4168625Resource as Bug4168625Resource121* -->cache Bug4168625Resource as Bug4168625Resource_en122* -->cache Bug4168625Resource as Bug4168625Resource_en_US123* -->cache Bug4168625Resource as Bug4168625Resource_fr124* -->cache Bug4168625Resource as Bug4168625Resource_fr_FR125* -->return Bug4168625Resource126* getBundle("Bug4168625Resource", new Locale("fr", "FR"));127* -->find cached Bug4168625Resource_fr_FR128* -->return Bug4168625Resource_en_US (which is realy Bug4168625Resource)129* </pre>130* <P>131*132*/133public void testCacheFailures() throws Exception {134checkResourceLoading("Bug4168625Resource", new Locale("fr", "FR"));135}136137/**138* Previous versions of ResourceBundle have had the following139* caching behavior. Assume the current locale is locale is en_US.140* The classes Bug4168625Resource_en_US, and Bug4168625Resource_en don't141* exist. The class Bug4168625Resource does.142* <P>143* <pre>144* getBundle("Bug4168625Resource", new Locale("en", "US"));145* -->try to load Bug4168625Resource_en_US146* -->try to load Bug4168625Resource_en147* -->try to load Bug4168625Resource_en_US148* -->try to load Bug4168625Resource_en149* -->load Bug4168625Resource150* -->cache Bug4168625Resource as Bug4168625Resource151* -->cache Bug4168625Resource as Bug4168625Resource_en152* -->cache Bug4168625Resource as Bug4168625Resource_en_US153* -->return Bug4168625Resource154* </pre>155* <P>156* The redundant loads of Bug4168625Resource_en_US and Bug4168625Resource_en157* should not occur. The desired behavior is as follows:158* <P>159* <pre>160* getBundle("Bug4168625Resource", new Locale("en", "US"));161* -->try to load Bug4168625Resource_en_US162* -->try to load Bug4168625Resource_en163* -->load Bug4168625Resource164* -->cache Bug4168625Resource as Bug4168625Resource165* -->cache Bug4168625Resource as Bug4168625Resource_en166* -->cache Bug4168625Resource as Bug4168625Resource_en_US167* -->return Bug4168625Resource168* </pre>169* <P>170*/171public void testRedundantLoads() throws Exception {172checkResourceLoading("Bug4168625Resource", Locale.getDefault());173}174175/**176* Ensure that resources are only loaded once and are cached correctly177*/178private void checkResourceLoading(String resName, Locale l) throws Exception {179final Loader loader = new Loader( new String[] { "Bug4168625Class" }, new String[] { "Bug4168625Resource3_en_US", "Bug4168625Resource3_en_CA" });180final Class c = loader.loadClass("Bug4168625Class");181Bug4168625Getter test = (Bug4168625Getter)c.newInstance();182final String resClassName;183if (l.toString().length() > 0) {184resClassName = resName+"_"+l;185} else {186resClassName = resName;187}188189Object bundle = test.getResourceBundle(resName, l);190loader.logClasses("Initial lookup of "+resClassName+" generated the following loads:");191192final Vector lastLoad = new Vector(loader.loadedClasses.size());193boolean dups = false;194for (int i = loader.loadedClasses.size() - 1; i >= 0 ; i--) {195final Object item = loader.loadedClasses.elementAt(i);196loader.loadedClasses.removeElementAt(i);197if (loader.loadedClasses.contains(item)) {198logln("Resource loaded more than once: "+item);199dups = true;200} else {201lastLoad.addElement(item);202}203}204if (dups) {205errln("ResourceBundle loaded some classes multiple times");206}207208loader.loadedClasses.removeAllElements();209bundle = test.getResourceBundle(resName, l);210loader.logClasses("Second lookup of "+resClassName+" generated the following loads:");211212dups = false;213for (int i = 0; i < loader.loadedClasses.size(); i++) {214Object item = loader.loadedClasses.elementAt(i);215if (lastLoad.contains(item)) {216logln("ResourceBundle did not cache "+item+" correctly");217dups = true;218}219}220if (dups) {221errln("Resource bundle not caching some classes properly");222}223}224225private class ConcurrentLoadingThread extends Thread {226private Loader loader;227public Object bundle;228private Bug4168625Getter test;229private Locale locale;230private String resourceName = "Bug4168625Resource3";231public ConcurrentLoadingThread(Loader loader, Bug4168625Getter test, Locale l, String resourceName) {232this.loader = loader;233this.test = test;234this.locale = l;235this.resourceName = resourceName;236}237public ConcurrentLoadingThread(Loader loader, Bug4168625Getter test, Locale l) {238this.loader = loader;239this.test = test;240this.locale = l;241}242public void run() {243try {244logln(">>"+threadName()+">run");245bundle = test.getResourceBundle(resourceName, locale);246} catch (Exception e) {247errln("TEST CAUGHT UNEXPECTED EXCEPTION: "+e);248} finally {249logln("<<"+threadName()+"<run");250}251}252public synchronized void waitUntilPinged() {253logln(">>"+threadName()+">waitUntilPinged");254loader.notifyEveryone();255try {256wait(30000); //wait 30 seconds max.257} catch (InterruptedException e) {258logln("Test deadlocked.");259}260logln("<<"+threadName()+"<waitUntilPinged");261}262public synchronized void ping() {263logln(">>"+threadName()+">ping "+threadName(this));264notifyAll();265logln("<<"+threadName()+"<ping "+threadName(this));266}267};268269/**270* This test ensures that multiple resources can be loading at the same271* time as long as they don't depend on each other in some way.272*/273public void testConcurrentLoading() throws Exception {274final Loader loader = new Loader( new String[] { "Bug4168625Class" }, new String[] { "Bug4168625Resource3_en_US", "Bug4168625Resource3_en_CA" });275final Class c = loader.loadClass("Bug4168625Class");276final Bug4168625Getter test = (Bug4168625Getter)c.newInstance();277278ConcurrentLoadingThread thread1 = new ConcurrentLoadingThread(loader, test, new Locale("en", "CA"));279ConcurrentLoadingThread thread2 = new ConcurrentLoadingThread(loader, test, new Locale("en", "IE"));280281thread1.start(); //start thread 1282loader.waitForNotify(1); //wait for thread1 to do getBundle & block in loader283thread2.start(); //start second thread284thread2.join(); //wait until thread2 terminates.285286//Thread1 should be blocked inside getBundle at the class loader287//Thread2 should have completed its getBundle call and terminated288if (!thread1.isAlive() || thread2.isAlive()) {289errln("ResourceBundle.getBundle not allowing legal concurrent loads");290}291292thread1.ping(); //continue thread1293thread1.join();294}295296/**297* This test ensures that a resource loads correctly (with all its parents)298* when memory is very low (ex. the cache gets purged during a load).299*/300public void testLowMemoryLoad() throws Exception {301final String[] classToLoad = { "Bug4168625Class" };302final String[] classToWait = { "Bug4168625Resource3_en_US","Bug4168625Resource3_en","Bug4168625Resource3" };303final Loader loader = new Loader(classToLoad, classToWait);304final Class c = loader.loadClass("Bug4168625Class");305final Bug4168625Getter test = (Bug4168625Getter)c.newInstance();306causeResourceBundleCacheFlush();307308ConcurrentLoadingThread thread1 = new ConcurrentLoadingThread(loader, test, new Locale("en", "US"));309thread1.start(); //start thread 1310loader.waitForNotify(1); //wait for thread1 to do getBundle(en_US) & block in loader311causeResourceBundleCacheFlush(); //cause a cache flush312thread1.ping(); //kick thread 1313loader.waitForNotify(2); //wait for thread1 to do getBundle(en) & block in loader314causeResourceBundleCacheFlush(); //cause a cache flush315thread1.ping(); //kick thread 1316loader.waitForNotify(3); //wait for thread1 to do getBundle(en) & block in loader317causeResourceBundleCacheFlush(); //cause a cache flush318thread1.ping(); //kick thread 1319thread1.join(); //wait until thread1 terminates320321ResourceBundle bundle = (ResourceBundle)thread1.bundle;322String s1 = bundle.getString("Bug4168625Resource3_en_US");323String s2 = bundle.getString("Bug4168625Resource3_en");324String s3 = bundle.getString("Bug4168625Resource3");325if ((s1 == null) || (s2 == null) || (s3 == null)) {326errln("Bundle not constructed correctly. The parent chain is incorrect.");327}328}329330/**331* A simple class loader that loads classes from the current332* working directory. The loader will block the current thread333* of execution before it returns when it tries to load334* the class "Bug4168625Resource3_en_US".335*/336private static final String CLASS_PREFIX = "";337private static final String CLASS_SUFFIX = ".class";338339private static final class SimpleLoader extends ClassLoader {340private boolean network = false;341342public SimpleLoader() {343super(SimpleLoader.class.getClassLoader());344this.network = false;345}346public SimpleLoader(boolean simulateNetworkLoad) {347super(SimpleLoader.class.getClassLoader());348this.network = simulateNetworkLoad;349}350public Class loadClass(final String className, final boolean resolveIt)351throws ClassNotFoundException {352Class result;353synchronized (this) {354result = findLoadedClass(className);355if (result == null) {356if (network) {357try {358Thread.sleep(100);359} catch (java.lang.InterruptedException e) {360}361}362result = getParent().loadClass(className);363if ((result != null) && resolveIt) {364resolveClass(result);365}366}367}368return result;369}370}371372private final class Loader extends ClassLoader {373public final Vector loadedClasses = new Vector();374private String[] classesToLoad;375private String[] classesToWaitFor;376377public Loader() {378super(Loader.class.getClassLoader());379classesToLoad = new String[0];380classesToWaitFor = new String[0];381}382383public Loader(final String[] classesToLoadIn, final String[] classesToWaitForIn) {384super(Loader.class.getClassLoader());385classesToLoad = classesToLoadIn;386classesToWaitFor = classesToWaitForIn;387}388389/**390* Load a class. Files we can load take preference over ones the system391* can load.392*/393private byte[] getClassData(final String className) {394boolean shouldLoad = false;395for (int i = classesToLoad.length-1; i >= 0; --i) {396if (className.equals(classesToLoad[i])) {397shouldLoad = true;398break;399}400}401402if (shouldLoad) {403final String name = CLASS_PREFIX+className+CLASS_SUFFIX;404try {405final InputStream fi = this.getClass().getClassLoader().getResourceAsStream(name);406final byte[] result = new byte[fi.available()];407fi.read(result);408return result;409} catch (Exception e) {410logln("Error loading test class: "+name);411logln(e.toString());412return null;413}414} else {415return null;416}417}418419/**420* Load a class. Files we can load take preference over ones the system421* can load.422*/423public Class loadClass(final String className, final boolean resolveIt)424throws ClassNotFoundException {425Class result;426synchronized (this) {427try {428logln(">>"+threadName()+">load "+className);429loadedClasses.addElement(className);430431result = findLoadedClass(className);432if (result == null) {433final byte[] classData = getClassData(className);434if (classData == null) {435//we don't have a local copy of this one436logln("Loading system class: "+className);437result = loadFromSystem(className);438} else {439result = defineClass(classData, 0, classData.length);440if (result == null) {441//there was an error defining the class442result = loadFromSystem(className);443}444}445if ((result != null) && resolveIt) {446resolveClass(result);447}448}449} catch (ClassNotFoundException e) {450// Ignore loading of Bug4168625ResourceProvider451if (className.equals("Bug4168625ResourceProvider")) {452logln("Ignoring " + className);453loadedClasses.remove(className);454return null;455}456throw e;457}458}459for (int i = classesToWaitFor.length-1; i >= 0; --i) {460if (className.equals(classesToWaitFor[i])) {461rendezvous();462break;463}464}465logln("<<"+threadName()+"<load "+className);466return result;467}468469/**470* Delegate loading to its parent class loader that loads the test classes.471* In othervm mode, the parent class loader is the system class loader;472* in samevm mode, the parent class loader is the jtreg URLClassLoader.473*/474private Class loadFromSystem(String className) throws ClassNotFoundException {475return getParent().loadClass(className);476}477478public void logClasses(String title) {479logln(title);480for (int i = 0; i < loadedClasses.size(); i++) {481logln(" "+loadedClasses.elementAt(i));482}483logln("");484}485486public int notifyCount = 0;487public int waitForNotify(int count) {488return waitForNotify(count, 0);489}490public synchronized int waitForNotify(int count, long time) {491logln(">>"+threadName()+">waitForNotify");492if (count > notifyCount) {493try {494wait(time);495} catch (InterruptedException e) {496}497} else {498logln(" count("+count+") > notifyCount("+notifyCount+")");499}500logln("<<"+threadName()+"<waitForNotify");501return notifyCount;502}503private synchronized void notifyEveryone() {504logln(">>"+threadName()+">notifyEveryone");505notifyCount++;506notifyAll();507logln("<<"+threadName()+"<notifyEveryone");508}509private void rendezvous() {510final Thread current = Thread.currentThread();511if (current instanceof ConcurrentLoadingThread) {512((ConcurrentLoadingThread)current).waitUntilPinged();513}514}515}516517private static String threadName() {518return threadName(Thread.currentThread());519}520521private static String threadName(Thread t) {522String temp = t.toString();523int ndx = temp.indexOf("Thread[");524temp = temp.substring(ndx + "Thread[".length());525ndx = temp.indexOf(',');526temp = temp.substring(0, ndx);527return temp;528}529530/** Fill memory to force all SoftReferences to be GCed */531private void causeResourceBundleCacheFlush() {532logln("Filling memory...");533int allocationSize = 1024;534Vector memoryHog = new Vector();535try {536while (true) {537memoryHog.addElement(new byte[allocationSize]);538allocationSize *= 2;539}540} catch (Throwable e) {541logln("Caught "+e+" filling memory");542} finally{543memoryHog = null;544System.gc();545}546logln("last allocation size: " + allocationSize);547}548549/**550* NOTE: this problem is not externally testable and can only be551* verified through code inspection unless special code to force552* a task switch is inserted into ResourceBundle.553* The class Bug4168625Resource_sp exists. It's parent bundle554* (Bug4168625Resource) contains a resource string with the tag555* "language" but Bug4168625Resource_sp does not.556* Assume two threads are executing, ThreadA and ThreadB and they both557* load a resource Bug4168625Resource with from sp locale.558* ResourceBundle.getBundle adds a bundle to the bundle cache (in559* findBundle) before it sets the bundle's parent (in getBundle after560* returning from findBundle).561* <P>562* <pre>563* ThreadA.getBundle("Bug4168625Resource", new Locale("sp"));564* A-->load Bug4168625Resource_sp565* A-->find cached Bug4168625Resource566* A-->cache Bug4168625Resource_sp as Bug4168625Resource_sp567* ThreadB.getBundle("Bug4168625Resource", new Locale("sp"));568* B-->find cached Bug4168625Resource_sp569* B-->return Bug4168625Resource_sp570* ThreadB.bundle.getString("language");571* B-->try to find "language" in Bug4168625Resource_sp572* B-->Bug4168625Resource_sp does not have a parent, so return null;573* ThreadB.System.out.println("Some unknown country");574* A-->set parent of Bug4168625Resource_sp to Bug4168625Resource575* A-->return Bug4168625Resource_sp (the same bundle ThreadB got)576* ThreadA.bundle.getString("language");577* A-->try to find "language" in Bug4168625Resource_sp578* A-->try to find "language" in Bug4168625Resource (parent of Bug4168625Resource_sp)579* A-->return the string580* ThreadA.System.out.println("Langauge = "+country);581* ThreadB.bundle.getString("language");582* B-->try to find "language" in Bug4168625Resource_sp583* B-->try to find "language" in Bug4168625Resource (parent of Bug4168625Resource_sp)584* B-->return the string585* ThreadB.System.out.println("Langauge = "+country);586* </pre>587* <P>588* Note that the first call to getString() by ThreadB returns null, but the second589* returns a value. Thus to ThreadB, the bundle appears to change. ThreadA gets590* the expected results right away.591*/592}593594595