Author: ivaynberg
Date: Mon May 17 21:19:50 2010
New Revision: 945368

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

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

Modified: 
wicket/branches/wicket-1.4.x/wicket-ioc/src/main/java/org/apache/wicket/injection/Injector.java
URL: 
http://svn.apache.org/viewvc/wicket/branches/wicket-1.4.x/wicket-ioc/src/main/java/org/apache/wicket/injection/Injector.java?rev=945368&r1=945367&r2=945368&view=diff
==============================================================================
--- 
wicket/branches/wicket-1.4.x/wicket-ioc/src/main/java/org/apache/wicket/injection/Injector.java
 (original)
+++ 
wicket/branches/wicket-1.4.x/wicket-ioc/src/main/java/org/apache/wicket/injection/Injector.java
 Mon May 17 21:19:50 2010
@@ -18,17 +18,14 @@ 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.Component;
 import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.Page;
 import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.util.collections.ClassMetaCache;
 
 /**
  * Injector scans fields of an object instance and checks if the specified
@@ -40,9 +37,11 @@ import org.apache.wicket.markup.html.pan
  */
 public class Injector
 {
+       private static final Field[] EMPTY_FIELDS = new Field[0];
+
        private static Injector instance = new Injector();
 
-       private final Map<ClassLoader, ConcurrentHashMap<String, Field[]>> 
cache = Collections.synchronizedMap(new WeakHashMap<ClassLoader, 
ConcurrentHashMap<String, Field[]>>());
+       private final ClassMetaCache<Field[]> cache = new 
ClassMetaCache<Field[]>();
 
        /**
         * @return static instance of ProxyInjector
@@ -85,24 +84,7 @@ public class Injector
        {
                final Class<?> clazz = object.getClass();
 
-               Field[] fields = null;
-
-               // try cache
-               ConcurrentHashMap<String, Field[]> container = 
cache.get(clazz.getClassLoader());
-               if (container != null)
-               {
-                       fields = container.get(clazz.getName());
-               }
-
-               if (fields == null)
-               {
-                       fields = findFields(clazz, factory);
-
-                       // write to cache
-                       container = new ConcurrentHashMap<String, Field[]>();
-                       container.put(clazz.getName(), fields);
-                       cache.put(clazz.getClassLoader(), container);
-               }
+               Field[] fields = getFields(clazz, factory);
 
                for (int i = 0; i < fields.length; i++)
                {
@@ -142,6 +124,28 @@ public class Injector
        }
 
        /**
+        * caches results of {...@link #getFields(Class, IFieldValueFactory)}
+        * 
+        * @param clazz
+        * @param factory
+        * @return cached results as returned by {...@link #getFields(Class, 
IFieldValueFactory)}
+        */
+       private Field[] getFields(Class<?> clazz, IFieldValueFactory factory)
+       {
+               Field[] fields = cache.get(clazz);
+
+               if (fields == null)
+               {
+                       fields = findFields(clazz, factory);
+
+                       // write to cache
+                       cache.put(clazz, fields);
+               }
+
+               return fields;
+       }
+
+       /**
         * Returns an array of fields that can be injected using the given 
field value factory
         * 
         * @param clazz
@@ -167,7 +171,6 @@ public class Injector
                        clazz = clazz.getSuperclass();
                }
 
-               return matched.toArray(new Field[matched.size()]);
+               return matched.size() == 0 ? EMPTY_FIELDS : matched.toArray(new 
Field[matched.size()]);
        }
-
 }

Added: 
wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java
URL: 
http://svn.apache.org/viewvc/wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java?rev=945368&view=auto
==============================================================================
--- 
wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java
 (added)
+++ 
wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java
 Mon May 17 21:19:50 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/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/util/collections/ClassMetaCache.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain


Reply via email to