You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@turbine.apache.org by Daniel Rall <dl...@finemaltcoding.com> on 2001/09/05 09:02:18 UTC

[PATCH] TurbineLocalizationService optimization

Jason and Kasper recently brought it to my attention that
ResourceBundles have some performance issues in web applications.  I
went through the code and discovered that getInstance() is implemented
synchronously.  This would definitely degrade performance in any
heavily-threaded application which called getInstance() often.

This patch for TurbineLocalizationService caches ResourceBundles in an
asynchronous manner (the previous implementation used Hashtables,
which are also synchonous).  This change does not affect the external
API of the LocalizationService implementation, and should alleviate
the problems encountered when using ResourceBundles in a web app.



Index: TurbineLocalizationService.java
===================================================================
RCS file: /home/cvs/jakarta-turbine-fulcrum/src/services/java/org/apache/fulcrum/localization/TurbineLocalizationService.java,v
retrieving revision 1.3
diff -u -u -r1.3 TurbineLocalizationService.java
--- TurbineLocalizationService.java	2001/09/04 23:22:15	1.3
+++ TurbineLocalizationService.java	2001/09/05 06:49:51
@@ -55,7 +55,7 @@
  */
 
 import javax.servlet.http.HttpServletRequest;
-import java.util.Hashtable;
+import java.util.HashMap;
 import java.util.Locale;
 import java.util.ResourceBundle;
 import java.util.StringTokenizer;
@@ -89,6 +89,7 @@
  * @author <a href="mailto:jm@mediaphil.de">Jonas Maurus</a>
  * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
  * @author <a href="mailto:novalidemail@foo.com">Frank Y. Kim</a>
+ * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
  * @version $Id: TurbineLocalizationService.java,v 1.3 2001/09/04 23:22:15 dlr Exp $
  */
 public class TurbineLocalizationService
