mirror of
https://github.com/apache/struts.git
synced 2026-08-05 14:47:09 +00:00
Update:
- Add support for an optional basic LRU cache for OGNL expressions and OGNL BeanInfo. - Add support for cache limits applying to both normal and LRU caches. For a normal cache the entire cache will flush when the limit is reached. - Add flags to allow switching between normal and LRU caches, and setting the maximum sizes.
This commit is contained in:
@@ -41,6 +41,7 @@ import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
@@ -57,8 +58,14 @@ public class OgnlUtil {
|
||||
// Flag used to reduce flooding logs with WARNs about using DevMode excluded packages
|
||||
private final AtomicBoolean warnReported = new AtomicBoolean(false);
|
||||
|
||||
private final ConcurrentMap<String, Object> expressions = new ConcurrentHashMap<>();
|
||||
private final AtomicBoolean useLRUExpressionCache = new AtomicBoolean(false);
|
||||
private final AtomicBoolean useLRUBeanInfoCache = new AtomicBoolean(false);
|
||||
private final AtomicInteger expressionsCacheMaxSize = new AtomicInteger(25000);
|
||||
private final AtomicInteger beanInfoCacheMaxSize = new AtomicInteger(25000);
|
||||
private final ConcurrentMap<String, Object> expressionsCache = new ConcurrentHashMap<>();
|
||||
private final LRUCache<String, Object> expressionsCacheLRU = new LRUCache<>(expressionsCacheMaxSize.get(), 16, 0.75f);
|
||||
private final ConcurrentMap<Class<?>, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
|
||||
private final LRUCache<Class<?>, BeanInfo> beanInfoCacheLRU = new LRUCache<>(beanInfoCacheMaxSize.get(), 16, 0.75f);
|
||||
private TypeConverter defaultConverter;
|
||||
|
||||
private boolean devMode;
|
||||
@@ -103,6 +110,28 @@ public class OgnlUtil {
|
||||
enableExpressionCache = BooleanUtils.toBoolean(cache);
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE, required = false)
|
||||
protected void setExpressionsCacheMaxSize(String maxSize) {
|
||||
expressionsCacheMaxSize.set(Integer.parseInt(maxSize));
|
||||
expressionsCacheLRU.setEvictionLimit(expressionsCacheMaxSize.get());
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE, required = false)
|
||||
protected void setBeanInfoCacheMaxSize(String maxSize) {
|
||||
beanInfoCacheMaxSize.set(Integer.parseInt(maxSize));
|
||||
beanInfoCacheLRU.setEvictionLimit(beanInfoCacheMaxSize.get());
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUExpressionCache(String useLRUMode) {
|
||||
useLRUExpressionCache.set(BooleanUtils.toBoolean(useLRUMode));
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE, required = false)
|
||||
protected void setUseLRUBeanInfoCache(String useLRUMode) {
|
||||
useLRUBeanInfoCache.set(BooleanUtils.toBoolean(useLRUMode));
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, required = false)
|
||||
protected void setEnableEvalExpression(String evalExpression) {
|
||||
this.enableEvalExpression = BooleanUtils.toBoolean(evalExpression);
|
||||
@@ -273,7 +302,8 @@ public class OgnlUtil {
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public void clearExpressionCache() {
|
||||
expressions.clear();
|
||||
expressionsCache.clear();
|
||||
expressionsCacheLRU.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,7 +314,11 @@ public class OgnlUtil {
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public int expressionCacheSize() {
|
||||
return expressions.size();
|
||||
if (useLRUExpressionCache.get()) {
|
||||
return expressionsCacheLRU.size();
|
||||
} else {
|
||||
return expressionsCache.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,6 +336,7 @@ public class OgnlUtil {
|
||||
*/
|
||||
public void clearBeanInfoCache() {
|
||||
beanInfoCache.clear();
|
||||
beanInfoCacheLRU.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,7 +347,11 @@ public class OgnlUtil {
|
||||
* @since 2.5.21
|
||||
*/
|
||||
public int beanInfoCacheSize() {
|
||||
return beanInfoCache.size();
|
||||
if (useLRUBeanInfoCache.get()) {
|
||||
return beanInfoCacheLRU.size();
|
||||
} else {
|
||||
return beanInfoCache.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -532,11 +571,18 @@ public class OgnlUtil {
|
||||
private <T> Object compileAndExecute(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
|
||||
Object tree;
|
||||
if (enableExpressionCache) {
|
||||
tree = expressions.get(expression);
|
||||
final Map<String, Object> expressionsCacheRef = (useLRUExpressionCache.get() ? expressionsCacheLRU.backingMapReference() : expressionsCache);
|
||||
tree = expressionsCacheRef.get(expression);
|
||||
if (tree == null) {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkEnableEvalExpression(tree, context);
|
||||
expressions.putIfAbsent(expression, tree);
|
||||
expressionsCacheRef.putIfAbsent(expression, tree);
|
||||
if (! useLRUExpressionCache.get()) {
|
||||
// If using a standard cache, after attempting to add an expression check cache size against the limit.
|
||||
if (expressionsCacheRef.size() > expressionsCacheMaxSize.get()) {
|
||||
expressionsCacheRef.clear(); // Non-LRU cache has exceeded maximum configured size, so flush.
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
@@ -549,11 +595,18 @@ public class OgnlUtil {
|
||||
private <T> Object compileAndExecuteMethod(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
|
||||
Object tree;
|
||||
if (enableExpressionCache) {
|
||||
tree = expressions.get(expression);
|
||||
final Map<String, Object> expressionsCacheRef = (useLRUExpressionCache.get() ? expressionsCacheLRU.backingMapReference() : expressionsCache);
|
||||
tree = expressionsCacheRef.get(expression);
|
||||
if (tree == null) {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
checkSimpleMethod(tree, context);
|
||||
expressions.putIfAbsent(expression, tree);
|
||||
expressionsCacheRef.putIfAbsent(expression, tree);
|
||||
if (! useLRUExpressionCache.get()) {
|
||||
// If using a standard cache, after attempting to add an expression check cache size against the limit.
|
||||
if (expressionsCacheRef.size() > expressionsCacheMaxSize.get()) {
|
||||
expressionsCacheRef.clear(); // Non-LRU cache has exceeded maximum configured size, so flush.
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tree = Ognl.parseExpression(expression);
|
||||
@@ -759,11 +812,18 @@ public class OgnlUtil {
|
||||
* @throws IntrospectionException is thrown if an exception occurs during introspection.
|
||||
*/
|
||||
public BeanInfo getBeanInfo(Class<?> clazz) throws IntrospectionException {
|
||||
synchronized (beanInfoCache) {
|
||||
BeanInfo beanInfo = beanInfoCache.get(clazz);
|
||||
final Map<Class<?>, BeanInfo> beanInfoCacheRef = (useLRUBeanInfoCache.get() ? beanInfoCacheLRU.backingMapReference() : beanInfoCache);
|
||||
synchronized (beanInfoCacheRef) {
|
||||
BeanInfo beanInfo = beanInfoCacheRef.get(clazz);
|
||||
if (beanInfo == null) {
|
||||
beanInfo = Introspector.getBeanInfo(clazz, Object.class);
|
||||
beanInfoCache.putIfAbsent(clazz, beanInfo);
|
||||
beanInfoCacheRef.putIfAbsent(clazz, beanInfo);
|
||||
if (! useLRUBeanInfoCache.get()) {
|
||||
// If using a standard cache, after attempting to add beanInfo check cache size against the limit.
|
||||
if (beanInfoCacheRef.size() > beanInfoCacheMaxSize.get()) {
|
||||
beanInfoCacheRef.clear(); // Non-LRU cache has exceeded maximum configured size, so flush.
|
||||
}
|
||||
}
|
||||
}
|
||||
return beanInfo;
|
||||
}
|
||||
@@ -822,4 +882,66 @@ public class OgnlUtil {
|
||||
T execute(Object tree) throws OgnlException;
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic LRUCache implementation that utilizes a {@link Collections#synchronizedMap(java.util.Map)}
|
||||
* backed by a {@link LinkedHashMap}. May be replaced by a more efficient implementation in the future.
|
||||
*
|
||||
* @param <Key> Key type for the LRUCache
|
||||
* @param <Value> Value type for the LRUCache
|
||||
*/
|
||||
protected class LRUCache<Key, Value> {
|
||||
private final Map<Key, Value> lruCache;
|
||||
private final AtomicInteger cacheEvictionLimit = new AtomicInteger(25000);
|
||||
|
||||
public LRUCache(int evictionLimit, int initialCapacity, float loadFactor) {
|
||||
this.cacheEvictionLimit.set(evictionLimit);
|
||||
// Access-order mode selected (order mode true in LinkedHashMap constructor).
|
||||
lruCache = Collections.synchronizedMap (new LinkedHashMap<Key, Value>(initialCapacity, loadFactor, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Key,Value> eldest) {
|
||||
return (this.size() > cacheEvictionLimit.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Value get(Key key) {
|
||||
return lruCache.get(key);
|
||||
}
|
||||
|
||||
public void put(Key key, Value value) {
|
||||
lruCache.put(key, value);
|
||||
}
|
||||
|
||||
public void putIfAbsent(Key key, Value value) {
|
||||
lruCache.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return lruCache.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
lruCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the {@link LRUCache} is only intended to be used internally, we can cheat a bit
|
||||
* and allow access to the backing map directly via its reference. Allows it to be used
|
||||
* like the other {@link Map} caches within the module.
|
||||
*
|
||||
* @return The Map that backs the LRU cache.
|
||||
*/
|
||||
public Map<Key, Value> backingMapReference() {
|
||||
return lruCache;
|
||||
}
|
||||
|
||||
public int getEvictionLimit() {
|
||||
return this.cacheEvictionLimit.get();
|
||||
}
|
||||
|
||||
public void setEvictionLimit(int cacheEvictionLimit) {
|
||||
this.cacheEvictionLimit.set(cacheEvictionLimit);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +249,31 @@ public final class StrutsConstants {
|
||||
/** Throw RuntimeException when a property is not found, or the evaluation of the expression fails */
|
||||
public static final String STRUTS_EL_THROW_EXCEPTION = "struts.el.throwExceptionOnFailure";
|
||||
|
||||
/**
|
||||
* Specifies a maximum number of cached BeanInfo used by OgnlUtility. Not specified/set by default. If
|
||||
* a positive integer is specified, it will set a limit whose behaviour depends on whether the
|
||||
* normal (default) cache or optional LRU cache is in place.
|
||||
*
|
||||
* For the normal (default) cache, exceeding the maximum will cause the entire cache to flush (clear).
|
||||
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
|
||||
* removed when a new entry needs to be added (cache is fully-utilized).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE = "struts.ognl.beanInfoCacheMaxSize";
|
||||
|
||||
/**
|
||||
* Set the cache mode of the BeanInfo cache used by OgnlUtility. A value of true means enable
|
||||
* least-recently-used (LRU) mode, a value of false (or any non-true value) means to use the
|
||||
* default cache.
|
||||
*
|
||||
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_BEANINFO_CACHE_MAXSIZE})
|
||||
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_BEANINFO_CACHE_LRU_MODE = "struts.ognl.beanInfoCacheLRUMode";
|
||||
|
||||
/**
|
||||
* Logs properties that are not found (very verbose)
|
||||
* @since 2.6
|
||||
@@ -274,6 +299,31 @@ public final class StrutsConstants {
|
||||
*/
|
||||
public static final String STRUTS_ENABLE_OGNL_EXPRESSION_CACHE = STRUTS_OGNL_ENABLE_EXPRESSION_CACHE;
|
||||
|
||||
/**
|
||||
* Specifies a maximum number of cached parsed OGNL expressions. Not specified/set by default. If
|
||||
* a positive integer is specified, it will set a limit whose behaviour depends on whether the
|
||||
* normal (default) cache or optional LRU cache is in place.
|
||||
*
|
||||
* For the normal (default) cache, exceeding the maximum will cause the entire cache to flush (clear).
|
||||
* For the optional LRU cache, once the maximum is reached, the least-recently-used (LRU) entry will be
|
||||
* removed when a new entry needs to be added (cache is fully-utilized).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE = "struts.ognl.expressionCacheMaxSize";
|
||||
|
||||
/**
|
||||
* Set the cache mode of the parsed OGNL expression cache. A value of true means enable
|
||||
* least-recently-used (LRU) mode, a value of false (or any non-true value) means to use the
|
||||
* default cache.
|
||||
*
|
||||
* Note: When enabling LRU cache mode you must also set a maximum size (via {@link #STRUTS_OGNL_EXPRESSION_CACHE_MAXSIZE})
|
||||
* for it to be effective. Otherwise, there is no condition to evict a LRU entry (cache has no limit).
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
public static final String STRUTS_OGNL_EXPRESSION_CACHE_LRU_MODE = "struts.ognl.expressionCacheLRUMode";
|
||||
|
||||
/**
|
||||
* Enables evaluation of OGNL expressions
|
||||
* @since 2.6
|
||||
|
||||
@@ -224,6 +224,32 @@ struts.ognl.logMissingProperties=false
|
||||
### if the application generates a lot of different expressions
|
||||
struts.ognl.enableExpressionCache=true
|
||||
|
||||
### Specify a limit to the number of entries in the OGNL expressionCache.
|
||||
### For the standard expressionCache mode, when the limit is exceeded the entire cache's
|
||||
### content will be cleared (can help prevent memory leaks).
|
||||
### For expressionCacheLRUMode true, the limit will ensure the cache does not exceed
|
||||
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
|
||||
### NOTE: If not set, the default is 25000, which may be excessive.
|
||||
#struts.ognl.expressionCacheMaxSize=1000
|
||||
|
||||
### Indicates if the OGNL expressionCache should use LRU mode.
|
||||
### NOTE: When true, make sure to set the expressionCacheMaxSize to a reasonable value
|
||||
### for your application. Otherwise the default limit will never (practically) be reached.
|
||||
#struts.ognl.expressionCacheLRUMode=false
|
||||
|
||||
### Specify a limit to the number of entries in the OGNL beanInfoCache.
|
||||
### For the standard beanInfoCache mode, when the limit is exceeded the entire cache's
|
||||
### content will be cleared (can help prevent memory leaks).
|
||||
### For beanInfoCacheLRUMode true, the limit will ensure the cache does not exceed
|
||||
### that size, dropping the oldest (least-recently-used) expressions to add new ones.
|
||||
### NOTE: If not set, the default is 25000, which may be excessive.
|
||||
#struts.ognl.beanInfoCacheMaxSize=1000
|
||||
|
||||
### Indicates if the OGNL beanInfoCache should use LRU mode.
|
||||
### NOTE: When true, make sure to set the beanInfoCacheMaxSize to a reasonable value
|
||||
### for your application. Otherwise the default limit will never (practically) be reached.
|
||||
#struts.ognl.beanInfoCacheLRUMode=false
|
||||
|
||||
### Indicates if Dispatcher should handle unexpected exceptions by calling sendError()
|
||||
### or simply rethrow it as a ServletException to allow future processing by other frameworks like Spring Security
|
||||
struts.handle.exception=true
|
||||
|
||||
@@ -129,6 +129,62 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertSame(expr0, expr2);
|
||||
}
|
||||
|
||||
public void testCacheEnabledMaxSize() throws OgnlException {
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setExpressionsCacheMaxSize("1");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
assertEquals("Expression cache size should be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
// Next epxression cached should cause the cache to clear (exceeding maximum sized).
|
||||
Object expr3 = ognlUtil.compile("test1");
|
||||
assertEquals("Expression cache should be empty", 0, ognlUtil.expressionCacheSize());
|
||||
Object expr4 = ognlUtil.compile("test1");
|
||||
Object expr5 = ognlUtil.compile("test1");
|
||||
assertEquals("Expression cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
assertNotSame("2nd test expression cache attempt will exceed size and force clear, but somehow they match ?", expr3, expr4);
|
||||
assertSame(expr4, expr5);
|
||||
// Next epxression cached should cause the cache to clear (exceeding maximum sized).
|
||||
Object expr6 = ognlUtil.compile("test");
|
||||
assertEquals("Expression cache should be empty", 0, ognlUtil.expressionCacheSize());
|
||||
Object expr7 = ognlUtil.compile("test");
|
||||
Object expr8 = ognlUtil.compile("test");
|
||||
assertNotSame("2nd test expression cache attempt will exceed size and force clear, but somehow they match ?", expr6, expr7);
|
||||
assertSame(expr7, expr8);
|
||||
assertEquals("Expression LRU cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
assertNotSame("1st test expression identical after ejection from LRU cache ?", expr5, expr0);
|
||||
ognlUtil.setUseLRUExpressionCache("false");
|
||||
}
|
||||
|
||||
public void testLRUCacheEnabled() throws OgnlException {
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setUseLRUExpressionCache("true");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
ognlUtil.setUseLRUExpressionCache("false");
|
||||
}
|
||||
|
||||
public void testLRUCacheEnabledMaxSize() throws OgnlException {
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setUseLRUExpressionCache("true");
|
||||
ognlUtil.setExpressionsCacheMaxSize("1");
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
assertSame(expr0, expr2);
|
||||
assertEquals("Expression LRU cache size should be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
Object expr3 = ognlUtil.compile("test1");
|
||||
Object expr4 = ognlUtil.compile("test1");
|
||||
assertSame(expr3, expr4);
|
||||
assertEquals("Expression LRU cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
Object expr5 = ognlUtil.compile("test");
|
||||
Object expr6 = ognlUtil.compile("test");
|
||||
assertSame(expr5, expr6);
|
||||
assertEquals("Expression LRU cache size should still be at its limit", 1, ognlUtil.expressionCacheSize());
|
||||
assertNotSame("1st test expression identical after ejection from LRU cache ?", expr5, expr0);
|
||||
ognlUtil.setUseLRUExpressionCache("false");
|
||||
}
|
||||
|
||||
public void testExpressionIsCachedIrrespectiveOfItsExecutionStatus() throws OgnlException {
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = (OgnlContext) ognlUtil.createDefaultContext(foo);
|
||||
@@ -149,6 +205,29 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testExpressionIsLRUCachedIrrespectiveOfItsExecutionStatus() throws OgnlException {
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setUseLRUExpressionCache("true");
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = (OgnlContext) ognlUtil.createDefaultContext(foo);
|
||||
|
||||
// Expression which executes with success
|
||||
try {
|
||||
ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_FINAL_PUBLIC_ATTRIBUTE", context, foo);
|
||||
assertEquals("Successfully executed expression must have been cached", ognlUtil.expressionCacheSize(), 1);
|
||||
} catch (Exception ex) {
|
||||
fail("Expression execution should have succeeded here. Exception: " + ex);
|
||||
}
|
||||
// Expression which executes with failure
|
||||
try {
|
||||
ognlUtil.getValue("@com.opensymphony.xwork2.ognl.OgnlUtilTest@STATIC_PRIVATE_ATTRIBUTE", context, foo);
|
||||
fail("Expression execution should have failed here");
|
||||
} catch (Exception ex) {
|
||||
assertEquals("Expression with failed execution must have been cached nevertheless", ognlUtil.expressionCacheSize(), 2);
|
||||
}
|
||||
ognlUtil.setUseLRUExpressionCache("false");
|
||||
}
|
||||
|
||||
public void testMethodExpressionIsCachedIrrespectiveOfItsExecutionStatus() throws Exception {
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = (OgnlContext) ognlUtil.createDefaultContext(foo);
|
||||
@@ -193,6 +272,32 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertTrue("Expression cache empty after usage ?", ognlUtil.expressionCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testClearExpressionLRUCache() throws OgnlException {
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
ognlUtil.setUseLRUExpressionCache("true");
|
||||
// Test that the expression cache is functioning as expected.
|
||||
Object expr0 = ognlUtil.compile("test");
|
||||
Object expr1 = ognlUtil.compile("test");
|
||||
Object expr2 = ognlUtil.compile("test");
|
||||
// Cache in effect, so expr0, expr1, expr2 should be the same.
|
||||
assertSame(expr0, expr1);
|
||||
assertSame(expr0, expr2);
|
||||
assertTrue("Expression cache empty before clear ?", ognlUtil.expressionCacheSize() > 0);
|
||||
// Clear the Epxression cache and confirm subsequent requests are new.
|
||||
ognlUtil.clearExpressionCache();
|
||||
assertEquals("Expression cache not empty after clear ?", 0, ognlUtil.expressionCacheSize());
|
||||
Object expr3 = ognlUtil.compile("test");
|
||||
Object expr4 = ognlUtil.compile("test");
|
||||
Object expr5 = ognlUtil.compile("test");
|
||||
// Cache cleared, expr3 should be a new instance.
|
||||
assertNotSame(expr0, expr3);
|
||||
// Cache still in effect, so expr3, expr4, expr5 should be the same.
|
||||
assertSame(expr3, expr4);
|
||||
assertSame(expr3, expr5);
|
||||
assertTrue("Expression cache empty after usage ?", ognlUtil.expressionCacheSize() > 0);
|
||||
ognlUtil.setUseLRUExpressionCache("false");
|
||||
}
|
||||
|
||||
public void testClearBeanInfoCache() throws IntrospectionException {
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
@@ -242,6 +347,79 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testBeanInfoCache() throws IntrospectionException {
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
// Test that the BeanInfo cache is functioning as expected.
|
||||
Object beanInfo1_1 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_2 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_3 = ognlUtil.getBeanInfo(testBean1);
|
||||
// Cache in effect, so beanInfo1_1, beanInfo1_2, beanInfo1_3 should be the same.
|
||||
assertSame(beanInfo1_1, beanInfo1_2);
|
||||
assertSame(beanInfo1_1, beanInfo1_3);
|
||||
Object beanInfo2_1 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_2 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_3 = ognlUtil.getBeanInfo(testBean2);
|
||||
// Cache in effect, so beanInfo2_1, beanInfo2_2, beanInfo2_3 should be the same.
|
||||
assertSame(beanInfo2_1, beanInfo2_2);
|
||||
assertSame(beanInfo2_1, beanInfo2_3);
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_1, beanInfo2_1);
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
}
|
||||
|
||||
public void testBeanInfoLRUCache() throws IntrospectionException {
|
||||
ognlUtil.setUseLRUBeanInfoCache("true");
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
// Test that the BeanInfo cache is functioning as expected.
|
||||
Object beanInfo1_1 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_2 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_3 = ognlUtil.getBeanInfo(testBean1);
|
||||
// Cache in effect, so beanInfo1_1, beanInfo1_2, beanInfo1_3 should be the same.
|
||||
assertSame(beanInfo1_1, beanInfo1_2);
|
||||
assertSame(beanInfo1_1, beanInfo1_3);
|
||||
Object beanInfo2_1 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_2 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_3 = ognlUtil.getBeanInfo(testBean2);
|
||||
// Cache in effect, so beanInfo2_1, beanInfo2_2, beanInfo2_3 should be the same.
|
||||
assertSame(beanInfo2_1, beanInfo2_2);
|
||||
assertSame(beanInfo2_1, beanInfo2_3);
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_1, beanInfo2_1);
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
ognlUtil.setUseLRUBeanInfoCache("true");
|
||||
}
|
||||
|
||||
public void testBeanInfoLRUCacheLimits() throws IntrospectionException {
|
||||
ognlUtil.setUseLRUBeanInfoCache("true");
|
||||
ognlUtil.setBeanInfoCacheMaxSize("1");
|
||||
final TestBean1 testBean1 = new TestBean1();
|
||||
final TestBean2 testBean2 = new TestBean2();
|
||||
// Test that the BeanInfo cache is functioning as expected.
|
||||
Object beanInfo1_1 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_2 = ognlUtil.getBeanInfo(testBean1);
|
||||
Object beanInfo1_3 = ognlUtil.getBeanInfo(testBean1);
|
||||
// Cache in effect, so beanInfo1_1, beanInfo1_2, beanInfo1_3 should be the same.
|
||||
assertSame(beanInfo1_1, beanInfo1_2);
|
||||
assertSame(beanInfo1_1, beanInfo1_3);
|
||||
Object beanInfo2_1 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_2 = ognlUtil.getBeanInfo(testBean2);
|
||||
Object beanInfo2_3 = ognlUtil.getBeanInfo(testBean2);
|
||||
// Cache in effect, so beanInfo2_1, beanInfo2_2, beanInfo2_3 should be the same.
|
||||
assertSame(beanInfo2_1, beanInfo2_2);
|
||||
assertSame(beanInfo2_1, beanInfo2_3);
|
||||
// BeanInfo for TestBean1 and TestBean2 should always be different.
|
||||
assertNotSame(beanInfo1_1, beanInfo2_1);
|
||||
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
|
||||
assertEquals("BeanInfo LRU cache size should be at its limit", 1, ognlUtil.beanInfoCacheSize());
|
||||
// LRU cache should not contain TestBean1 beaninfo anymore. A new entry should exist in the cache.
|
||||
Object beanInfo1_4 = ognlUtil.getBeanInfo(testBean1);
|
||||
assertNotSame("BeanInfo dropped from LRU cache is the same as newly added ?", beanInfo1_1, beanInfo1_4);
|
||||
ognlUtil.setUseLRUBeanInfoCache("false");
|
||||
ognlUtil.setBeanInfoCacheMaxSize(String.valueOf(Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
public void testClearRuntimeCache() {
|
||||
// Confirm that no exceptions or failures arise when calling the convenience global clear method.
|
||||
OgnlUtil.clearRuntimeCache();
|
||||
@@ -1525,6 +1703,36 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testOgnlUtilLRUCacheClass() throws OgnlException {
|
||||
OgnlUtil.LRUCache<Integer, String> lruCache = ognlUtil.new LRUCache<>(2, 16, 0.75f);
|
||||
Map<Integer, String> backingMap = lruCache.backingMapReference();
|
||||
assertNotNull("Backing Map somehow null ?", backingMap);
|
||||
assertEquals("Initial evictionLimit did not match initial value", 2, lruCache.getEvictionLimit());
|
||||
lruCache.setEvictionLimit(3);
|
||||
assertEquals("Updated evictionLimit did not match updated value", 3, lruCache.getEvictionLimit());
|
||||
String lookupResult = lruCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of empty cache returned non-null value ?", lookupResult);
|
||||
lruCache.put(Integer.valueOf(0), "Zero");
|
||||
lookupResult = lruCache.get(Integer.valueOf(0));
|
||||
assertEquals("Retrieved value does not match put value ?", "Zero", lookupResult);
|
||||
lruCache.put(Integer.valueOf(1), "One");
|
||||
lruCache.put(Integer.valueOf(2), "Two");
|
||||
assertEquals("LRU cache not size evictionlimit after adding three values ?", lruCache.getEvictionLimit(), lruCache.size());
|
||||
lookupResult = lruCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
lruCache.put(Integer.valueOf(3), "Three");
|
||||
assertEquals("LRU cache not size evictionlimit after adding values ?", lruCache.getEvictionLimit(), lruCache.size());
|
||||
lookupResult = lruCache.get(Integer.valueOf(0));
|
||||
assertNull("Lookup of value 0 (should have dropped off LRU cache) returned non-null value ?", lookupResult);
|
||||
lruCache.putIfAbsent(Integer.valueOf(2), "Two");
|
||||
lookupResult = lruCache.get(Integer.valueOf(2));
|
||||
assertEquals("Retrieved value does not match put value ?", "Two", lookupResult);
|
||||
assertEquals("LRUCache and backing map size different after puts ?", lruCache.size(), backingMap.size());
|
||||
lruCache.clear();
|
||||
assertEquals("LRU cache not empty after clear ?", 0, lruCache.size());
|
||||
assertEquals("LRUCache and backing map size different after clear ?", lruCache.size(), backingMap.size());
|
||||
}
|
||||
|
||||
private void reloadTestContainerConfiguration(boolean devMode, boolean allowStaticMethod) {
|
||||
loadConfigurationProviders(new StubConfigurationProvider() {
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user