Author: markt Date: Wed Jan 8 14:10:38 2014 New Revision: 1556539 URL: http://svn.apache.org/r1556539 Log: Fix issue with Manager app and other apps that use i18n in the UI when a request that specifies an Accept-Language of English ahead of French, Spanish or Japanese. Port all the other improvements to the StringManager from trunk as well
Modified: tomcat/tc6.0.x/trunk/STATUS.txt tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/res/StringManager.java tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Modified: tomcat/tc6.0.x/trunk/STATUS.txt URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/STATUS.txt?rev=1556539&r1=1556538&r2=1556539&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/STATUS.txt (original) +++ tomcat/tc6.0.x/trunk/STATUS.txt Wed Jan 8 14:10:38 2014 @@ -32,15 +32,6 @@ PATCHES PROPOSED TO BACKPORT: [ New proposals should be added at the end of the list ] -* Fix issue with Manager app and other apps that use i18n in the UI when a - request that specifies an Accept-Language of English ahead of French, Spanish - or Japanese. - Port all the other improvements to the StringManager from trunk as well - http://people.apache.org/~markt/patches/2013-12-17-webapp-locale-tc6.patch - +1: markt, remm, jboynes - +1: kkolinko (a typo in changelog: s/associated/associate/) - -1: - * Add support for limiting the size of chunk extensions when using chunked encoding. http://people.apache.org/~markt/patches/2013-12-17-chunk-extensions-tc6-v2.patch Modified: tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/res/StringManager.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/res/StringManager.java?rev=1556539&r1=1556538&r2=1556539&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/res/StringManager.java (original) +++ tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/res/StringManager.java Wed Jan 8 14:10:38 2014 @@ -18,8 +18,11 @@ package org.apache.tomcat.util.res; import java.text.MessageFormat; +import java.util.Enumeration; import java.util.Hashtable; +import java.util.LinkedHashMap; import java.util.Locale; +import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; @@ -52,12 +55,16 @@ import java.util.ResourceBundle; public class StringManager { + // Locale.ROOT isn't available until Java 6 + private static final Locale LOCALE_ROOT = new Locale("", "", ""); + + private static int LOCALE_CACHE_SIZE = 10; + /** * The ResourceBundle for this StringManager. */ - - private ResourceBundle bundle; - private Locale locale; + private final ResourceBundle bundle; + private final Locale locale; /** * Creates a new StringManager for a given package. This is a @@ -67,22 +74,36 @@ public class StringManager { * * @param packageName Name of package to create StringManager for. */ - - private StringManager(String packageName) { - this( packageName, Locale.getDefault() ); - } - - private StringManager(String packageName, Locale loc) { + private StringManager(String packageName, Locale locale) { String bundleName = packageName + ".LocalStrings"; - bundle = ResourceBundle.getBundle(bundleName, loc); + ResourceBundle bnd = null; + try { + bnd = ResourceBundle.getBundle(bundleName, locale); + } catch( MissingResourceException ex ) { + // Try from the current loader (that's the case for trusted apps) + // Should only be required if using a TC5 style classloader structure + // where common != shared != server + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if( cl != null ) { + try { + bnd = ResourceBundle.getBundle(bundleName, locale, cl); + } catch(MissingResourceException ex2) { + // Ignore + } + } + } + bundle = bnd; // Get the actual locale, which may be different from the requested one - locale = bundle.getLocale(); - } - - private StringManager(ResourceBundle bundle ) - { - this.bundle=bundle; - locale = bundle.getLocale(); + if (bundle != null) { + Locale bundleLocale = bundle.getLocale(); + if (bundleLocale.equals(LOCALE_ROOT)) { + this.locale = Locale.ENGLISH; + } else { + this.locale = bundleLocale; + } + } else { + this.locale = null; + } } /** @@ -94,7 +115,6 @@ public class StringManager { bundle or null if not found. @throws IllegalArgumentException if <i>key</i> is null. */ - public String getString(String key) { if(key == null){ String msg = "key may not have a null value"; @@ -104,11 +124,15 @@ public class StringManager { String str = null; - try{ - str = bundle.getString(key); - }catch(MissingResourceException mre){ + try { + // Avoid NPE if bundle is null and treat it like an MRE + if (bundle != null) { + str = bundle.getString(key); + } + } catch (MissingResourceException mre) { //bad: shouldn't mask an exception the following way: - // str = "[cannot find message associated with key '" + key + "' due to " + mre + "]"; + // str = "[cannot find message associated with key '" + key + + // "' due to " + mre + "]"; // because it hides the fact that the String was missing // from the calling code. //good: could just throw the exception (or wrap it in another) @@ -130,7 +154,6 @@ public class StringManager { * @param key * @param args */ - public String getString(final String key, final Object... args) { String value = getString(key); if (value == null) { @@ -142,11 +165,19 @@ public class StringManager { return mf.format(args, new StringBuffer(), null).toString(); } + /** + * Identify the Locale this StringManager is associated with + */ + public Locale getLocale() { + return locale; + } + // -------------------------------------------------------------- // STATIC SUPPORT METHODS // -------------------------------------------------------------- - private static Hashtable managers = new Hashtable(); + private static final Map<String,Map<Locale,StringManager>> managers = + new Hashtable<String,Map<Locale,StringManager>>(); /** * Get the StringManager for a particular package. If a manager for @@ -155,42 +186,87 @@ public class StringManager { * * @param packageName The package name */ - public synchronized static StringManager getManager(String packageName) { - StringManager mgr = (StringManager)managers.get(packageName); - if (mgr == null) { - mgr = new StringManager(packageName); - managers.put(packageName, mgr); - } - return mgr; + public static final synchronized StringManager getManager( + String packageName) { + return getManager(packageName, Locale.getDefault()); + } + + /** + * Get the StringManager for a particular package and Locale. If a manager + * for a package/Locale combination already exists, it will be reused, else + * a new StringManager will be created and returned. + * + * @param packageName The package name + * @param locale The Locale + */ + public static final synchronized StringManager getManager( + String packageName, Locale locale) { + + Map<Locale,StringManager> map = managers.get(packageName); + if (map == null) { + /* + * Don't want the HashMap to be expanded beyond LOCALE_CACHE_SIZE. + * Expansion occurs when size() exceeds capacity. Therefore keep + * size at or below capacity. + * removeEldestEntry() executes after insertion therefore the test + * for removal needs to use one less than the maximum desired size + * + */ + map = new LinkedHashMap<Locale,StringManager>(LOCALE_CACHE_SIZE, 1, true) { + private static final long serialVersionUID = 1L; + @Override + protected boolean removeEldestEntry( + Map.Entry<Locale,StringManager> eldest) { + if (size() > (LOCALE_CACHE_SIZE - 1)) { + return true; + } + return false; + } + }; + managers.put(packageName, map); + } + + StringManager mgr = map.get(locale); + if (mgr == null) { + mgr = new StringManager(packageName, locale); + map.put(locale, mgr); + } + return mgr; } /** - * Get the StringManager for a particular package. If a manager for - * a package already exists, it will be reused, else a new - * StringManager will be created and returned. + * Retrieve the StringManager for a list of Locales. The first StringManager + * found will be returned. * - * @param bundle The resource bundle + * @param requestedLocales the list of Locales + * + * @return the found StringManager or the default StringManager */ - public synchronized static StringManager getManager(ResourceBundle bundle) { - return new StringManager( bundle ); + public static StringManager getManager(String packageName, + Enumeration<Locale> requestedLocales) { + while (requestedLocales.hasMoreElements()) { + Locale locale = requestedLocales.nextElement(); + StringManager result = getManager(packageName, locale); + if (result.getLocale().equals(locale)) { + return result; + } + } + // Return the default + return getManager(packageName); } - + /** - * Get the StringManager for a particular package and Locale. If a manager for - * a package already exists, it will be reused, else a new - * StringManager will be created for that Locale and returned. + * Always returns null. * - * @param packageName The package name - * @param loc The locale - */ - - public synchronized static StringManager getManager(String packageName,Locale loc) { - StringManager mgr = (StringManager)managers.get(packageName+"_"+loc.toString()); - if (mgr == null) { - mgr = new StringManager(packageName,loc); - managers.put(packageName+"_"+loc.toString(), mgr); - } - return mgr; + * @param bundle The resource bundle + * + * @return Always <code>null</code> + * + * @deprecated This is unused in Tomcat 6 and will be removed in Tomcat 7 + */ + @Deprecated + public static final synchronized StringManager getManager( + ResourceBundle bundle) { + return null; } - } Modified: tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml?rev=1556539&r1=1556538&r2=1556539&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Wed Jan 8 14:10:38 2014 @@ -74,6 +74,12 @@ command-line option to change service start wait time from default of 10 seconds. (schultz) </fix> + <fix> + Correctly associate the default resource bundle with the English locale + so that requests that specify an Accept-Language of English ahead of + French, Spanish or Japanese get the English messages they asked for. + (markt) + </fix> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org