You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by iv...@apache.org on 2010/05/17 23:22:33 UTC

svn commit: r945374 - in /wicket/trunk: wicket-ioc/src/main/java/org/apache/wicket/injection/Injector.java wicket-util/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java

Author: ivaynberg
Date: Mon May 17 21:22:32 2010
New Revision: 945374

URL: http://svn.apache.org/viewvc?rev=945374&view=rev
Log:
WICKET-2741 non-performant Collections.synchronizedMap() should be replaced with ConcurrentMap

Added:
    wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java   (with props)
Modified:
    wicket/trunk/wicket-ioc/src/main/java/org/apache/wicket/injection/Injector.java

Modified: wicket/trunk/wicket-ioc/src/main/java/org/apache/wicket/injection/Injector.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-ioc/src/main/java/org/apache/wicket/injection/Injector.java?rev=945374&r1=945373&r2=945374&view=diff
==============================================================================
--- wicket/trunk/wicket-ioc/src/main/java/org/apache/wicket/injection/Injector.java (original)
+++ wicket/trunk/wicket-ioc/src/main/java/org/apache/wicket/injection/Injector.java Mon May 17 21:22:32 2010
@@ -18,14 +18,11 @@ package org.apache.wicket.injection;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.wicket.Application;
 import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.util.collections.ClassMetaCache;
 
 /**
  * Injector scans fields of an object instance and checks if the specified
@@ -42,7 +39,7 @@ public abstract class Injector
 		private static final long serialVersionUID = 1L;
 	};
 
-	private final Map<ClassLoader, ConcurrentHashMap<String, Field[]>> cache = Collections.synchronizedMap(new WeakHashMap<ClassLoader, ConcurrentHashMap<String, Field[]>>());
+	private final ClassMetaCache<Field[]> cache = new ClassMetaCache<Field[]>();
 
 	/**
 	 * Binds current instance of the injector to the Application. After this method is called this
@@ -88,20 +85,15 @@ public abstract class Injector
 		Field[] fields = null;
 
 		// try cache
-		ConcurrentHashMap<String, Field[]> container = cache.get(clazz.getClassLoader());
-		if (container != null)
-		{
-			fields = container.get(clazz.getName());
-		}
+		fields = cache.get(clazz);
 
 		if (fields == null)
 		{
+			// cache miss, discover fields
 			fields = findFields(clazz, factory);
 
 			// write to cache
-			container = new ConcurrentHashMap<String, Field[]>();
-			container.put(clazz.getName(), fields);
-			cache.put(clazz.getClassLoader(), container);
+			cache.put(clazz, fields);
 		}
 
 		for (int i = 0; i < fields.length; i++)

Added: wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java?rev=945374&view=auto
==============================================================================
--- wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java (added)
+++ wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java Mon May 17 21:22:32 2010
@@ -0,0 +1,101 @@
+package org.apache.wicket.util.collections;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class wraps a WeakHashMap that holds one ConcurrentHashMap per ClassLoader. In the rare
+ * event of a previously unmapped ClassLoader, the WeakHashMap is replaced by a new one. This avoids
+ * any synchronization overhead, much like a {@link java.util.concurrent.CopyOnWriteArrayList}
+ * 
+ * @param <T>
+ *            type of objects stored in cache
+ */
+public class ClassMetaCache<T>
+{
+	private volatile Map<ClassLoader, ConcurrentHashMap<String, T>> cache = Collections.emptyMap();
+
+	/**
+	 * Puts value into cache
+	 * 
+	 * @param key
+	 * @param value
+	 * @return value previously stored in cache for this key, or {@code null} if none
+	 */
+	public T put(Class<?> key, T value)
+	{
+		ConcurrentHashMap<String, T> container = getClassLoaderCache(key.getClassLoader(), true);
+		return container.put(key(key), value);
+	}
+
+	/**
+	 * Gets value from cache or returns {@code null} if not in cache
+	 * 
+	 * @param key
+	 * @return value stored in cache or {@code null} if none
+	 */
+	public T get(Class<?> key)
+	{
+		ConcurrentHashMap<String, T> container = getClassLoaderCache(key.getClassLoader(), false);
+		if (container == null)
+		{
+			return null;
+		}
+		else
+		{
+			return container.get(key(key));
+		}
+	}
+
+	/**
+	 * @param classLoader
+	 * @param create
+	 * @return a {@link ConcurrentHashMap} mapping class names to injectable fields, never
+	 *         <code>null</code>
+	 */
+	private ConcurrentHashMap<String, T> getClassLoaderCache(ClassLoader classLoader, boolean create)
+	{
+		ConcurrentHashMap<String, T> container = cache.get(classLoader);
+		if (container == null)
+		{
+			if (!create)
+			{
+				return container;
+			}
+
+			// only lock in rare event of unknown ClassLoader
+			synchronized (this)
+			{
+				// check again inside lock
+				container = cache.get(classLoader);
+				if (container == null)
+				{
+					container = new ConcurrentHashMap<String, T>();
+
+					/*
+					 * don't write to current cache, copy instead
+					 */
+					Map<ClassLoader, ConcurrentHashMap<String, T>> newCache = new WeakHashMap<ClassLoader, ConcurrentHashMap<String, T>>(
+						cache);
+					newCache.put(classLoader, container);
+					cache = Collections.unmodifiableMap(newCache);
+				}
+			}
+		}
+		return container;
+	}
+
+	/**
+	 * converts class into a key used by the cache
+	 * 
+	 * @param clazz
+	 * 
+	 * @return string representation of the clazz
+	 */
+	private static String key(Class<?> clazz)
+	{
+		return clazz.getName();
+	}
+}
\ No newline at end of file

Propchange: wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain