Author: jboynes Date: Sat Oct 23 16:44:03 2010 New Revision: 1026644 URL: http://svn.apache.org/viewvc?rev=1026644&view=rev Log: refactor c:set to resolve memory leak from bug #33934
Modified: tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/SetSupport.java tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/rt/core/SetTag.java tomcat/taglibs/standard/trunk/impl/src/test/java/org/apache/taglibs/standard/tag/common/core/SetSupportTest.java tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/tag/el/core/SetTag.java Modified: tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/SetSupport.java URL: http://svn.apache.org/viewvc/tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/SetSupport.java?rev=1026644&r1=1026643&r2=1026644&view=diff ============================================================================== --- tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/SetSupport.java (original) +++ tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/SetSupport.java Sat Oct 23 16:44:03 2010 @@ -26,6 +26,7 @@ import java.lang.reflect.Method; import java.util.Map; +import javax.servlet.jsp.JspApplicationContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspFactory; import javax.servlet.jsp.JspTagException; @@ -44,22 +45,13 @@ import org.apache.taglibs.standard.resou /** * <p>Support for handlers of the <set> tag.</p> * - * <p>The protected <code>value</code> and <code>valueSpecified</code> - * attributes must be set in sync. That is, if you set the value then - * you should set <code>valueSpecified</code> to <code>true<code>, if you unset the value, then - * you should set <code>valueSpecified</code> to <code>false<code>. </p> - * * @author Shawn Bayern */ -public class SetSupport extends BodyTagSupport { +public abstract class SetSupport extends BodyTagSupport { //********************************************************************* // Internal state - protected Object value; // tag attribute - protected boolean valueSpecified; // status - protected Object target; // tag attribute - protected String property; // tag attribute private String var; // tag attribute private String scope; // tag attribute @@ -71,20 +63,14 @@ public class SetSupport extends BodyTagS * not provide other constructors and are expected to call the * superclass constructor. */ - public SetSupport() { + protected SetSupport() { super(); - init(); - } - - // resets local state - private void init() { - value = target = property = var = scope = null; - valueSpecified = false; } // Releases any resources we may have (or inherit) public void release() { - init(); + var = null; + scope = null; super.release(); } @@ -94,27 +80,30 @@ public class SetSupport extends BodyTagS public int doEndTag() throws JspException { - // what we'll store in scope:var - Object result = getResult(); - // decide what to do with the result if (var != null) { - exportToVariable(result); - } else if (target == null) { - // can happen if target evaluates to null - throw new JspTagException(Resources.getMessage("SET_INVALID_TARGET")); - } else if (target instanceof Map) { - exportToMapProperty(result); + exportToVariable(getResult()); } else { - exportToBeanProperty(result); + Object target = evalTarget(); + if (target == null) { + // can happen if target evaluates to null + throw new JspTagException(Resources.getMessage("SET_INVALID_TARGET")); + } + + String property = evalProperty(); + if (target instanceof Map) { + exportToMapProperty(target, property, getResult()); + } else { + exportToBeanProperty(target, property, getResult()); + } } return EVAL_PAGE; } - Object getResult() { - if (valueSpecified) { - return value; + Object getResult() throws JspException { + if (isValueSpecified()) { + return evalValue(); } else if (bodyContent == null) { return ""; } else { @@ -128,6 +117,38 @@ public class SetSupport extends BodyTagS } /** + * Indicates that the value attribute was specified. + * If no value attribute is supplied then the value is taken from the tag's body content. + * + * @return true if the value attribute was specified + */ + protected abstract boolean isValueSpecified(); + + /** + * Evaluate the value attribute. + * + * @return the result of evaluating the value attribute + * @throws JspException if there was a problem evaluating the expression + */ + protected abstract Object evalValue() throws JspException; + + /** + * Evaluate the target attribute. + * + * @return the result of evaluating the target attribute + * @throws JspException if there was a problem evaluating the expression + */ + protected abstract Object evalTarget() throws JspException; + + /** + * Evaluate the property attribute. + * + * @return the result of evaluating the property attribute + * @throws JspException if there was a problem evaluating the expression + */ + protected abstract String evalProperty() throws JspException; + + /** * Export the result into a scoped variable. * * @param result the value to export @@ -173,9 +194,11 @@ public class SetSupport extends BodyTagS /** * Export the result into a Map. * + * @param target the Map to export into + * @param property the key to export into * @param result the value to export */ - void exportToMapProperty(Object result) { + void exportToMapProperty(Object target, String property, Object result) { @SuppressWarnings("unchecked") Map<Object, Object> map = (Map<Object, Object>) target; if (result == null) { @@ -188,10 +211,12 @@ public class SetSupport extends BodyTagS /** * Export the result into a bean property. * + * @param target the bean to export into + * @param property the bean property to set * @param result the value to export * @throws JspTagException if there was a problem exporting the result */ - void exportToBeanProperty(Object result) throws JspTagException { + void exportToBeanProperty(Object target, String property, Object result) throws JspTagException { PropertyDescriptor[] descriptors; try { descriptors = Introspector.getBeanInfo(target.getClass()).getPropertyDescriptors(); @@ -234,9 +259,12 @@ public class SetSupport extends BodyTagS return null; } Class<?> expectedType = m.getParameterTypes()[0]; - JspFactory jspFactory = JspFactory.getDefaultFactory(); - ExpressionFactory expressionFactory = jspFactory.getJspApplicationContext(pageContext.getServletContext()).getExpressionFactory(); - return expressionFactory.coerceToType(value, expectedType); + return getExpressionFactory().coerceToType(value, expectedType); + } + + protected ExpressionFactory getExpressionFactory() { + JspApplicationContext appContext = JspFactory.getDefaultFactory().getJspApplicationContext(pageContext.getServletContext()); + return appContext.getExpressionFactory(); } //********************************************************************* Modified: tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/rt/core/SetTag.java URL: http://svn.apache.org/viewvc/tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/rt/core/SetTag.java?rev=1026644&r1=1026643&r2=1026644&view=diff ============================================================================== --- tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/rt/core/SetTag.java (original) +++ tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/tag/rt/core/SetTag.java Sat Oct 23 16:44:03 2010 @@ -20,29 +20,59 @@ package org.apache.taglibs.standard.tag. import org.apache.taglibs.standard.tag.common.core.SetSupport; /** - * <p>Tag handler for <set> in JSTL's rtexprvalue library.</p> + * JSTL 1.1 compatible version of <set> that accepts expression results for attribute values. * * @author Shawn Bayern */ public class SetTag extends SetSupport { + private boolean valueSpecified; + private Object value; + private Object target; + private String property; - //********************************************************************* - // Accessors + public SetTag() { + } - // for tag attribute public void setValue(Object value) { this.value = value; this.valueSpecified = true; } - // for tag attribute public void setTarget(Object target) { this.target = target; } - // for tag attribute public void setProperty(String property) { this.property = property; } + + @Override + public void release() { + value = null; + target = null; + property = null; + valueSpecified = false; + super.release(); + } + + @Override + protected boolean isValueSpecified() { + return valueSpecified; + } + + @Override + protected Object evalValue() { + return value; + } + + @Override + protected Object evalTarget() { + return target; + } + + @Override + protected String evalProperty() { + return property; + } } Modified: tomcat/taglibs/standard/trunk/impl/src/test/java/org/apache/taglibs/standard/tag/common/core/SetSupportTest.java URL: http://svn.apache.org/viewvc/tomcat/taglibs/standard/trunk/impl/src/test/java/org/apache/taglibs/standard/tag/common/core/SetSupportTest.java?rev=1026644&r1=1026643&r2=1026644&view=diff ============================================================================== --- tomcat/taglibs/standard/trunk/impl/src/test/java/org/apache/taglibs/standard/tag/common/core/SetSupportTest.java (original) +++ tomcat/taglibs/standard/trunk/impl/src/test/java/org/apache/taglibs/standard/tag/common/core/SetSupportTest.java Sat Oct 23 16:44:03 2010 @@ -64,8 +64,6 @@ public class SetSupportTest { bean = new Bean(); - tag = new SetSupport(); - tag.setPageContext(pageContext); ExpressionFactory expressionFactory = createMock(ExpressionFactory.class); JspApplicationContext applicationContext = createMock(JspApplicationContext.class); @@ -85,9 +83,9 @@ public class SetSupportTest { @Test public void testSyntax1WithNoScope() throws JspException { + tag = new MockSetSupport(VALUE); + tag.setPageContext(pageContext); tag.setVar(VAR); - tag.valueSpecified = true; - tag.value = VALUE; // verify mapper is checked but that no action is taken expect(vm.resolveVariable(VAR)).andReturn(null); @@ -99,10 +97,10 @@ public class SetSupportTest { @Test public void testSyntax1WithNullScope() throws JspException { + tag = new MockSetSupport(VALUE); + tag.setPageContext(pageContext); tag.setVar(VAR); tag.setScope(null); - tag.valueSpecified = true; - tag.value = VALUE; // verify mapper is checked but that no action is taken expect(vm.resolveVariable(VAR)).andReturn(null); @@ -114,10 +112,10 @@ public class SetSupportTest { @Test public void testSyntax1WithPageScope() throws JspException { + tag = new MockSetSupport(VALUE); + tag.setPageContext(pageContext); tag.setVar(VAR); tag.setScope("page"); - tag.valueSpecified = true; - tag.value = VALUE; // verify mapper is checked but that no action is taken expect(vm.resolveVariable(VAR)).andReturn(null); @@ -129,10 +127,10 @@ public class SetSupportTest { @Test public void testSyntax1WithNonPageScope() throws JspException { + tag = new MockSetSupport(VALUE); + tag.setPageContext(pageContext); tag.setVar(VAR); tag.setScope("request"); - tag.valueSpecified = true; - tag.value = VALUE; // verify mapper is not checked pageContext.setAttribute(VAR, VALUE, PageContext.REQUEST_SCOPE); @@ -143,9 +141,9 @@ public class SetSupportTest { @Test public void testSyntax1WithNullValueAndNoScope() throws JspException { + tag = new MockSetSupport(null); + tag.setPageContext(pageContext); tag.setVar(VAR); - tag.valueSpecified = true; - tag.value = null; // verify mapper is checked but that no action is taken expect(vm.resolveVariable(VAR)).andReturn(null); @@ -157,10 +155,10 @@ public class SetSupportTest { @Test public void testSyntax1WithNullValueAndNonPageScope() throws JspException { + tag = new MockSetSupport(null); + tag.setPageContext(pageContext); tag.setVar(VAR); tag.setScope("request"); - tag.valueSpecified = true; - tag.value = null; // verify mapper is checked but that no action is taken expect(vm.resolveVariable(VAR)).andReturn(null); @@ -174,11 +172,9 @@ public class SetSupportTest { public void testSyntax3WithMap() throws JspException { @SuppressWarnings("unchecked") Map<String, Object> target = createMock(Map.class); - tag.target = target; - tag.property = PROPERTY; - tag.valueSpecified = true; - tag.value = VALUE; - + tag = new MockSetSupport(VALUE, target, PROPERTY); + tag.setPageContext(pageContext); + expect(target.put(PROPERTY, VALUE)).andStubReturn(null); replay(target); tag.doEndTag(); @@ -189,10 +185,8 @@ public class SetSupportTest { public void testSyntax3WithMapWhenPropertyIsNull() throws JspException { @SuppressWarnings("unchecked") Map<String, Object> target = createMock(Map.class); - tag.target = target; - tag.property = null; - tag.valueSpecified = true; - tag.value = VALUE; + tag = new MockSetSupport(VALUE, target, null); + tag.setPageContext(pageContext); expect(target.put(null, VALUE)).andStubReturn(null); replay(target); @@ -204,10 +198,8 @@ public class SetSupportTest { public void testSyntax3WithMapWhenValueIsNull() throws JspException { @SuppressWarnings("unchecked") Map<String, Object> target = createMock(Map.class); - tag.target = target; - tag.property = PROPERTY; - tag.valueSpecified = true; - tag.value = null; + tag = new MockSetSupport(null, target, PROPERTY); + tag.setPageContext(pageContext); expect(target.remove(PROPERTY)).andStubReturn(null); replay(target); @@ -217,10 +209,8 @@ public class SetSupportTest { @Test public void testSyntax3WithBean() throws JspException { - tag.target = bean; - tag.property = PROPERTY; - tag.valueSpecified = true; - tag.value = VALUE; + tag = new MockSetSupport(VALUE, bean, PROPERTY); + tag.setPageContext(pageContext); tag.doEndTag(); Assert.assertEquals(VALUE, bean.getProperty()); @@ -228,10 +218,8 @@ public class SetSupportTest { @Test public void testSyntax3WithBeanAndNullValue() throws JspException { - tag.target = bean; - tag.property = PROPERTY; - tag.valueSpecified = true; - tag.value = null; + tag = new MockSetSupport(null, bean, PROPERTY); + tag.setPageContext(pageContext); tag.doEndTag(); Assert.assertNull(bean.getProperty()); @@ -239,10 +227,8 @@ public class SetSupportTest { @Test public void testSyntax3WithBeanAndUndefinedProperty() throws JspException { - tag.target = bean; - tag.property = "undefined"; - tag.valueSpecified = true; - tag.value = VALUE; + tag = new MockSetSupport(VALUE, bean, "undefined"); + tag.setPageContext(pageContext); try { tag.doEndTag(); @@ -253,10 +239,8 @@ public class SetSupportTest { @Test public void testSyntax3WithBeanAndReadOnlyProperty() throws JspException { - tag.target = bean; - tag.property = "readOnly"; - tag.valueSpecified = true; - tag.value = VALUE; + tag = new MockSetSupport(VALUE, bean, "readOnly"); + tag.setPageContext(pageContext); try { tag.doEndTag(); @@ -267,10 +251,8 @@ public class SetSupportTest { @Test public void testSyntax3WhenTargetIsNull() throws JspException { - tag.target = null; - tag.property = PROPERTY; - tag.valueSpecified = true; - tag.value = VALUE; + tag = new MockSetSupport(VALUE, null, PROPERTY); + tag.setPageContext(pageContext); try { tag.doEndTag(); @@ -287,9 +269,9 @@ public class SetSupportTest { */ @Test public void test49526WhenNotMapped() throws JspException { + tag = new MockSetSupport(VALUE); + tag.setPageContext(pageContext); tag.setVar(VAR); - tag.valueSpecified = true; - tag.value = VALUE; // verify mapper is checked but that no action is taken expect(vm.resolveVariable(VAR)).andReturn(null); @@ -306,9 +288,9 @@ public class SetSupportTest { */ @Test public void test49526WhenAlreadyMapped() throws JspException { + tag = new MockSetSupport(VALUE); + tag.setPageContext(pageContext); tag.setVar(VAR); - tag.valueSpecified = true; - tag.value = VALUE; // verify mapper is checked and the mapped variable removed ValueExpression ve = createMock(ValueExpression.class); @@ -327,9 +309,9 @@ public class SetSupportTest { */ @Test public void test49526WhenNotUsingPageContext() throws JspException { + tag = new MockSetSupport(VALUE); + tag.setPageContext(pageContext); tag.setVar(VAR); - tag.valueSpecified = true; - tag.value = VALUE; tag.setScope("request"); // verify mapper is not checked @@ -340,22 +322,20 @@ public class SetSupportTest { } @Test - public void testResultFromValueAttribute() { - tag.valueSpecified = true; - tag.value = VALUE; + public void testResultFromValueAttribute() throws JspException { + tag = new MockSetSupport(VALUE); Assert.assertSame(VALUE, tag.getResult()); } @Test - public void testResultFromNullValueAttribute() { - tag.valueSpecified = true; - tag.value = null; + public void testResultFromNullValueAttribute() throws JspException { + tag = new MockSetSupport(null); Assert.assertNull(tag.getResult()); } @Test - public void testResultFromBodyContent() { - tag.valueSpecified = false; + public void testResultFromBodyContent() throws JspException { + tag = new MockSetSupport(); BodyContent bodyContent = createMock(BodyContent.class); expect(bodyContent.getString()).andStubReturn(" Hello "); replay(bodyContent); @@ -364,20 +344,68 @@ public class SetSupportTest { } @Test - public void testResultFromNullBodyContent() { - tag.valueSpecified = false; + public void testResultFromNullBodyContent() throws JspException { + tag = new MockSetSupport(); tag.setBodyContent(null); - Assert.assertEquals("", tag.getResult()); + Assert.assertEquals(tag.getResult(), ""); } @Test - public void testResultFromEmptyBodyContent() { - tag.valueSpecified = false; + public void testResultFromEmptyBodyContent() throws JspException { + tag = new MockSetSupport(); BodyContent bodyContent = createMock(BodyContent.class); expect(bodyContent.getString()).andStubReturn(null); Assert.assertEquals("", tag.getResult()); } + public static class MockSetSupport extends SetSupport { + private final boolean valueSpecified; + private final Object value; + private final Object target; + private final String property; + + public MockSetSupport() { + this.value = null; + this.valueSpecified = false; + this.target = null; + this.property = null; + } + + public MockSetSupport(Object value, Object target, String property) { + this.value = value; + this.valueSpecified = true; + this.target = target; + this.property = property; + } + + public MockSetSupport(Object value) { + this.value = value; + this.valueSpecified = true; + this.target = null; + this.property = null; + } + + @Override + protected boolean isValueSpecified() { + return valueSpecified; + } + + @Override + protected Object evalValue() { + return value; + } + + @Override + protected Object evalTarget() { + return target; + } + + @Override + protected String evalProperty() { + return property; + } + } + public static class Bean { private String property; Modified: tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/tag/el/core/SetTag.java URL: http://svn.apache.org/viewvc/tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/tag/el/core/SetTag.java?rev=1026644&r1=1026643&r2=1026644&view=diff ============================================================================== --- tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/tag/el/core/SetTag.java (original) +++ tomcat/taglibs/standard/trunk/jstlel/src/main/java/org/apache/taglibs/standard/tag/el/core/SetTag.java Sat Oct 23 16:44:03 2010 @@ -13,7 +13,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ + */ package org.apache.taglibs.standard.tag.el.core; @@ -23,108 +23,70 @@ import org.apache.taglibs.standard.tag.c import org.apache.taglibs.standard.tag.common.core.SetSupport; /** - * <p>A handler for <set>, which redirects the browser to a - * new URL. + * JSTL 1.0 compatible version of <set> that accepts expressions for attribute values. * * @author Shawn Bayern */ public class SetTag extends SetSupport { - //********************************************************************* - // 'Private' state (implementation details) - - private String value_; // stores EL-based property - private String target_; // stores EL-based property - private String property_; // stores EL-based property - - - //********************************************************************* - // Constructor + private boolean valueSpecified; + private String valueExpression; + private String targetExpression; + private String propertyExpression; public SetTag() { - super(); - init(); } + public void setValue(String value) { + this.valueExpression = value; + this.valueSpecified = true; + } - //********************************************************************* - // Tag logic - - // evaluates expression and chains to parent - public int doStartTag() throws JspException { - - // evaluate any expressions we were passed, once per invocation - evaluateExpressions(); - - // chain to the parent implementation - return super.doStartTag(); + public void setTarget(String target) { + this.targetExpression = target; } + public void setProperty(String property) { + this.propertyExpression = property; + } - // Releases any resources we may have (or inherit) + @Override public void release() { + valueExpression = null; + targetExpression = null; + propertyExpression = null; + valueSpecified = false; super.release(); - init(); - } - - - //********************************************************************* - // Accessor methods - - public void setValue(String value_) { - this.value_ = value_; - this.valueSpecified = true; } - public void setTarget(String target_) { - this.target_ = target_; + @Override + protected boolean isValueSpecified() { + return valueSpecified; } - public void setProperty(String property_) { - this.property_ = property_; + @Override + protected Object evalValue() throws JspException { + try { + return ExpressionUtil.evalNotNull("set", "value", valueExpression, Object.class, this, pageContext); + } catch (NullAttributeException ex) { + // explicitly let 'value' be null + return null; + } } - - //********************************************************************* - // Private (utility) methods - - // (re)initializes state (during release() or construction) - private void init() { - // null implies "no expression" - value_ = target_ = property_ = null; + @Override + protected Object evalTarget() throws JspException { + return ExpressionUtil.evalNotNull("set", "target", targetExpression, Object.class, this, pageContext); } - /* Evaluates expressions as necessary */ - private void evaluateExpressions() throws JspException { - /* - * Note: we don't check for type mismatches here; we assume - * the expression evaluator will return the expected type - * (by virtue of knowledge we give it about what that type is). - * A ClassCastException here is truly unexpected, so we let it - * propagate up. - */ - - // 'value' - try { - value = ExpressionUtil.evalNotNull( - "set", "value", value_, Object.class, this, pageContext); - } catch (NullAttributeException ex) { - // explicitly let 'value' be null - value = null; - } - - // 'target' - target = ExpressionUtil.evalNotNull( - "set", "target", target_, Object.class, this, pageContext); - - // 'property' - try { - property = (String) ExpressionUtil.evalNotNull( - "set", "property", property_, String.class, this, pageContext); + @Override + protected String evalProperty() throws JspException { + try { + return (String) ExpressionUtil.evalNotNull("set", "property", propertyExpression, String.class, this, pageContext); } catch (NullAttributeException ex) { // explicitly let 'property' be null - property = null; + return null; } } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org