Author: fmeschbe
Date: Tue Apr 5 10:02:47 2011
New Revision: 1088949
URL: http://svn.apache.org/viewvc?rev=1088949&view=rev
Log:
SLING-2048 Replace Map-of-Maps by a single ConcurrentHashMap with Key object to
prevent serialization and make access to the ResurceBundle in the cache
easier/simpler
Modified:
sling/trunk/contrib/extensions/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java
Modified:
sling/trunk/contrib/extensions/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java
URL:
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java?rev=1088949&r1=1088948&r2=1088949&view=diff
==============================================================================
---
sling/trunk/contrib/extensions/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java
(original)
+++
sling/trunk/contrib/extensions/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java
Tue Apr 5 10:02:47 2011
@@ -24,6 +24,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
+import java.util.concurrent.ConcurrentHashMap;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
@@ -94,10 +95,11 @@ public class JcrResourceBundleProvider i
private ResourceResolver resourceResolver;
/**
- * Matrix of cached resource bundles. The first key is the resource bundle
- * base name, the second key is the Locale.
+ * Map of cached resource bundles indexed by a key combined of the pertient
+ * base name and <code>Locale</code> used to load and identify the
+ * <code>ResourceBundle</code>.
*/
- private final Map<String, Map<Locale, ResourceBundle>> resourceBundleCache
= new HashMap<String, Map<Locale, ResourceBundle>>();
+ private final ConcurrentHashMap<Key, ResourceBundle> resourceBundleCache =
new ConcurrentHashMap<JcrResourceBundleProvider.Key, ResourceBundle>();
/**
* Return root resource bundle as created on-demand by
@@ -154,9 +156,7 @@ public class JcrResourceBundleProvider i
*/
public void onEvent(EventIterator events) {
log.debug("onEvent: Resource changes, removing cached
ResourceBundles");
- synchronized (resourceBundleCache) {
- resourceBundleCache.clear();
- }
+ resourceBundleCache.clear();
}
// ---------- SCR Integration
----------------------------------------------
@@ -221,36 +221,27 @@ public class JcrResourceBundleProvider i
*/
private ResourceBundle getResourceBundleInternal(String baseName,
Locale locale) {
- ResourceBundle resourceBundle = null;
- synchronized (resourceBundleCache) {
- Map<Locale, ResourceBundle> appBundles =
resourceBundleCache.get(baseName);
- if (appBundles != null) {
- resourceBundle = appBundles.get(locale);
- }
- }
+
+ final Key key = new Key(baseName, locale);
+ ResourceBundle resourceBundle = resourceBundleCache.get(key);
if (resourceBundle == null) {
+ log.debug(
+ "getResourceBundleInternal({}, {}): reading from Repository",
+ new Object[] { baseName, locale });
resourceBundle = createResourceBundle(baseName, locale);
-
- synchronized (resourceBundleCache) {
- Map<Locale, ResourceBundle> appBundles =
resourceBundleCache.get(baseName);
- if (appBundles == null) {
- appBundles = new HashMap<Locale, ResourceBundle>();
- resourceBundleCache.put(baseName, appBundles);
- }
-
- // while creating the resource bundle, another thread may
- // have created the same and already stored it in the cache.
- // in this case we don't use the one we just created but use
- // the bundle from the cache. Otherwise, we store our bundle
- // in the cache and keep using it.
- if (appBundles.containsKey(locale)) {
- resourceBundle = appBundles.get(locale);
- } else {
- appBundles.put(locale, resourceBundle);
- }
+ if (resourceBundleCache.putIfAbsent(key, resourceBundle) != null) {
+ resourceBundle = resourceBundleCache.get(key);
+ log.debug(
+ "getResourceBundleInternal({}, {}): duplicate creation,
using existing ResourceBundle",
+ new Object[] { baseName, locale
+ });
}
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("getResourceBundleInternal({}, {}) ==> {}", new Object[]
{
+ baseName, locale, resourceBundle });
}
return resourceBundle;
@@ -392,10 +383,7 @@ public class JcrResourceBundleProvider i
ResourceResolver resolver = this.resourceResolver;
this.resourceResolver = null;
-
- synchronized (resourceBundleCache) {
- this.resourceBundleCache.clear();
- }
+ this.resourceBundleCache.clear();
if (resolver != null) {
@@ -485,4 +473,64 @@ public class JcrResourceBundleProvider i
return new Locale(parts[0], parts[1], parts[2]);
}
+ //---------- internal class
+
+ /**
+ * The <code>Key</code> class encapsulates the base name and Locale for the
+ * key of the {@link #resourceBundleCache} map.
+ */
+ private static class Key {
+
+ final String baseName;
+
+ final Locale locale;
+
+ // precomputed hash code, because this will always be used due to
+ // this instance being used as a key in a HashMap.
+ final int hashCode;
+
+ Key(final String baseName, final Locale locale) {
+
+ int hc = 0;
+ if (baseName != null) {
+ hc += 17 * baseName.hashCode();
+ }
+ if (locale != null) {
+ hc += 13 * locale.hashCode();
+ }
+
+ this.baseName = baseName;
+ this.locale = locale;
+ this.hashCode = hc;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof Key) {
+ Key other = (Key) obj;
+ return equals(this.baseName, other.baseName)
+ && equals(this.locale, other.locale);
+ }
+
+ return false;
+ }
+
+ private static boolean equals(Object o1, Object o2) {
+ if (o1 == null) {
+ if (o2 != null) {
+ return false;
+ }
+ } else if (!o1.equals(o2)) {
+ return false;
+ }
+ return true;
+ }
+ }
}