Author: markt Date: Wed Jul 3 13:17:50 2013 New Revision: 1499388 URL: http://svn.apache.org/r1499388 Log: EL 3.0 Implement new method Creating ExpressionFactory instances is relatively expensive so they need to be cached but caching them is a potential memory leak since applications may ship their own implementations. Copy the caching approach from the ExpressionFactory implementation.
Modified: tomcat/trunk/java/javax/el/ELContext.java tomcat/trunk/java/javax/el/Util.java Modified: tomcat/trunk/java/javax/el/ELContext.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/javax/el/ELContext.java?rev=1499388&r1=1499387&r2=1499388&view=diff ============================================================================== --- tomcat/trunk/java/javax/el/ELContext.java (original) +++ tomcat/trunk/java/javax/el/ELContext.java Wed Jul 3 13:17:50 2013 @@ -155,4 +155,23 @@ public abstract class ELContext { } } } + + /** + * @since EL 3.0 + */ + public Object convertToType(Object obj, Class<?> type) { + + boolean originalResolved = isPropertyResolved(); + try { + ELResolver resolver = getELResolver(); + Object result = resolver.convertToType(this, obj, type); + if (isPropertyResolved()) { + return result; + } + } finally { + setPropertyResolved(originalResolved); + } + + return Util.getExpressionFactory().coerceToType(type, type); + } } Modified: tomcat/trunk/java/javax/el/Util.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/javax/el/Util.java?rev=1499388&r1=1499387&r2=1499388&view=diff ============================================================================== --- tomcat/trunk/java/javax/el/Util.java (original) +++ tomcat/trunk/java/javax/el/Util.java Wed Jul 3 13:17:50 2013 @@ -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) { @@ -64,4 +71,112 @@ class Util { } + private static final CacheValue nullTcclFactory = new CacheValue(); + private static final ConcurrentMap<CacheKey, CacheValue> factoryCache = + new ConcurrentHashMap<>(); + + /** + * 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<>(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<>(factory); + } + } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org