This is an automated email from the ASF dual-hosted git repository. yasserzamani pushed a commit to branch struts-2-5-x in repository https://gitbox.apache.org/repos/asf/struts.git
The following commit(s) were added to refs/heads/struts-2-5-x by this push: new 9216e8e 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). new 4ae1a6a Merge pull request #361 from JCgH4164838Gh792C124B5/localS2_25x_OgnlCachecontrol 9216e8e is described below commit 9216e8e22c67393f29665186dd25ac5c4bf753e4 Author: JCgH4164838Gh792C124B5 <43964333+jcgh4164838gh792c12...@users.noreply.github.com> AuthorDate: Sat Jun 15 11:40:58 2019 -0400 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). --- .../com/opensymphony/xwork2/ognl/OgnlUtil.java | 70 +++++++++++++ .../com/opensymphony/xwork2/ognl/OgnlUtilTest.java | 112 +++++++++++++++++++++ 2 files changed, 182 insertions(+) diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java index 20be3dc..071ed71 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java @@ -189,6 +189,76 @@ public class OgnlUtil { } /** + * 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. * diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java index 9c5e59b..5f1a5a5 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlUtilTest.java @@ -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; + } + } + }