diff --git a/org.springframework.web/src/main/java/org/springframework/web/util/ExpressionEvaluationUtils.java b/org.springframework.web/src/main/java/org/springframework/web/util/ExpressionEvaluationUtils.java index 11fd01e475..da95b6bc91 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/util/ExpressionEvaluationUtils.java +++ b/org.springframework.web/src/main/java/org/springframework/web/util/ExpressionEvaluationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.web.util; +import javax.servlet.ServletContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.el.ELException; @@ -30,6 +31,10 @@ import org.springframework.util.Assert; * invoking the EL evaluator, treating the value as "normal" expression * (i.e. a literal String value) else. * + *

See {@link #isSpringJspExpressionSupportActive} for guidelines + * on when to use Spring's JSP expression support as opposed to the + * built-in expression support in JSP 2.0+ containers. + * * @author Juergen Hoeller * @author Alef Arendsen * @since 11.07.2003 @@ -37,11 +42,57 @@ import org.springframework.util.Assert; */ public abstract class ExpressionEvaluationUtils { + /** + * Expression support parameter at the servlet context level + * (i.e. a context-param in web.xml): "springJspExpressionSupport". + */ + public static final String EXPRESSION_SUPPORT_CONTEXT_PARAM = "springJspExpressionSupport"; + public static final String EXPRESSION_PREFIX = "${"; public static final String EXPRESSION_SUFFIX = "}"; + /** + * Check whether Spring's JSP expression support is actually active. + *

Note that JSP 2.0+ containers come with expression support themselves: + * However, it will only be active for web applications declaring Servlet 2.4 + * or higher in their web.xml deployment descriptor. + *

If a web.xml context-param named "springJspExpressionSupport" is + * found, its boolean value will be taken to decide whether this support is active. + * If not found, the default is for expression support to be inactive on Servlet 3.0 + * containers with web applications declaring Servlet 2.4 or higher in their + * web.xml. For backwards compatibility, Spring's expression support + * will remain active for applications declaring Servlet 2.3 or earlier. However, + * on Servlet 2.4/2.5 containers, we can't find out what the application has declared, + * so we'll also fall back to keeping expression support active in such a case. + *

Recommendations: Explicitly set "springJspExpressionSupport" to "false" + * in order to prevent double evaluation for Servlet 2.4+ based applications. + * On Servlet 3.0 containers, this will be done for you by default by the framework. + * If for some reason you nevertheless want Spring's JSP expression support to be + * active, explicitly set the "springJspExpressionSupport" context-param to "true". + * @param pageContext current JSP PageContext + * @return true if active (ExpressionEvaluationUtils will actually evaluate expressions); + * false if not active (ExpressionEvaluationUtils will return given values as-is, + * relying on the JSP container pre-evaluating values before passing them to JSP tag attributes) + */ + public static boolean isSpringJspExpressionSupportActive(PageContext pageContext) { + ServletContext sc = pageContext.getServletContext(); + String springJspExpressionSupport = sc.getInitParameter(EXPRESSION_SUPPORT_CONTEXT_PARAM); + if (springJspExpressionSupport != null) { + return Boolean.valueOf(springJspExpressionSupport); + } + if (sc.getMajorVersion() >= 3) { + // We're on a Servlet 3.0+ container: Let's check what the application declares... + if (sc.getEffectiveMajorVersion() > 2 || sc.getEffectiveMinorVersion() > 3) { + // Application declares Servlet 2.4+ in its web.xml: JSP 2.0 expressions active. + // Skip our own expression support in order to prevent double evaluation. + return false; + } + } + return true; + } + /** * Check if the given expression value is an EL expression. * @param value the expression to check @@ -67,7 +118,7 @@ public abstract class ExpressionEvaluationUtils { public static Object evaluate(String attrName, String attrValue, Class resultClass, PageContext pageContext) throws JspException { - if (isExpressionLanguage(attrValue)) { + if (isSpringJspExpressionSupportActive(pageContext) && isExpressionLanguage(attrValue)) { return doEvaluate(attrName, attrValue, resultClass, pageContext); } else if (attrValue != null && resultClass != null && !resultClass.isInstance(attrValue)) { @@ -90,7 +141,7 @@ public abstract class ExpressionEvaluationUtils { public static Object evaluate(String attrName, String attrValue, PageContext pageContext) throws JspException { - if (isExpressionLanguage(attrValue)) { + if (isSpringJspExpressionSupportActive(pageContext) && isExpressionLanguage(attrValue)) { return doEvaluate(attrName, attrValue, Object.class, pageContext); } else { @@ -109,7 +160,7 @@ public abstract class ExpressionEvaluationUtils { public static String evaluateString(String attrName, String attrValue, PageContext pageContext) throws JspException { - if (isExpressionLanguage(attrValue)) { + if (isSpringJspExpressionSupportActive(pageContext) && isExpressionLanguage(attrValue)) { return (String) doEvaluate(attrName, attrValue, String.class, pageContext); } else { @@ -128,7 +179,7 @@ public abstract class ExpressionEvaluationUtils { public static int evaluateInteger(String attrName, String attrValue, PageContext pageContext) throws JspException { - if (isExpressionLanguage(attrValue)) { + if (isSpringJspExpressionSupportActive(pageContext) && isExpressionLanguage(attrValue)) { return (Integer) doEvaluate(attrName, attrValue, Integer.class, pageContext); } else { @@ -147,7 +198,7 @@ public abstract class ExpressionEvaluationUtils { public static boolean evaluateBoolean(String attrName, String attrValue, PageContext pageContext) throws JspException { - if (isExpressionLanguage(attrValue)) { + if (isSpringJspExpressionSupportActive(pageContext) && isExpressionLanguage(attrValue)) { return (Boolean) doEvaluate(attrName, attrValue, Boolean.class, pageContext); } else { @@ -155,7 +206,6 @@ public abstract class ExpressionEvaluationUtils { } } - /** * Actually evaluate the given expression (be it EL or a literal String value) * to an Object of a given type. Supports concatenated expressions, diff --git a/org.springframework.web/src/test/java/org/springframework/mock/web/MockServletContext.java b/org.springframework.web/src/test/java/org/springframework/mock/web/MockServletContext.java index 828b430e6c..828af03d2e 100644 --- a/org.springframework.web/src/test/java/org/springframework/mock/web/MockServletContext.java +++ b/org.springframework.web/src/test/java/org/springframework/mock/web/MockServletContext.java @@ -76,8 +76,8 @@ import org.springframework.web.util.WebUtils; * and XmlWebApplicationContext with an underlying MockServletContext (as long as * the MockServletContext has been configured with a FileSystemResourceLoader). * - * Supports the Servlet 3.0 API level, but throws {@link UnsupportedOperationException} - * for all methods introduced in Servlet 3.0. + *

Supports Servlet 3.0 API level, but throws {@link UnsupportedOperationException} + * for most methods introduced in Servlet 3.0. * * @author Rod Johnson * @author Juergen Hoeller @@ -102,8 +102,14 @@ public class MockServletContext implements ServletContext { private String contextPath = ""; + private int majorVersion = 2; + private int minorVersion = 5; + private int effectiveMajorVersion = 2; + + private int effectiveMinorVersion = 5; + private final Map contexts = new HashMap(); private final Map initParameters = new LinkedHashMap(); @@ -191,14 +197,15 @@ public class MockServletContext implements ServletContext { return this.contexts.get(contextPath); } + public void setMajorVersion(int majorVersion) { + this.majorVersion = majorVersion; + } + public int getMajorVersion() { - return 2; + return this.majorVersion; } public void setMinorVersion(int minorVersion) { - if (minorVersion < 3 || minorVersion > 5) { - throw new IllegalArgumentException("Only Servlet minor versions between 3 and 5 are supported"); - } this.minorVersion = minorVersion; } @@ -206,6 +213,22 @@ public class MockServletContext implements ServletContext { return this.minorVersion; } + public void setEffectiveMajorVersion(int effectiveMajorVersion) { + this.effectiveMajorVersion = effectiveMajorVersion; + } + + public int getEffectiveMajorVersion() { + return this.effectiveMajorVersion; + } + + public void setEffectiveMinorVersion(int effectiveMinorVersion) { + this.effectiveMinorVersion = effectiveMinorVersion; + } + + public int getEffectiveMinorVersion() { + return this.effectiveMinorVersion; + } + public String getMimeType(String filePath) { return MimeTypeResolver.getMimeType(filePath); } @@ -444,14 +467,6 @@ public class MockServletContext implements ServletContext { throw new UnsupportedOperationException(); } - public int getEffectiveMajorVersion() { - throw new UnsupportedOperationException(); - } - - public int getEffectiveMinorVersion() { - throw new UnsupportedOperationException(); - } - public Set getEffectiveSessionTrackingModes() { throw new UnsupportedOperationException(); } diff --git a/org.springframework.web/src/test/java/org/springframework/web/util/ExpressionEvaluationUtilsTests.java b/org.springframework.web/src/test/java/org/springframework/web/util/ExpressionEvaluationUtilsTests.java index ab38e51888..25a6aa0dc3 100644 --- a/org.springframework.web/src/test/java/org/springframework/web/util/ExpressionEvaluationUtilsTests.java +++ b/org.springframework.web/src/test/java/org/springframework/web/util/ExpressionEvaluationUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,18 +24,55 @@ import javax.servlet.jsp.el.ExpressionEvaluator; import javax.servlet.jsp.el.FunctionMapper; import javax.servlet.jsp.el.VariableResolver; -import junit.framework.TestCase; +import org.junit.Test; import org.springframework.mock.web.MockExpressionEvaluator; import org.springframework.mock.web.MockPageContext; +import org.springframework.mock.web.MockServletContext; + +import static org.junit.Assert.*; /** * @author Aled Arendsen * @author Juergen Hoeller * @since 16.09.2003 */ -public class ExpressionEvaluationUtilsTests extends TestCase { +public class ExpressionEvaluationUtilsTests { + + @Test + public void testIsSpringJspExpressionSupportActive() { + MockServletContext sc = new MockServletContext(); + PageContext pc = new MockPageContext(sc); + assertTrue(ExpressionEvaluationUtils.isSpringJspExpressionSupportActive(pc)); + sc.addInitParameter("springJspExpressionSupport", "false"); + assertFalse(ExpressionEvaluationUtils.isSpringJspExpressionSupportActive(pc)); + } + + @Test + public void testIsSpringJspExpressionSupportActiveOnServlet30() { + MockServletContext sc = new MockServletContext(); + sc.setMajorVersion(3); + sc.setMinorVersion(0); + PageContext pc = new MockPageContext(sc); + assertFalse(ExpressionEvaluationUtils.isSpringJspExpressionSupportActive(pc)); + sc.addInitParameter("springJspExpressionSupport", "true"); + assertTrue(ExpressionEvaluationUtils.isSpringJspExpressionSupportActive(pc)); + } + + @Test + public void testIsSpringJspExpressionSupportActiveOnServlet30WithServlet23Application() { + MockServletContext sc = new MockServletContext(); + sc.setMajorVersion(3); + sc.setMinorVersion(0); + sc.setEffectiveMajorVersion(2); + sc.setEffectiveMinorVersion(3); + PageContext pc = new MockPageContext(sc); + assertTrue(ExpressionEvaluationUtils.isSpringJspExpressionSupportActive(pc)); + sc.addInitParameter("springJspExpressionSupport", "false"); + assertFalse(ExpressionEvaluationUtils.isSpringJspExpressionSupportActive(pc)); + } + @Test public void testIsExpressionLanguage() { assertTrue(ExpressionEvaluationUtils.isExpressionLanguage("${bla}")); assertTrue(ExpressionEvaluationUtils.isExpressionLanguage("bla${bla}")); @@ -43,6 +80,7 @@ public class ExpressionEvaluationUtilsTests extends TestCase { assertFalse(ExpressionEvaluationUtils.isExpressionLanguage("bla$b{")); } + @Test public void testEvaluate() throws Exception { PageContext ctx = new MockPageContext(); ctx.setAttribute("bla", "blie"); @@ -59,6 +97,7 @@ public class ExpressionEvaluationUtilsTests extends TestCase { } } + @Test public void testEvaluateWithConcatenation() throws Exception { PageContext ctx = new MockPageContext(); ctx.setAttribute("bla", "blie"); @@ -98,6 +137,7 @@ public class ExpressionEvaluationUtilsTests extends TestCase { } } + @Test public void testEvaluateString() throws Exception { PageContext ctx = new MockPageContext(); ctx.setAttribute("bla", "blie"); @@ -106,6 +146,7 @@ public class ExpressionEvaluationUtilsTests extends TestCase { assertEquals("blie", ExpressionEvaluationUtils.evaluateString("test", "blie", ctx)); } + @Test public void testEvaluateStringWithConcatenation() throws Exception { PageContext ctx = new MockPageContext(); ctx.setAttribute("bla", "blie"); @@ -137,6 +178,7 @@ public class ExpressionEvaluationUtilsTests extends TestCase { } + @Test public void testEvaluateInteger() throws Exception { PageContext ctx = new MockPageContext(); ctx.setAttribute("bla", new Integer(1)); @@ -145,6 +187,7 @@ public class ExpressionEvaluationUtilsTests extends TestCase { assertEquals(21, ExpressionEvaluationUtils.evaluateInteger("test", "21", ctx)); } + @Test public void testEvaluateBoolean() throws Exception { PageContext ctx = new MockPageContext(); ctx.setAttribute("bla", new Boolean(true)); @@ -153,6 +196,7 @@ public class ExpressionEvaluationUtilsTests extends TestCase { assertTrue(ExpressionEvaluationUtils.evaluateBoolean("test", "true", ctx)); } + @Test public void testRepeatedEvaluate() throws Exception { PageContext ctx = new CountingMockPageContext(); CountingMockExpressionEvaluator eval = (CountingMockExpressionEvaluator) ctx.getExpressionEvaluator(); @@ -172,6 +216,7 @@ public class ExpressionEvaluationUtilsTests extends TestCase { assertEquals(4, eval.evaluateCount); } + @Test public void testEvaluateWithComplexConcatenation() throws Exception { PageContext ctx = new CountingMockPageContext(); CountingMockExpressionEvaluator eval = (CountingMockExpressionEvaluator) ctx.getExpressionEvaluator();