Proposed WW-5035 enhancement:

- Provide cache clearing methods for OgnlUtil (expression cache, BeanInfo
cache).
- Provide methods to check the cache sizes (entry number for expression
cache, BeanInfo cache).
- Provide static method to clear the OgnlRuntime cache (convenience
method).
This commit is contained in:
JCgH4164838Gh792C124B5
2019-06-15 11:40:58 -04:00
parent 651eac2c57
commit 9216e8e22c
2 changed files with 182 additions and 0 deletions
@@ -188,6 +188,76 @@ public class OgnlUtil {
return disallowProxyMemberAccess;
}
/**
* Convenience mechanism to clear the OGNL Runtime Cache via OgnlUtil. May be utilized
* by applications that generate many unique OGNL expressions over time.
*
* Note: This call affects the global OGNL cache, see ({@link ognl.OgnlRuntime#clearCache()} for details.
*
* Warning: Frequent calling if this method may negatively impact performance, but may be required
* to avoid memory exhaustion (resource leak) with too many OGNL expressions being cached.
*
* @since 2.5.21
*/
public static void clearRuntimeCache() {
OgnlRuntime.clearCache();
}
/**
* Provide a mechanism to clear the OGNL expression cache. May be utilized by applications
* that generate many unique OGNL expressions over time.
*
* Note: This call affects the current OgnlUtil instance. For Struts this is often a Singleton
* instance so it can be "effectively global".
*
* Warning: Frequent calling if this method may negatively impact performance, but may be required
* to avoid memory exhaustion (resource leak) with too many OGNL expressions being cached.
*
* @since 2.5.21
*/
public void clearExpressionCache() {
expressions.clear();
}
/**
* Check the size of the expression cache (current number of elements).
*
* @return current number of elements in the expression cache.
*
* @since 2.5.21
*/
public int expressionCacheSize() {
return expressions.size();
}
/**
* Provide a mechanism to clear the BeanInfo cache. May be utilized by applications
* that request BeanInfo and/or PropertyDescriptors for many unique classes or objects over time
* (especially dynamic objects).
*
* Note: This call affects the current OgnlUtil instance. For Struts this is often a Singleton
* instance so it can be "effectively global".
*
* Warning: Frequent calling if this method may negatively impact performance, but may be required
* to avoid memory exhaustion (resource leak) with too many BeanInfo elements being cached.
*
* @since 2.5.21
*/
public void clearBeanInfoCache() {
beanInfoCache.clear();
}
/**
* Check the size of the BeanInfo cache (current number of elements).
*
* @return current number of elements in the BeanInfo cache.
*
* @since 2.5.21
*/
public int beanInfoCacheSize() {
return beanInfoCache.size();
}
/**
* Sets the object's properties using the default type converter, defaulting to not throw
* exceptions for problems setting the properties.
@@ -31,6 +31,7 @@ import com.opensymphony.xwork2.test.User;
import com.opensymphony.xwork2.util.*;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
import java.beans.IntrospectionException;
import ognl.*;
import org.apache.struts2.StrutsConstants;
@@ -113,6 +114,84 @@ public class OgnlUtilTest extends XWorkTestCase {
assertSame(expr0, expr2);
}
public void testClearExpressionCache() throws OgnlException {
ognlUtil.setEnableExpressionCache("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();
assertTrue("Expression cache not empty after clear ?", ognlUtil.expressionCacheSize() == 0);
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);
}
public void testClearBeanInfoCache() 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 before clear ?", ognlUtil.beanInfoCacheSize() > 0);
// Clear the BeanInfo cache and confirm subsequent requests are new.
ognlUtil.clearBeanInfoCache();
assertTrue("BeanInfo cache not empty after clear ?", ognlUtil.beanInfoCacheSize() == 0);
Object beanInfo1_4 = ognlUtil.getBeanInfo(testBean1);
Object beanInfo1_5 = ognlUtil.getBeanInfo(testBean1);
Object beanInfo1_6 = ognlUtil.getBeanInfo(testBean1);
// Cache in effect, so beanInfo1_4, beanInfo1_5, beanInfo1_6 should be the same.
assertSame(beanInfo1_4, beanInfo1_5);
assertSame(beanInfo1_4, beanInfo1_6);
// Cache was cleared in-between, so beanInfo1_1/beanInfo1_2/beanInfo1_3 should differ
// from beanInfo1_4/beanInfo1_5/beanInfo1_6.
assertNotSame(beanInfo1_1, beanInfo1_4);
assertNotSame(beanInfo1_2, beanInfo1_5);
assertNotSame(beanInfo1_3, beanInfo1_6);
Object beanInfo2_4 = ognlUtil.getBeanInfo(testBean2);
Object beanInfo2_5 = ognlUtil.getBeanInfo(testBean2);
Object beanInfo2_6 = ognlUtil.getBeanInfo(testBean2);
// Cache in effect, so beanInfo2_4, beanInfo2_5, beanInfo2_6 should be the same.
assertSame(beanInfo2_4, beanInfo2_5);
assertSame(beanInfo2_4, beanInfo2_6);
// Cache was cleared in-between, so beanInfo2_1/beanInfo2_2/beanInfo2_3 should differ
// from beanInfo2_4/beanInfo2_5/beanInfo2_6.
assertNotSame(beanInfo2_1, beanInfo2_4);
assertNotSame(beanInfo2_2, beanInfo2_5);
assertNotSame(beanInfo2_3, beanInfo2_6);
// BeanInfo for TestBean1 and TestBean2 should always be different.
assertNotSame(beanInfo1_4, beanInfo2_4);
assertTrue("BeanInfo cache empty after usage ?", ognlUtil.beanInfoCacheSize() > 0);
}
public void testClearRuntimeCache() {
// Confirm that no exceptions or failures arise when calling the convenience global clear method.
OgnlUtil.clearRuntimeCache();
}
public void testCacheDisabled() throws OgnlException {
ognlUtil.setEnableExpressionCache("false");
Object expr0 = ognlUtil.compile("test");
@@ -1333,4 +1412,37 @@ public class OgnlUtilTest extends XWorkTestCase {
}
}
class TestBean1 {
private String testBeanProperty;
public TestBean1() {
testBeanProperty = "defaultTestBean1Property";
}
public String getTestBeanProperty() {
return testBeanProperty;
}
public void setTestBeanProperty(String testBeanProperty) {
this.testBeanProperty = testBeanProperty;
}
}
class TestBean2 {
private String testBeanProperty;
public TestBean2() {
testBeanProperty = "defaultTestBean2Property";
}
public String getTestBeanProperty() {
return testBeanProperty;
}
public void setTestBeanProperty(String testBeanProperty) {
this.testBeanProperty = testBeanProperty;
}
}
}