You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by ad...@apache.org on 2007/12/30 19:35:24 UTC

svn commit: r607574 - /ofbiz/trunk/framework/base/src/base/org/ofbiz/base/util/UtilProperties.java

Author: adrianc
Date: Sun Dec 30 10:35:23 2007
New Revision: 607574

URL: http://svn.apache.org/viewvc?rev=607574&view=rev
Log:
Final change to UtilProperties.java, based on work in https://issues.apache.org/jira/browse/OFBIZ-1485.

The ResourceBundle cache is now managed by the custom ResourceBundle class - which reduces memory used and also allows for better properties file cache clearing. Clearing the "properties.UtilPropertiesBundleCache" cache works as expected now.

Modified:
    ofbiz/trunk/framework/base/src/base/org/ofbiz/base/util/UtilProperties.java

Modified: ofbiz/trunk/framework/base/src/base/org/ofbiz/base/util/UtilProperties.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/src/base/org/ofbiz/base/util/UtilProperties.java?rev=607574&r1=607573&r2=607574&view=diff
==============================================================================
--- ofbiz/trunk/framework/base/src/base/org/ofbiz/base/util/UtilProperties.java (original)
+++ ofbiz/trunk/framework/base/src/base/org/ofbiz/base/util/UtilProperties.java Sun Dec 30 10:35:23 2007
@@ -48,32 +48,32 @@
 import org.ofbiz.base.util.string.FlexibleStringExpander;
 import org.ofbiz.base.util.cache.UtilCache;
 
