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

Reply via email to