@@ -96,11 +97,10 @@
     implements LocalizationService
 {
     /**
-     * The ResourceBundles in this service.
-     * Key=bundle name
-     * Value=Hashtable containing ResourceBundles keyed by Locale.
+     * Bundle name keys a HashMap of the ResourceBundles in this
+     * service (which is in turn keyed by Locale).
      */
-    private static Hashtable bundles = null;
+    private static HashMap bundles = null;
 
     /** The name of the default bundle to use. */
     private static String defaultBundle = null;
@@ -124,7 +124,7 @@
     public void init()
         throws InitializationException
     {
-        bundles = new Hashtable();
+        bundles = new HashMap();
         defaultBundle = getConfiguration().getString("locale.default.bundle");
         defaultLanguage = getConfiguration()
             .getString("locale.default.language", "en").trim();
@@ -241,45 +241,69 @@
                                     Locale locale)
     {
         bundleName = bundleName.trim();
+        ResourceBundle rb = (ResourceBundle) bundles.get(bundleName);
+        HashMap bundlesByLocale = (HashMap) bundles.get(bundleName);
 
-        if ( bundles.containsKey(bundleName) )
+        if (bundlesByLocale != null)
         {
-            Hashtable locales = (Hashtable)bundles.get(bundleName);
+            rb = (ResourceBundle) bundlesByLocale.get(locale);
 
-            if ( locales.containsKey(locale) )
+            if (rb == null)
             {
-                return (ResourceBundle)locales.get(locale);
+                // Try to create a ResourceBundle for this Locale.
+                rb = cacheBundle(bundleName, locale);
             }
-            else
+        }
+        else
+        {
+            synchronized (bundles)
             {
-                // Try to create a ResourceBundle for this Locale.
-                ResourceBundle rb =
-                    ResourceBundle.getBundle(bundleName, locale);
-
-                // Cache the ResourceBundle in memory.
-                locales.put( rb.getLocale(), rb );
+                bundlesByLocale = (HashMap) bundles.get(bundleName);
 
-                return rb;
+                if (bundlesByLocale == null)
+                {
+                    // Create a ResourceBundle for requested Locale
+                    // and cache in memory.
+                    rb = cacheBundle(bundleName, locale);
+                }
             }
         }
-        else
+
+        return rb;
+    }
+
+    /**
+     * Caches the named bundle for fast lookups.  This operation is
+     * relatively expesive in terms of memory use, but is optimized
+     * for run-time speed.
+     */
+    private ResourceBundle cacheBundle(String bundleName, Locale locale)
+    {
+        ResourceBundle rb = null;
+        synchronized (bundles)
         {
-            // Create a ResourceBundle for requested Locale.
-            ResourceBundle rb =
-                ResourceBundle.getBundle(bundleName, locale);
-
-            // Cache the ResourceBundle in memory.
-            Hashtable bundlesByLocale = new Hashtable();
-            bundlesByLocale.put(locale, rb);
-
-            // Can't call getLocale(), because that is jdk2.  This
-            // needs to be changed back, since the above approach
-            // caches extra Locale and Bundle objects.
-            // bundlesByLocale.put( rb.getLocale(), rb );
-            bundles.put(bundleName, bundlesByLocale);
+            HashMap bundlesByLocale = (HashMap) bundles.get(bundleName);
+            rb =  (bundlesByLocale == null ? null :
+                   (ResourceBundle) bundlesByLocale.get(bundleName));
+
+            if (rb == null)
+            {
+                HashMap bundlesByName = new HashMap(bundles);
+                bundlesByLocale = (bundlesByLocale == null ? new HashMap(3) :
+                                   new HashMap(bundlesByLocale));
+                rb = ResourceBundle.getBundle(bundleName, locale);
+
+                // Can't call getLocale(), because that is jdk2.  This
+                // needs to be changed back, since the above approach
+                // caches extra Locale and Bundle objects.
+                // bundlesByLocale.put( rb.getLocale(), rb );
+                bundlesByLocale.put(locale, rb);
 
-            return rb;
+                bundlesByName.put(bundleName, bundlesByLocale);
+                this.bundles = bundlesByName;
+            }
         }
+        return rb;
     }
 
     /**

---------------------------------------------------------------------
To unsubscribe, e-mail: turbine-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: turbine-dev-help@jakarta.apache.org


Re: [PATCH] TurbineLocalizationService optimization

Posted by Daniel Rall <dl...@finemaltcoding.com>.
I was a bit hasty with that last patch.  Here's revised version which
I'll be checking in tomorrow (unless any issues are pointed out).

Index: TurbineLocalizationService.java
===================================================================
RCS file: /home/cvs/jakarta-turbine-fulcrum/src/services/java/org/apache/fulcrum/localization/TurbineLocalizationService.java,v
retrieving revision 1.3
diff -u -u -r1.3 TurbineLocalizationService.java
--- TurbineLocalizationService.java	2001/09/04 23:22:15	1.3
+++ TurbineLocalizationService.java	2001/09/05 07:22:00
@@ -55,7 +55,7 @@
  */
 
 import javax.servlet.http.HttpServletRequest;
-import java.util.Hashtable;
+import java.util.HashMap;
 import java.util.Locale;
 import java.util.ResourceBundle;
 import java.util.StringTokenizer;
@@ -89,6 +89,7 @@
  * @author <a href="mailto:jm@mediaphil.de">Jonas Maurus</a>
  * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
  * @author <a href="mailto:novalidemail@foo.com">Frank Y. Kim</a>
+ * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
  * @version $Id: TurbineLocalizationService.java,v 1.3 2001/09/04 23:22:15 dlr Exp $
  */
 public class TurbineLocalizationService
@@ -96,11 +97,10 @@
     implements LocalizationService
 {
     /**
-     * The ResourceBundles in this service.
-     * Key=bundle name
-     * Value=Hashtable containing ResourceBundles keyed by Locale.
+     * Bundle name keys a HashMap of the ResourceBundles in this
+     * service (which is in turn keyed by Locale).
      */
-    private static Hashtable bundles = null;
+    private static HashMap bundles = null;
 
     /** The name of the default bundle to use. */
     private static String defaultBundle = null;
@@ -124,7 +124,7 @@
     public void init()
         throws InitializationException
     {
-        bundles = new Hashtable();
+        bundles = new HashMap();
         defaultBundle = getConfiguration().getString("locale.default.bundle");
         defaultLanguage = getConfiguration()
             .getString("locale.default.language", "en").trim();
@@ -240,46 +240,55 @@
     public ResourceBundle getBundle(String bundleName,
                                     Locale locale)
     {
+        ResourceBundle rb = null;
         bundleName = bundleName.trim();
+        HashMap bundlesByLocale = (HashMap) bundles.get(bundleName);
 
-        if ( bundles.containsKey(bundleName) )
+        if (bundlesByLocale != null)
         {
-            Hashtable locales = (Hashtable)bundles.get(bundleName);
+            rb = (ResourceBundle) bundlesByLocale.get(locale);
 
-            if ( locales.containsKey(locale) )
+            if (rb == null)
             {
-                return (ResourceBundle)locales.get(locale);
+                rb = cacheBundle(bundleName, locale);
             }
-            else
-            {
-                // Try to create a ResourceBundle for this Locale.
-                ResourceBundle rb =
-                    ResourceBundle.getBundle(bundleName, locale);
-
-                // Cache the ResourceBundle in memory.
-                locales.put( rb.getLocale(), rb );
-
-                return rb;
-            }
         }
         else
         {
-            // Create a ResourceBundle for requested Locale.
-            ResourceBundle rb =
-                ResourceBundle.getBundle(bundleName, locale);
+            rb = cacheBundle(bundleName, locale);
+        }
+        return rb;
+    }
 
-            // Cache the ResourceBundle in memory.
-            Hashtable bundlesByLocale = new Hashtable();
-            bundlesByLocale.put(locale, rb);
+    /**
+     * Caches the named bundle for fast lookups.  This operation is
+     * relatively expesive in terms of memory use, but is optimized
+     * for run-time speed.
+     */
+    private synchronized ResourceBundle cacheBundle(String bundleName,
+                                                    Locale locale)
+    {
+        HashMap bundlesByLocale = (HashMap) bundles.get(bundleName);
+        ResourceBundle rb = (bundlesByLocale == null ? null :
+                             (ResourceBundle) bundlesByLocale.get(bundleName));
 
+        if (rb == null)
+        {
+            HashMap bundlesByName = new HashMap(bundles);
+            bundlesByLocale = (bundlesByLocale == null ? new HashMap(3) :
+                               new HashMap(bundlesByLocale));
+            rb = ResourceBundle.getBundle(bundleName, locale);
+
             // Can't call getLocale(), because that is jdk2.  This
             // needs to be changed back, since the above approach
             // caches extra Locale and Bundle objects.
             // bundlesByLocale.put( rb.getLocale(), rb );
-            bundles.put(bundleName, bundlesByLocale);
+            bundlesByLocale.put(locale, rb);
 
-            return rb;
+            bundlesByName.put(bundleName, bundlesByLocale);
+            this.bundles = bundlesByName;
         }
+        return rb;
     }
 
     /**

---------------------------------------------------------------------
To unsubscribe, e-mail: turbine-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: turbine-dev-help@jakarta.apache.org