-/**
- * Generic Property Accessor with Cache - Utilities for working with properties files
- *
+/** Generic Property Accessor with Cache - Utilities for working with properties files.
+ * <p>UtilProperties divides properties files into two classes: non-locale-specific -
+ * which are used for application parameters, configuration settings, etc; and
+ * locale-specific - which are used for UI labels, system messages, etc. Each class
+ * of properties files is kept in its own cache.</p>
+ * <p>The locale-specific class of properties files can be in any one of three
+ * formats: the standard text-based key=value format (*.properties file), the Java
+ * XML properties format, and the OFBiz-specific XML file format
+ * (see the <a href="#toProperties(java.io.InputStream,%20java.util.Locale,%20java.util.Properties)">toProperties</a>
+ * method).</p>
  */
 @SuppressWarnings("serial")
 public class UtilProperties implements java.io.Serializable {
 
     public static final String module = UtilProperties.class.getName();
 
-    /** An instance of the generic cache for storing the FlexibleProperties
-     *  corresponding to each properties file keyed by a String for the resource location.
-     * This will be used for both non-locale and locale keyed FexibleProperties instances.
+    /** An instance of the generic cache for storing the non-locale-specific properties.
+     *  Each FlexibleProperties instance is keyed by the resource String.
      */
     protected static UtilCache<String, FlexibleProperties> resourceCache = new UtilCache<String, FlexibleProperties>("properties.UtilPropertiesResourceCache");
 
-    /** An instance of the generic cache for storing the FlexibleProperties
-     *  corresponding to each properties file keyed by a URL object
+    /** An instance of the generic cache for storing the non-locale-specific properties.
+     *  Each FlexibleProperties instance is keyed by the file's URL.
      */
     protected static UtilCache<String, FlexibleProperties> urlCache = new UtilCache<String, FlexibleProperties>("properties.UtilPropertiesUrlCache");
 
-    /** An instance of the generic cache for storing the ResourceBundle
-     *  corresponding to each properties file keyed by a String for the resource location and the locale
-     */
-    protected static UtilCache<String, ResourceBundle> bundleLocaleCache = new UtilCache<String, ResourceBundle>("properties.UtilPropertiesBundleLocaleCache");
-
-
     /** Compares the specified property to the compareString, returns true if they are the same, false otherwise
      * @param resource The name of the resource - if the properties file is 'webevent.properties', the resource name is 'webevent'
      * @param name The name of the property in the properties file
@@ -366,13 +366,8 @@
 
     // ========= Locale & Resource Based Methods ==========
 
-    /** Returns the value of the specified property name from the specified resource/properties file corresponding to the given locale.
-     *  <br/>
-     *  <br/> Two reasons why we do not use the FlexibleProperties class for this:
-     *  <ul>
-     *    <li>Doesn't support flexible locale based naming: try fname_locale (5 letter), then fname_locale (2 letter lang only), then fname</li>
-     *    <li>Does not support parent properties/bundles so that if the fname_locale5 file doesn't have it then fname_locale2 is tried, then the fname bundle</li>
-     *  </ul>
+    /** Returns the value of the specified property name from the specified
+     *  resource/properties file corresponding to the given locale.
      * @param resource The name of the resource - can be a file, class, or URL
      * @param name The name of the property in the properties file
      * @param locale The locale that the given resource will correspond to
@@ -382,16 +377,11 @@
         if (resource == null || resource.length() <= 0) return "";
         if (name == null || name.length() <= 0) return "";
 
-        Map<String, Object> bundle = getResourceBundleMap(resource, locale);
+        ResourceBundle bundle = getResourceBundle(resource, locale);
 
         if (bundle == null) return "";
 
-        String value = null;
-        try {
-            value = (String)bundle.get(name);
-        } catch (Exception e) {
-            Debug.log(e.getMessage(), module);
-        }
+        String value = (String) bundle.getString(name);
         return value == null ? "" : value.trim();
     }
 
@@ -466,84 +456,48 @@
         return getMessage(resource, name, UtilGenerics.toMap(String.class, context), locale);
     }
 
+    protected static Set<String> resourceNotFoundMessagesShown = FastSet.newInstance();
     /** Returns the specified resource/properties file as a ResourceBundle
      * @param resource The name of the resource - can be a file, class, or URL
      * @param locale The locale that the given resource will correspond to
      * @return The ResourceBundle
      */
     public static ResourceBundle getResourceBundle(String resource, Locale locale) {
-        ResourceBundleMapWrapper.InternalRbmWrapper bundleMap = getInternalRbmWrapper(resource, locale);
-        if (bundleMap == null) {
-            return null;
+        if (UtilValidate.isEmpty(resource)) {
+            throw new IllegalArgumentException("resource cannot be null or empty");
+        }
+        if (locale == null) {
+            throw new IllegalArgumentException("locale cannot be null");
         }
-        ResourceBundle theBundle = bundleMap.getResourceBundle();
-        return theBundle;
+        ResourceBundle bundle = null;
+        try {
+            bundle = UtilResourceBundle.getBundle(resource, locale, (ClassLoader) null);
+        } catch (MissingResourceException e) {
+            String resourceCacheKey = resource + "_" + locale.toString();
+            if (!resourceNotFoundMessagesShown.contains(resourceCacheKey)) {
+                resourceNotFoundMessagesShown.add(resourceCacheKey);
+                Debug.log("[UtilProperties.getPropertyValue] could not find resource: " + resource + " for locale " + locale.toString(), module);
+            }
+            throw new IllegalArgumentException("Could not find resource bundle [" + resource + "] in the locale [" + locale.toString() + "]");
+        }
+        return bundle;
     }
 
-    /** Returns the specified resource/properties file as a Map with the original ResourceBundle in the Map under the key _RESOURCE_BUNDLE_
+    /** Returns the specified resource/properties file as a Map with the original
+     *  ResourceBundle in the Map under the key _RESOURCE_BUNDLE_
      * @param resource The name of the resource - can be a file, class, or URL
      * @param locale The locale that the given resource will correspond to
      * @return Map containing all entries in The ResourceBundle
      */
     public static Map<String, Object> getResourceBundleMap(String resource, Locale locale) {
-        if (locale == null) {
-            throw new IllegalArgumentException("Locale cannot be null");
-        }
-
-        ResourceBundleMapWrapper.InternalRbmWrapper bundleMap = getInternalRbmWrapper(resource, locale);
-        return new ResourceBundleMapWrapper(bundleMap);
+        return new ResourceBundleMapWrapper(getInternalRbmWrapper(resource, locale));
     }
 
     public static ResourceBundleMapWrapper.InternalRbmWrapper getInternalRbmWrapper(String resource, Locale locale) {
-        String resourceCacheKey = resource + "_" + locale.toString();
-        ResourceBundle bundle = bundleLocaleCache.get(resourceCacheKey);
-        if (bundle == null) {
-            synchronized (bundleLocaleCache) {
-                bundle = bundleLocaleCache.get(resourceCacheKey);
-                if (bundle == null) {
-                    bundle = getBaseResourceBundle(resource, locale);
-                    if (bundle == null) {
-                        throw new IllegalArgumentException("Could not find resource bundle [" + resource + "] in the locale [" + locale + "]");
-                    }
-                    // TODO: Make this smarter by checking the Locale of the ResourceBundle -
-                    // there might be an instance of this bundle already in the cache.
-                    // See http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html#getLocale()
-                    bundleLocaleCache.put(resourceCacheKey, bundle);
-                }
-            }
-        }
+        ResourceBundle bundle = getResourceBundle(resource, locale);
         return new ResourceBundleMapWrapper.InternalRbmWrapper(bundle);
     }
 
-    protected static Set<String> resourceNotFoundMessagesShown = FastSet.newInstance();
-    protected static ResourceBundle getBaseResourceBundle(String resource, Locale locale) {
-        if (resource == null || resource.length() <= 0) return null;
-        if (locale == null) locale = Locale.getDefault();
-
-        ClassLoader loader = Thread.currentThread().getContextClassLoader();
-        ResourceBundle bundle = null;
-        try {
-            bundle = UtilResourceBundle.getBundle(resource, locale, loader);
-        } catch (MissingResourceException e) {
-            String resourceFullName = resource + "_" + locale.toString();
-            if (!resourceNotFoundMessagesShown.contains(resourceFullName)) {
-                resourceNotFoundMessagesShown.add(resourceFullName);
-                Debug.log("[UtilProperties.getPropertyValue] could not find resource: " + resource + " for locale " + locale.toString() + ": " + e.toString(), module);
-                return null;
-            }
-        }
-        if (bundle == null) {
-            String resourceFullName = resource + "_" + locale.toString();
-            if (!resourceNotFoundMessagesShown.contains(resourceFullName)) {
-                resourceNotFoundMessagesShown.add(resourceFullName);
-                Debug.log("[UtilProperties.getPropertyValue] could not find resource: " + resource + " for locale " + locale.toString(), module);
-                return null;
-            }
-        }
-
-        return bundle;
-    }
-
     /** Returns the specified resource/properties file.<p>Note that this method
      * will return a Properties instance for the specified locale <em>only</em> -
      * if you need <a href="http://www.w3.org/International/">I18n</a> properties, then use
@@ -711,9 +665,6 @@
         if (UtilValidate.isEmpty(resource)) {
             throw new IllegalArgumentException("resource cannot be null or empty");
         }
-        if (locale == null) {
-            throw new IllegalArgumentException("locale cannot be null");
-        }
         String resourceName = createResourceName(resource, locale);
         if (propertiesNotFound.contains(resourceName)) {
             return null;
@@ -794,8 +745,7 @@
                 throw new IllegalArgumentException("locale cannot be null");
             }
             String localeString = locale.toString();
-            for (Iterator<? extends Element> p = propertyList.iterator(); p.hasNext();) {
-                Element property = p.next();
+            for (Element property : propertyList) {
                 Element value = UtilXml.firstChildElement(property, "value", "xml:lang", localeString);
                 if (value != null) {
                     if (properties == null) {
@@ -811,8 +761,7 @@
             throw new InvalidPropertiesFormatException("XML properties file invalid or empty");
         }
         // Java XML properties file format
-        for (Iterator<? extends Element> p = propertyList.iterator(); p.hasNext();) {
-            Element property = p.next();
+        for (Element property : propertyList) {
             String value = UtilXml.elementValue(property);
             if (value != null) {
                 if (properties == null) {
@@ -825,9 +774,11 @@
     }
 
     /** Custom ResourceBundle class. This class extends ResourceBundle
-     * to add support for the OFBiz custom XML properties file format.
+     * to add custom bundle caching code and support for the OFBiz custom XML
+     * properties file format.
      */
     public static class UtilResourceBundle extends ResourceBundle {
+        protected static UtilCache<String, ResourceBundle> bundleCache = new UtilCache<String, ResourceBundle>("properties.UtilPropertiesBundleCache");
         protected Properties properties = null;
         protected Locale locale = null;
 
@@ -840,42 +791,41 @@
         }
 
         public static ResourceBundle getBundle(String resource, Locale locale, ClassLoader loader) throws MissingResourceException {
-            ResourceBundle bundle = null;
-            try {
-                bundle = ResourceBundle.getBundle(resource, locale, loader);
-            } catch (MissingResourceException e) {
-                // do nothing
-            }
-            if (bundle != null) {
-                return bundle;
-            }
-            double startTime = System.currentTimeMillis();
-            FastList<Locale> candidateLocales = (FastList<Locale>) getCandidateLocales(locale);
-            ResourceBundle parentBundle = null;
-            synchronized (bundleLocaleCache) {
-                while (candidateLocales.size() > 0) {
-                    Locale candidateLocale = candidateLocales.removeLast();
-                    // ResourceBundles are connected together as a singly-linked list
-                    String parentName = createResourceName(resource, candidateLocale);
-                    ResourceBundle lookupBundle = bundleLocaleCache.get(parentName);
-                    if (lookupBundle == null) {
-                        Properties newProps = getProperties(resource, candidateLocale);
-                        if (UtilValidate.isNotEmpty(newProps)) {
-                            bundle = new UtilResourceBundle(newProps, candidateLocale, parentBundle);
-                            bundleLocaleCache.put(parentName, bundle);
+            String resourceName = createResourceName(resource, locale);
+            ResourceBundle bundle = bundleCache.get(resourceName);
+            if (bundle == null) {
+                synchronized (bundleCache) {
+                    if (bundle != null) {
+                        return bundle;
+                    }
+                    double startTime = System.currentTimeMillis();
+                    FastList<Locale> candidateLocales = (FastList<Locale>) getCandidateLocales(locale);
+                    ResourceBundle parentBundle = null;
+                    while (candidateLocales.size() > 0) {
+                        Locale candidateLocale = candidateLocales.removeLast();
+                        // ResourceBundles are connected together as a singly-linked list
+                        String parentName = createResourceName(resource, candidateLocale);
+                        ResourceBundle lookupBundle = bundleCache.get(parentName);
+                        if (lookupBundle == null) {
+                            Properties newProps = getProperties(resource, candidateLocale);
+                            if (UtilValidate.isNotEmpty(newProps)) {
+                                bundle = new UtilResourceBundle(newProps, candidateLocale, parentBundle);
+                                bundleCache.put(parentName, bundle);
+                                parentBundle = bundle;
+                            }
+                        } else {
                             parentBundle = bundle;
+                            bundle = lookupBundle;
                         }
-                    } else {
-                        parentBundle = bundle;
-                        bundle = lookupBundle;
                     }
+                    if (bundle == null) {
+                        throw new MissingResourceException("Resource " + resource + ", locale " + locale + " not found", null, null);
+                    }
+                    double totalTime = System.currentTimeMillis() - startTime;
+                    Debug.logInfo("ResourceBundle " + resource + " (" + locale + ") created in " + totalTime + " mS", module);
+                    bundleCache.put(resourceName, bundle);
                 }
             }
-            double totalTime = System.currentTimeMillis() - startTime;
-            if (bundle == null) {
-                throw new MissingResourceException("Resource " + resource + ", locale " + locale + " not found", null, null);
-            }
-            Debug.logInfo("ResourceBundle " + resource + " (" + locale + ") created in " + totalTime + " mS", module);
             return bundle;
         }