Author: markt
Date: Fri Feb 21 10:45:01 2014
New Revision: 1570519

URL: http://svn.apache.org/r1570519
Log:
Backport some more EL refactoring in preparation for the fix for 
https://issues.apache.org/bugzilla/show_bug.cgi?id=56147

Modified:
    tomcat/tc7.0.x/trunk/   (props changed)
    tomcat/tc7.0.x/trunk/java/javax/el/Util.java

Propchange: tomcat/tc7.0.x/trunk/
------------------------------------------------------------------------------
  Merged /tomcat/trunk:r1499388

Modified: tomcat/tc7.0.x/trunk/java/javax/el/Util.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/javax/el/Util.java?rev=1570519&r1=1570518&r2=1570519&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/javax/el/Util.java (original)
+++ tomcat/tc7.0.x/trunk/java/javax/el/Util.java Fri Feb 21 10:45:01 2014
@@ -16,10 +16,16 @@
  */
 package javax.el;
 
+import java.lang.ref.WeakReference;
 import java.text.MessageFormat;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 class Util {
 
@@ -38,6 +44,7 @@ class Util {
         // All other instances of Throwable will be silently swallowed
     }
 
+
     static String message(ELContext context, String name, Object... props) {
         Locale locale = null;
         if (context != null) {
@@ -63,6 +70,116 @@ class Util {
         }
     }
 
+
+    private static final CacheValue nullTcclFactory = new CacheValue();
+    private static final ConcurrentMap<CacheKey, CacheValue> factoryCache =
+            new ConcurrentHashMap<CacheKey, CacheValue>();
+
+    /**
+     * Provides a per class loader cache of ExpressionFactory instances without
+     * pinning any in memory as that could trigger a memory leak.
+     */
+    static ExpressionFactory getExpressionFactory() {
+
+        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+        CacheValue cacheValue = null;
+        ExpressionFactory factory = null;
+
+        if (tccl == null) {
+            cacheValue = nullTcclFactory;
+        } else {
+            CacheKey key = new CacheKey(tccl);
+            cacheValue = factoryCache.get(key);
+            if (cacheValue == null) {
+                CacheValue newCacheValue = new CacheValue();
+                cacheValue = factoryCache.putIfAbsent(key, newCacheValue);
+                if (cacheValue == null) {
+                    cacheValue = newCacheValue;
+                }
+            }
+        }
+
+        final Lock readLock = cacheValue.getLock().readLock();
+        readLock.lock();
+        try {
+            factory = cacheValue.getExpressionFactory();
+        } finally {
+            readLock.unlock();
+        }
+
+        if (factory == null) {
+            final Lock writeLock = cacheValue.getLock().writeLock();
+            try {
+                writeLock.lock();
+                factory = cacheValue.getExpressionFactory();
+                if (factory == null) {
+                    factory = ExpressionFactory.newInstance();
+                    cacheValue.setExpressionFactory(factory);
+                }
+            } finally {
+                writeLock.unlock();
+            }
+        }
+
+        return factory;
+    }
+
+
+    /**
+     * Key used to cache default ExpressionFactory information per class
+     * loader. The class loader reference is never {@code null}, because
+     * {@code null} tccl is handled separately.
+     */
+    private static class CacheKey {
+        private final int hash;
+        private final WeakReference<ClassLoader> ref;
+
+        public CacheKey(ClassLoader key) {
+            hash = key.hashCode();
+            ref = new WeakReference<ClassLoader>(key);
+        }
+
+        @Override
+        public int hashCode() {
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (!(obj instanceof CacheKey)) {
+                return false;
+            }
+            ClassLoader thisKey = ref.get();
+            if (thisKey == null) {
+                return false;
+            }
+            return thisKey == ((CacheKey) obj).ref.get();
+        }
+    }
+
+    private static class CacheValue {
+        private final ReadWriteLock lock = new ReentrantReadWriteLock();
+        private WeakReference<ExpressionFactory> ref;
+
+        public CacheValue() {
+        }
+
+        public ReadWriteLock getLock() {
+            return lock;
+        }
+
+        public ExpressionFactory getExpressionFactory() {
+            return ref != null ? ref.get() : null;
+        }
+
+        public void setExpressionFactory(ExpressionFactory factory) {
+            ref = new WeakReference<ExpressionFactory>(factory);
+        }
+    }
+
     /*
      * This method duplicates code in org.apache.el.util.ReflectionUtil. When
      * making changes keep the code in sync.



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to