revised EvalTag implementation

This commit is contained in:
Juergen Hoeller 2010-02-16 18:16:47 +00:00
parent 4ae1709313
commit 64fc4c23ea
6 changed files with 81 additions and 79 deletions

View File

@ -38,7 +38,7 @@ import org.springframework.util.ClassUtils;
import org.springframework.validation.Validator; import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.servlet.handler.ConversionServiceHandlerInterceptor; import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
import org.springframework.web.servlet.handler.MappedInterceptor; import org.springframework.web.servlet.handler.MappedInterceptor;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter; import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping; import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;
@ -112,7 +112,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
annAdapterDef.getPropertyValues().add("messageConverters", getMessageConverters(source)); annAdapterDef.getPropertyValues().add("messageConverters", getMessageConverters(source));
String annAdapterName = parserContext.getReaderContext().registerWithGeneratedName(annAdapterDef); String annAdapterName = parserContext.getReaderContext().registerWithGeneratedName(annAdapterDef);
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceHandlerInterceptor.class); RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
csInterceptorDef.setSource(source); csInterceptorDef.setSource(source);
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService); csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class); RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2008 the original author or authors. * Copyright 2002-2010 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,7 +17,6 @@
package org.springframework.web.servlet.handler; package org.springframework.web.servlet.handler;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -26,28 +25,35 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* Interceptor that places the configured {@link ConversionService} in request scope so it's available during request processing. * Interceptor that places the configured {@link ConversionService} in request scope
* Mainly for use within JSP tags such as the spring:eval tag. * so it's available during request processing. The request attribute name is
* The request attribute name is "org.springframework.core.convert.ConversionService", the value of ConversionService.class.getName(); * "org.springframework.core.convert.ConversionService", the value of
* <code>ConversionService.class.getName()</code>.
*
* <p>Mainly for use within JSP tags such as the spring:eval tag.
*
* @author Keith Donald * @author Keith Donald
* @since 3.0.1 * @since 3.0.1
*/ */
public class ConversionServiceHandlerInterceptor extends HandlerInterceptorAdapter { public class ConversionServiceExposingInterceptor extends HandlerInterceptorAdapter {
private final ConversionService conversionService; private final ConversionService conversionService;
/** /**
* Creates a new {@link ConversionServiceHandlerInterceptor}. * Creates a new {@link ConversionServiceExposingInterceptor}.
* @param conversionService the conversion service to export to request scope when this interceptor is invoked. * @param conversionService the conversion service to export to request scope when this interceptor is invoked
*/ */
public ConversionServiceHandlerInterceptor(ConversionService conversionService) { public ConversionServiceExposingInterceptor(ConversionService conversionService) {
Assert.notNull(conversionService, "The ConversionService may not be null"); Assert.notNull(conversionService, "The ConversionService may not be null");
this.conversionService = conversionService; this.conversionService = conversionService;
} }
@Override @Override
public final boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws ServletException, IOException { throws ServletException, IOException {
request.setAttribute(ConversionService.class.getName(), this.conversionService); request.setAttribute(ConversionService.class.getName(), this.conversionService);
return true; return true;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2010 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,12 +17,9 @@
package org.springframework.web.servlet.tags; package org.springframework.web.servlet.tags;
import java.io.IOException; import java.io.IOException;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext; import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.el.ELException;
import org.springframework.beans.BeansException;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.expression.AccessException; import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
@ -41,15 +38,17 @@ import org.springframework.web.util.TagUtils;
/** /**
* JSP tag for evaluating expressions with the Spring Expression Language (SpEL). * JSP tag for evaluating expressions with the Spring Expression Language (SpEL).
* Supports the standard JSP evaluation context consisting of implicit variables and scoped attributes. * Supports the standard JSP evaluation context consisting of implicit variables and scoped attributes.
* *
* @author Keith Donald * @author Keith Donald
* @since 3.0.1 * @since 3.0.1
*/ */
public class EvalTag extends HtmlEscapingAwareTag { public class EvalTag extends HtmlEscapingAwareTag {
private ExpressionParser expressionParser; private final ExpressionParser expressionParser = new SpelExpressionParser();
private String expression; private EvaluationContext evaluationContext;
private Expression expression;
private String var; private String var;
@ -57,11 +56,18 @@ public class EvalTag extends HtmlEscapingAwareTag {
private boolean javaScriptEscape = false; private boolean javaScriptEscape = false;
@Override
public void setPageContext(PageContext pageContext) {
super.setPageContext(pageContext);
this.evaluationContext = createEvaluationContext(pageContext);
}
/** /**
* Set the expression to evaluate. * Set the expression to evaluate.
*/ */
public void setExpression(String expression) { public void setExpression(String expression) {
this.expression = expression; this.expression = this.expressionParser.parseExpression(expression);
} }
/** /**
@ -89,55 +95,51 @@ public class EvalTag extends HtmlEscapingAwareTag {
ExpressionEvaluationUtils.evaluateBoolean("javaScriptEscape", javaScriptEscape, this.pageContext); ExpressionEvaluationUtils.evaluateBoolean("javaScriptEscape", javaScriptEscape, this.pageContext);
} }
@Override @Override
public int doStartTagInternal() throws JspException { public int doStartTagInternal() throws JspException {
this.expressionParser = new SpelExpressionParser();
return EVAL_BODY_INCLUDE; return EVAL_BODY_INCLUDE;
} }
@Override @Override
public int doEndTag() throws JspException { public int doEndTag() throws JspException {
Expression expression = this.expressionParser.parseExpression(this.expression);
EvaluationContext context = createEvaluationContext();
if (this.var == null) { if (this.var == null) {
try { try {
String result = expression.getValue(context, String.class); String result = this.expression.getValue(this.evaluationContext, String.class);
result = isHtmlEscape() ? HtmlUtils.htmlEscape(result) : result; result = isHtmlEscape() ? HtmlUtils.htmlEscape(result) : result;
result = this.javaScriptEscape ? JavaScriptUtils.javaScriptEscape(result) : result; result = this.javaScriptEscape ? JavaScriptUtils.javaScriptEscape(result) : result;
pageContext.getOut().print(result); pageContext.getOut().print(result);
} }
catch (IOException e) { catch (IOException ex) {
throw new JspException(e); throw new JspException(ex);
} }
} }
else { else {
Object result = expression.getValue(context); Object result = this.expression.getValue(this.evaluationContext);
pageContext.setAttribute(var, result, scope); pageContext.setAttribute(this.var, result, this.scope);
} }
return EVAL_PAGE; return EVAL_PAGE;
} }
private EvaluationContext createEvaluationContext() {
private EvaluationContext createEvaluationContext(PageContext pageContext) {
StandardEvaluationContext context = new StandardEvaluationContext(); StandardEvaluationContext context = new StandardEvaluationContext();
context.addPropertyAccessor(new JspPropertyAccessor(this.pageContext)); context.addPropertyAccessor(new JspPropertyAccessor(pageContext));
ConversionService conversionService = getConversionService(); ConversionService conversionService = getConversionService(pageContext);
if (conversionService != null) { if (conversionService != null) {
context.setTypeConverter(new StandardTypeConverter(conversionService)); context.setTypeConverter(new StandardTypeConverter(conversionService));
} }
return context; return context;
} }
private ConversionService getConversionService() { private ConversionService getConversionService(PageContext pageContext) {
try { return (ConversionService) pageContext.getRequest().getAttribute(ConversionService.class.getName());
return (ConversionService) this.pageContext.getRequest().getAttribute(ConversionService.class.getName());
} catch (BeansException e) {
return null;
}
} }
private static class JspPropertyAccessor implements PropertyAccessor { private static class JspPropertyAccessor implements PropertyAccessor {
private PageContext pageContext; private final PageContext pageContext;
public JspPropertyAccessor(PageContext pageContext) { public JspPropertyAccessor(PageContext pageContext) {
this.pageContext = pageContext; this.pageContext = pageContext;
@ -147,17 +149,11 @@ public class EvalTag extends HtmlEscapingAwareTag {
return null; return null;
} }
public boolean canRead(EvaluationContext context, Object target, public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
String name) throws AccessException { return (resolveImplicitVariable(name) != null || this.pageContext.findAttribute(name) != null);
Object implicitVar = resolveImplicitVariable(name);
if (implicitVar != null) {
return true;
}
return this.pageContext.findAttribute(name) != null;
} }
public TypedValue read(EvaluationContext context, Object target, public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
String name) throws AccessException {
Object implicitVar = resolveImplicitVariable(name); Object implicitVar = resolveImplicitVariable(name);
if (implicitVar != null) { if (implicitVar != null) {
return new TypedValue(implicitVar); return new TypedValue(implicitVar);
@ -165,24 +161,23 @@ public class EvalTag extends HtmlEscapingAwareTag {
return new TypedValue(this.pageContext.findAttribute(name)); return new TypedValue(this.pageContext.findAttribute(name));
} }
public boolean canWrite(EvaluationContext context, Object target, public boolean canWrite(EvaluationContext context, Object target, String name) {
String name) throws AccessException {
return false; return false;
} }
public void write(EvaluationContext context, Object target, public void write(EvaluationContext context, Object target, String name, Object newValue) {
String name, Object newValue) throws AccessException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
private Object resolveImplicitVariable(String name) throws AccessException { private Object resolveImplicitVariable(String name) throws AccessException {
try { try {
return this.pageContext.getVariableResolver().resolveVariable(name); return this.pageContext.getVariableResolver().resolveVariable(name);
} catch (ELException e) { }
throw new AccessException("Unexpected exception occurred accessing '" + name + "' as an implicit variable", e); catch (Exception ex) {
throw new AccessException(
"Unexpected exception occurred accessing '" + name + "' as an implicit variable", ex);
} }
} }
} }
} }

View File

@ -1,12 +1,12 @@
/* /*
* Copyright 2002-2006 the original author or authors. * Copyright 2002-2010 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -47,9 +47,10 @@ import org.springframework.web.servlet.support.RequestContext;
*/ */
public abstract class RequestContextAwareTag extends TagSupport implements TryCatchFinally { public abstract class RequestContextAwareTag extends TagSupport implements TryCatchFinally {
/** {@link javax.servlet.jsp.PageContext} attribute for page-level /**
* {@link javax.servlet.jsp.PageContext} attribute for page-level
* {@link RequestContext} instance. * {@link RequestContext} instance.
* */ */
public static final String REQUEST_CONTEXT_PAGE_ATTRIBUTE = public static final String REQUEST_CONTEXT_PAGE_ATTRIBUTE =
"org.springframework.web.servlet.tags.REQUEST_CONTEXT"; "org.springframework.web.servlet.tags.REQUEST_CONTEXT";

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2010 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -53,7 +53,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.ConversionServiceHandlerInterceptor; import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter; import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
@ -115,8 +115,8 @@ public class MvcNamespaceTests {
HandlerExecutionChain chain = mapping.getHandler(request); HandlerExecutionChain chain = mapping.getHandler(request);
assertEquals(2, chain.getInterceptors().length); assertEquals(2, chain.getInterceptors().length);
assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor);
ConversionServiceHandlerInterceptor interceptor = (ConversionServiceHandlerInterceptor) chain.getInterceptors()[1]; ConversionServiceExposingInterceptor interceptor = (ConversionServiceExposingInterceptor) chain.getInterceptors()[1];
interceptor.preHandle(request, response, handler); interceptor.preHandle(request, response, handler);
assertSame(appContext.getBean(ConversionService.class), request.getAttribute(ConversionService.class.getName())); assertSame(appContext.getBean(ConversionService.class), request.getAttribute(ConversionService.class.getName()));
@ -144,8 +144,8 @@ public class MvcNamespaceTests {
HandlerExecutionChain chain = mapping.getHandler(request); HandlerExecutionChain chain = mapping.getHandler(request);
assertEquals(2, chain.getInterceptors().length); assertEquals(2, chain.getInterceptors().length);
assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor);
ConversionServiceHandlerInterceptor interceptor = (ConversionServiceHandlerInterceptor) chain.getInterceptors()[1]; ConversionServiceExposingInterceptor interceptor = (ConversionServiceExposingInterceptor) chain.getInterceptors()[1];
interceptor.preHandle(request, response, handler); interceptor.preHandle(request, response, handler);
assertSame(appContext.getBean("conversionService"), request.getAttribute(ConversionService.class.getName())); assertSame(appContext.getBean("conversionService"), request.getAttribute(ConversionService.class.getName()));
@ -194,7 +194,7 @@ public class MvcNamespaceTests {
HandlerExecutionChain chain = mapping.getHandler(request); HandlerExecutionChain chain = mapping.getHandler(request);
assertEquals(4, chain.getInterceptors().length); assertEquals(4, chain.getInterceptors().length);
assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor);
assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor);
assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor);
@ -225,7 +225,7 @@ public class MvcNamespaceTests {
HandlerExecutionChain chain = mapping.getHandler(request); HandlerExecutionChain chain = mapping.getHandler(request);
assertEquals(4, chain.getInterceptors().length); assertEquals(4, chain.getInterceptors().length);
assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor);
assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor);
assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor);
LocaleChangeInterceptor interceptor = (LocaleChangeInterceptor) chain.getInterceptors()[2]; LocaleChangeInterceptor interceptor = (LocaleChangeInterceptor) chain.getInterceptors()[2];
@ -249,7 +249,7 @@ public class MvcNamespaceTests {
HandlerExecutionChain chain = mapping.getHandler(request); HandlerExecutionChain chain = mapping.getHandler(request);
assertEquals(4, chain.getInterceptors().length); assertEquals(4, chain.getInterceptors().length);
assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor);
assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor);
assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor);
@ -263,7 +263,7 @@ public class MvcNamespaceTests {
request.setMethod("GET"); request.setMethod("GET");
chain = mapping2.getHandler(request); chain = mapping2.getHandler(request);
assertEquals(4, chain.getInterceptors().length); assertEquals(4, chain.getInterceptors().length);
assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor);
assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor);
assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor);
ModelAndView mv = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler()); ModelAndView mv = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler());
@ -272,7 +272,7 @@ public class MvcNamespaceTests {
request.setRequestURI("/bar"); request.setRequestURI("/bar");
chain = mapping2.getHandler(request); chain = mapping2.getHandler(request);
assertEquals(4, chain.getInterceptors().length); assertEquals(4, chain.getInterceptors().length);
assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceExposingInterceptor);
assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor);
assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor);
ModelAndView mv2 = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler()); ModelAndView mv2 = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler());

View File

@ -1,12 +1,12 @@
/* /*
* Copyright 2008 the original author or authors. * Copyright 2002-2010 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.