Initial import of web servlet module
This commit is contained in:
parent
4df7d71c1e
commit
af47a8b79b
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="org.springframework.web.servlet">
|
||||
<property file="${basedir}/../build.properties"/>
|
||||
<import file="${basedir}/../build-spring-framework/package-bundle.xml"/>
|
||||
<import file="${basedir}/../spring-build/standard/default.xml"/>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
|
||||
<ivy-module
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
|
||||
version="1.3">
|
||||
|
||||
<info organisation="org.springframework" module="${ant.project.name}">
|
||||
<license name="Apache 2.0" url="http://www.apache.org/licenses/LICENSE-2.0"/>
|
||||
</info>
|
||||
|
||||
<configurations>
|
||||
<include file="${spring.build.dir}/common/default-ivy-configurations.xml"/>
|
||||
</configurations>
|
||||
|
||||
<publications>
|
||||
<artifact name="${ant.project.name}"/>
|
||||
<artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
|
||||
</publications>
|
||||
|
||||
<dependencies>
|
||||
<!-- compile dependencies -->
|
||||
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.logging" rev="1.1.1" conf="compile->runtime" />
|
||||
<dependency org="org.springframework" name="org.springframework.context" rev="latest.integration" conf="compile->compile" />
|
||||
<dependency org="org.springframework" name="org.springframework.context.support" rev="latest.integration" conf="compile->compile" />
|
||||
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration" conf="compile->compile" />
|
||||
<dependency org="org.springframework" name="org.springframework.web" rev="latest.integration" conf="compile->compile" />
|
||||
<!-- optional dependencies -->
|
||||
<dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="2.5.0" conf="optional->compile" />
|
||||
<dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp.jstl" rev="1.1.2" />
|
||||
<dependency org="org.freemarker" name="com.springsource.freemarker" rev="2.3.12" conf="optional->compile" />
|
||||
<dependency org="org.apache.velocity" name="com.springsource.org.apache.velocity" rev="1.5.0" conf="optional->compile" />
|
||||
<dependency org="org.apache.velocity" name="com.springsource.org.apache.velocity.tools.view" rev="1.4.0" conf="optional->compile" />
|
||||
<dependency org="org.apache.tiles" name="org.apache.tiles-library" rev="2.0.5.osgi" conf="optional->compile" />
|
||||
<dependency org="net.sourceforge.jasperreports" name="com.springsource.net.sf.jasperreports" rev="2.0.5" conf="optional->compile" />
|
||||
<dependency org="net.sourceforge.jexcelapi" name="com.springsource.jxl" rev="2.6.6" conf="optional->compile" />
|
||||
<dependency org="org.apache.poi" name="com.springsource.org.apache.poi" rev="3.0.2.FINAL" conf="optional->compile" />
|
||||
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.fileupload" rev="1.2.0" conf="optional->compile" />
|
||||
<!--
|
||||
<dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp" rev="2.1.0" conf="optional->compile" />
|
||||
<dependency org="javax.el" name="com.springsource.javax.el" rev="2.1.0" conf="optional->compile" />
|
||||
<dependency org="org.apache.myfaces" name="com.springsource.org.apache.myfaces.javax.faces" rev="1.2.2" conf="optional->compile" />
|
||||
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="optional->runtime" />
|
||||
<dependency org="javax.xml.rpc" name="com.springsource.javax.xml.rpc" rev="1.1.0" conf="optional->runtime" />
|
||||
<dependency org="org.apache.axis" name="com.springsource.org.apache.axis" rev="1.4.0" conf="optional->runtime" />
|
||||
<dependency org="com.caucho" name="com.springsource.com.caucho" rev="3.1.5" conf="optional->runtime" />
|
||||
-->
|
||||
<!-- test dependencies -->
|
||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.4.0" conf="test->runtime" />
|
||||
|
||||
</dependencies>
|
||||
|
||||
</ivy-module>
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.parent</artifactId>
|
||||
<version>3.0-M1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>org.springframework.web</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Framework: Web</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>com.springsource.javax.servlet</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.el</groupId>
|
||||
<artifactId>com.springsource.javax.el</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>com.springsource.javax.servlet.jsp</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.rpc</groupId>
|
||||
<artifactId>com.springsource.javax.xml.rpc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aopalliance</groupId>
|
||||
<artifactId>com.springsource.org.aopalliance</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.myfaces</groupId>
|
||||
<artifactId>com.springsource.org.apache.myfaces.javax.faces</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.caucho</groupId>
|
||||
<artifactId>com.springsource.com.caucho</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.log4j</groupId>
|
||||
<artifactId>com.springsource.org.apache.log4j</artifactId>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>com.springsource.org.apache.commons.httpclient</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.axis</groupId>
|
||||
<artifactId>com.springsource.org.apache.axis</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.taglibs</groupId>
|
||||
<artifactId>com.springsource.org.apache.taglibs.standard</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.util.HtmlUtils;
|
||||
|
||||
/**
|
||||
* Errors wrapper that adds automatic HTML escaping to the wrapped instance,
|
||||
* for convenient usage in HTML views. Can be retrieved easily via
|
||||
* RequestContext's <code>getErrors</code> method.
|
||||
*
|
||||
* <p>Note that BindTag does <i>not</i> use this class to avoid unnecessary
|
||||
* creation of ObjectError instances. It just escapes the messages and values
|
||||
* that get copied into the respective BindStatus instance.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 01.03.2003
|
||||
* @see org.springframework.web.servlet.support.RequestContext#getErrors
|
||||
* @see org.springframework.web.servlet.tags.BindTag
|
||||
*/
|
||||
public class EscapedErrors implements Errors {
|
||||
|
||||
private final Errors source;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new EscapedErrors instance for the given source instance.
|
||||
*/
|
||||
public EscapedErrors(Errors source) {
|
||||
if (source == null) {
|
||||
throw new IllegalArgumentException("Cannot wrap a null instance");
|
||||
}
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Errors getSource() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
|
||||
public String getObjectName() {
|
||||
return this.source.getObjectName();
|
||||
}
|
||||
|
||||
public void setNestedPath(String nestedPath) {
|
||||
this.source.setNestedPath(nestedPath);
|
||||
}
|
||||
|
||||
public String getNestedPath() {
|
||||
return this.source.getNestedPath();
|
||||
}
|
||||
|
||||
public void pushNestedPath(String subPath) {
|
||||
this.source.pushNestedPath(subPath);
|
||||
}
|
||||
|
||||
public void popNestedPath() throws IllegalStateException {
|
||||
this.source.popNestedPath();
|
||||
}
|
||||
|
||||
|
||||
public void reject(String errorCode) {
|
||||
this.source.reject(errorCode);
|
||||
}
|
||||
|
||||
public void reject(String errorCode, String defaultMessage) {
|
||||
this.source.reject(errorCode, defaultMessage);
|
||||
}
|
||||
|
||||
public void reject(String errorCode, Object[] errorArgs, String defaultMessage) {
|
||||
this.source.reject(errorCode, errorArgs, defaultMessage);
|
||||
}
|
||||
|
||||
public void rejectValue(String field, String errorCode) {
|
||||
this.source.rejectValue(field, errorCode);
|
||||
}
|
||||
|
||||
public void rejectValue(String field, String errorCode, String defaultMessage) {
|
||||
this.source.rejectValue(field, errorCode, defaultMessage);
|
||||
}
|
||||
|
||||
public void rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage) {
|
||||
this.source.rejectValue(field, errorCode, errorArgs, defaultMessage);
|
||||
}
|
||||
|
||||
public void addAllErrors(Errors errors) {
|
||||
this.source.addAllErrors(errors);
|
||||
}
|
||||
|
||||
|
||||
public boolean hasErrors() {
|
||||
return this.source.hasErrors();
|
||||
}
|
||||
|
||||
public int getErrorCount() {
|
||||
return this.source.getErrorCount();
|
||||
}
|
||||
|
||||
public List getAllErrors() {
|
||||
return escapeObjectErrors(this.source.getAllErrors());
|
||||
}
|
||||
|
||||
public boolean hasGlobalErrors() {
|
||||
return this.source.hasGlobalErrors();
|
||||
}
|
||||
|
||||
public int getGlobalErrorCount() {
|
||||
return this.source.getGlobalErrorCount();
|
||||
}
|
||||
|
||||
public List getGlobalErrors() {
|
||||
return escapeObjectErrors(this.source.getGlobalErrors());
|
||||
}
|
||||
|
||||
public ObjectError getGlobalError() {
|
||||
return escapeObjectError(this.source.getGlobalError());
|
||||
}
|
||||
|
||||
public boolean hasFieldErrors() {
|
||||
return this.source.hasFieldErrors();
|
||||
}
|
||||
|
||||
public int getFieldErrorCount() {
|
||||
return this.source.getFieldErrorCount();
|
||||
}
|
||||
|
||||
public List getFieldErrors() {
|
||||
return this.source.getFieldErrors();
|
||||
}
|
||||
|
||||
public FieldError getFieldError() {
|
||||
return this.source.getFieldError();
|
||||
}
|
||||
|
||||
public boolean hasFieldErrors(String field) {
|
||||
return this.source.hasFieldErrors(field);
|
||||
}
|
||||
|
||||
public int getFieldErrorCount(String field) {
|
||||
return this.source.getFieldErrorCount(field);
|
||||
}
|
||||
|
||||
public List getFieldErrors(String field) {
|
||||
return escapeObjectErrors(this.source.getFieldErrors(field));
|
||||
}
|
||||
|
||||
public FieldError getFieldError(String field) {
|
||||
return (FieldError) escapeObjectError(this.source.getFieldError(field));
|
||||
}
|
||||
|
||||
public Object getFieldValue(String field) {
|
||||
Object value = this.source.getFieldValue(field);
|
||||
return (value instanceof String ? HtmlUtils.htmlEscape((String) value) : value);
|
||||
}
|
||||
|
||||
public Class getFieldType(String field) {
|
||||
return this.source.getFieldType(field);
|
||||
}
|
||||
|
||||
private ObjectError escapeObjectError(ObjectError source) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
if (source instanceof FieldError) {
|
||||
FieldError fieldError = (FieldError) source;
|
||||
Object value = fieldError.getRejectedValue();
|
||||
if (value instanceof String) {
|
||||
value = HtmlUtils.htmlEscape((String) value);
|
||||
}
|
||||
return new FieldError(
|
||||
fieldError.getObjectName(), fieldError.getField(), value,
|
||||
fieldError.isBindingFailure(), fieldError.getCodes(),
|
||||
fieldError.getArguments(), HtmlUtils.htmlEscape(fieldError.getDefaultMessage()));
|
||||
}
|
||||
return new ObjectError(
|
||||
source.getObjectName(), source.getCodes(), source.getArguments(),
|
||||
HtmlUtils.htmlEscape(source.getDefaultMessage()));
|
||||
}
|
||||
|
||||
private List escapeObjectErrors(List source) {
|
||||
List escaped = new ArrayList(source.size());
|
||||
for (Iterator it = source.iterator(); it.hasNext();) {
|
||||
ObjectError objectError = (ObjectError)it.next();
|
||||
escaped.add(escapeObjectError(objectError));
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind;
|
||||
|
||||
/**
|
||||
* {@link ServletRequestBindingException} subclass that indicates a missing parameter.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.2
|
||||
*/
|
||||
public class MissingServletRequestParameterException extends ServletRequestBindingException {
|
||||
|
||||
private String parameterName;
|
||||
|
||||
private String parameterType;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for MissingServletRequestParameterException.
|
||||
* @param parameterName the name of the missing parameter
|
||||
* @param parameterType the expected type of the missing parameter
|
||||
*/
|
||||
public MissingServletRequestParameterException(String parameterName, String parameterType) {
|
||||
super("");
|
||||
this.parameterName = parameterName;
|
||||
this.parameterType = parameterType;
|
||||
}
|
||||
|
||||
|
||||
public String getMessage() {
|
||||
return "Required " + this.parameterType + " parameter '" + this.parameterName + "' is not present";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the offending parameter.
|
||||
*/
|
||||
public String getParameterName() {
|
||||
return this.parameterName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the expected type of the offending parameter.
|
||||
*/
|
||||
public String getParameterType() {
|
||||
return this.parameterType;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
|
||||
/**
|
||||
* Parameter extraction methods, for an approach distinct from data binding,
|
||||
* in which parameters of specific types are required.
|
||||
*
|
||||
* <p>This approach is very useful for simple submissions, where binding
|
||||
* request parameters to a command object would be overkill.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Keith Donald
|
||||
* @deprecated as of Spring 2.0: use ServletRequestUtils instead
|
||||
* @see ServletRequestUtils
|
||||
*/
|
||||
public abstract class RequestUtils {
|
||||
|
||||
/**
|
||||
* Throw a ServletException if the given HTTP request method should be rejected.
|
||||
* @param request request to check
|
||||
* @param method method (such as "GET") which should be rejected
|
||||
* @throws ServletException if the given HTTP request is rejected
|
||||
*/
|
||||
public static void rejectRequestMethod(HttpServletRequest request, String method) throws ServletException {
|
||||
if (request.getMethod().equals(method)) {
|
||||
throw new HttpRequestMethodNotSupportedException(method);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an Integer parameter, or <code>null</code> if not present.
|
||||
* Throws an exception if it the parameter value isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the Integer value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static Integer getIntParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getIntParameter(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an int parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static int getIntParameter(HttpServletRequest request, String name, int defaultVal) {
|
||||
return ServletRequestUtils.getIntParameter(request, name, defaultVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of int parameters, return an empty array if not found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static int[] getIntParameters(HttpServletRequest request, String name) {
|
||||
return ServletRequestUtils.getIntParameters(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an int parameter, throwing an exception if it isn't found or isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static int getRequiredIntParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getRequiredIntParameter(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of int parameters, throwing an exception if not found or one is not a number..
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static int[] getRequiredIntParameters(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getRequiredIntParameters(request, name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a Long parameter, or <code>null</code> if not present.
|
||||
* Throws an exception if it the parameter value isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the Long value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static Long getLongParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getLongParameter(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a long parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static long getLongParameter(HttpServletRequest request, String name, long defaultVal) {
|
||||
return ServletRequestUtils.getLongParameter(request, name, defaultVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of long parameters, return an empty array if not found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static long[] getLongParameters(HttpServletRequest request, String name) {
|
||||
return ServletRequestUtils.getLongParameters(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a long parameter, throwing an exception if it isn't found or isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static long getRequiredLongParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getRequiredLongParameter(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of long parameters, throwing an exception if not found or one is not a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static long[] getRequiredLongParameters(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getRequiredLongParameters(request, name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a Float parameter, or <code>null</code> if not present.
|
||||
* Throws an exception if it the parameter value isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the Float value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static Float getFloatParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getFloatParameter(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a float parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static float getFloatParameter(HttpServletRequest request, String name, float defaultVal) {
|
||||
return ServletRequestUtils.getFloatParameter(request, name, defaultVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of float parameters, return an empty array if not found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static float[] getFloatParameters(HttpServletRequest request, String name) {
|
||||
return ServletRequestUtils.getFloatParameters(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a float parameter, throwing an exception if it isn't found or isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static float getRequiredFloatParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getRequiredFloatParameter(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of float parameters, throwing an exception if not found or one is not a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static float[] getRequiredFloatParameters(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getRequiredFloatParameters(request, name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a Double parameter, or <code>null</code> if not present.
|
||||
* Throws an exception if it the parameter value isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the Double value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static Double getDoubleParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getDoubleParameter(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a double parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static double getDoubleParameter(HttpServletRequest request, String name, double defaultVal) {
|
||||
return ServletRequestUtils.getDoubleParameter(request, name, defaultVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of double parameters, return an empty array if not found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static double[] getDoubleParameters(HttpServletRequest request, String name) {
|
||||
return ServletRequestUtils.getDoubleParameters(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a double parameter, throwing an exception if it isn't found or isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static double getRequiredDoubleParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getRequiredDoubleParameter(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of double parameters, throwing an exception if not found or one is not a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static double[] getRequiredDoubleParameters(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return ServletRequestUtils.getRequiredDoubleParameters(request, name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a Boolean parameter, or <code>null</code> if not present.
|
||||
* Throws an exception if it the parameter value isn't a boolean.
|
||||
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||
* treats every other non-empty value as false (i.e. parses leniently).
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the Boolean value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static Boolean getBooleanParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
if (request.getParameter(name) == null) {
|
||||
return null;
|
||||
}
|
||||
return (getRequiredBooleanParameter(request, name) ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||
* treats every other non-empty value as false (i.e. parses leniently).
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static boolean getBooleanParameter(HttpServletRequest request, String name, boolean defaultVal) {
|
||||
if (request.getParameter(name) == null) {
|
||||
return defaultVal;
|
||||
}
|
||||
try {
|
||||
return getRequiredBooleanParameter(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of boolean parameters, return an empty array if not found.
|
||||
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||
* treats every other non-empty value as false (i.e. parses leniently).
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static boolean[] getBooleanParameters(HttpServletRequest request, String name) {
|
||||
try {
|
||||
return getRequiredBooleanParameters(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return new boolean[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean parameter, throwing an exception if it isn't found
|
||||
* or isn't a boolean.
|
||||
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||
* treats every other non-empty value as false (i.e. parses leniently).
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static boolean getRequiredBooleanParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
boolean value = ServletRequestUtils.getRequiredBooleanParameter(request, name);
|
||||
if (!value && "".equals(request.getParameter(name))) {
|
||||
throw new ServletRequestBindingException(
|
||||
"Required boolean parameter '" + name + "' contains no value");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of boolean parameters, throwing an exception if not found
|
||||
* or one isn't a boolean.
|
||||
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||
* treats every other non-empty value as false (i.e. parses leniently).
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static boolean[] getRequiredBooleanParameters(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
boolean[] values = ServletRequestUtils.getRequiredBooleanParameters(request, name);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (!values[i] && "".equals(request.getParameterValues(name)[i])) {
|
||||
throw new ServletRequestBindingException(
|
||||
"Required boolean parameter '" + name + "' contains no value");
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a String parameter, or <code>null</code> if not present.
|
||||
* Throws an exception if it the parameter value is empty.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the String value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static String getStringParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
if (request.getParameter(name) == null) {
|
||||
return null;
|
||||
}
|
||||
return getRequiredStringParameter(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a String parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value to default to enable checks of whether it was supplied.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static String getStringParameter(HttpServletRequest request, String name, String defaultVal) {
|
||||
if (request.getParameter(name) == null) {
|
||||
return defaultVal;
|
||||
}
|
||||
try {
|
||||
return getRequiredStringParameter(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of String parameters, return an empty array if not found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static String[] getStringParameters(HttpServletRequest request, String name) {
|
||||
try {
|
||||
return getRequiredStringParameters(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return new String[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a String parameter, throwing an exception if it isn't found or is empty.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static String getRequiredStringParameter(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
String value = ServletRequestUtils.getRequiredStringParameter(request, name);
|
||||
if ("".equals(value)) {
|
||||
throw new ServletRequestBindingException(
|
||||
"Required string parameter '" + name + "' contains no value");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of String parameters, throwing an exception if not found or one is empty.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static String[] getRequiredStringParameters(HttpServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
String[] values = ServletRequestUtils.getRequiredStringParameters(request, name);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if ("".equals(values[i])) {
|
||||
throw new ServletRequestBindingException(
|
||||
"Required string parameter '" + name + "' contains no value");
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind;
|
||||
|
||||
import org.springframework.web.util.NestedServletException;
|
||||
|
||||
/**
|
||||
* Fatal binding exception, thrown when we want to
|
||||
* treat binding exceptions as unrecoverable.
|
||||
*
|
||||
* <p>Extends ServletException for convenient throwing in any Servlet resource
|
||||
* (such as a Filter), and NestedServletException for proper root cause handling
|
||||
* (as the plain ServletException doesn't expose its root cause at all).
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class ServletRequestBindingException extends NestedServletException {
|
||||
|
||||
/**
|
||||
* Constructor for ServletRequestBindingException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public ServletRequestBindingException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for ServletRequestBindingException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause
|
||||
*/
|
||||
public ServletRequestBindingException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
|
||||
/**
|
||||
* Special {@link org.springframework.validation.DataBinder} to perform data binding
|
||||
* from servlet request parameters to JavaBeans, including support for multipart files.
|
||||
*
|
||||
* <p>See the DataBinder/WebDataBinder superclasses for customization options,
|
||||
* which include specifying allowed/required fields, and registering custom
|
||||
* property editors.
|
||||
*
|
||||
* <p>Used by Spring Web MVC's BaseCommandController and MultiActionController.
|
||||
* Note that BaseCommandController and its subclasses allow for easy customization
|
||||
* of the binder instances that they use through overriding <code>initBinder</code>.
|
||||
*
|
||||
* <p>Can also be used for manual data binding in custom web controllers:
|
||||
* for example, in a plain Controller implementation or in a MultiActionController
|
||||
* handler method. Simply instantiate a ServletRequestDataBinder for each binding
|
||||
* process, and invoke <code>bind</code> with the current ServletRequest as argument:
|
||||
*
|
||||
* <pre class="code">
|
||||
* MyBean myBean = new MyBean();
|
||||
* // apply binder to custom target object
|
||||
* ServletRequestDataBinder binder = new ServletRequestDataBinder(myBean);
|
||||
* // register custom editors, if desired
|
||||
* binder.registerCustomEditor(...);
|
||||
* // trigger actual binding of request parameters
|
||||
* binder.bind(request);
|
||||
* // optionally evaluate binding errors
|
||||
* Errors errors = binder.getErrors();
|
||||
* ...</pre>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see #bind(javax.servlet.ServletRequest)
|
||||
* @see #registerCustomEditor
|
||||
* @see #setAllowedFields
|
||||
* @see #setRequiredFields
|
||||
* @see #setFieldMarkerPrefix
|
||||
* @see org.springframework.web.servlet.mvc.BaseCommandController#initBinder
|
||||
*/
|
||||
public class ServletRequestDataBinder extends WebDataBinder {
|
||||
|
||||
/**
|
||||
* Create a new ServletRequestDataBinder instance, with default object name.
|
||||
* @param target the target object to bind onto (or <code>null</code>
|
||||
* if the binder is just used to convert a plain parameter value)
|
||||
* @see #DEFAULT_OBJECT_NAME
|
||||
*/
|
||||
public ServletRequestDataBinder(Object target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ServletRequestDataBinder instance.
|
||||
* @param target the target object to bind onto (or <code>null</code>
|
||||
* if the binder is just used to convert a plain parameter value)
|
||||
* @param objectName the name of the target object
|
||||
*/
|
||||
public ServletRequestDataBinder(Object target, String objectName) {
|
||||
super(target, objectName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind the parameters of the given request to this binder's target,
|
||||
* also binding multipart files in case of a multipart request.
|
||||
* <p>This call can create field errors, representing basic binding
|
||||
* errors like a required field (code "required"), or type mismatch
|
||||
* between value and bean property (code "typeMismatch").
|
||||
* <p>Multipart files are bound via their parameter name, just like normal
|
||||
* HTTP parameters: i.e. "uploadedFile" to an "uploadedFile" bean property,
|
||||
* invoking a "setUploadedFile" setter method.
|
||||
* <p>The type of the target property for a multipart file can be MultipartFile,
|
||||
* byte[], or String. The latter two receive the contents of the uploaded file;
|
||||
* all metadata like original file name, content type, etc are lost in those cases.
|
||||
* @param request request with parameters to bind (can be multipart)
|
||||
* @see org.springframework.web.multipart.MultipartHttpServletRequest
|
||||
* @see org.springframework.web.multipart.MultipartFile
|
||||
* @see #bindMultipartFiles
|
||||
* @see #bind(org.springframework.beans.PropertyValues)
|
||||
*/
|
||||
public void bind(ServletRequest request) {
|
||||
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
|
||||
if (request instanceof MultipartHttpServletRequest) {
|
||||
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
bindMultipartFiles(multipartRequest.getFileMap(), mpvs);
|
||||
}
|
||||
doBind(mpvs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Treats errors as fatal.
|
||||
* <p>Use this method only if it's an error if the input isn't valid.
|
||||
* This might be appropriate if all input is from dropdowns, for example.
|
||||
* @throws ServletRequestBindingException subclass of ServletException on any binding problem
|
||||
*/
|
||||
public void closeNoCatch() throws ServletRequestBindingException {
|
||||
if (getBindingResult().hasErrors()) {
|
||||
throw new ServletRequestBindingException(
|
||||
"Errors binding onto object '" + getBindingResult().getObjectName() + "'",
|
||||
new BindException(getBindingResult()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2002-2005 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* PropertyValues implementation created from parameters in a ServletRequest.
|
||||
* Can look for all property values beginning with a certain prefix and
|
||||
* prefix separator (default is "_").
|
||||
*
|
||||
* <p>For example, with a prefix of "spring", "spring_param1" and
|
||||
* "spring_param2" result in a Map with "param1" and "param2" as keys.
|
||||
*
|
||||
* <p>This class is not immutable to be able to efficiently remove property
|
||||
* values that should be ignored for binding.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see org.springframework.web.util.WebUtils#getParametersStartingWith
|
||||
*/
|
||||
public class ServletRequestParameterPropertyValues extends MutablePropertyValues {
|
||||
|
||||
/** Default prefix separator */
|
||||
public static final String DEFAULT_PREFIX_SEPARATOR = "_";
|
||||
|
||||
|
||||
/**
|
||||
* Create new ServletRequestPropertyValues using no prefix
|
||||
* (and hence, no prefix separator).
|
||||
* @param request HTTP request
|
||||
*/
|
||||
public ServletRequestParameterPropertyValues(ServletRequest request) {
|
||||
this(request, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new ServletRequestPropertyValues using the given prefix and
|
||||
* the default prefix separator (the underscore character "_").
|
||||
* @param request HTTP request
|
||||
* @param prefix the prefix for parameters (the full prefix will
|
||||
* consist of this plus the separator)
|
||||
* @see #DEFAULT_PREFIX_SEPARATOR
|
||||
*/
|
||||
public ServletRequestParameterPropertyValues(ServletRequest request, String prefix) {
|
||||
this(request, prefix, DEFAULT_PREFIX_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new ServletRequestPropertyValues supplying both prefix and
|
||||
* prefix separator.
|
||||
* @param request HTTP request
|
||||
* @param prefix the prefix for parameters (the full prefix will
|
||||
* consist of this plus the separator)
|
||||
* @param prefixSeparator separator delimiting prefix (e.g. "spring")
|
||||
* and the rest of the parameter name ("param1", "param2")
|
||||
*/
|
||||
public ServletRequestParameterPropertyValues(ServletRequest request, String prefix, String prefixSeparator) {
|
||||
super(WebUtils.getParametersStartingWith(
|
||||
request, (prefix != null) ? prefix + prefixSeparator : null));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,697 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
/**
|
||||
* Parameter extraction methods, for an approach distinct from data binding,
|
||||
* in which parameters of specific types are required.
|
||||
*
|
||||
* <p>This approach is very useful for simple submissions, where binding
|
||||
* request parameters to a command object would be overkill.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Keith Donald
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class ServletRequestUtils {
|
||||
|
||||
private static final IntParser INT_PARSER = new IntParser();
|
||||
|
||||
private static final LongParser LONG_PARSER = new LongParser();
|
||||
|
||||
private static final FloatParser FLOAT_PARSER = new FloatParser();
|
||||
|
||||
private static final DoubleParser DOUBLE_PARSER = new DoubleParser();
|
||||
|
||||
private static final BooleanParser BOOLEAN_PARSER = new BooleanParser();
|
||||
|
||||
private static final StringParser STRING_PARSER = new StringParser();
|
||||
|
||||
|
||||
/**
|
||||
* Get an Integer parameter, or <code>null</code> if not present.
|
||||
* Throws an exception if it the parameter value isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the Integer value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static Integer getIntParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
if (request.getParameter(name) == null) {
|
||||
return null;
|
||||
}
|
||||
return new Integer(getRequiredIntParameter(request, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an int parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static int getIntParameter(ServletRequest request, String name, int defaultVal) {
|
||||
if (request.getParameter(name) == null) {
|
||||
return defaultVal;
|
||||
}
|
||||
try {
|
||||
return getRequiredIntParameter(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of int parameters, return an empty array if not found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static int[] getIntParameters(ServletRequest request, String name) {
|
||||
try {
|
||||
return getRequiredIntParameters(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return new int[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an int parameter, throwing an exception if it isn't found or isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static int getRequiredIntParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return INT_PARSER.parseInt(name, request.getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of int parameters, throwing an exception if not found or one is not a number..
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static int[] getRequiredIntParameters(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return INT_PARSER.parseInts(name, request.getParameterValues(name));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a Long parameter, or <code>null</code> if not present.
|
||||
* Throws an exception if it the parameter value isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the Long value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static Long getLongParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
if (request.getParameter(name) == null) {
|
||||
return null;
|
||||
}
|
||||
return new Long(getRequiredLongParameter(request, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a long parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static long getLongParameter(ServletRequest request, String name, long defaultVal) {
|
||||
if (request.getParameter(name) == null) {
|
||||
return defaultVal;
|
||||
}
|
||||
try {
|
||||
return getRequiredLongParameter(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of long parameters, return an empty array if not found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static long[] getLongParameters(ServletRequest request, String name) {
|
||||
try {
|
||||
return getRequiredLongParameters(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return new long[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a long parameter, throwing an exception if it isn't found or isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static long getRequiredLongParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return LONG_PARSER.parseLong(name, request.getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of long parameters, throwing an exception if not found or one is not a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static long[] getRequiredLongParameters(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return LONG_PARSER.parseLongs(name, request.getParameterValues(name));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a Float parameter, or <code>null</code> if not present.
|
||||
* Throws an exception if it the parameter value isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the Float value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static Float getFloatParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
if (request.getParameter(name) == null) {
|
||||
return null;
|
||||
}
|
||||
return new Float(getRequiredFloatParameter(request, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a float parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static float getFloatParameter(ServletRequest request, String name, float defaultVal) {
|
||||
if (request.getParameter(name) == null) {
|
||||
return defaultVal;
|
||||
}
|
||||
try {
|
||||
return getRequiredFloatParameter(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of float parameters, return an empty array if not found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static float[] getFloatParameters(ServletRequest request, String name) {
|
||||
try {
|
||||
return getRequiredFloatParameters(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return new float[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a float parameter, throwing an exception if it isn't found or isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static float getRequiredFloatParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return FLOAT_PARSER.parseFloat(name, request.getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of float parameters, throwing an exception if not found or one is not a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static float[] getRequiredFloatParameters(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return FLOAT_PARSER.parseFloats(name, request.getParameterValues(name));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a Double parameter, or <code>null</code> if not present.
|
||||
* Throws an exception if it the parameter value isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the Double value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static Double getDoubleParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
if (request.getParameter(name) == null) {
|
||||
return null;
|
||||
}
|
||||
return new Double(getRequiredDoubleParameter(request, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a double parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static double getDoubleParameter(ServletRequest request, String name, double defaultVal) {
|
||||
if (request.getParameter(name) == null) {
|
||||
return defaultVal;
|
||||
}
|
||||
try {
|
||||
return getRequiredDoubleParameter(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of double parameters, return an empty array if not found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static double[] getDoubleParameters(ServletRequest request, String name) {
|
||||
try {
|
||||
return getRequiredDoubleParameters(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return new double[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a double parameter, throwing an exception if it isn't found or isn't a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static double getRequiredDoubleParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return DOUBLE_PARSER.parseDouble(name, request.getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of double parameters, throwing an exception if not found or one is not a number.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static double[] getRequiredDoubleParameters(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return DOUBLE_PARSER.parseDoubles(name, request.getParameterValues(name));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a Boolean parameter, or <code>null</code> if not present.
|
||||
* Throws an exception if it the parameter value isn't a boolean.
|
||||
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||
* treats every other non-empty value as false (i.e. parses leniently).
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the Boolean value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static Boolean getBooleanParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
if (request.getParameter(name) == null) {
|
||||
return null;
|
||||
}
|
||||
return (getRequiredBooleanParameter(request, name) ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||
* treats every other non-empty value as false (i.e. parses leniently).
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static boolean getBooleanParameter(ServletRequest request, String name, boolean defaultVal) {
|
||||
if (request.getParameter(name) == null) {
|
||||
return defaultVal;
|
||||
}
|
||||
try {
|
||||
return getRequiredBooleanParameter(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of boolean parameters, return an empty array if not found.
|
||||
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||
* treats every other non-empty value as false (i.e. parses leniently).
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static boolean[] getBooleanParameters(ServletRequest request, String name) {
|
||||
try {
|
||||
return getRequiredBooleanParameters(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return new boolean[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean parameter, throwing an exception if it isn't found
|
||||
* or isn't a boolean.
|
||||
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||
* treats every other non-empty value as false (i.e. parses leniently).
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static boolean getRequiredBooleanParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return BOOLEAN_PARSER.parseBoolean(name, request.getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of boolean parameters, throwing an exception if not found
|
||||
* or one isn't a boolean.
|
||||
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||
* treats every other non-empty value as false (i.e. parses leniently).
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static boolean[] getRequiredBooleanParameters(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return BOOLEAN_PARSER.parseBooleans(name, request.getParameterValues(name));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a String parameter, or <code>null</code> if not present.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @return the String value, or <code>null</code> if not present
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static String getStringParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
if (request.getParameter(name) == null) {
|
||||
return null;
|
||||
}
|
||||
return getRequiredStringParameter(request, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a String parameter, with a fallback value. Never throws an exception.
|
||||
* Can pass a distinguished value to default to enable checks of whether it was supplied.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @param defaultVal the default value to use as fallback
|
||||
*/
|
||||
public static String getStringParameter(ServletRequest request, String name, String defaultVal) {
|
||||
String val = request.getParameter(name);
|
||||
return (val != null ? val : defaultVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of String parameters, return an empty array if not found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter with multiple possible values
|
||||
*/
|
||||
public static String[] getStringParameters(ServletRequest request, String name) {
|
||||
try {
|
||||
return getRequiredStringParameters(request, name);
|
||||
}
|
||||
catch (ServletRequestBindingException ex) {
|
||||
return new String[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a String parameter, throwing an exception if it isn't found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static String getRequiredStringParameter(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return STRING_PARSER.validateRequiredString(name, request.getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of String parameters, throwing an exception if not found.
|
||||
* @param request current HTTP request
|
||||
* @param name the name of the parameter
|
||||
* @throws ServletRequestBindingException a subclass of ServletException,
|
||||
* so it doesn't need to be caught
|
||||
*/
|
||||
public static String[] getRequiredStringParameters(ServletRequest request, String name)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
return STRING_PARSER.validateRequiredStrings(name, request.getParameterValues(name));
|
||||
}
|
||||
|
||||
|
||||
private abstract static class ParameterParser {
|
||||
|
||||
protected final Object parse(String name, String parameter) throws ServletRequestBindingException {
|
||||
validateRequiredParameter(name, parameter);
|
||||
try {
|
||||
return doParse(parameter);
|
||||
}
|
||||
catch (NumberFormatException ex) {
|
||||
throw new ServletRequestBindingException(
|
||||
"Required " + getType() + " parameter '" + name + "' with value of '" +
|
||||
parameter + "' is not a valid number", ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected final void validateRequiredParameter(String name, Object parameter)
|
||||
throws ServletRequestBindingException {
|
||||
|
||||
if (parameter == null) {
|
||||
throw new MissingServletRequestParameterException(name, getType());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract String getType();
|
||||
|
||||
protected abstract Object doParse(String parameter) throws NumberFormatException;
|
||||
}
|
||||
|
||||
|
||||
private static class IntParser extends ParameterParser {
|
||||
|
||||
protected String getType() {
|
||||
return "int";
|
||||
}
|
||||
|
||||
protected Object doParse(String s) throws NumberFormatException {
|
||||
return Integer.valueOf(s);
|
||||
}
|
||||
|
||||
public int parseInt(String name, String parameter) throws ServletRequestBindingException {
|
||||
return ((Number) parse(name, parameter)).intValue();
|
||||
}
|
||||
|
||||
public int[] parseInts(String name, String[] values) throws ServletRequestBindingException {
|
||||
validateRequiredParameter(name, values);
|
||||
int[] parameters = new int[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
parameters[i] = parseInt(name, values[i]);
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LongParser extends ParameterParser {
|
||||
|
||||
protected String getType() {
|
||||
return "long";
|
||||
}
|
||||
|
||||
protected Object doParse(String parameter) throws NumberFormatException {
|
||||
return Long.valueOf(parameter);
|
||||
}
|
||||
|
||||
public long parseLong(String name, String parameter) throws ServletRequestBindingException {
|
||||
return ((Number) parse(name, parameter)).longValue();
|
||||
}
|
||||
|
||||
public long[] parseLongs(String name, String[] values) throws ServletRequestBindingException {
|
||||
validateRequiredParameter(name, values);
|
||||
long[] parameters = new long[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
parameters[i] = parseLong(name, values[i]);
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class FloatParser extends ParameterParser {
|
||||
|
||||
protected String getType() {
|
||||
return "float";
|
||||
}
|
||||
|
||||
protected Object doParse(String parameter) throws NumberFormatException {
|
||||
return Float.valueOf(parameter);
|
||||
}
|
||||
|
||||
public float parseFloat(String name, String parameter) throws ServletRequestBindingException {
|
||||
return ((Number) parse(name, parameter)).floatValue();
|
||||
}
|
||||
|
||||
public float[] parseFloats(String name, String[] values) throws ServletRequestBindingException {
|
||||
validateRequiredParameter(name, values);
|
||||
float[] parameters = new float[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
parameters[i] = parseFloat(name, values[i]);
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class DoubleParser extends ParameterParser {
|
||||
|
||||
protected String getType() {
|
||||
return "double";
|
||||
}
|
||||
|
||||
protected Object doParse(String parameter) throws NumberFormatException {
|
||||
return Double.valueOf(parameter);
|
||||
}
|
||||
|
||||
public double parseDouble(String name, String parameter) throws ServletRequestBindingException {
|
||||
return ((Number) parse(name, parameter)).doubleValue();
|
||||
}
|
||||
|
||||
public double[] parseDoubles(String name, String[] values) throws ServletRequestBindingException {
|
||||
validateRequiredParameter(name, values);
|
||||
double[] parameters = new double[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
parameters[i] = parseDouble(name, values[i]);
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BooleanParser extends ParameterParser {
|
||||
|
||||
protected String getType() {
|
||||
return "boolean";
|
||||
}
|
||||
|
||||
protected Object doParse(String parameter) throws NumberFormatException {
|
||||
return (parameter.equalsIgnoreCase("true") || parameter.equalsIgnoreCase("on") ||
|
||||
parameter.equalsIgnoreCase("yes") || parameter.equals("1") ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
|
||||
public boolean parseBoolean(String name, String parameter) throws ServletRequestBindingException {
|
||||
return ((Boolean) parse(name, parameter)).booleanValue();
|
||||
}
|
||||
|
||||
public boolean[] parseBooleans(String name, String[] values) throws ServletRequestBindingException {
|
||||
validateRequiredParameter(name, values);
|
||||
boolean[] parameters = new boolean[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
parameters[i] = parseBoolean(name, values[i]);
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class StringParser extends ParameterParser {
|
||||
|
||||
protected String getType() {
|
||||
return "string";
|
||||
}
|
||||
|
||||
protected Object doParse(String parameter) throws NumberFormatException {
|
||||
return parameter;
|
||||
}
|
||||
|
||||
public String validateRequiredString(String name, String value) throws ServletRequestBindingException {
|
||||
validateRequiredParameter(name, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public String[] validateRequiredStrings(String name, String[] values) throws ServletRequestBindingException {
|
||||
validateRequiredParameter(name, values);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
validateRequiredParameter(name, values[i]);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* Special {@link DataBinder} for data binding from web request parameters
|
||||
* to JavaBean objects. Designed for web environments, but not dependent on
|
||||
* the Servlet API; serves as base class for more specific DataBinder variants,
|
||||
* such as {@link org.springframework.web.bind.ServletRequestDataBinder}.
|
||||
*
|
||||
* <p>Includes support for field markers which address a common problem with
|
||||
* HTML checkboxes and select options: detecting that a field was part of
|
||||
* the form, but did not generate a request parameter because it was empty.
|
||||
* A field marker allows to detect that state and reset the corresponding
|
||||
* bean property accordingly.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2
|
||||
* @see #registerCustomEditor
|
||||
* @see #setAllowedFields
|
||||
* @see #setRequiredFields
|
||||
* @see #setFieldMarkerPrefix
|
||||
* @see ServletRequestDataBinder
|
||||
*/
|
||||
public class WebDataBinder extends DataBinder {
|
||||
|
||||
/**
|
||||
* Default prefix that field marker parameters start with, followed by the field
|
||||
* name: e.g. "_subscribeToNewsletter" for a field "subscribeToNewsletter".
|
||||
* <p>Such a marker parameter indicates that the field was visible, that is,
|
||||
* existed in the form that caused the submission. If no corresponding field
|
||||
* value parameter was found, the field will be reset. The value of the field
|
||||
* marker parameter does not matter in this case; an arbitrary value can be used.
|
||||
* This is particularly useful for HTML checkboxes and select options.
|
||||
* @see #setFieldMarkerPrefix
|
||||
*/
|
||||
public static final String DEFAULT_FIELD_MARKER_PREFIX = "_";
|
||||
|
||||
|
||||
private String fieldMarkerPrefix = DEFAULT_FIELD_MARKER_PREFIX;
|
||||
|
||||
private boolean bindEmptyMultipartFiles = true;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new WebDataBinder instance, with default object name.
|
||||
* @param target the target object to bind onto (or <code>null</code>
|
||||
* if the binder is just used to convert a plain parameter value)
|
||||
* @see #DEFAULT_OBJECT_NAME
|
||||
*/
|
||||
public WebDataBinder(Object target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new WebDataBinder instance.
|
||||
* @param target the target object to bind onto (or <code>null</code>
|
||||
* if the binder is just used to convert a plain parameter value)
|
||||
* @param objectName the name of the target object
|
||||
*/
|
||||
public WebDataBinder(Object target, String objectName) {
|
||||
super(target, objectName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify a prefix that can be used for parameters that mark potentially
|
||||
* empty fields, having "prefix + field" as name. Such a marker parameter is
|
||||
* checked by existence: You can send any value for it, for example "visible".
|
||||
* This is particularly useful for HTML checkboxes and select options.
|
||||
* <p>Default is "_", for "_FIELD" parameters (e.g. "_subscribeToNewsletter").
|
||||
* Set this to null if you want to turn off the empty field check completely.
|
||||
* <p>HTML checkboxes only send a value when they're checked, so it is not
|
||||
* possible to detect that a formerly checked box has just been unchecked,
|
||||
* at least not with standard HTML means.
|
||||
* <p>One way to address this is to look for a checkbox parameter value if
|
||||
* you know that the checkbox has been visible in the form, resetting the
|
||||
* checkbox if no value found. In Spring web MVC, this typically happens
|
||||
* in a custom <code>onBind</code> implementation.
|
||||
* <p>This auto-reset mechanism addresses this deficiency, provided
|
||||
* that a marker parameter is sent for each checkbox field, like
|
||||
* "_subscribeToNewsletter" for a "subscribeToNewsletter" field.
|
||||
* As the marker parameter is sent in any case, the data binder can
|
||||
* detect an empty field and automatically reset its value.
|
||||
* @see #DEFAULT_FIELD_MARKER_PREFIX
|
||||
* @see org.springframework.web.servlet.mvc.BaseCommandController#onBind
|
||||
*/
|
||||
public void setFieldMarkerPrefix(String fieldMarkerPrefix) {
|
||||
this.fieldMarkerPrefix = fieldMarkerPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prefix for parameters that mark potentially empty fields.
|
||||
*/
|
||||
public String getFieldMarkerPrefix() {
|
||||
return this.fieldMarkerPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to bind empty MultipartFile parameters. Default is "true".
|
||||
* <p>Turn this off if you want to keep an already bound MultipartFile
|
||||
* when the user resubmits the form without choosing a different file.
|
||||
* Else, the already bound MultipartFile will be replaced by an empty
|
||||
* MultipartFile holder.
|
||||
* @see org.springframework.web.multipart.MultipartFile
|
||||
*/
|
||||
public void setBindEmptyMultipartFiles(boolean bindEmptyMultipartFiles) {
|
||||
this.bindEmptyMultipartFiles = bindEmptyMultipartFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to bind empty MultipartFile parameters.
|
||||
*/
|
||||
public boolean isBindEmptyMultipartFiles() {
|
||||
return this.bindEmptyMultipartFiles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This implementation performs a field marker check
|
||||
* before delegating to the superclass binding process.
|
||||
* @see #checkFieldMarkers
|
||||
*/
|
||||
protected void doBind(MutablePropertyValues mpvs) {
|
||||
checkFieldMarkers(mpvs);
|
||||
super.doBind(mpvs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the given property values for field markers,
|
||||
* i.e. for fields that start with the field marker prefix.
|
||||
* <p>The existence of a field marker indicates that the specified
|
||||
* field existed in the form. If the property values do not contain
|
||||
* a corresponding field value, the field will be considered as empty
|
||||
* and will be reset appropriately.
|
||||
* @param mpvs the property values to be bound (can be modified)
|
||||
* @see #getFieldMarkerPrefix
|
||||
* @see #getEmptyValue(String, Class)
|
||||
*/
|
||||
protected void checkFieldMarkers(MutablePropertyValues mpvs) {
|
||||
if (getFieldMarkerPrefix() != null) {
|
||||
String fieldMarkerPrefix = getFieldMarkerPrefix();
|
||||
PropertyValue[] pvArray = mpvs.getPropertyValues();
|
||||
for (int i = 0; i < pvArray.length; i++) {
|
||||
PropertyValue pv = pvArray[i];
|
||||
if (pv.getName().startsWith(fieldMarkerPrefix)) {
|
||||
String field = pv.getName().substring(fieldMarkerPrefix.length());
|
||||
if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
|
||||
Class fieldType = getPropertyAccessor().getPropertyType(field);
|
||||
mpvs.addPropertyValue(field, getEmptyValue(field, fieldType));
|
||||
}
|
||||
mpvs.removePropertyValue(pv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine an empty value for the specified field.
|
||||
* <p>Default implementation returns <code>Boolean.FALSE</code>
|
||||
* for boolean fields and an empty array of array types.
|
||||
* Else, <code>null</code> is used as default.
|
||||
* @param field the name of the field
|
||||
* @param fieldType the type of the field
|
||||
* @return the empty value (for most fields: null)
|
||||
*/
|
||||
protected Object getEmptyValue(String field, Class fieldType) {
|
||||
if (fieldType != null && boolean.class.equals(fieldType) || Boolean.class.equals(fieldType)) {
|
||||
// Special handling of boolean property.
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
else if (fieldType != null && fieldType.isArray()) {
|
||||
// Special handling of array property.
|
||||
return Array.newInstance(fieldType.getComponentType(), 0);
|
||||
}
|
||||
else {
|
||||
// Default value: try null.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind the multipart files contained in the given request, if any
|
||||
* (in case of a multipart request).
|
||||
* <p>Multipart files will only be added to the property values if they
|
||||
* are not empty or if we're configured to bind empty multipart files too.
|
||||
* @param multipartFiles Map of field name String to MultipartFile object
|
||||
* @param mpvs the property values to be bound (can be modified)
|
||||
* @see org.springframework.web.multipart.MultipartFile
|
||||
* @see #setBindEmptyMultipartFiles
|
||||
*/
|
||||
protected void bindMultipartFiles(Map multipartFiles, MutablePropertyValues mpvs) {
|
||||
for (Iterator it = multipartFiles.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
String key = (String) entry.getKey();
|
||||
MultipartFile value = (MultipartFile) entry.getValue();
|
||||
if (isBindEmptyMultipartFiles() || !value.isEmpty()) {
|
||||
mpvs.addPropertyValue(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation that identifies methods which initialize the
|
||||
* {@link org.springframework.web.bind.WebDataBinder} which
|
||||
* will be used for populating command and form object arguments
|
||||
* of annotated handler methods.
|
||||
*
|
||||
* <p>Such init-binder methods support all arguments that {@link RequestMapping}
|
||||
* supports, except for command/form objects and corresponding validation result
|
||||
* objects. Init-binder methods must not have a return value; they are usually
|
||||
* declared as <code>void</code>.
|
||||
*
|
||||
* <p>Typical arguments are {@link org.springframework.web.bind.WebDataBinder}
|
||||
* in combination with {@link org.springframework.web.context.request.WebRequest}
|
||||
* or {@link java.util.Locale}, allowing to register context-specific editors.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see org.springframework.web.bind.WebDataBinder
|
||||
* @see org.springframework.web.context.request.WebRequest
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface InitBinder {
|
||||
|
||||
/**
|
||||
* The names of command/form attributes and/or request parameters
|
||||
* that this init-binder method is supposed to apply to.
|
||||
* <p>Default is to apply to all command/form attributes and all request parameters
|
||||
* processed by the annotated handler class. Specifying model attribute names or
|
||||
* request parameter names here restricts the init-binder method to those specific
|
||||
* attributes/parameters, with different init-binder methods typically applying to
|
||||
* different groups of attributes or parameters.
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation that binds a method parameter or method return value
|
||||
* to a named model attribute, exposed to a web view. Supported
|
||||
* for {@link RequestMapping} annotated handler classes.
|
||||
*
|
||||
* <p>Can be used to expose command objects to a web view, using
|
||||
* specific attribute names, through annotating corresponding
|
||||
* parameters of a {@link RequestMapping} annotated handler method).
|
||||
*
|
||||
* <p>Can also be used to expose reference data to a web view
|
||||
* through annotating accessor methods in a controller class which
|
||||
* is based on {@link RequestMapping} annotated handler methods,
|
||||
* with such accessor methods allowed to have any arguments that
|
||||
* {@link RequestMapping} supports for handler methods, returning
|
||||
* the model attribute value to expose.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ModelAttribute {
|
||||
|
||||
/**
|
||||
* The name of the model attribute to bind to.
|
||||
* <p>The default model attribute name is inferred from the declared
|
||||
* attribute type (i.e. the method parameter type or method return type),
|
||||
* based on the non-qualified class name:
|
||||
* e.g. "orderAddress" for class "mypackage.OrderAddress",
|
||||
* or "orderAddressList" for "List<mypackage.OrderAddress>".
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation for mapping web requests onto specific handler classes and/or
|
||||
* handler methods. Provides consistent style between Servlet and Portlet
|
||||
* environments, with the semantics adapting to the concrete environment.
|
||||
*
|
||||
* <p><b>NOTE:</b> Method-level mappings are only allowed to narrow the mapping
|
||||
* expressed at the class level (if any). HTTP paths / portlet modes need to
|
||||
* uniquely map onto specific handler beans, with any given path / mode only
|
||||
* allowed to be mapped onto one specific handler bean (not spread across
|
||||
* multiple handler beans). It is strongly recommended to co-locate related
|
||||
* handler methods into the same bean.
|
||||
*
|
||||
* <p>Handler methods which are annotated with this annotation are allowed
|
||||
* to have very flexible signatures. They may have arguments of the following
|
||||
* types, in arbitrary order (except for validation results, which need to
|
||||
* follow right after the corresponding command object, if desired):
|
||||
* <ul>
|
||||
* <li>Request and/or response objects (Servlet API or Portlet API).
|
||||
* You may choose any specific request/response type, e.g.
|
||||
* {@link javax.servlet.ServletRequest} / {@link javax.servlet.http.HttpServletRequest}
|
||||
* or {@link javax.portlet.PortletRequest} / {@link javax.portlet.ActionRequest} /
|
||||
* {@link javax.portlet.RenderRequest}. Note that in the Portlet case,
|
||||
* an explicitly declared action/render argument is also used for mapping
|
||||
* specific request types onto a handler method (in case of no other
|
||||
* information given that differentiates between action and render requests).
|
||||
* <li>Session object (Servlet API or Portlet API): either
|
||||
* {@link javax.servlet.http.HttpSession} or {@link javax.portlet.PortletSession}.
|
||||
* An argument of this type will enforce the presence of a corresponding session.
|
||||
* As a consequence, such an argument will never be <code>null</code>.
|
||||
* <i>Note that session access may not be thread-safe, in particular in a
|
||||
* Servlet environment: Consider switching the
|
||||
* {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#setSynchronizeOnSession "synchronizeOnSession"}
|
||||
* flag to "true" if multiple requests are allowed to access a session concurrently.</i>
|
||||
* <li>{@link org.springframework.web.context.request.WebRequest} or
|
||||
* {@link org.springframework.web.context.request.NativeWebRequest}.
|
||||
* Allows for generic request parameter access as well as request/session
|
||||
* attribute access, without ties to the native Servlet/Portlet API.
|
||||
* <li>{@link java.util.Locale} for the current request locale
|
||||
* (determined by the most specific locale resolver available,
|
||||
* i.e. the configured {@link org.springframework.web.servlet.LocaleResolver}
|
||||
* in a Servlet environment and the portal locale in a Portlet environment).
|
||||
* <li>{@link java.io.InputStream} / {@link java.io.Reader} for access
|
||||
* to the request's content. This will be the raw InputStream/Reader as
|
||||
* exposed by the Servlet/Portlet API.
|
||||
* <li>{@link java.io.OutputStream} / {@link java.io.Writer} for generating
|
||||
* the response's content. This will be the raw OutputStream/Writer as
|
||||
* exposed by the Servlet/Portlet API.
|
||||
* <li>{@link RequestParam @RequestParam} annotated parameters for access to
|
||||
* specific Servlet/Portlet request parameters. Parameter values will be
|
||||
* converted to the declared method argument type.
|
||||
* <li>{@link java.util.Map} / {@link org.springframework.ui.Model} /
|
||||
* {@link org.springframework.ui.ModelMap} for enriching the implicit model
|
||||
* that will be exposed to the web view.
|
||||
* <li>Command/form objects to bind parameters to: as bean properties or fields,
|
||||
* with customizable type conversion, depending on {@link InitBinder} methods
|
||||
* and/or the HandlerAdapter configuration - see the "webBindingInitializer"
|
||||
* property on AnnotationMethodHandlerAdapter.
|
||||
* Such command objects along with their validation results will be exposed
|
||||
* as model attributes, by default using the non-qualified command class name
|
||||
* in property notation (e.g. "orderAddress" for type "mypackage.OrderAddress").
|
||||
* Specify a parameter-level {@link ModelAttribute} annotation for declaring
|
||||
* a specific model attribute name.
|
||||
* <li>{@link org.springframework.validation.Errors} /
|
||||
* {@link org.springframework.validation.BindingResult} validation results
|
||||
* for a preceding command/form object (the immediate preceding argument).
|
||||
* <li>{@link org.springframework.web.bind.support.SessionStatus} status handle
|
||||
* for marking form processing as complete (triggering the cleanup of session
|
||||
* attributes that have been indicated by the {@link SessionAttributes} annotation
|
||||
* at the handler type level).
|
||||
* </ul>
|
||||
*
|
||||
* <p>The following return types are supported for handler methods:
|
||||
* <ul>
|
||||
* <li>A <code>ModelAndView</code> object (Servlet MVC or Portlet MVC),
|
||||
* with the model implicitly enriched with command objects and the results
|
||||
* of {@link ModelAttribute} annotated reference data accessor methods.
|
||||
* <li>A {@link org.springframework.ui.Model Model} object, with the view name
|
||||
* implicitly determined through a {@link org.springframework.web.servlet.RequestToViewNameTranslator}
|
||||
* and the model implicitly enriched with command objects and the results
|
||||
* of {@link ModelAttribute} annotated reference data accessor methods.
|
||||
* <li>A {@link java.util.Map} object for exposing a model,
|
||||
* with the view name implicitly determined through a
|
||||
* {@link org.springframework.web.servlet.RequestToViewNameTranslator}
|
||||
* and the model implicitly enriched with command objects and the results
|
||||
* of {@link ModelAttribute} annotated reference data accessor methods.
|
||||
* <li>A {@link org.springframework.web.servlet.View} object, with the
|
||||
* model implicitly determined through command objects and
|
||||
* {@link ModelAttribute} annotated reference data accessor methods.
|
||||
* The handler method may also programmatically enrich the model by
|
||||
* declaring a {@link org.springframework.ui.Model} argument (see above).
|
||||
* <li>A {@link java.lang.String} value which is interpreted as view name,
|
||||
* with the model implicitly determined through command objects and
|
||||
* {@link ModelAttribute} annotated reference data accessor methods.
|
||||
* The handler method may also programmatically enrich the model by
|
||||
* declaring a {@link org.springframework.ui.ModelMap} argument
|
||||
* (see above).
|
||||
* <li><code>void</code> if the method handles the response itself (by
|
||||
* writing the response content directly, declaring an argument of type
|
||||
* {@link javax.servlet.ServletResponse} / {@link javax.servlet.http.HttpServletResponse}
|
||||
* / {@link javax.portlet.RenderResponse} for that purpose)
|
||||
* or if the view name is supposed to be implicitly determined through a
|
||||
* {@link org.springframework.web.servlet.RequestToViewNameTranslator}
|
||||
* (not declaring a response argument in the handler method signature;
|
||||
* only applicable in a Servlet environment).
|
||||
* <li>Any other return type will be considered as single model attribute
|
||||
* to be exposed to the view, using the attribute name specified through
|
||||
* {@link ModelAttribute} at the method level (or the default attribute name
|
||||
* based on the return type's class name otherwise). The model will be
|
||||
* implicitly enriched with command objects and the results of
|
||||
* {@link ModelAttribute} annotated reference data accessor methods.
|
||||
* </ul>
|
||||
*
|
||||
* <p><b>NOTE: <code>@RequestMapping</code> will only be processed if a
|
||||
* corresponding <code>HandlerMapping</code> (for type level annotations)
|
||||
* and/or <code>HandlerAdapter</code> (for method level annotations) is
|
||||
* present in the dispatcher.</b> This is the case by default in both
|
||||
* <code>DispatcherServlet</code> and <code>DispatcherPortlet</code>.
|
||||
* However, if you are defining custom <code>HandlerMappings</code> or
|
||||
* <code>HandlerAdapters</code>, then you need to make sure that a
|
||||
* corresponding custom <code>DefaultAnnotationHandlerMapping</code>
|
||||
* and/or <code>AnnotationMethodHandlerAdapter</code> is defined as well
|
||||
* - provided that you intend to use <code>@RequestMapping</code>.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see RequestParam
|
||||
* @see ModelAttribute
|
||||
* @see SessionAttributes
|
||||
* @see InitBinder
|
||||
* @see org.springframework.web.context.request.WebRequest
|
||||
* @see org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
|
||||
* @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
|
||||
* @see org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping
|
||||
* @see org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface RequestMapping {
|
||||
|
||||
/**
|
||||
* The primary mapping expressed by this annotation.
|
||||
* <p>In a Servlet environment: the path mapping URIs (e.g. "/myPath.do").
|
||||
* Ant-style path patterns are also supported (e.g. "/myPath/*.do").
|
||||
* At the method level, relative paths (e.g. "edit.do") are supported
|
||||
* within the primary mapping expressed at the type level.
|
||||
* <p>In a Portlet environment: the mapped portlet modes
|
||||
* (i.e. "EDIT", "VIEW", "HELP" or any custom modes).
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* When used at the type level, all method-level mappings inherit
|
||||
* this primary mapping, narrowing it for a specific handler method.
|
||||
* <p>In case of Servlet-based handler methods, the method names are
|
||||
* taken into account for narrowing if no path was specified explicitly,
|
||||
* according to the specified
|
||||
* {@link org.springframework.web.servlet.mvc.multiaction.MethodNameResolver}
|
||||
* (by default an
|
||||
* {@link org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver}).
|
||||
* Note that this only applies in case of ambiguous annotation mappings
|
||||
* that do not specify a path mapping explicitly. In other words,
|
||||
* the method name is only used for narrowing among a set of matching
|
||||
* methods; it does not constitute a primary path mapping itself.
|
||||
* <p>If you have a single default method (without explicit path mapping),
|
||||
* then all requests without a more specific mapped method found will
|
||||
* be dispatched to it. If you have multiple such default methods, then
|
||||
* the method name will be taken into account for choosing between them.
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* The HTTP request methods to map to, narrowing the primary mapping:
|
||||
* GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE.
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* When used at the type level, all method-level mappings inherit
|
||||
* this HTTP method restriction (i.e. the type-level restriction
|
||||
* gets checked before the handler method is even resolved).
|
||||
* <p><b>Currently only supported in Servlet environments!</b>
|
||||
* To be supported for Portlet 2.0 resource requests in Spring 3.0 as well.
|
||||
*/
|
||||
RequestMethod[] method() default {};
|
||||
|
||||
/**
|
||||
* The parameters of the mapped request, narrowing the primary mapping.
|
||||
* <p>Same format for any environment: a sequence of "myParam=myValue" style
|
||||
* expressions, with a request only mapped if each such parameter is found
|
||||
* to have the given value. "myParam" style expressions are also supported,
|
||||
* with such parameters having to be present in the request (allowed to have
|
||||
* any value). Finally, "!myParam" style expressions indicate that the
|
||||
* specified parameter is <i>not</i> supposed to be present in the request.
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* When used at the type level, all method-level mappings inherit
|
||||
* this parameter restriction (i.e. the type-level restriction
|
||||
* gets checked before the handler method is even resolved).
|
||||
* <p>In a Servlet environment, parameter mappings are considered as restrictions
|
||||
* that are enforced at the type level. The primary path mapping (i.e. the
|
||||
* specified URI value) still has to uniquely identify the target handler, with
|
||||
* parameter mappings simply expressing preconditions for invoking the handler.
|
||||
* <p>In a Portlet environment, parameters are taken into account as mapping
|
||||
* differentiators, i.e. the primary portlet mode mapping plus the parameter
|
||||
* conditions uniquely identify the target handler. Different handlers may be
|
||||
* mapped onto the same portlet mode, as long as their parameter mappings differ.
|
||||
*/
|
||||
String[] params() default {};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.annotation;
|
||||
|
||||
/**
|
||||
* Java 5 enumeration of HTTP request methods. Intended for use
|
||||
* with the {@link RequestMapping#method()} attribute of the
|
||||
* {@link RequestMapping} annotation.
|
||||
*
|
||||
* <p>Note that, by default, {@link org.springframework.web.servlet.DispatcherServlet}
|
||||
* supports GET, HEAD, POST, PUT and DELETE only. DispatcherServlet will
|
||||
* process TRACE and OPTIONS with the default HttpServlet behavior unless
|
||||
* explicitly told to dispatch those request types as well: Check out
|
||||
* the "dispatchOptionsRequest" and "dispatchTraceRequest" properties,
|
||||
* switching them to "true" if necessary.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see RequestMapping
|
||||
* @see org.springframework.web.servlet.DispatcherServlet#setDispatchOptionsRequest
|
||||
* @see org.springframework.web.servlet.DispatcherServlet#setDispatchTraceRequest
|
||||
*/
|
||||
public enum RequestMethod {
|
||||
|
||||
GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation which indicates that a method parameter should be bound
|
||||
* to a web request parameter. Supported for {@link RequestMapping}
|
||||
* annotated handler methods in Servlet and Portlet environments.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see RequestMapping
|
||||
* @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
|
||||
* @see org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface RequestParam {
|
||||
|
||||
/**
|
||||
* The request parameter to bind to.
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Whether the parameter is required.
|
||||
* <p>Default is <code>true</code>, leading to an exception thrown in case
|
||||
* of the parameter missing in the request. Switch this to <code>false</code>
|
||||
* if you prefer a <code>null</value> in case of the parameter missing.
|
||||
*/
|
||||
boolean required() default true;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation that indicates the session attributes that a specific handler
|
||||
* uses. This will typically list the names of model attributes which should be
|
||||
* transparently stored in the session or some conversational storage,
|
||||
* serving as form-backing beans. <b>Declared at the type level,</b> applying
|
||||
* to the model attributes that the annotated handler class operates on.
|
||||
*
|
||||
* <p><b>NOTE:</b> Session attributes as indicated using this annotation
|
||||
* correspond to a specific handler's model attributes, getting transparently
|
||||
* stored in a conversational session. Those attributes will be removed once
|
||||
* the handler indicates completion of its conversational session. Therefore,
|
||||
* use this facility for such conversational attributes which are supposed
|
||||
* to be stored in the session <i>temporarily</i> during the course of a
|
||||
* specific handler's conversation.
|
||||
*
|
||||
* <p>For permanent session attributes, e.g. a user authentication object,
|
||||
* use the traditional <code>session.setAttribute</code> method instead.
|
||||
* Alternatively, consider using the attribute management capabilities of the
|
||||
* generic {@link org.springframework.web.context.request.WebRequest} interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface SessionAttributes {
|
||||
|
||||
/**
|
||||
* The names of session attributes in the model, to be stored in the
|
||||
* session or some conversational storage.
|
||||
* <p>Note: This indicates the model attribute names. The session attribute
|
||||
* names may or may not match the model attribute names; applications should
|
||||
* not rely on the session attribute names but rather operate on the model only.
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* The types of session attributes in the model, to be stored in the
|
||||
* session or some conversational storage. All model attributes of this
|
||||
* type will be stored in the session, regardless of attribute name.
|
||||
*/
|
||||
Class[] types() default {};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Annotations for binding requests to controllers and handler methods
|
||||
as well as for binding request parameters to method arguments.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.annotation.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
|
||||
/**
|
||||
* Exception indicating that the execution of an annotated MVC handler method failed.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.6
|
||||
* @see HandlerMethodInvoker#invokeHandlerMethod
|
||||
*/
|
||||
public class HandlerMethodInvocationException extends NestedRuntimeException {
|
||||
|
||||
/**
|
||||
* Create a new HandlerMethodInvocationException for the given Method handle and cause.
|
||||
* @param handlerMethod the handler method handle
|
||||
* @param cause the cause of the invocation failure
|
||||
*/
|
||||
public HandlerMethodInvocationException(Method handlerMethod, Throwable cause) {
|
||||
super("Failed to invoke handler method [" + handlerMethod + "]", cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,487 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.annotation.support;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.bind.support.SimpleSessionStatus;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebRequestDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
|
||||
/**
|
||||
* Support class for invoking an annotated handler method.
|
||||
* Operates on the introspection results of a {@link HandlerMethodResolver}
|
||||
* for a specific handler type.
|
||||
*
|
||||
* <p>Used by {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter}
|
||||
* and {@link org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.2
|
||||
* @see #invokeHandlerMethod
|
||||
*/
|
||||
public class HandlerMethodInvoker {
|
||||
|
||||
/**
|
||||
* We'll create a lot of these objects, so we don't want a new logger every time.
|
||||
*/
|
||||
private static final Log logger = LogFactory.getLog(HandlerMethodInvoker.class);
|
||||
|
||||
private final HandlerMethodResolver methodResolver;
|
||||
|
||||
private final WebBindingInitializer bindingInitializer;
|
||||
|
||||
private final SessionAttributeStore sessionAttributeStore;
|
||||
|
||||
private final ParameterNameDiscoverer parameterNameDiscoverer;
|
||||
|
||||
private final WebArgumentResolver[] customArgumentResolvers;
|
||||
|
||||
private final SimpleSessionStatus sessionStatus = new SimpleSessionStatus();
|
||||
|
||||
|
||||
public HandlerMethodInvoker(HandlerMethodResolver methodResolver) {
|
||||
this(methodResolver, null);
|
||||
}
|
||||
|
||||
public HandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer) {
|
||||
this(methodResolver, bindingInitializer, new DefaultSessionAttributeStore(), null);
|
||||
}
|
||||
|
||||
public HandlerMethodInvoker(
|
||||
HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer,
|
||||
SessionAttributeStore sessionAttributeStore, ParameterNameDiscoverer parameterNameDiscoverer,
|
||||
WebArgumentResolver... customArgumentResolvers) {
|
||||
|
||||
this.methodResolver = methodResolver;
|
||||
this.bindingInitializer = bindingInitializer;
|
||||
this.sessionAttributeStore = sessionAttributeStore;
|
||||
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||
this.customArgumentResolvers = customArgumentResolvers;
|
||||
}
|
||||
|
||||
|
||||
public final Object invokeHandlerMethod(
|
||||
Method handlerMethod, Object handler, NativeWebRequest webRequest, ExtendedModelMap implicitModel)
|
||||
throws Exception {
|
||||
|
||||
Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
|
||||
try {
|
||||
boolean debug = logger.isDebugEnabled();
|
||||
for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
|
||||
Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
|
||||
Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
|
||||
if (debug) {
|
||||
logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
|
||||
}
|
||||
Object attrValue = doInvokeMethod(attributeMethodToInvoke, handler, args);
|
||||
String attrName = AnnotationUtils.findAnnotation(attributeMethodToInvoke, ModelAttribute.class).value();
|
||||
if ("".equals(attrName)) {
|
||||
Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
|
||||
attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
|
||||
}
|
||||
implicitModel.addAttribute(attrName, attrValue);
|
||||
}
|
||||
Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
|
||||
if (debug) {
|
||||
logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
|
||||
}
|
||||
return doInvokeMethod(handlerMethodToInvoke, handler, args);
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
// Throw exception with full handler method context...
|
||||
throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object[] resolveHandlerArguments(
|
||||
Method handlerMethod, Object handler, NativeWebRequest webRequest, ExtendedModelMap implicitModel)
|
||||
throws Exception {
|
||||
|
||||
Class[] paramTypes = handlerMethod.getParameterTypes();
|
||||
Object[] args = new Object[paramTypes.length];
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
MethodParameter methodParam = new MethodParameter(handlerMethod, i);
|
||||
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
|
||||
GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
|
||||
String paramName = null;
|
||||
boolean paramRequired = false;
|
||||
String attrName = null;
|
||||
Object[] paramAnns = methodParam.getParameterAnnotations();
|
||||
|
||||
for (int j = 0; j < paramAnns.length; j++) {
|
||||
Object paramAnn = paramAnns[j];
|
||||
if (RequestParam.class.isInstance(paramAnn)) {
|
||||
RequestParam requestParam = (RequestParam) paramAnn;
|
||||
paramName = requestParam.value();
|
||||
paramRequired = requestParam.required();
|
||||
break;
|
||||
}
|
||||
else if (ModelAttribute.class.isInstance(paramAnn)) {
|
||||
ModelAttribute attr = (ModelAttribute) paramAnn;
|
||||
attrName = attr.value();
|
||||
}
|
||||
}
|
||||
if (paramName != null && attrName != null) {
|
||||
throw new IllegalStateException("@RequestParam and @ModelAttribute are an exclusive choice -" +
|
||||
"do not specify both on the same parameter: " + handlerMethod);
|
||||
}
|
||||
|
||||
Class paramType = methodParam.getParameterType();
|
||||
|
||||
if (paramName == null && attrName == null) {
|
||||
Object argValue = resolveCommonArgument(methodParam, webRequest);
|
||||
if (argValue != WebArgumentResolver.UNRESOLVED) {
|
||||
args[i] = argValue;
|
||||
}
|
||||
else {
|
||||
if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
|
||||
args[i] = implicitModel;
|
||||
}
|
||||
else if (SessionStatus.class.isAssignableFrom(paramType)) {
|
||||
args[i] = this.sessionStatus;
|
||||
}
|
||||
else if (Errors.class.isAssignableFrom(paramType)) {
|
||||
throw new IllegalStateException("Errors/BindingResult argument declared " +
|
||||
"without preceding model attribute. Check your handler method signature!");
|
||||
}
|
||||
else if (BeanUtils.isSimpleProperty(paramType)) {
|
||||
paramName = "";
|
||||
}
|
||||
else {
|
||||
attrName = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (paramName != null) {
|
||||
args[i] = resolveRequestParam(paramName, paramRequired, methodParam, webRequest, handler);
|
||||
}
|
||||
else if (attrName != null) {
|
||||
WebDataBinder binder = resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
|
||||
boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||
if (binder.getTarget() != null) {
|
||||
doBind(webRequest, binder, !assignBindingResult);
|
||||
}
|
||||
args[i] = binder.getTarget();
|
||||
if (assignBindingResult) {
|
||||
args[i + 1] = binder.getBindingResult();
|
||||
i++;
|
||||
}
|
||||
implicitModel.putAll(binder.getBindingResult().getModel());
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
private void initBinder(Object handler, String attrName, WebDataBinder binder, NativeWebRequest webRequest)
|
||||
throws Exception {
|
||||
|
||||
if (this.bindingInitializer != null) {
|
||||
this.bindingInitializer.initBinder(binder, webRequest);
|
||||
}
|
||||
if (handler != null) {
|
||||
Set<Method> initBinderMethods = this.methodResolver.getInitBinderMethods();
|
||||
if (!initBinderMethods.isEmpty()) {
|
||||
boolean debug = logger.isDebugEnabled();
|
||||
for (Method initBinderMethod : initBinderMethods) {
|
||||
Method methodToInvoke = BridgeMethodResolver.findBridgedMethod(initBinderMethod);
|
||||
String[] targetNames = AnnotationUtils.findAnnotation(methodToInvoke, InitBinder.class).value();
|
||||
if (targetNames.length == 0 || Arrays.asList(targetNames).contains(attrName)) {
|
||||
Object[] initBinderArgs = resolveInitBinderArguments(handler, methodToInvoke, binder, webRequest);
|
||||
if (debug) {
|
||||
logger.debug("Invoking init-binder method: " + methodToInvoke);
|
||||
}
|
||||
Object returnValue = doInvokeMethod(methodToInvoke, handler, initBinderArgs);
|
||||
if (returnValue != null) {
|
||||
throw new IllegalStateException(
|
||||
"InitBinder methods must not have a return value: " + methodToInvoke);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] resolveInitBinderArguments(Object handler, Method initBinderMethod,
|
||||
WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
Class[] initBinderParams = initBinderMethod.getParameterTypes();
|
||||
Object[] initBinderArgs = new Object[initBinderParams.length];
|
||||
|
||||
for (int i = 0; i < initBinderArgs.length; i++) {
|
||||
MethodParameter methodParam = new MethodParameter(initBinderMethod, i);
|
||||
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
|
||||
GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
|
||||
String paramName = null;
|
||||
boolean paramRequired = false;
|
||||
Object[] paramAnns = methodParam.getParameterAnnotations();
|
||||
|
||||
for (int j = 0; j < paramAnns.length; j++) {
|
||||
Object paramAnn = paramAnns[j];
|
||||
if (RequestParam.class.isInstance(paramAnn)) {
|
||||
RequestParam requestParam = (RequestParam) paramAnn;
|
||||
paramName = requestParam.value();
|
||||
paramRequired = requestParam.required();
|
||||
break;
|
||||
}
|
||||
else if (ModelAttribute.class.isInstance(paramAnn)) {
|
||||
throw new IllegalStateException(
|
||||
"@ModelAttribute is not supported on @InitBinder methods: " + initBinderMethod);
|
||||
}
|
||||
}
|
||||
|
||||
if (paramName == null) {
|
||||
Object argValue = resolveCommonArgument(methodParam, webRequest);
|
||||
if (argValue != WebArgumentResolver.UNRESOLVED) {
|
||||
initBinderArgs[i] = argValue;
|
||||
}
|
||||
else {
|
||||
Class paramType = initBinderParams[i];
|
||||
if (paramType.isInstance(binder)) {
|
||||
initBinderArgs[i] = binder;
|
||||
}
|
||||
else if (BeanUtils.isSimpleProperty(paramType)) {
|
||||
paramName = "";
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unsupported argument [" + paramType.getName() +
|
||||
"] for @InitBinder method: " + initBinderMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (paramName != null) {
|
||||
initBinderArgs[i] = resolveRequestParam(paramName, paramRequired, methodParam, webRequest, null);
|
||||
}
|
||||
}
|
||||
|
||||
return initBinderArgs;
|
||||
}
|
||||
|
||||
private Object resolveRequestParam(String paramName, boolean paramRequired,
|
||||
MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
|
||||
throws Exception {
|
||||
|
||||
Class paramType = methodParam.getParameterType();
|
||||
if ("".equals(paramName)) {
|
||||
paramName = methodParam.getParameterName();
|
||||
if (paramName == null) {
|
||||
throw new IllegalStateException("No parameter specified for @RequestParam argument of type [" +
|
||||
paramType.getName() + "], and no parameter name information found in class file either.");
|
||||
}
|
||||
}
|
||||
Object paramValue = null;
|
||||
if (webRequest.getNativeRequest() instanceof MultipartRequest) {
|
||||
paramValue = ((MultipartRequest) webRequest.getNativeRequest()).getFile(paramName);
|
||||
}
|
||||
if (paramValue == null) {
|
||||
String[] paramValues = webRequest.getParameterValues(paramName);
|
||||
if (paramValues != null) {
|
||||
paramValue = (paramValues.length == 1 ? paramValues[0] : paramValues);
|
||||
}
|
||||
}
|
||||
if (paramValue == null) {
|
||||
if (paramRequired) {
|
||||
raiseMissingParameterException(paramName, paramType);
|
||||
}
|
||||
if (paramType.isPrimitive()) {
|
||||
throw new IllegalStateException("Optional " + paramType + " parameter '" + paramName +
|
||||
"' is not present but cannot be translated into a null value due to being declared as a " +
|
||||
"primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
|
||||
}
|
||||
}
|
||||
WebDataBinder binder = createBinder(webRequest, null, paramName);
|
||||
initBinder(handlerForInitBinderCall, paramName, binder, webRequest);
|
||||
return binder.convertIfNecessary(paramValue, paramType, methodParam);
|
||||
}
|
||||
|
||||
private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
|
||||
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {
|
||||
|
||||
// Bind request parameter onto object...
|
||||
String name = attrName;
|
||||
if ("".equals(name)) {
|
||||
name = Conventions.getVariableNameForParameter(methodParam);
|
||||
}
|
||||
Class paramType = methodParam.getParameterType();
|
||||
Object bindObject = null;
|
||||
if (implicitModel.containsKey(name)) {
|
||||
bindObject = implicitModel.get(name);
|
||||
}
|
||||
else if (this.methodResolver.isSessionAttribute(name, paramType)) {
|
||||
bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
|
||||
if (bindObject == null) {
|
||||
raiseSessionRequiredException("Session attribute '" + name + "' required - not found in session");
|
||||
}
|
||||
}
|
||||
else {
|
||||
bindObject = BeanUtils.instantiateClass(paramType);
|
||||
}
|
||||
WebDataBinder binder = createBinder(webRequest, bindObject, name);
|
||||
initBinder(handler, name, binder, webRequest);
|
||||
return binder;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void updateModelAttributes(
|
||||
Object handler, Map mavModel, ExtendedModelMap implicitModel, NativeWebRequest webRequest)
|
||||
throws Exception {
|
||||
|
||||
if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
|
||||
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
|
||||
this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
|
||||
}
|
||||
}
|
||||
|
||||
// Expose model attributes as session attributes, if required.
|
||||
// Expose BindingResults for all attributes, making custom editors available.
|
||||
Map<String, Object> model = (mavModel != null ? mavModel : implicitModel);
|
||||
for (String attrName : new HashSet<String>(model.keySet())) {
|
||||
Object attrValue = model.get(attrName);
|
||||
boolean isSessionAttr =
|
||||
this.methodResolver.isSessionAttribute(attrName, (attrValue != null ? attrValue.getClass() : null));
|
||||
if (isSessionAttr && !this.sessionStatus.isComplete()) {
|
||||
this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
|
||||
}
|
||||
if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX) &&
|
||||
(isSessionAttr || isBindingCandidate(attrValue))) {
|
||||
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
|
||||
if (mavModel != null && !model.containsKey(bindingResultKey)) {
|
||||
WebDataBinder binder = createBinder(webRequest, attrValue, attrName);
|
||||
initBinder(handler, attrName, binder, webRequest);
|
||||
mavModel.put(bindingResultKey, binder.getBindingResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given value qualifies as a "binding candidate",
|
||||
* i.e. might potentially be subject to bean-style data binding later on.
|
||||
*/
|
||||
protected boolean isBindingCandidate(Object value) {
|
||||
return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&
|
||||
!(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
|
||||
}
|
||||
|
||||
private Object doInvokeMethod(Method method, Object target, Object[] args) throws Exception {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
try {
|
||||
return method.invoke(target, args);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
ReflectionUtils.rethrowException(ex.getTargetException());
|
||||
}
|
||||
throw new IllegalStateException("Should never get here");
|
||||
}
|
||||
|
||||
|
||||
protected void raiseMissingParameterException(String paramName, Class paramType) throws Exception {
|
||||
throw new IllegalStateException("Missing parameter '" + paramName + "' of type [" + paramType.getName() + "]");
|
||||
}
|
||||
|
||||
protected void raiseSessionRequiredException(String message) throws Exception {
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
|
||||
protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
|
||||
throws Exception {
|
||||
|
||||
return new WebRequestDataBinder(target, objectName);
|
||||
}
|
||||
|
||||
protected void doBind(NativeWebRequest webRequest, WebDataBinder binder, boolean failOnErrors)
|
||||
throws Exception {
|
||||
|
||||
WebRequestDataBinder requestBinder = (WebRequestDataBinder) binder;
|
||||
requestBinder.bind(webRequest);
|
||||
if (failOnErrors) {
|
||||
requestBinder.closeNoCatch();
|
||||
}
|
||||
}
|
||||
|
||||
protected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest)
|
||||
throws Exception {
|
||||
|
||||
// Invoke custom argument resolvers if present...
|
||||
if (this.customArgumentResolvers != null) {
|
||||
for (WebArgumentResolver argumentResolver : this.customArgumentResolvers) {
|
||||
Object value = argumentResolver.resolveArgument(methodParameter, webRequest);
|
||||
if (value != WebArgumentResolver.UNRESOLVED) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resolution of standard parameter types...
|
||||
Class paramType = methodParameter.getParameterType();
|
||||
Object value = resolveStandardArgument(paramType, webRequest);
|
||||
if (value != WebArgumentResolver.UNRESOLVED && !ClassUtils.isAssignableValue(paramType, value)) {
|
||||
throw new IllegalStateException("Standard argument type [" + paramType.getName() +
|
||||
"] resolved to incompatible value of type [" + (value != null ? value.getClass() : null) +
|
||||
"]. Consider declaring the argument type in a less specific fashion.");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest)
|
||||
throws Exception {
|
||||
|
||||
if (WebRequest.class.isAssignableFrom(parameterType)) {
|
||||
return webRequest;
|
||||
}
|
||||
return WebArgumentResolver.UNRESOLVED;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.annotation.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
|
||||
/**
|
||||
* Support class for resolving web method annotations in a handler type.
|
||||
* Processes <code>@RequestMapping</code>, <code>@InitBinder</code>,
|
||||
* <code>@ModelAttribute</code> and <code>@SessionAttributes</code>.
|
||||
*
|
||||
* <p>Used by {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter}
|
||||
* and {@link org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.2
|
||||
* @see org.springframework.web.bind.annotation.RequestMapping
|
||||
* @see org.springframework.web.bind.annotation.InitBinder
|
||||
* @see org.springframework.web.bind.annotation.ModelAttribute
|
||||
* @see org.springframework.web.bind.annotation.SessionAttributes
|
||||
*/
|
||||
public class HandlerMethodResolver {
|
||||
|
||||
private final Set<Method> handlerMethods = new LinkedHashSet<Method>();
|
||||
|
||||
private final Set<Method> initBinderMethods = new LinkedHashSet<Method>();
|
||||
|
||||
private final Set<Method> modelAttributeMethods = new LinkedHashSet<Method>();
|
||||
|
||||
private final RequestMapping typeLevelMapping;
|
||||
|
||||
private final boolean sessionAttributesFound;
|
||||
|
||||
private final Set<String> sessionAttributeNames = new HashSet<String>();
|
||||
|
||||
private final Set<Class> sessionAttributeTypes = new HashSet<Class>();
|
||||
|
||||
private final Set<String> actualSessionAttributeNames = Collections.synchronizedSet(new HashSet<String>(4));
|
||||
|
||||
|
||||
/**
|
||||
* Create a new HandlerMethodResolver for the specified handler type.
|
||||
* @param handlerType the handler class to introspect
|
||||
*/
|
||||
public HandlerMethodResolver(final Class<?> handlerType) {
|
||||
ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) {
|
||||
if (method.isAnnotationPresent(RequestMapping.class)) {
|
||||
handlerMethods.add(ClassUtils.getMostSpecificMethod(method, handlerType));
|
||||
}
|
||||
else if (method.isAnnotationPresent(InitBinder.class)) {
|
||||
initBinderMethods.add(ClassUtils.getMostSpecificMethod(method, handlerType));
|
||||
}
|
||||
else if (method.isAnnotationPresent(ModelAttribute.class)) {
|
||||
modelAttributeMethods.add(ClassUtils.getMostSpecificMethod(method, handlerType));
|
||||
}
|
||||
}
|
||||
});
|
||||
this.typeLevelMapping = handlerType.getAnnotation(RequestMapping.class);
|
||||
SessionAttributes sessionAttributes = handlerType.getAnnotation(SessionAttributes.class);
|
||||
this.sessionAttributesFound = (sessionAttributes != null);
|
||||
if (this.sessionAttributesFound) {
|
||||
this.sessionAttributeNames.addAll(Arrays.asList(sessionAttributes.value()));
|
||||
this.sessionAttributeTypes.addAll(Arrays.asList(sessionAttributes.types()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final boolean hasHandlerMethods() {
|
||||
return !this.handlerMethods.isEmpty();
|
||||
}
|
||||
|
||||
public final Set<Method> getHandlerMethods() {
|
||||
return this.handlerMethods;
|
||||
}
|
||||
|
||||
public final Set<Method> getInitBinderMethods() {
|
||||
return this.initBinderMethods;
|
||||
}
|
||||
|
||||
public final Set<Method> getModelAttributeMethods() {
|
||||
return this.modelAttributeMethods;
|
||||
}
|
||||
|
||||
public boolean hasTypeLevelMapping() {
|
||||
return (this.typeLevelMapping != null);
|
||||
}
|
||||
|
||||
public RequestMapping getTypeLevelMapping() {
|
||||
return this.typeLevelMapping;
|
||||
}
|
||||
|
||||
public boolean hasSessionAttributes() {
|
||||
return this.sessionAttributesFound;
|
||||
}
|
||||
|
||||
public boolean isSessionAttribute(String attrName, Class attrType) {
|
||||
if (this.sessionAttributeNames.contains(attrName) || this.sessionAttributeTypes.contains(attrType)) {
|
||||
this.actualSessionAttributeNames.add(attrName);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getActualSessionAttributeNames() {
|
||||
return this.actualSessionAttributeNames;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for web annotation processing.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Provides web-specific data binding functionality.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.support;
|
||||
|
||||
import org.springframework.beans.PropertyEditorRegistrar;
|
||||
import org.springframework.validation.BindingErrorProcessor;
|
||||
import org.springframework.validation.MessageCodesResolver;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
/**
|
||||
* Convenient {@link WebBindingInitializer} for declarative configuration
|
||||
* in a Spring application context. Allows for reusing pre-configured
|
||||
* initializers with multiple controller/handlers.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see #setDirectFieldAccess
|
||||
* @see #setMessageCodesResolver
|
||||
* @see #setBindingErrorProcessor
|
||||
* @see #setPropertyEditorRegistrar
|
||||
*/
|
||||
public class ConfigurableWebBindingInitializer implements WebBindingInitializer {
|
||||
|
||||
private boolean directFieldAccess = false;
|
||||
|
||||
private MessageCodesResolver messageCodesResolver;
|
||||
|
||||
private BindingErrorProcessor bindingErrorProcessor;
|
||||
|
||||
private PropertyEditorRegistrar[] propertyEditorRegistrars;
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to use direct field access instead of bean property access.
|
||||
* <p>Default is <code>false</code>, using bean property access.
|
||||
* Switch this to <code>true</code> for enforcing direct field access.
|
||||
*/
|
||||
public final void setDirectFieldAccess(boolean directFieldAccess) {
|
||||
this.directFieldAccess = directFieldAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the strategy to use for resolving errors into message codes.
|
||||
* Applies the given strategy to all data binders used by this controller.
|
||||
* <p>Default is <code>null</code>, i.e. using the default strategy of
|
||||
* the data binder.
|
||||
* @see org.springframework.validation.DataBinder#setMessageCodesResolver
|
||||
*/
|
||||
public final void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) {
|
||||
this.messageCodesResolver = messageCodesResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the strategy to use for resolving errors into message codes.
|
||||
*/
|
||||
public final MessageCodesResolver getMessageCodesResolver() {
|
||||
return this.messageCodesResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the strategy to use for processing binding errors, that is,
|
||||
* required field errors and <code>PropertyAccessException</code>s.
|
||||
* <p>Default is <code>null</code>, that is, using the default strategy
|
||||
* of the data binder.
|
||||
* @see org.springframework.validation.DataBinder#setBindingErrorProcessor
|
||||
*/
|
||||
public final void setBindingErrorProcessor(BindingErrorProcessor bindingErrorProcessor) {
|
||||
this.bindingErrorProcessor = bindingErrorProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the strategy to use for processing binding errors.
|
||||
*/
|
||||
public final BindingErrorProcessor getBindingErrorProcessor() {
|
||||
return this.bindingErrorProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a single PropertyEditorRegistrar to be applied
|
||||
* to every DataBinder that this controller uses.
|
||||
*/
|
||||
public final void setPropertyEditorRegistrar(PropertyEditorRegistrar propertyEditorRegistrar) {
|
||||
this.propertyEditorRegistrars = new PropertyEditorRegistrar[] {propertyEditorRegistrar};
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify multiple PropertyEditorRegistrars to be applied
|
||||
* to every DataBinder that this controller uses.
|
||||
*/
|
||||
public final void setPropertyEditorRegistrars(PropertyEditorRegistrar[] propertyEditorRegistrars) {
|
||||
this.propertyEditorRegistrars = propertyEditorRegistrars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PropertyEditorRegistrars to be applied
|
||||
* to every DataBinder that this controller uses.
|
||||
*/
|
||||
public final PropertyEditorRegistrar[] getPropertyEditorRegistrars() {
|
||||
return this.propertyEditorRegistrars;
|
||||
}
|
||||
|
||||
|
||||
public void initBinder(WebDataBinder binder, WebRequest request) {
|
||||
if (this.directFieldAccess) {
|
||||
binder.initDirectFieldAccess();
|
||||
}
|
||||
if (this.messageCodesResolver != null) {
|
||||
binder.setMessageCodesResolver(this.messageCodesResolver);
|
||||
}
|
||||
if (this.bindingErrorProcessor != null) {
|
||||
binder.setBindingErrorProcessor(this.bindingErrorProcessor);
|
||||
}
|
||||
if (this.propertyEditorRegistrars != null) {
|
||||
for (int i = 0; i < this.propertyEditorRegistrars.length; i++) {
|
||||
this.propertyEditorRegistrars[i].registerCustomEditors(binder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.support;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link SessionAttributeStore} interface,
|
||||
* storing the attributes in the WebRequest session (i.e. HttpSession
|
||||
* or PortletSession).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see #setAttributeNamePrefix
|
||||
* @see org.springframework.web.context.request.WebRequest#setAttribute
|
||||
* @see org.springframework.web.context.request.WebRequest#getAttribute
|
||||
* @see org.springframework.web.context.request.WebRequest#removeAttribute
|
||||
*/
|
||||
public class DefaultSessionAttributeStore implements SessionAttributeStore {
|
||||
|
||||
private String attributeNamePrefix = "";
|
||||
|
||||
|
||||
/**
|
||||
* Specify a prefix to use for the attribute names in the backend session.
|
||||
* <p>Default is to use no prefix, storing the session attributes with the
|
||||
* same name as in the model.
|
||||
*/
|
||||
public void setAttributeNamePrefix(String attributeNamePrefix) {
|
||||
this.attributeNamePrefix = (attributeNamePrefix != null ? attributeNamePrefix : "");
|
||||
}
|
||||
|
||||
|
||||
public void storeAttribute(WebRequest request, String attributeName, Object attributeValue) {
|
||||
Assert.notNull(request, "WebRequest must not be null");
|
||||
Assert.notNull(attributeName, "Attribute name must not be null");
|
||||
Assert.notNull(attributeValue, "Attribute value must not be null");
|
||||
String storeAttributeName = getAttributeNameInSession(request, attributeName);
|
||||
request.setAttribute(storeAttributeName, attributeValue, WebRequest.SCOPE_SESSION);
|
||||
}
|
||||
|
||||
public Object retrieveAttribute(WebRequest request, String attributeName) {
|
||||
Assert.notNull(request, "WebRequest must not be null");
|
||||
Assert.notNull(attributeName, "Attribute name must not be null");
|
||||
String storeAttributeName = getAttributeNameInSession(request, attributeName);
|
||||
return request.getAttribute(storeAttributeName, WebRequest.SCOPE_SESSION);
|
||||
}
|
||||
|
||||
public void cleanupAttribute(WebRequest request, String attributeName) {
|
||||
Assert.notNull(request, "WebRequest must not be null");
|
||||
Assert.notNull(attributeName, "Attribute name must not be null");
|
||||
String storeAttributeName = getAttributeNameInSession(request, attributeName);
|
||||
request.removeAttribute(storeAttributeName, WebRequest.SCOPE_SESSION);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the attribute name in the backend session.
|
||||
* <p>The default implementation simply prepends the configured
|
||||
* {@link #setAttributeNamePrefix "attributeNamePrefix"}, if any.
|
||||
* @param request the current request
|
||||
* @param attributeName the name of the attribute
|
||||
* @return the attribute name in the backend session
|
||||
*/
|
||||
protected String getAttributeNameInSession(WebRequest request, String attributeName) {
|
||||
return this.attributeNamePrefix + attributeName;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.support;
|
||||
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
/**
|
||||
* Strategy interface for storing model attributes in a backend session.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see org.springframework.web.bind.annotation.SessionAttributes
|
||||
*/
|
||||
public interface SessionAttributeStore {
|
||||
|
||||
/**
|
||||
* Store the supplied attribute in the backend session.
|
||||
* <p>Can be called for new attributes as well as for existing attributes.
|
||||
* In the latter case, this signals that the attribute value may have been modified.
|
||||
* @param request the current request
|
||||
* @param attributeName the name of the attribute
|
||||
* @param attributeValue the attribute value to store
|
||||
*/
|
||||
void storeAttribute(WebRequest request, String attributeName, Object attributeValue);
|
||||
|
||||
/**
|
||||
* Retrieve the specified attribute from the backend session.
|
||||
* <p>This will typically be called with the expectation that the
|
||||
* attribute is already present, with an exception to be thrown
|
||||
* if this method returns <code>null</code>.
|
||||
* @param request the current request
|
||||
* @param attributeName the name of the attribute
|
||||
* @return the current attribute value, or <code>null</code> if none
|
||||
*/
|
||||
Object retrieveAttribute(WebRequest request, String attributeName);
|
||||
|
||||
/**
|
||||
* Clean up the specified attribute in the backend session.
|
||||
* <p>Indicates that the attribute name will not be used anymore.
|
||||
* @param request the current request
|
||||
* @param attributeName the name of the attribute
|
||||
*/
|
||||
void cleanupAttribute(WebRequest request, String attributeName);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.support;
|
||||
|
||||
/**
|
||||
* Simple interface that can be injected into handler methods, allowing them to
|
||||
* signal that their session processing is complete. The handler invoker may
|
||||
* then follow up with appropriate cleanup, e.g. of session attributes which
|
||||
* have been implicitly created during this handler's processing (according to
|
||||
* the
|
||||
* {@link org.springframework.web.bind.annotation.SessionAttributes @SessionAttributes}
|
||||
* annotation).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see org.springframework.web.bind.annotation.RequestMapping
|
||||
* @see org.springframework.web.bind.annotation.SessionAttributes
|
||||
*/
|
||||
public interface SessionStatus {
|
||||
|
||||
/**
|
||||
* Mark the current handler's session processing as complete, allowing for
|
||||
* cleanup of session attributes.
|
||||
*/
|
||||
void setComplete();
|
||||
|
||||
/**
|
||||
* Return whether the current handler's session processing has been marked
|
||||
* as complete.
|
||||
*/
|
||||
boolean isComplete();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.support;
|
||||
|
||||
/**
|
||||
* Simple implementation of the {@link SessionStatus} interface,
|
||||
* keeping the <code>complete</code> flag as an instance variable.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public class SimpleSessionStatus implements SessionStatus {
|
||||
|
||||
private boolean complete = false;
|
||||
|
||||
|
||||
public void setComplete() {
|
||||
this.complete = true;
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
return this.complete;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* SPI for resolving custom arguments for a specific handler method parameter.
|
||||
* Typically implemented to detect sppecial parameter types, resolving
|
||||
* well-known argument values for them.
|
||||
*
|
||||
* <p>A typical implementation could look like as follows:
|
||||
*
|
||||
* <pre class="code">
|
||||
* public class MySpecialArgumentResolver implements ArgumentResolver {
|
||||
*
|
||||
* public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) {
|
||||
* if (methodParameter.getParameterType().equals(MySpecialArg.class)) {
|
||||
* return new MySpecialArg("myValue");
|
||||
* }
|
||||
* return UNRESOLVED;
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.2
|
||||
* @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#setCustomArgumentResolvers
|
||||
* @see org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter#setCustomArgumentResolvers
|
||||
*/
|
||||
public interface WebArgumentResolver {
|
||||
|
||||
/**
|
||||
* Marker to be returned when the resolver does not know how to
|
||||
* handle the given method parameter.
|
||||
*/
|
||||
Object UNRESOLVED = new Object();
|
||||
|
||||
|
||||
/**
|
||||
* Resolve an argument for the given handler method parameter within the given web request.
|
||||
* @param methodParameter the handler method parameter to resolve
|
||||
* @param webRequest the current web request, allowing access to the native request as well
|
||||
* @return the argument value, or <code>UNRESOLVED</code> if not resolvable
|
||||
* @throws Exception in case of resolution failure
|
||||
*/
|
||||
Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.support;
|
||||
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
/**
|
||||
* Callback interface for initializing a {@link org.springframework.web.bind.WebDataBinder}
|
||||
* for performing data binding in the context of a specific web request.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public interface WebBindingInitializer {
|
||||
|
||||
/**
|
||||
* Initialize the given DataBinder for the given request.
|
||||
* @param binder the DataBinder to initialize
|
||||
* @param request the web request that the data binding happens within
|
||||
*/
|
||||
void initBinder(WebDataBinder binder, WebRequest request);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.bind.support;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
|
||||
/**
|
||||
* Special {@link org.springframework.validation.DataBinder} to perform data binding
|
||||
* from web request parameters to JavaBeans, including support for multipart files.
|
||||
*
|
||||
* <p>See the DataBinder/WebDataBinder superclasses for customization options,
|
||||
* which include specifying allowed/required fields, and registering custom
|
||||
* property editors.
|
||||
*
|
||||
* <p>Can also used for manual data binding in custom web controllers or interceptors
|
||||
* that build on Spring's {@link org.springframework.web.context.request.WebRequest}
|
||||
* abstraction: e.g. in a {@link org.springframework.web.context.request.WebRequestInterceptor}
|
||||
* implementation. Simply instantiate a WebRequestDataBinder for each binding
|
||||
* process, and invoke <code>bind</code> with the current WebRequest as argument:
|
||||
*
|
||||
* <pre class="code">
|
||||
* MyBean myBean = new MyBean();
|
||||
* // apply binder to custom target object
|
||||
* WebRequestDataBinder binder = new WebRequestDataBinder(myBean);
|
||||
* // register custom editors, if desired
|
||||
* binder.registerCustomEditor(...);
|
||||
* // trigger actual binding of request parameters
|
||||
* binder.bind(request);
|
||||
* // optionally evaluate binding errors
|
||||
* Errors errors = binder.getErrors();
|
||||
* ...</pre>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.2
|
||||
* @see #bind(org.springframework.web.context.request.WebRequest)
|
||||
* @see #registerCustomEditor
|
||||
* @see #setAllowedFields
|
||||
* @see #setRequiredFields
|
||||
* @see #setFieldMarkerPrefix
|
||||
*/
|
||||
public class WebRequestDataBinder extends WebDataBinder {
|
||||
|
||||
/**
|
||||
* Create a new WebRequestDataBinder instance, with default object name.
|
||||
* @param target the target object to bind onto (or <code>null</code>
|
||||
* if the binder is just used to convert a plain parameter value)
|
||||
* @see #DEFAULT_OBJECT_NAME
|
||||
*/
|
||||
public WebRequestDataBinder(Object target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new WebRequestDataBinder instance.
|
||||
* @param target the target object to bind onto (or <code>null</code>
|
||||
* if the binder is just used to convert a plain parameter value)
|
||||
* @param objectName the name of the target object
|
||||
*/
|
||||
public WebRequestDataBinder(Object target, String objectName) {
|
||||
super(target, objectName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind the parameters of the given request to this binder's target,
|
||||
* also binding multipart files in case of a multipart request.
|
||||
* <p>This call can create field errors, representing basic binding
|
||||
* errors like a required field (code "required"), or type mismatch
|
||||
* between value and bean property (code "typeMismatch").
|
||||
* <p>Multipart files are bound via their parameter name, just like normal
|
||||
* HTTP parameters: i.e. "uploadedFile" to an "uploadedFile" bean property,
|
||||
* invoking a "setUploadedFile" setter method.
|
||||
* <p>The type of the target property for a multipart file can be MultipartFile,
|
||||
* byte[], or String. The latter two receive the contents of the uploaded file;
|
||||
* all metadata like original file name, content type, etc are lost in those cases.
|
||||
* @param request request with parameters to bind (can be multipart)
|
||||
* @see org.springframework.web.multipart.MultipartRequest
|
||||
* @see org.springframework.web.multipart.MultipartFile
|
||||
* @see #bindMultipartFiles
|
||||
* @see #bind(org.springframework.beans.PropertyValues)
|
||||
*/
|
||||
public void bind(WebRequest request) {
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues(request.getParameterMap());
|
||||
if (request instanceof NativeWebRequest) {
|
||||
Object nativeRequest = ((NativeWebRequest) request).getNativeRequest();
|
||||
if (nativeRequest instanceof MultipartRequest) {
|
||||
MultipartRequest multipartRequest = (MultipartRequest) request;
|
||||
bindMultipartFiles(multipartRequest.getFileMap(), mpvs);
|
||||
}
|
||||
}
|
||||
doBind(mpvs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Treats errors as fatal.
|
||||
* <p>Use this method only if it's an error if the input isn't valid.
|
||||
* This might be appropriate if all input is from dropdowns, for example.
|
||||
* @throws BindException if binding errors have been encountered
|
||||
*/
|
||||
public void closeNoCatch() throws BindException {
|
||||
if (getBindingResult().hasErrors()) {
|
||||
throw new BindException(getBindingResult());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for web data binding.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2002-2005 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart;
|
||||
|
||||
/**
|
||||
* MultipartException subclass thrown when an upload exceeds the
|
||||
* maximum upload size allowed.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public class MaxUploadSizeExceededException extends MultipartException {
|
||||
|
||||
private final long maxUploadSize;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for MaxUploadSizeExceededException.
|
||||
* @param maxUploadSize the maximum upload size allowed
|
||||
*/
|
||||
public MaxUploadSizeExceededException(long maxUploadSize) {
|
||||
this(maxUploadSize, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MaxUploadSizeExceededException.
|
||||
* @param maxUploadSize the maximum upload size allowed
|
||||
* @param ex root cause from multipart parsing API in use
|
||||
*/
|
||||
public MaxUploadSizeExceededException(long maxUploadSize, Throwable ex) {
|
||||
super("Maximum upload size of " + maxUploadSize + " bytes exceeded", ex);
|
||||
this.maxUploadSize = maxUploadSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the maximum upload size allowed.
|
||||
*/
|
||||
public long getMaxUploadSize() {
|
||||
return maxUploadSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
|
||||
/**
|
||||
* Exception thrown when multipart resolution fails.
|
||||
*
|
||||
* @author Trevor D. Cook
|
||||
* @author Juergen Hoeller
|
||||
* @since 29.09.2003
|
||||
* @see MultipartResolver#resolveMultipart
|
||||
* @see org.springframework.web.multipart.support.MultipartFilter
|
||||
*/
|
||||
public class MultipartException extends NestedRuntimeException {
|
||||
|
||||
/**
|
||||
* Constructor for MultipartException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public MultipartException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MultipartException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the multipart parsing API in use
|
||||
*/
|
||||
public MultipartException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A representation of an uploaded file received in a multipart request.
|
||||
*
|
||||
* <p>The file contents are either stored in memory or temporarily on disk.
|
||||
* In either case, the user is responsible for copying file contents to a
|
||||
* session-level or persistent store as and if desired. The temporary storages
|
||||
* will be cleared at the end of request processing.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Trevor D. Cook
|
||||
* @since 29.09.2003
|
||||
* @see org.springframework.web.multipart.MultipartHttpServletRequest
|
||||
* @see org.springframework.web.multipart.MultipartResolver
|
||||
*/
|
||||
public interface MultipartFile {
|
||||
|
||||
/**
|
||||
* Return the name of the parameter in the multipart form.
|
||||
* @return the name of the parameter (never <code>null</code> or empty)
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return the original filename in the client's filesystem.
|
||||
* <p>This may contain path information depending on the browser used,
|
||||
* but it typically will not with any other than Opera.
|
||||
* @return the original filename, or the empty String if no file
|
||||
* has been chosen in the multipart form
|
||||
*/
|
||||
String getOriginalFilename();
|
||||
|
||||
/**
|
||||
* Return the content type of the file.
|
||||
* @return the content type, or <code>null</code> if not defined
|
||||
* (or no file has been chosen in the multipart form)
|
||||
*/
|
||||
String getContentType();
|
||||
|
||||
/**
|
||||
* Return whether the uploaded file is empty, that is, either no file has
|
||||
* been chosen in the multipart form or the chosen file has no content.
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Return the size of the file in bytes.
|
||||
* @return the size of the file, or 0 if empty
|
||||
*/
|
||||
long getSize();
|
||||
|
||||
/**
|
||||
* Return the contents of the file as an array of bytes.
|
||||
* @return the contents of the file as bytes, or an empty byte array if empty
|
||||
* @throws IOException in case of access errors (if the temporary store fails)
|
||||
*/
|
||||
byte[] getBytes() throws IOException;
|
||||
|
||||
/**
|
||||
* Return an InputStream to read the contents of the file from.
|
||||
* The user is responsible for closing the stream.
|
||||
* @return the contents of the file as stream, or an empty stream if empty
|
||||
* @throws IOException in case of access errors (if the temporary store fails)
|
||||
*/
|
||||
InputStream getInputStream() throws IOException;
|
||||
|
||||
/**
|
||||
* Transfer the received file to the given destination file.
|
||||
* <p>This may either move the file in the filesystem, copy the file in the
|
||||
* filesystem, or save memory-held contents to the destination file.
|
||||
* If the destination file already exists, it will be deleted first.
|
||||
* <p>If the file has been moved in the filesystem, this operation cannot
|
||||
* be invoked again. Therefore, call this method just once to be able to
|
||||
* work with any storage mechanism.
|
||||
* @param dest the destination file
|
||||
* @throws IOException in case of reading or writing errors
|
||||
* @throws IllegalStateException if the file has already been moved
|
||||
* in the filesystem and is not available anymore for another transfer
|
||||
*/
|
||||
void transferTo(File dest) throws IOException, IllegalStateException;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Provides additional methods for dealing with multipart content within a
|
||||
* servlet request, allowing to access uploaded files.
|
||||
* Implementations also need to override the standard
|
||||
* {@link javax.servlet.ServletRequest} methods for parameter access, making
|
||||
* multipart parameters available.
|
||||
*
|
||||
* <p>A concrete implementation is
|
||||
* {@link org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest}.
|
||||
* As an intermediate step,
|
||||
* {@link org.springframework.web.multipart.support.AbstractMultipartHttpServletRequest}
|
||||
* can be subclassed.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Trevor D. Cook
|
||||
* @since 29.09.2003
|
||||
* @see MultipartResolver
|
||||
* @see MultipartFile
|
||||
* @see javax.servlet.http.HttpServletRequest#getParameter
|
||||
* @see javax.servlet.http.HttpServletRequest#getParameterNames
|
||||
* @see javax.servlet.http.HttpServletRequest#getParameterMap
|
||||
* @see org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest
|
||||
* @see org.springframework.web.multipart.support.AbstractMultipartHttpServletRequest
|
||||
*/
|
||||
public interface MultipartHttpServletRequest extends HttpServletRequest, MultipartRequest {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This interface defines the multipart request access operations
|
||||
* that are exposed for actual multipart requests. It is extended
|
||||
* by {@link MultipartHttpServletRequest} and the Portlet
|
||||
* {@link org.springframework.web.portlet.multipart.MultipartActionRequest}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.2
|
||||
*/
|
||||
public interface MultipartRequest {
|
||||
|
||||
/**
|
||||
* Return an {@link java.util.Iterator} of String objects containing the
|
||||
* parameter names of the multipart files contained in this request. These
|
||||
* are the field names of the form (like with normal parameters), not the
|
||||
* original file names.
|
||||
* @return the names of the files
|
||||
*/
|
||||
Iterator getFileNames();
|
||||
|
||||
/**
|
||||
* Return the contents plus description of an uploaded file in this request,
|
||||
* or <code>null</code> if it does not exist.
|
||||
* @param name a String specifying the parameter name of the multipart file
|
||||
* @return the uploaded content in the form of a {@link org.springframework.web.multipart.MultipartFile} object
|
||||
*/
|
||||
MultipartFile getFile(String name);
|
||||
|
||||
/**
|
||||
* Return a {@link java.util.Map} of the multipart files contained in this request.
|
||||
* @return a map containing the parameter names as keys, and the
|
||||
* {@link org.springframework.web.multipart.MultipartFile} objects as values
|
||||
* @see MultipartFile
|
||||
*/
|
||||
Map getFileMap();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* A strategy interface for multipart file upload resolution in accordance
|
||||
* with <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>.
|
||||
* Implementations are typically usable both within an application context
|
||||
* and standalone.
|
||||
*
|
||||
* <p>There is only one concrete implementation included in Spring,
|
||||
* as of Spring 2.5:
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.web.multipart.commons.CommonsMultipartResolver} for Jakarta Commons FileUpload
|
||||
* </ul>
|
||||
*
|
||||
* <p>There is no default resolver implementation used for Spring
|
||||
* {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlets},
|
||||
* as an application might choose to parse its multipart requests itself. To define
|
||||
* an implementation, create a bean with the id "multipartResolver" in a
|
||||
* {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet's}
|
||||
* application context. Such a resolver gets applied to all requests handled
|
||||
* by that {@link org.springframework.web.servlet.DispatcherServlet}.
|
||||
*
|
||||
* <p>If a {@link org.springframework.web.servlet.DispatcherServlet} detects
|
||||
* a multipart request, it will resolve it via the configured
|
||||
* {@link org.springframework.web.multipart.MultipartResolver} and pass on a
|
||||
* wrapped {@link javax.servlet.http.HttpServletRequest}.
|
||||
* Controllers can then cast their given request to the
|
||||
* {@link org.springframework.web.multipart.MultipartHttpServletRequest}
|
||||
* interface, which permits access to any
|
||||
* {@link org.springframework.web.multipart.MultipartFile MultipartFiles}.
|
||||
* Note that this cast is only supported in case of an actual multipart request.
|
||||
*
|
||||
* <pre class="code">
|
||||
* public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
|
||||
* MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
* MultipartFile multipartFile = multipartRequest.getFile("image");
|
||||
* ...
|
||||
* }</pre>
|
||||
*
|
||||
* Instead of direct access, command or form controllers can register a
|
||||
* {@link org.springframework.web.multipart.support.ByteArrayMultipartFileEditor}
|
||||
* or {@link org.springframework.web.multipart.support.StringMultipartFileEditor}
|
||||
* with their data binder, to automatically apply multipart content to command
|
||||
* bean properties.
|
||||
*
|
||||
* <p>As an alternative to using a
|
||||
* {@link org.springframework.web.multipart.MultipartResolver} with a
|
||||
* {@link org.springframework.web.servlet.DispatcherServlet},
|
||||
* a {@link org.springframework.web.multipart.support.MultipartFilter} can be
|
||||
* registered in <code>web.xml</code>. It will delegate to a corresponding
|
||||
* {@link org.springframework.web.multipart.MultipartResolver} bean in the root
|
||||
* application context. This is mainly intended for applications that do not
|
||||
* use Spring's own web MVC framework.
|
||||
*
|
||||
* <p>Note: There is hardly ever a need to access the
|
||||
* {@link org.springframework.web.multipart.MultipartResolver} itself
|
||||
* from application code. It will simply do its work behind the scenes,
|
||||
* making
|
||||
* {@link org.springframework.web.multipart.MultipartHttpServletRequest MultipartHttpServletRequests}
|
||||
* available to controllers.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Trevor D. Cook
|
||||
* @since 29.09.2003
|
||||
* @see MultipartHttpServletRequest
|
||||
* @see MultipartFile
|
||||
* @see org.springframework.web.multipart.commons.CommonsMultipartResolver
|
||||
* @see org.springframework.web.multipart.support.ByteArrayMultipartFileEditor
|
||||
* @see org.springframework.web.multipart.support.StringMultipartFileEditor
|
||||
* @see org.springframework.web.servlet.DispatcherServlet
|
||||
*/
|
||||
public interface MultipartResolver {
|
||||
|
||||
/**
|
||||
* Determine if the given request contains multipart content.
|
||||
* <p>Will typically check for content type "multipart/form-data", but the actually
|
||||
* accepted requests might depend on the capabilities of the resolver implementation.
|
||||
* @param request the servlet request to be evaluated
|
||||
* @return whether the request contains multipart content
|
||||
*/
|
||||
boolean isMultipart(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Parse the given HTTP request into multipart files and parameters,
|
||||
* and wrap the request inside a
|
||||
* {@link org.springframework.web.multipart.MultipartHttpServletRequest} object
|
||||
* that provides access to file descriptors and makes contained
|
||||
* parameters accessible via the standard ServletRequest methods.
|
||||
* @param request the servlet request to wrap (must be of a multipart content type)
|
||||
* @return the wrapped servlet request
|
||||
* @throws MultipartException if the servlet request is not multipart, or if
|
||||
* implementation-specific problems are encountered (such as exceeding file size limits)
|
||||
* @see MultipartHttpServletRequest#getFile
|
||||
* @see MultipartHttpServletRequest#getFileNames
|
||||
* @see MultipartHttpServletRequest#getFileMap
|
||||
* @see javax.servlet.http.HttpServletRequest#getParameter
|
||||
* @see javax.servlet.http.HttpServletRequest#getParameterNames
|
||||
* @see javax.servlet.http.HttpServletRequest#getParameterMap
|
||||
*/
|
||||
MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;
|
||||
|
||||
/**
|
||||
* Cleanup any resources used for the multipart handling,
|
||||
* like a storage for the uploaded files.
|
||||
* @param request the request to cleanup resources for
|
||||
*/
|
||||
void cleanupMultipart(MultipartHttpServletRequest request);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart.commons;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.FileItemFactory;
|
||||
import org.apache.commons.fileupload.FileUpload;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Base class for multipart resolvers that use Jakarta Commons FileUpload
|
||||
* 1.1 or higher.
|
||||
*
|
||||
* <p>Provides common configuration properties and parsing functionality
|
||||
* for multipart requests, using a Map of Spring CommonsMultipartFile instances
|
||||
* as representation of uploaded files and a String-based parameter Map as
|
||||
* representation of uploaded form fields.
|
||||
*
|
||||
* <p>Subclasses implement concrete resolution strategies for Servlet or Portlet
|
||||
* environments: see CommonsMultipartResolver and CommonsPortletMultipartResolver,
|
||||
* respectively. This base class is not tied to either of those APIs, factoring
|
||||
* out common functionality.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see CommonsMultipartFile
|
||||
* @see CommonsMultipartResolver
|
||||
* @see org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver
|
||||
*/
|
||||
public abstract class CommonsFileUploadSupport {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final DiskFileItemFactory fileItemFactory;
|
||||
|
||||
private final FileUpload fileUpload;
|
||||
|
||||
private boolean uploadTempDirSpecified = false;
|
||||
|
||||
|
||||
/**
|
||||
* Instantiate a new CommonsFileUploadSupport with its
|
||||
* corresponding FileItemFactory and FileUpload instances.
|
||||
* @see #newFileItemFactory
|
||||
* @see #newFileUpload
|
||||
*/
|
||||
public CommonsFileUploadSupport() {
|
||||
this.fileItemFactory = newFileItemFactory();
|
||||
this.fileUpload = newFileUpload(getFileItemFactory());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the underlying <code>org.apache.commons.fileupload.disk.DiskFileItemFactory</code>
|
||||
* instance. There is hardly any need to access this.
|
||||
* @return the underlying DiskFileItemFactory instance
|
||||
*/
|
||||
public DiskFileItemFactory getFileItemFactory() {
|
||||
return this.fileItemFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying <code>org.apache.commons.fileupload.FileUpload</code>
|
||||
* instance. There is hardly any need to access this.
|
||||
* @return the underlying FileUpload instance
|
||||
*/
|
||||
public FileUpload getFileUpload() {
|
||||
return this.fileUpload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum allowed size (in bytes) before uploads are refused.
|
||||
* -1 indicates no limit (the default).
|
||||
* @param maxUploadSize the maximum upload size allowed
|
||||
* @see org.apache.commons.fileupload.FileUploadBase#setSizeMax
|
||||
*/
|
||||
public void setMaxUploadSize(long maxUploadSize) {
|
||||
this.fileUpload.setSizeMax(maxUploadSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum allowed size (in bytes) before uploads are written to disk.
|
||||
* Uploaded files will still be received past this amount, but they will not be
|
||||
* stored in memory. Default is 10240, according to Commons FileUpload.
|
||||
* @param maxInMemorySize the maximum in memory size allowed
|
||||
* @see org.apache.commons.fileupload.disk.DiskFileItemFactory#setSizeThreshold
|
||||
*/
|
||||
public void setMaxInMemorySize(int maxInMemorySize) {
|
||||
this.fileItemFactory.setSizeThreshold(maxInMemorySize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default character encoding to use for parsing requests,
|
||||
* to be applied to headers of individual parts and to form fields.
|
||||
* Default is ISO-8859-1, according to the Servlet spec.
|
||||
* <p>If the request specifies a character encoding itself, the request
|
||||
* encoding will override this setting. This also allows for generically
|
||||
* overriding the character encoding in a filter that invokes the
|
||||
* <code>ServletRequest.setCharacterEncoding</code> method.
|
||||
* @param defaultEncoding the character encoding to use
|
||||
* @see javax.servlet.ServletRequest#getCharacterEncoding
|
||||
* @see javax.servlet.ServletRequest#setCharacterEncoding
|
||||
* @see WebUtils#DEFAULT_CHARACTER_ENCODING
|
||||
* @see org.apache.commons.fileupload.FileUploadBase#setHeaderEncoding
|
||||
*/
|
||||
public void setDefaultEncoding(String defaultEncoding) {
|
||||
this.fileUpload.setHeaderEncoding(defaultEncoding);
|
||||
}
|
||||
|
||||
protected String getDefaultEncoding() {
|
||||
String encoding = getFileUpload().getHeaderEncoding();
|
||||
if (encoding == null) {
|
||||
encoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
|
||||
}
|
||||
return encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the temporary directory where uploaded files get stored.
|
||||
* Default is the servlet container's temporary directory for the web application.
|
||||
* @see org.springframework.web.util.WebUtils#TEMP_DIR_CONTEXT_ATTRIBUTE
|
||||
*/
|
||||
public void setUploadTempDir(Resource uploadTempDir) throws IOException {
|
||||
if (!uploadTempDir.exists() && !uploadTempDir.getFile().mkdirs()) {
|
||||
throw new IllegalArgumentException("Given uploadTempDir [" + uploadTempDir + "] could not be created");
|
||||
}
|
||||
this.fileItemFactory.setRepository(uploadTempDir.getFile());
|
||||
this.uploadTempDirSpecified = true;
|
||||
}
|
||||
|
||||
protected boolean isUploadTempDirSpecified() {
|
||||
return this.uploadTempDirSpecified;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory method for a Commons DiskFileItemFactory instance.
|
||||
* <p>Default implementation returns a standard DiskFileItemFactory.
|
||||
* Can be overridden to use a custom subclass, e.g. for testing purposes.
|
||||
* @return the new DiskFileItemFactory instance
|
||||
*/
|
||||
protected DiskFileItemFactory newFileItemFactory() {
|
||||
return new DiskFileItemFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for a Commons FileUpload instance.
|
||||
* <p><b>To be implemented by subclasses.</b>
|
||||
* @param fileItemFactory the Commons FileItemFactory to build upon
|
||||
* @return the Commons FileUpload instance
|
||||
*/
|
||||
protected abstract FileUpload newFileUpload(FileItemFactory fileItemFactory);
|
||||
|
||||
|
||||
/**
|
||||
* Determine an appropriate FileUpload instance for the given encoding.
|
||||
* <p>Default implementation returns the shared FileUpload instance
|
||||
* if the encoding matches, else creates a new FileUpload instance
|
||||
* with the same configuration other than the desired encoding.
|
||||
* @param encoding the character encoding to use
|
||||
* @return an appropriate FileUpload instance.
|
||||
*/
|
||||
protected FileUpload prepareFileUpload(String encoding) {
|
||||
FileUpload fileUpload = getFileUpload();
|
||||
FileUpload actualFileUpload = fileUpload;
|
||||
|
||||
// Use new temporary FileUpload instance if the request specifies
|
||||
// its own encoding that does not match the default encoding.
|
||||
if (encoding != null && !encoding.equals(fileUpload.getHeaderEncoding())) {
|
||||
actualFileUpload = newFileUpload(getFileItemFactory());
|
||||
actualFileUpload.setSizeMax(fileUpload.getSizeMax());
|
||||
actualFileUpload.setHeaderEncoding(encoding);
|
||||
}
|
||||
|
||||
return actualFileUpload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given List of Commons FileItems into a Spring MultipartParsingResult,
|
||||
* containing Spring MultipartFile instances and a Map of multipart parameter.
|
||||
* @param fileItems the Commons FileIterms to parse
|
||||
* @param encoding the encoding to use for form fields
|
||||
* @return the Spring MultipartParsingResult
|
||||
* @see CommonsMultipartFile#CommonsMultipartFile(org.apache.commons.fileupload.FileItem)
|
||||
*/
|
||||
protected MultipartParsingResult parseFileItems(List fileItems, String encoding) {
|
||||
Map multipartFiles = new HashMap();
|
||||
Map multipartParameters = new HashMap();
|
||||
|
||||
// Extract multipart files and multipart parameters.
|
||||
for (Iterator it = fileItems.iterator(); it.hasNext();) {
|
||||
FileItem fileItem = (FileItem) it.next();
|
||||
if (fileItem.isFormField()) {
|
||||
String value = null;
|
||||
if (encoding != null) {
|
||||
try {
|
||||
value = fileItem.getString(encoding);
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not decode multipart item '" + fileItem.getFieldName() +
|
||||
"' with encoding '" + encoding + "': using platform default");
|
||||
}
|
||||
value = fileItem.getString();
|
||||
}
|
||||
}
|
||||
else {
|
||||
value = fileItem.getString();
|
||||
}
|
||||
String[] curParam = (String[]) multipartParameters.get(fileItem.getFieldName());
|
||||
if (curParam == null) {
|
||||
// simple form field
|
||||
multipartParameters.put(fileItem.getFieldName(), new String[] { value });
|
||||
}
|
||||
else {
|
||||
// array of simple form fields
|
||||
String[] newParam = StringUtils.addStringToArray(curParam, value);
|
||||
multipartParameters.put(fileItem.getFieldName(), newParam);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// multipart file field
|
||||
CommonsMultipartFile file = new CommonsMultipartFile(fileItem);
|
||||
if (multipartFiles.put(file.getName(), file) != null) {
|
||||
throw new MultipartException(
|
||||
"Multiple files for field name [" + file.getName() + "] found - not supported by MultipartResolver");
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found multipart file [" + file.getName() + "] of size " + file.getSize() +
|
||||
" bytes with original filename [" + file.getOriginalFilename() + "], stored " +
|
||||
file.getStorageDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
return new MultipartParsingResult(multipartFiles, multipartParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup the Spring MultipartFiles created during multipart parsing,
|
||||
* potentially holding temporary data on disk.
|
||||
* <p>Deletes the underlying Commons FileItem instances.
|
||||
* @param multipartFiles Collection of MultipartFile instances
|
||||
* @see org.apache.commons.fileupload.FileItem#delete()
|
||||
*/
|
||||
protected void cleanupFileItems(Collection multipartFiles) {
|
||||
for (Iterator it = multipartFiles.iterator(); it.hasNext();) {
|
||||
CommonsMultipartFile file = (CommonsMultipartFile) it.next();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cleaning up multipart file [" + file.getName() + "] with original filename [" +
|
||||
file.getOriginalFilename() + "], stored " + file.getStorageDescription());
|
||||
}
|
||||
file.getFileItem().delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Holder for a Map of Spring MultipartFiles and a Map of
|
||||
* multipart parameters.
|
||||
*/
|
||||
protected static class MultipartParsingResult {
|
||||
|
||||
private final Map multipartFiles;
|
||||
|
||||
private final Map multipartParameters;
|
||||
|
||||
/**
|
||||
* Create a new MultipartParsingResult.
|
||||
* @param multipartFiles Map of field name to MultipartFile instance
|
||||
* @param multipartParameters Map of field name to form field String value
|
||||
*/
|
||||
public MultipartParsingResult(Map multipartFiles, Map multipartParameters) {
|
||||
this.multipartFiles = multipartFiles;
|
||||
this.multipartParameters = multipartParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the multipart files as Map of field name to MultipartFile instance.
|
||||
*/
|
||||
public Map getMultipartFiles() {
|
||||
return this.multipartFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the multipart parameters as Map of field name to form field String value.
|
||||
*/
|
||||
public Map getMultipartParameters() {
|
||||
return this.multipartParameters;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart.commons;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.FileUploadException;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItem;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* MultipartFile implementation for Jakarta Commons FileUpload.
|
||||
*
|
||||
* <p><b>NOTE:</b> As of Spring 2.0, this class requires Commons FileUpload 1.1
|
||||
* or higher. The implementation does not use any deprecated FileUpload 1.0 API
|
||||
* anymore, to be compatible with future Commons FileUpload releases.
|
||||
*
|
||||
* @author Trevor D. Cook
|
||||
* @author Juergen Hoeller
|
||||
* @since 29.09.2003
|
||||
* @see CommonsMultipartResolver
|
||||
*/
|
||||
public class CommonsMultipartFile implements MultipartFile, Serializable {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(CommonsMultipartFile.class);
|
||||
|
||||
private final FileItem fileItem;
|
||||
|
||||
private final long size;
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance wrapping the given FileItem.
|
||||
* @param fileItem the FileItem to wrap
|
||||
*/
|
||||
public CommonsMultipartFile(FileItem fileItem) {
|
||||
this.fileItem = fileItem;
|
||||
this.size = this.fileItem.getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying <code>org.apache.commons.fileupload.FileItem</code>
|
||||
* instance. There is hardly any need to access this.
|
||||
*/
|
||||
public final FileItem getFileItem() {
|
||||
return this.fileItem;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return this.fileItem.getFieldName();
|
||||
}
|
||||
|
||||
public String getOriginalFilename() {
|
||||
String filename = this.fileItem.getName();
|
||||
if (filename == null) {
|
||||
// Should never happen.
|
||||
return "";
|
||||
}
|
||||
// check for Unix-style path
|
||||
int pos = filename.lastIndexOf("/");
|
||||
if (pos == -1) {
|
||||
// check for Windows-style path
|
||||
pos = filename.lastIndexOf("\\");
|
||||
}
|
||||
if (pos != -1) {
|
||||
// any sort of path separator found
|
||||
return filename.substring(pos + 1);
|
||||
}
|
||||
else {
|
||||
// plain name
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return this.fileItem.getContentType();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return (this.size == 0);
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
if (!isAvailable()) {
|
||||
throw new IllegalStateException("File has been moved - cannot be read again");
|
||||
}
|
||||
byte[] bytes = this.fileItem.get();
|
||||
return (bytes != null ? bytes : new byte[0]);
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (!isAvailable()) {
|
||||
throw new IllegalStateException("File has been moved - cannot be read again");
|
||||
}
|
||||
InputStream inputStream = this.fileItem.getInputStream();
|
||||
return (inputStream != null ? inputStream : new ByteArrayInputStream(new byte[0]));
|
||||
}
|
||||
|
||||
public void transferTo(File dest) throws IOException, IllegalStateException {
|
||||
if (!isAvailable()) {
|
||||
throw new IllegalStateException("File has already been moved - cannot be transferred again");
|
||||
}
|
||||
|
||||
if (dest.exists() && !dest.delete()) {
|
||||
throw new IOException(
|
||||
"Destination file [" + dest.getAbsolutePath() + "] already exists and could not be deleted");
|
||||
}
|
||||
|
||||
try {
|
||||
this.fileItem.write(dest);
|
||||
if (logger.isDebugEnabled()) {
|
||||
String action = "transferred";
|
||||
if (!this.fileItem.isInMemory()) {
|
||||
action = isAvailable() ? "copied" : "moved";
|
||||
}
|
||||
logger.debug("Multipart file '" + getName() + "' with original filename [" +
|
||||
getOriginalFilename() + "], stored " + getStorageDescription() + ": " +
|
||||
action + " to [" + dest.getAbsolutePath() + "]");
|
||||
}
|
||||
}
|
||||
catch (FileUploadException ex) {
|
||||
throw new IllegalStateException(ex.getMessage());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error("Could not transfer to file", ex);
|
||||
throw new IOException("Could not transfer to file: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the multipart content is still available.
|
||||
* If a temporary file has been moved, the content is no longer available.
|
||||
*/
|
||||
protected boolean isAvailable() {
|
||||
// If in memory, it's available.
|
||||
if (this.fileItem.isInMemory()) {
|
||||
return true;
|
||||
}
|
||||
// Check actual existence of temporary file.
|
||||
if (this.fileItem instanceof DiskFileItem) {
|
||||
return ((DiskFileItem) this.fileItem).getStoreLocation().exists();
|
||||
}
|
||||
// Check whether current file size is different than original one.
|
||||
return (this.fileItem.getSize() == this.size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a description for the storage location of the multipart content.
|
||||
* Tries to be as specific as possible: mentions the file location in case
|
||||
* of a temporary file.
|
||||
*/
|
||||
public String getStorageDescription() {
|
||||
if (this.fileItem.isInMemory()) {
|
||||
return "in memory";
|
||||
}
|
||||
else if (this.fileItem instanceof DiskFileItem) {
|
||||
return "at [" + ((DiskFileItem) this.fileItem).getStoreLocation().getAbsolutePath() + "]";
|
||||
}
|
||||
else {
|
||||
return "on disk";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart.commons;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.fileupload.FileItemFactory;
|
||||
import org.apache.commons.fileupload.FileUpload;
|
||||
import org.apache.commons.fileupload.FileUploadBase;
|
||||
import org.apache.commons.fileupload.FileUploadException;
|
||||
import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
||||
import org.apache.commons.fileupload.servlet.ServletRequestContext;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.multipart.MultipartResolver;
|
||||
import org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Servlet-based {@link org.springframework.web.multipart.MultipartResolver} implementation
|
||||
* for <a href="http://jakarta.apache.org/commons/fileupload">Jakarta Commons FileUpload</a>
|
||||
* 1.1 or above. Commons FileUpload 1.2 or above is recommended.
|
||||
*
|
||||
* <p>Provides "maxUploadSize", "maxInMemorySize" and "defaultEncoding" settings as
|
||||
* bean properties (inherited from {@link CommonsFileUploadSupport}). See corresponding
|
||||
* ServletFileUpload / DiskFileItemFactory properties ("sizeMax", "sizeThreshold",
|
||||
* "headerEncoding") for details in terms of defaults and accepted values.
|
||||
*
|
||||
* <p>Saves temporary files to the servlet container's temporary directory.
|
||||
* Needs to be initialized <i>either</i> by an application context <i>or</i>
|
||||
* via the constructor that takes a ServletContext (for standalone usage).
|
||||
*
|
||||
* @author Trevor D. Cook
|
||||
* @author Juergen Hoeller
|
||||
* @since 29.09.2003
|
||||
* @see #CommonsMultipartResolver(ServletContext)
|
||||
* @see #setResolveLazily
|
||||
* @see org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver
|
||||
* @see org.apache.commons.fileupload.servlet.ServletFileUpload
|
||||
* @see org.apache.commons.fileupload.disk.DiskFileItemFactory
|
||||
*/
|
||||
public class CommonsMultipartResolver extends CommonsFileUploadSupport
|
||||
implements MultipartResolver, ServletContextAware {
|
||||
|
||||
private final boolean commonsFileUpload12Present =
|
||||
ClassUtils.hasMethod(ServletFileUpload.class, "isMultipartContent", new Class[] {HttpServletRequest.class});
|
||||
|
||||
private boolean resolveLazily = false;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for use as bean. Determines the servlet container's
|
||||
* temporary directory via the ServletContext passed in as through the
|
||||
* ServletContextAware interface (typically by a WebApplicationContext).
|
||||
* @see #setServletContext
|
||||
* @see org.springframework.web.context.ServletContextAware
|
||||
* @see org.springframework.web.context.WebApplicationContext
|
||||
*/
|
||||
public CommonsMultipartResolver() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for standalone usage. Determines the servlet container's
|
||||
* temporary directory via the given ServletContext.
|
||||
* @param servletContext the ServletContext to use
|
||||
*/
|
||||
public CommonsMultipartResolver(ServletContext servletContext) {
|
||||
this();
|
||||
setServletContext(servletContext);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to resolve the multipart request lazily at the time of
|
||||
* file or parameter access.
|
||||
* <p>Default is "false", resolving the multipart elements immediately, throwing
|
||||
* corresponding exceptions at the time of the {@link #resolveMultipart} call.
|
||||
* Switch this to "true" for lazy multipart parsing, throwing parse exceptions
|
||||
* once the application attempts to obtain multipart files or parameters.
|
||||
*/
|
||||
public void setResolveLazily(boolean resolveLazily) {
|
||||
this.resolveLazily = resolveLazily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the underlying <code>org.apache.commons.fileupload.servlet.ServletFileUpload</code>
|
||||
* instance. Can be overridden to use a custom subclass, e.g. for testing purposes.
|
||||
* @param fileItemFactory the Commons FileItemFactory to use
|
||||
* @return the new ServletFileUpload instance
|
||||
*/
|
||||
protected FileUpload newFileUpload(FileItemFactory fileItemFactory) {
|
||||
return new ServletFileUpload(fileItemFactory);
|
||||
}
|
||||
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
if (!isUploadTempDirSpecified()) {
|
||||
getFileItemFactory().setRepository(WebUtils.getTempDir(servletContext));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isMultipart(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return false;
|
||||
}
|
||||
else if (commonsFileUpload12Present) {
|
||||
return ServletFileUpload.isMultipartContent(request);
|
||||
}
|
||||
else {
|
||||
return ServletFileUpload.isMultipartContent(new ServletRequestContext(request));
|
||||
}
|
||||
}
|
||||
|
||||
public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
|
||||
Assert.notNull(request, "Request must not be null");
|
||||
if (this.resolveLazily) {
|
||||
return new DefaultMultipartHttpServletRequest(request) {
|
||||
protected void initializeMultipart() {
|
||||
MultipartParsingResult parsingResult = parseRequest(request);
|
||||
setMultipartFiles(parsingResult.getMultipartFiles());
|
||||
setMultipartParameters(parsingResult.getMultipartParameters());
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
MultipartParsingResult parsingResult = parseRequest(request);
|
||||
return new DefaultMultipartHttpServletRequest(
|
||||
request, parsingResult.getMultipartFiles(), parsingResult.getMultipartParameters());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given servlet request, resolving its multipart elements.
|
||||
* @param request the request to parse
|
||||
* @return the parsing result
|
||||
* @throws MultipartException if multipart resolution failed.
|
||||
*/
|
||||
protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
|
||||
String encoding = determineEncoding(request);
|
||||
FileUpload fileUpload = prepareFileUpload(encoding);
|
||||
try {
|
||||
List fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
|
||||
return parseFileItems(fileItems, encoding);
|
||||
}
|
||||
catch (FileUploadBase.SizeLimitExceededException ex) {
|
||||
throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
|
||||
}
|
||||
catch (FileUploadException ex) {
|
||||
throw new MultipartException("Could not parse multipart servlet request", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the encoding for the given request.
|
||||
* Can be overridden in subclasses.
|
||||
* <p>The default implementation checks the request encoding,
|
||||
* falling back to the default encoding specified for this resolver.
|
||||
* @param request current HTTP request
|
||||
* @return the encoding for the request (never <code>null</code>)
|
||||
* @see javax.servlet.ServletRequest#getCharacterEncoding
|
||||
* @see #setDefaultEncoding
|
||||
*/
|
||||
protected String determineEncoding(HttpServletRequest request) {
|
||||
String encoding = request.getCharacterEncoding();
|
||||
if (encoding == null) {
|
||||
encoding = getDefaultEncoding();
|
||||
}
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public void cleanupMultipart(MultipartHttpServletRequest request) {
|
||||
if (request != null) {
|
||||
try {
|
||||
cleanupFileItems(request.getFileMap().values());
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.warn("Failed to perform multipart cleanup for servlet request", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
MultipartResolver implementation for
|
||||
<a href="http://jakarta.apache.org/commons/fileupload">Jakarta Commons FileUpload</a>.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Multipart resolution framework for handling file uploads.
|
||||
Provides a MultipartResolver strategy interface,
|
||||
and a generic extension of the HttpServletRequest interface
|
||||
for accessing multipart files in web application code.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
|
||||
/**
|
||||
* Abstract base implementation of the MultipartHttpServletRequest interface.
|
||||
* Provides management of pre-generated MultipartFile instances.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 06.10.2003
|
||||
*/
|
||||
public abstract class AbstractMultipartHttpServletRequest extends HttpServletRequestWrapper
|
||||
implements MultipartHttpServletRequest {
|
||||
|
||||
private Map multipartFiles;
|
||||
|
||||
|
||||
/**
|
||||
* Wrap the given HttpServletRequest in a MultipartHttpServletRequest.
|
||||
* @param request the request to wrap
|
||||
*/
|
||||
protected AbstractMultipartHttpServletRequest(HttpServletRequest request) {
|
||||
super(request);
|
||||
}
|
||||
|
||||
|
||||
public Iterator getFileNames() {
|
||||
return getMultipartFiles().keySet().iterator();
|
||||
}
|
||||
|
||||
public MultipartFile getFile(String name) {
|
||||
return (MultipartFile) getMultipartFiles().get(name);
|
||||
}
|
||||
|
||||
public Map getFileMap() {
|
||||
return getMultipartFiles();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a Map with parameter names as keys and MultipartFile objects as values.
|
||||
* To be invoked by subclasses on initialization.
|
||||
*/
|
||||
protected final void setMultipartFiles(Map multipartFiles) {
|
||||
this.multipartFiles = Collections.unmodifiableMap(multipartFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the MultipartFile Map for retrieval,
|
||||
* lazily initializing it if necessary.
|
||||
* @see #initializeMultipart()
|
||||
*/
|
||||
protected Map getMultipartFiles() {
|
||||
if (this.multipartFiles == null) {
|
||||
initializeMultipart();
|
||||
}
|
||||
return this.multipartFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily initialize the multipart request, if possible.
|
||||
* Only called if not already eagerly initialized.
|
||||
*/
|
||||
protected void initializeMultipart() {
|
||||
throw new IllegalStateException("Multipart request not initialized");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart.support;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.propertyeditors.ByteArrayPropertyEditor;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* Custom {@link java.beans.PropertyEditor} for converting
|
||||
* {@link MultipartFile MultipartFiles} to byte arrays.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 13.10.2003
|
||||
*/
|
||||
public class ByteArrayMultipartFileEditor extends ByteArrayPropertyEditor {
|
||||
|
||||
public void setValue(Object value) {
|
||||
if (value instanceof MultipartFile) {
|
||||
MultipartFile multipartFile = (MultipartFile) value;
|
||||
try {
|
||||
super.setValue(multipartFile.getBytes());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
IllegalArgumentException iae = new IllegalArgumentException("Cannot read contents of multipart file");
|
||||
iae.initCause(ex);
|
||||
throw iae;
|
||||
}
|
||||
}
|
||||
else if (value instanceof byte[]) {
|
||||
super.setValue(value);
|
||||
}
|
||||
else {
|
||||
super.setValue(value != null ? value.toString().getBytes() : null);
|
||||
}
|
||||
}
|
||||
|
||||
public String getAsText() {
|
||||
byte[] value = (byte[]) getValue();
|
||||
return (value != null ? new String(value) : "");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Default implementation of the
|
||||
* {@link org.springframework.web.multipart.MultipartHttpServletRequest}
|
||||
* interface. Provides management of pre-generated parameter values.
|
||||
*
|
||||
* @author Trevor D. Cook
|
||||
* @author Juergen Hoeller
|
||||
* @since 29.09.2003
|
||||
* @see org.springframework.web.multipart.MultipartResolver
|
||||
*/
|
||||
public class DefaultMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {
|
||||
|
||||
private Map multipartParameters;
|
||||
|
||||
|
||||
/**
|
||||
* Wrap the given HttpServletRequest in a MultipartHttpServletRequest.
|
||||
* @param request the servlet request to wrap
|
||||
* @param multipartFiles a map of the multipart files
|
||||
* @param multipartParameters a map of the parameters to expose,
|
||||
* with Strings as keys and String arrays as values
|
||||
*/
|
||||
public DefaultMultipartHttpServletRequest(
|
||||
HttpServletRequest request, Map multipartFiles, Map multipartParameters) {
|
||||
|
||||
super(request);
|
||||
setMultipartFiles(multipartFiles);
|
||||
setMultipartParameters(multipartParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the given HttpServletRequest in a MultipartHttpServletRequest.
|
||||
* @param request the servlet request to wrap
|
||||
*/
|
||||
public DefaultMultipartHttpServletRequest(HttpServletRequest request) {
|
||||
super(request);
|
||||
}
|
||||
|
||||
|
||||
public Enumeration getParameterNames() {
|
||||
Set paramNames = new HashSet();
|
||||
Enumeration paramEnum = super.getParameterNames();
|
||||
while (paramEnum.hasMoreElements()) {
|
||||
paramNames.add(paramEnum.nextElement());
|
||||
}
|
||||
paramNames.addAll(getMultipartParameters().keySet());
|
||||
return Collections.enumeration(paramNames);
|
||||
}
|
||||
|
||||
public String getParameter(String name) {
|
||||
String[] values = (String[]) getMultipartParameters().get(name);
|
||||
if (values != null) {
|
||||
return (values.length > 0 ? values[0] : null);
|
||||
}
|
||||
return super.getParameter(name);
|
||||
}
|
||||
|
||||
public String[] getParameterValues(String name) {
|
||||
String[] values = (String[]) getMultipartParameters().get(name);
|
||||
if (values != null) {
|
||||
return values;
|
||||
}
|
||||
return super.getParameterValues(name);
|
||||
}
|
||||
|
||||
public Map getParameterMap() {
|
||||
Map paramMap = new HashMap();
|
||||
paramMap.putAll(super.getParameterMap());
|
||||
paramMap.putAll(getMultipartParameters());
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a Map with parameter names as keys and String array objects as values.
|
||||
* To be invoked by subclasses on initialization.
|
||||
*/
|
||||
protected final void setMultipartParameters(Map multipartParameters) {
|
||||
this.multipartParameters = multipartParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the multipart parameter Map for retrieval,
|
||||
* lazily initializing it if necessary.
|
||||
* @see #initializeMultipart()
|
||||
*/
|
||||
protected Map getMultipartParameters() {
|
||||
if (this.multipartParameters == null) {
|
||||
initializeMultipart();
|
||||
}
|
||||
return this.multipartParameters;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart.support;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.multipart.MultipartResolver;
|
||||
|
||||
/**
|
||||
* Servlet 2.3 Filter that resolves multipart requests via a MultipartResolver.
|
||||
* in the root web application context.
|
||||
*
|
||||
* <p>Looks up the MultipartResolver in Spring's root web application context.
|
||||
* Supports a "multipartResolverBeanName" filter init-param in <code>web.xml</code>;
|
||||
* the default bean name is "filterMultipartResolver". Looks up the MultipartResolver
|
||||
* on each request, to avoid initialization order issues (when using ContextLoaderServlet,
|
||||
* the root application context will get initialized <i>after</i> this filter).
|
||||
*
|
||||
* <p>MultipartResolver lookup is customizable: Override this filter's
|
||||
* <code>lookupMultipartResolver</code> method to use a custom MultipartResolver
|
||||
* instance, for example if not using a Spring web application context.
|
||||
* Note that the lookup method should not create a new MultipartResolver instance
|
||||
* for each call but rather return a reference to a pre-built instance.
|
||||
*
|
||||
* <p>Note: This filter is an <b>alternative</b> to using DispatcherServlet's
|
||||
* MultipartResolver support, for example for web applications with custom
|
||||
* web views that do not use Spring's web MVC. It should not be combined with
|
||||
* servlet-specific multipart resolution.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 08.10.2003
|
||||
* @see #setMultipartResolverBeanName
|
||||
* @see #lookupMultipartResolver
|
||||
* @see org.springframework.web.multipart.MultipartResolver
|
||||
* @see org.springframework.web.servlet.DispatcherServlet
|
||||
*/
|
||||
public class MultipartFilter extends OncePerRequestFilter {
|
||||
|
||||
public static final String DEFAULT_MULTIPART_RESOLVER_BEAN_NAME = "filterMultipartResolver";
|
||||
|
||||
private String multipartResolverBeanName = DEFAULT_MULTIPART_RESOLVER_BEAN_NAME;
|
||||
|
||||
|
||||
/**
|
||||
* Set the bean name of the MultipartResolver to fetch from Spring's
|
||||
* root application context. Default is "filterMultipartResolver".
|
||||
*/
|
||||
public void setMultipartResolverBeanName(String multipartResolverBeanName) {
|
||||
this.multipartResolverBeanName = multipartResolverBeanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean name of the MultipartResolver to fetch from Spring's
|
||||
* root application context.
|
||||
*/
|
||||
protected String getMultipartResolverBeanName() {
|
||||
return multipartResolverBeanName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check for a multipart request via this filter's MultipartResolver,
|
||||
* and wrap the original request with a MultipartHttpServletRequest if appropriate.
|
||||
* <p>All later elements in the filter chain, most importantly servlets, benefit
|
||||
* from proper parameter extraction in the multipart case, and are able to cast to
|
||||
* MultipartHttpServletRequest if they need to.
|
||||
*/
|
||||
protected void doFilterInternal(
|
||||
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
MultipartResolver multipartResolver = lookupMultipartResolver(request);
|
||||
|
||||
HttpServletRequest processedRequest = request;
|
||||
if (multipartResolver.isMultipart(processedRequest)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resolving multipart request [" + processedRequest.getRequestURI() +
|
||||
"] with MultipartFilter");
|
||||
}
|
||||
processedRequest = multipartResolver.resolveMultipart(processedRequest);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Request [" + processedRequest.getRequestURI() + "] is not a multipart request");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
filterChain.doFilter(processedRequest, response);
|
||||
}
|
||||
finally {
|
||||
if (processedRequest instanceof MultipartHttpServletRequest) {
|
||||
multipartResolver.cleanupMultipart((MultipartHttpServletRequest) processedRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the MultipartResolver that this filter should use,
|
||||
* taking the current HTTP request as argument.
|
||||
* <p>Default implementation delegates to the <code>lookupMultipartResolver</code>
|
||||
* without arguments.
|
||||
* @return the MultipartResolver to use
|
||||
* @see #lookupMultipartResolver()
|
||||
*/
|
||||
protected MultipartResolver lookupMultipartResolver(HttpServletRequest request) {
|
||||
return lookupMultipartResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a MultipartResolver bean in the root web application context.
|
||||
* Supports a "multipartResolverBeanName" filter init param; the default
|
||||
* bean name is "filterMultipartResolver".
|
||||
* <p>This can be overridden to use a custom MultipartResolver instance,
|
||||
* for example if not using a Spring web application context.
|
||||
* @return the MultipartResolver instance, or <code>null</code> if none found
|
||||
*/
|
||||
protected MultipartResolver lookupMultipartResolver() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using MultipartResolver '" + getMultipartResolverBeanName() + "' for MultipartFilter");
|
||||
}
|
||||
WebApplicationContext wac =
|
||||
WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
|
||||
return (MultipartResolver) wac.getBean(getMultipartResolverBeanName(), MultipartResolver.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.multipart.support;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* Custom {@link java.beans.PropertyEditor} for converting
|
||||
* {@link MultipartFile MultipartFiles} to Strings.
|
||||
*
|
||||
* <p>Allows one to specify the charset to use.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 13.10.2003
|
||||
*/
|
||||
public class StringMultipartFileEditor extends PropertyEditorSupport {
|
||||
|
||||
private final String charsetName;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link StringMultipartFileEditor}, using the default charset.
|
||||
*/
|
||||
public StringMultipartFileEditor() {
|
||||
this.charsetName = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link StringMultipartFileEditor}, using the given charset.
|
||||
* @param charsetName valid charset name
|
||||
* @see java.lang.String#String(byte[],String)
|
||||
*/
|
||||
public StringMultipartFileEditor(String charsetName) {
|
||||
this.charsetName = charsetName;
|
||||
}
|
||||
|
||||
|
||||
public void setAsText(String text) {
|
||||
setValue(text);
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
if (value instanceof MultipartFile) {
|
||||
MultipartFile multipartFile = (MultipartFile) value;
|
||||
try {
|
||||
super.setValue(this.charsetName != null ?
|
||||
new String(multipartFile.getBytes(), this.charsetName) :
|
||||
new String(multipartFile.getBytes()));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
IllegalArgumentException iae = new IllegalArgumentException("Cannot read contents of multipart file");
|
||||
iae.initCause(ex);
|
||||
throw iae;
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for the multipart resolution framework.
|
||||
Contains property editors for multipart files, and a
|
||||
servlet filter for multipart handling without Spring's web MVC.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,19 @@
|
|||
# Default implementation classes for DispatcherServlet's strategy interfaces.
|
||||
# Used as fallback when no matching beans are found in the DispatcherServlet context.
|
||||
# Not meant to be customized by application developers.
|
||||
|
||||
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
|
||||
|
||||
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
|
||||
|
||||
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
|
||||
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
|
||||
|
||||
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
|
||||
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
|
||||
org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter,\
|
||||
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
|
||||
|
||||
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
|
||||
|
||||
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
|
||||
|
|
@ -0,0 +1,647 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.SourceFilteringListener;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.ServletRequestHandledEvent;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
import org.springframework.web.context.support.XmlWebApplicationContext;
|
||||
import org.springframework.web.util.NestedServletException;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Base servlet for Spring's web framework. Provides integration with
|
||||
* a Spring application context, in a JavaBean-based overall solution.
|
||||
*
|
||||
* <p>This class offers the following functionality:
|
||||
* <ul>
|
||||
* <li>Manages a {@link org.springframework.web.context.WebApplicationContext}
|
||||
* instance per servlet. The servlet's configuration is determined by beans
|
||||
* in the servlet's namespace.
|
||||
* <li>Publishes events on request processing, whether or not a request is
|
||||
* successfully handled.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Subclasses must implement {@link #doService} to handle requests. Because this extends
|
||||
* {@link HttpServletBean} rather than HttpServlet directly, bean properties are
|
||||
* automatically mapped onto it. Subclasses can override {@link #initFrameworkServlet()}
|
||||
* for custom initialization.
|
||||
*
|
||||
* <p>Detects a "contextClass" parameter at the servlet init-param level,
|
||||
* falling back to the default context class,
|
||||
* {@link org.springframework.web.context.support.XmlWebApplicationContext},
|
||||
* if not found. Note that, with the default FrameworkServlet,
|
||||
* a custom context class needs to implement the
|
||||
* {@link org.springframework.web.context.ConfigurableWebApplicationContext} SPI.
|
||||
*
|
||||
* <p>Passes a "contextConfigLocation" servlet init-param to the context instance,
|
||||
* parsing it into potentially multiple file paths which can be separated by any
|
||||
* number of commas and spaces, like "test-servlet.xml, myServlet.xml".
|
||||
* If not explicitly specified, the context implementation is supposed to build a
|
||||
* default location from the namespace of the servlet.
|
||||
*
|
||||
* <p>Note: In case of multiple config locations, later bean definitions will
|
||||
* override ones defined in earlier loaded files, at least when using Spring's
|
||||
* default ApplicationContext implementation. This can be leveraged to
|
||||
* deliberately override certain bean definitions via an extra XML file.
|
||||
*
|
||||
* <p>The default namespace is "'servlet-name'-servlet", e.g. "test-servlet" for a
|
||||
* servlet-name "test" (leading to a "/WEB-INF/test-servlet.xml" default location
|
||||
* with XmlWebApplicationContext). The namespace can also be set explicitly via
|
||||
* the "namespace" servlet init-param.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @see #doService
|
||||
* @see #setContextClass
|
||||
* @see #setContextConfigLocation
|
||||
* @see #setNamespace
|
||||
*/
|
||||
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationListener {
|
||||
|
||||
/**
|
||||
* Suffix for WebApplicationContext namespaces. If a servlet of this class is
|
||||
* given the name "test" in a context, the namespace used by the servlet will
|
||||
* resolve to "test-servlet".
|
||||
*/
|
||||
public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";
|
||||
|
||||
/**
|
||||
* Default context class for FrameworkServlet.
|
||||
* @see org.springframework.web.context.support.XmlWebApplicationContext
|
||||
*/
|
||||
public static final Class DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
|
||||
|
||||
/**
|
||||
* Prefix for the ServletContext attribute for the WebApplicationContext.
|
||||
* The completion is the servlet name.
|
||||
*/
|
||||
public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";
|
||||
|
||||
|
||||
/** ServletContext attribute to find the WebApplicationContext in */
|
||||
private String contextAttribute;
|
||||
|
||||
/** WebApplicationContext implementation class to create */
|
||||
private Class contextClass = DEFAULT_CONTEXT_CLASS;
|
||||
|
||||
/** Namespace for this servlet */
|
||||
private String namespace;
|
||||
|
||||
/** Explicit context config location */
|
||||
private String contextConfigLocation;
|
||||
|
||||
/** Should we publish the context as a ServletContext attribute? */
|
||||
private boolean publishContext = true;
|
||||
|
||||
/** Should we publish a ServletRequestHandledEvent at the end of each request? */
|
||||
private boolean publishEvents = true;
|
||||
|
||||
/** Should we dispatch an HTTP OPTIONS request to {@link #doService}? */
|
||||
private boolean dispatchOptionsRequest = false;
|
||||
|
||||
/** Should we dispatch an HTTP TRACE request to {@link #doService}? */
|
||||
private boolean dispatchTraceRequest = false;
|
||||
|
||||
/** WebApplicationContext for this servlet */
|
||||
private WebApplicationContext webApplicationContext;
|
||||
|
||||
/** Flag used to detect whether onRefresh has already been called */
|
||||
private boolean refreshEventReceived = false;
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the ServletContext attribute which should be used to retrieve the
|
||||
* {@link WebApplicationContext} that this servlet is supposed to use.
|
||||
*/
|
||||
public void setContextAttribute(String contextAttribute) {
|
||||
this.contextAttribute = contextAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the ServletContext attribute which should be used to retrieve the
|
||||
* {@link WebApplicationContext} that this servlet is supposed to use.
|
||||
*/
|
||||
public String getContextAttribute() {
|
||||
return this.contextAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom context class. This class must be of type
|
||||
* {@link org.springframework.web.context.WebApplicationContext}.
|
||||
* <p>When using the default FrameworkServlet implementation,
|
||||
* the context class must also implement the
|
||||
* {@link org.springframework.web.context.ConfigurableWebApplicationContext}
|
||||
* interface.
|
||||
* @see #createWebApplicationContext
|
||||
*/
|
||||
public void setContextClass(Class contextClass) {
|
||||
this.contextClass = contextClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the custom context class.
|
||||
*/
|
||||
public Class getContextClass() {
|
||||
return this.contextClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom namespace for this servlet,
|
||||
* to be used for building a default context config location.
|
||||
*/
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the namespace for this servlet, falling back to default scheme if
|
||||
* no custom namespace was set: e.g. "test-servlet" for a servlet named "test".
|
||||
*/
|
||||
public String getNamespace() {
|
||||
return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the context config location explicitly, instead of relying on the default
|
||||
* location built from the namespace. This location string can consist of
|
||||
* multiple locations separated by any number of commas and spaces.
|
||||
*/
|
||||
public void setContextConfigLocation(String contextConfigLocation) {
|
||||
this.contextConfigLocation = contextConfigLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the explicit context config location, if any.
|
||||
*/
|
||||
public String getContextConfigLocation() {
|
||||
return this.contextConfigLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to publish this servlet's context as a ServletContext attribute,
|
||||
* available to all objects in the web container. Default is "true".
|
||||
* <p>This is especially handy during testing, although it is debatable whether
|
||||
* it's good practice to let other application objects access the context this way.
|
||||
*/
|
||||
public void setPublishContext(boolean publishContext) {
|
||||
this.publishContext = publishContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this servlet should publish a ServletRequestHandledEvent at the end
|
||||
* of each request. Default is "true"; can be turned off for a slight performance
|
||||
* improvement, provided that no ApplicationListeners rely on such events.
|
||||
* @see org.springframework.web.context.support.ServletRequestHandledEvent
|
||||
*/
|
||||
public void setPublishEvents(boolean publishEvents) {
|
||||
this.publishEvents = publishEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this servlet should dispatch an HTTP OPTIONS request to
|
||||
* the {@link #doService} method.
|
||||
* <p>Default is "false", applying {@link javax.servlet.http.HttpServlet}'s
|
||||
* default behavior (i.e. enumerating all standard HTTP request methods
|
||||
* as a response to the OPTIONS request).
|
||||
* <p>Turn this flag on if you prefer OPTIONS requests to go through the
|
||||
* regular dispatching chain, just like other HTTP requests. This usually
|
||||
* means that your controllers will receive those requests; make sure
|
||||
* that those endpoints are actually able to handle an OPTIONS request.
|
||||
* <p>Note that HttpServlet's default OPTIONS processing will be applied
|
||||
* in any case. Your controllers are simply available to override the
|
||||
* default headers and optionally generate a response body.
|
||||
*/
|
||||
public void setDispatchOptionsRequest(boolean dispatchOptionsRequest) {
|
||||
this.dispatchOptionsRequest = dispatchOptionsRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this servlet should dispatch an HTTP TRACE request to
|
||||
* the {@link #doService} method.
|
||||
* <p>Default is "false", applying {@link javax.servlet.http.HttpServlet}'s
|
||||
* default behavior (i.e. reflecting the message received back to the client).
|
||||
* <p>Turn this flag on if you prefer TRACE requests to go through the
|
||||
* regular dispatching chain, just like other HTTP requests. This usually
|
||||
* means that your controllers will receive those requests; make sure
|
||||
* that those endpoints are actually able to handle a TRACE request.
|
||||
* <p>Note that HttpServlet's default TRACE processing will be applied
|
||||
* in any case. Your controllers are simply available to override the
|
||||
* default headers and the default body, calling <code>response.reset()</code>
|
||||
* if necessary.
|
||||
*/
|
||||
public void setDispatchTraceRequest(boolean dispatchTraceRequest) {
|
||||
this.dispatchTraceRequest = dispatchTraceRequest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
|
||||
* have been set. Creates this servlet's WebApplicationContext.
|
||||
*/
|
||||
protected final void initServletBean() throws ServletException, BeansException {
|
||||
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
|
||||
}
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
this.webApplicationContext = initWebApplicationContext();
|
||||
initFrameworkServlet();
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
this.logger.error("Context initialization failed", ex);
|
||||
throw ex;
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
this.logger.error("Context initialization failed", ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
|
||||
elapsedTime + " ms");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize and publish the WebApplicationContext for this servlet.
|
||||
* <p>Delegates to {@link #createWebApplicationContext} for actual creation
|
||||
* of the context. Can be overridden in subclasses.
|
||||
* @return the WebApplicationContext instance
|
||||
* @throws BeansException if the context couldn't be initialized
|
||||
* @see #setContextClass
|
||||
* @see #setContextConfigLocation
|
||||
*/
|
||||
protected WebApplicationContext initWebApplicationContext() throws BeansException {
|
||||
WebApplicationContext wac = findWebApplicationContext();
|
||||
if (wac == null) {
|
||||
// No fixed context defined for this servlet - create a local one.
|
||||
WebApplicationContext parent =
|
||||
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
|
||||
wac = createWebApplicationContext(parent);
|
||||
}
|
||||
|
||||
if (!this.refreshEventReceived) {
|
||||
// Apparently not a ConfigurableApplicationContext with refresh support:
|
||||
// triggering initial onRefresh manually here.
|
||||
onRefresh(wac);
|
||||
}
|
||||
|
||||
if (this.publishContext) {
|
||||
// Publish the context as a servlet context attribute.
|
||||
String attrName = getServletContextAttributeName();
|
||||
getServletContext().setAttribute(attrName, wac);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
|
||||
"' as ServletContext attribute with name [" + attrName + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return wac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a <code>WebApplicationContext</code> from the <code>ServletContext</code>
|
||||
* attribute with the {@link #setContextAttribute configured name}. The
|
||||
* <code>WebApplicationContext</code> must have already been loaded and stored in the
|
||||
* <code>ServletContext</code> before this servlet gets initialized (or invoked).
|
||||
* <p>Subclasses may override this method to provide a different
|
||||
* <code>WebApplicationContext</code> retrieval strategy.
|
||||
* @return the WebApplicationContext for this servlet, or <code>null</code> if not found
|
||||
* @see #getContextAttribute()
|
||||
*/
|
||||
protected WebApplicationContext findWebApplicationContext() {
|
||||
String attrName = getContextAttribute();
|
||||
if (attrName == null) {
|
||||
return null;
|
||||
}
|
||||
WebApplicationContext wac =
|
||||
WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
|
||||
if (wac == null) {
|
||||
throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
|
||||
}
|
||||
return wac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate the WebApplicationContext for this servlet, either a default
|
||||
* {@link org.springframework.web.context.support.XmlWebApplicationContext}
|
||||
* or a {@link #setContextClass custom context class}, if set.
|
||||
* <p>This implementation expects custom contexts to implement the
|
||||
* {@link org.springframework.web.context.ConfigurableWebApplicationContext}
|
||||
* interface. Can be overridden in subclasses.
|
||||
* <p>Do not forget to register this servlet instance as application listener on the
|
||||
* created context (for triggering its {@link #onRefresh callback}, and to call
|
||||
* {@link org.springframework.context.ConfigurableApplicationContext#refresh()}
|
||||
* before returning the context instance.
|
||||
* @param parent the parent ApplicationContext to use, or <code>null</code> if none
|
||||
* @return the WebApplicationContext for this servlet
|
||||
* @throws BeansException if the context couldn't be initialized
|
||||
* @see org.springframework.web.context.support.XmlWebApplicationContext
|
||||
*/
|
||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)
|
||||
throws BeansException {
|
||||
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Servlet with name '" + getServletName() +
|
||||
"' will try to create custom WebApplicationContext context of class '" +
|
||||
getContextClass().getName() + "'" + ", using parent context [" + parent + "]");
|
||||
}
|
||||
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(getContextClass())) {
|
||||
throw new ApplicationContextException(
|
||||
"Fatal initialization error in servlet with name '" + getServletName() +
|
||||
"': custom WebApplicationContext class [" + getContextClass().getName() +
|
||||
"] is not of type ConfigurableWebApplicationContext");
|
||||
}
|
||||
|
||||
ConfigurableWebApplicationContext wac =
|
||||
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());
|
||||
wac.setParent(parent);
|
||||
wac.setServletContext(getServletContext());
|
||||
wac.setServletConfig(getServletConfig());
|
||||
wac.setNamespace(getNamespace());
|
||||
wac.setConfigLocation(getContextConfigLocation());
|
||||
wac.addApplicationListener(new SourceFilteringListener(wac, this));
|
||||
|
||||
postProcessWebApplicationContext(wac);
|
||||
wac.refresh();
|
||||
|
||||
return wac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the given WebApplicationContext before it is refreshed
|
||||
* and activated as context for this servlet.
|
||||
* <p>The default implementation is empty. <code>refresh()</code> will
|
||||
* be called automatically after this method returns.
|
||||
* @param wac the configured WebApplicationContext (not refreshed yet)
|
||||
* @see #createWebApplicationContext
|
||||
* @see ConfigurableWebApplicationContext#refresh()
|
||||
*/
|
||||
protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ServletContext attribute name for this servlet's WebApplicationContext.
|
||||
* <p>The default implementation returns
|
||||
* <code>SERVLET_CONTEXT_PREFIX + servlet name</code>.
|
||||
* @see #SERVLET_CONTEXT_PREFIX
|
||||
* @see #getServletName
|
||||
*/
|
||||
public String getServletContextAttributeName() {
|
||||
return SERVLET_CONTEXT_PREFIX + getServletName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this servlet's WebApplicationContext.
|
||||
*/
|
||||
public final WebApplicationContext getWebApplicationContext() {
|
||||
return this.webApplicationContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method will be invoked after any bean properties have been set and
|
||||
* the WebApplicationContext has been loaded. The default implementation is empty;
|
||||
* subclasses may override this method to perform any initialization they require.
|
||||
* @throws ServletException in case of an initialization exception
|
||||
* @throws BeansException if thrown by ApplicationContext methods
|
||||
*/
|
||||
protected void initFrameworkServlet() throws ServletException, BeansException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh this servlet's application context, as well as the
|
||||
* dependent state of the servlet.
|
||||
* @throws BeansException in case of errors
|
||||
* @see #getWebApplicationContext()
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#refresh()
|
||||
*/
|
||||
public void refresh() throws BeansException {
|
||||
WebApplicationContext wac = getWebApplicationContext();
|
||||
if (!(wac instanceof ConfigurableApplicationContext)) {
|
||||
throw new IllegalStateException("WebApplicationContext does not support refresh: " + wac);
|
||||
}
|
||||
((ConfigurableApplicationContext) wac).refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* ApplicationListener endpoint that receives events from this servlet's
|
||||
* WebApplicationContext.
|
||||
* <p>The default implementation calls {@link #onRefresh} in case of a
|
||||
* {@link org.springframework.context.event.ContextRefreshedEvent},
|
||||
* triggering a refresh of this servlet's context-dependent state.
|
||||
* @param event the incoming ApplicationContext event
|
||||
*/
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ContextRefreshedEvent) {
|
||||
this.refreshEventReceived = true;
|
||||
onRefresh(((ContextRefreshedEvent) event).getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method which can be overridden to add servlet-specific refresh work.
|
||||
* Called after successful context refresh.
|
||||
* <p>This implementation is empty.
|
||||
* @param context the current WebApplicationContext
|
||||
* @throws BeansException in case of errors
|
||||
* @see #refresh()
|
||||
*/
|
||||
protected void onRefresh(ApplicationContext context) throws BeansException {
|
||||
// For subclasses: do nothing by default.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegate GET requests to processRequest/doService.
|
||||
* <p>Will also be invoked by HttpServlet's default implementation of <code>doHead</code>,
|
||||
* with a <code>NoBodyResponse</code> that just captures the content length.
|
||||
* @see #doService
|
||||
* @see #doHead
|
||||
*/
|
||||
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
processRequest(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate POST requests to {@link #processRequest}.
|
||||
* @see #doService
|
||||
*/
|
||||
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
processRequest(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate PUT requests to {@link #processRequest}.
|
||||
* @see #doService
|
||||
*/
|
||||
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
processRequest(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate DELETE requests to {@link #processRequest}.
|
||||
* @see #doService
|
||||
*/
|
||||
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
processRequest(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate OPTIONS requests to {@link #processRequest}, if desired.
|
||||
* <p>Applies HttpServlet's standard OPTIONS processing first.
|
||||
* @see #doService
|
||||
*/
|
||||
protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
super.doOptions(request, response);
|
||||
if (this.dispatchOptionsRequest) {
|
||||
processRequest(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate TRACE requests to {@link #processRequest}, if desired.
|
||||
* <p>Applies HttpServlet's standard TRACE processing first.
|
||||
* @see #doService
|
||||
*/
|
||||
protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
super.doTrace(request, response);
|
||||
if (this.dispatchTraceRequest) {
|
||||
processRequest(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process this request, publishing an event regardless of the outcome.
|
||||
* <p>The actual event handling is performed by the abstract
|
||||
* {@link #doService} template method.
|
||||
*/
|
||||
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
Throwable failureCause = null;
|
||||
|
||||
try {
|
||||
doService(request, response);
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
failureCause = ex;
|
||||
throw ex;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
failureCause = ex;
|
||||
throw ex;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
failureCause = ex;
|
||||
throw new NestedServletException("Request processing failed", ex);
|
||||
}
|
||||
|
||||
finally {
|
||||
if (failureCause != null) {
|
||||
this.logger.debug("Could not complete request", failureCause);
|
||||
}
|
||||
else {
|
||||
this.logger.debug("Successfully completed request");
|
||||
}
|
||||
if (this.publishEvents) {
|
||||
// Whether or not we succeeded, publish an event.
|
||||
long processingTime = System.currentTimeMillis() - startTime;
|
||||
this.webApplicationContext.publishEvent(
|
||||
new ServletRequestHandledEvent(this,
|
||||
request.getRequestURI(), request.getRemoteAddr(),
|
||||
request.getMethod(), getServletConfig().getServletName(),
|
||||
WebUtils.getSessionId(request), getUsernameForRequest(request),
|
||||
processingTime, failureCause));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the username for the given request.
|
||||
* <p>The default implementation takes the name of the UserPrincipal, if any.
|
||||
* Can be overridden in subclasses.
|
||||
* @param request current HTTP request
|
||||
* @return the username, or <code>null</code> if none found
|
||||
* @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
|
||||
*/
|
||||
protected String getUsernameForRequest(HttpServletRequest request) {
|
||||
Principal userPrincipal = request.getUserPrincipal();
|
||||
return (userPrincipal != null ? userPrincipal.getName() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to do the work of request handling,
|
||||
* receiving a centralized callback for GET, POST, PUT and DELETE.
|
||||
* <p>The contract is essentially the same as that for the commonly overridden
|
||||
* <code>doGet</code> or <code>doPost</code> methods of HttpServlet.
|
||||
* <p>This class intercepts calls to ensure that exception handling and
|
||||
* event publication takes place.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @throws Exception in case of any kind of processing failure
|
||||
* @see javax.servlet.http.HttpServlet#doGet
|
||||
* @see javax.servlet.http.HttpServlet#doPost
|
||||
*/
|
||||
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception;
|
||||
|
||||
|
||||
/**
|
||||
* Close the WebApplicationContext of this servlet.
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#close()
|
||||
*/
|
||||
public void destroy() {
|
||||
getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
|
||||
if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) this.webApplicationContext).close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* MVC framework SPI interface, allowing parameterization of core MVC workflow.
|
||||
*
|
||||
* <p>Interface that must be implemented for each handler type to handle a request.
|
||||
* This interface is used to allow the DispatcherServlet to be indefinitely
|
||||
* extensible. The DispatcherServlet accesses all installed handlers through this
|
||||
* interface, meaning that it does not contain code specific to any handler type.
|
||||
*
|
||||
* <p>Note that a handler can be of type Object. This is to enable handlers from
|
||||
* other frameworks to be integrated with this framework without custom coding.
|
||||
*
|
||||
* <p>This interface is not intended for application developers. It is available
|
||||
* to handlers who want to develop their own web workflow.
|
||||
*
|
||||
* <p>Note: Implementations can implement the Ordered interface to be able to
|
||||
* specify a sorting order and thus a priority for getting applied by
|
||||
* DispatcherServlet. Non-Ordered instances get treated as lowest priority.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
|
||||
* @see org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter
|
||||
* @see org.springframework.web.servlet.handler.SimpleServletHandlerAdapter
|
||||
*/
|
||||
public interface HandlerAdapter {
|
||||
|
||||
/**
|
||||
* Given a handler instance, return whether or not this HandlerAdapter can
|
||||
* support it. Typical HandlerAdapters will base the decision on the handler
|
||||
* type. HandlerAdapters will usually only support one handler type each.
|
||||
* <p>A typical implementation:
|
||||
* <p><code>
|
||||
* return (handler instanceof MyHandler);
|
||||
* </code>
|
||||
* @param handler handler object to check
|
||||
* @return whether or not this object can use the given handler
|
||||
*/
|
||||
boolean supports(Object handler);
|
||||
|
||||
/**
|
||||
* Use the given handler to handle this request.
|
||||
* The workflow that is required may vary widely.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler handler to use. This object must have previously been passed
|
||||
* to the <code>supports</code> method of this interface, which must have
|
||||
* returned <code>true</code>.
|
||||
* @throws Exception in case of errors
|
||||
* @return ModelAndView object with the name of the view and the required
|
||||
* model data, or <code>null</code> if the request has been handled directly
|
||||
*/
|
||||
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
|
||||
|
||||
/**
|
||||
* Same contract as for HttpServlet's <code>getLastModified</code> method.
|
||||
* Can simply return -1 if there's no support in the handler class.
|
||||
* @param request current HTTP request
|
||||
* @param handler handler to use
|
||||
* @return the lastModified value for the given handler
|
||||
* @see javax.servlet.http.HttpServlet#getLastModified
|
||||
* @see org.springframework.web.servlet.mvc.LastModified#getLastModified
|
||||
*/
|
||||
long getLastModified(HttpServletRequest request, Object handler);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by objects than can resolve exceptions thrown
|
||||
* during handler mapping or execution, in the typical case to error views.
|
||||
* Implementors are typically registered as beans in the application context.
|
||||
*
|
||||
* <p>Error views are analogous to the error page JSPs, but can be used with
|
||||
* any kind of exception including any checked exception, with potentially
|
||||
* fine-granular mappings for specific handlers.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 22.11.2003
|
||||
*/
|
||||
public interface HandlerExceptionResolver {
|
||||
|
||||
/**
|
||||
* Try to resolve the given exception that got thrown during on handler execution,
|
||||
* returning a ModelAndView that represents a specific error page if appropriate.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the
|
||||
* time of the exception (for example, if multipart resolution failed)
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @return a corresponding ModelAndView to forward to, or <code>null</code> for default processing
|
||||
*/
|
||||
ModelAndView resolveException(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Handler execution chain, consisting of handler object and any handler interceptors.
|
||||
* Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 20.06.2003
|
||||
* @see HandlerInterceptor
|
||||
*/
|
||||
public class HandlerExecutionChain {
|
||||
|
||||
private final Object handler;
|
||||
|
||||
private HandlerInterceptor[] interceptors;
|
||||
|
||||
private List interceptorList;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new HandlerExecutionChain.
|
||||
* @param handler the handler object to execute
|
||||
*/
|
||||
public HandlerExecutionChain(Object handler) {
|
||||
this(handler, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new HandlerExecutionChain.
|
||||
* @param handler the handler object to execute
|
||||
* @param interceptors the array of interceptors to apply
|
||||
* (in the given order) before the handler itself executes
|
||||
*/
|
||||
public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) {
|
||||
if (handler instanceof HandlerExecutionChain) {
|
||||
HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
|
||||
this.handler = originalChain.getHandler();
|
||||
this.interceptorList = new ArrayList();
|
||||
CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
|
||||
CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
|
||||
}
|
||||
else {
|
||||
this.handler = handler;
|
||||
this.interceptors = interceptors;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the handler object to execute.
|
||||
* @return the handler object
|
||||
*/
|
||||
public Object getHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
public void addInterceptor(HandlerInterceptor interceptor) {
|
||||
initInterceptorList();
|
||||
this.interceptorList.add(interceptor);
|
||||
}
|
||||
|
||||
public void addInterceptors(HandlerInterceptor[] interceptors) {
|
||||
if (interceptors != null) {
|
||||
initInterceptorList();
|
||||
for (int i = 0; i < interceptors.length; i++) {
|
||||
this.interceptorList.add(interceptors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initInterceptorList() {
|
||||
if (this.interceptorList == null) {
|
||||
this.interceptorList = new ArrayList();
|
||||
}
|
||||
if (this.interceptors != null) {
|
||||
for (int i = 0; i < this.interceptors.length; i++) {
|
||||
this.interceptorList.add(this.interceptors[i]);
|
||||
}
|
||||
this.interceptors = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array of interceptors to apply (in the given order).
|
||||
* @return the array of HandlerInterceptors instances (may be <code>null</code>)
|
||||
*/
|
||||
public HandlerInterceptor[] getInterceptors() {
|
||||
if (this.interceptors == null && this.interceptorList != null) {
|
||||
this.interceptors = (HandlerInterceptor[])
|
||||
this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
|
||||
}
|
||||
return this.interceptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to the handler's <code>toString()</code>.
|
||||
*/
|
||||
public String toString() {
|
||||
return String.valueOf(handler);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Workflow interface that allows for customized handler execution chains.
|
||||
* Applications can register any number of existing or custom interceptors
|
||||
* for certain groups of handlers, to add common preprocessing behavior
|
||||
* without needing to modify each handler implementation.
|
||||
*
|
||||
* <p>A HandlerInterceptor gets called before the appropriate HandlerAdapter
|
||||
* triggers the execution of the handler itself. This mechanism can be used
|
||||
* for a large field of preprocessing aspects, e.g. for authorization checks,
|
||||
* or common handler behavior like locale or theme changes. Its main purpose
|
||||
* is to allow for factoring out repetitive handler code.
|
||||
*
|
||||
* <p>Typically an interceptor chain is defined per HandlerMapping bean,
|
||||
* sharing its granularity. To be able to apply a certain interceptor chain
|
||||
* to a group of handlers, one needs to map the desired handlers via one
|
||||
* HandlerMapping bean. The interceptors themselves are defined as beans
|
||||
* in the application context, referenced by the mapping bean definition
|
||||
* via its "interceptors" property (in XML: a <list> of <ref>).
|
||||
*
|
||||
* <p>HandlerInterceptor is basically similar to a Servlet 2.3 Filter, but in
|
||||
* contrast to the latter it just allows custom pre-processing with the option
|
||||
* of prohibiting the execution of the handler itself, and custom post-processing.
|
||||
* Filters are more powerful, for example they allow for exchanging the request
|
||||
* and response objects that are handed down the chain. Note that a filter
|
||||
* gets configured in web.xml, a HandlerInterceptor in the application context.
|
||||
*
|
||||
* <p>As a basic guideline, fine-grained handler-related preprocessing tasks are
|
||||
* candidates for HandlerInterceptor implementations, especially factored-out
|
||||
* common handler code and authorization checks. On the other hand, a Filter
|
||||
* is well-suited for request content and view content handling, like multipart
|
||||
* forms and GZIP compression. This typically shows when one needs to map the
|
||||
* filter to certain content types (e.g. images), or to all requests.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 20.06.2003
|
||||
* @see HandlerExecutionChain#getInterceptors
|
||||
* @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter
|
||||
* @see org.springframework.web.servlet.handler.AbstractHandlerMapping#setInterceptors
|
||||
* @see org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor
|
||||
* @see org.springframework.web.servlet.i18n.LocaleChangeInterceptor
|
||||
* @see org.springframework.web.servlet.theme.ThemeChangeInterceptor
|
||||
* @see javax.servlet.Filter
|
||||
*/
|
||||
public interface HandlerInterceptor {
|
||||
|
||||
/**
|
||||
* Intercept the execution of a handler. Called after HandlerMapping determined
|
||||
* an appropriate handler object, but before HandlerAdapter invokes the handler.
|
||||
* <p>DispatcherServlet processes a handler in an execution chain, consisting
|
||||
* of any number of interceptors, with the handler itself at the end.
|
||||
* With this method, each interceptor can decide to abort the execution chain,
|
||||
* typically sending a HTTP error or writing a custom response.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler chosen handler to execute, for type and/or instance evaluation
|
||||
* @return <code>true</code> if the execution chain should proceed with the
|
||||
* next interceptor or the handler itself. Else, DispatcherServlet assumes
|
||||
* that this interceptor has already dealt with the response itself.
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Intercept the execution of a handler. Called after HandlerAdapter actually
|
||||
* invoked the handler, but before the DispatcherServlet renders the view.
|
||||
* Can expose additional model objects to the view via the given ModelAndView.
|
||||
* <p>DispatcherServlet processes a handler in an execution chain, consisting
|
||||
* of any number of interceptors, with the handler itself at the end.
|
||||
* With this method, each interceptor can post-process an execution,
|
||||
* getting applied in inverse order of the execution chain.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler chosen handler to execute, for type and/or instance examination
|
||||
* @param modelAndView the <code>ModelAndView</code> that the handler returned
|
||||
* (can also be <code>null</code>)
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
void postHandle(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Callback after completion of request processing, that is, after rendering
|
||||
* the view. Will be called on any outcome of handler execution, thus allows
|
||||
* for proper resource cleanup.
|
||||
* <p>Note: Will only be called if this interceptor's <code>preHandle</code>
|
||||
* method has successfully completed and returned <code>true</code>!
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler chosen handler to execute, for type and/or instance examination
|
||||
* @param ex exception thrown on handler execution, if any
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
void afterCompletion(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
|
||||
throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by objects that define a mapping between
|
||||
* requests and handler objects.
|
||||
*
|
||||
* <p>This class can be implemented by application developers, although this is not
|
||||
* necessary, as {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}
|
||||
* and {@link org.springframework.web.servlet.handler.SimpleUrlHandlerMapping}
|
||||
* are included in the framework. The former is the default if no
|
||||
* HandlerMapping bean is registered in the application context.
|
||||
*
|
||||
* <p>HandlerMapping implementations can support mapped interceptors but do not
|
||||
* have to. A handler will always be wrapped in a {@link HandlerExecutionChain}
|
||||
* instance, optionally accompanied by some {@link HandlerInterceptor} instances.
|
||||
* The DispatcherServlet will first call each HandlerInterceptor's
|
||||
* <code>preHandle</code> method in the given order, finally invoking the handler
|
||||
* itself if all <code>preHandle</code> methods have returned <code>true</code>.
|
||||
*
|
||||
* <p>The ability to parameterize this mapping is a powerful and unusual
|
||||
* capability of this MVC framework. For example, it is possible to write
|
||||
* a custom mapping based on session state, cookie state or many other
|
||||
* variables. No other MVC framework seems to be equally flexible.
|
||||
*
|
||||
* <p>Note: Implementations can implement the {@link org.springframework.core.Ordered}
|
||||
* interface to be able to specify a sorting order and thus a priority for getting
|
||||
* applied by DispatcherServlet. Non-Ordered instances get treated as lowest priority.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see org.springframework.core.Ordered
|
||||
* @see org.springframework.web.servlet.handler.AbstractHandlerMapping
|
||||
* @see org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
|
||||
* @see org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
|
||||
*/
|
||||
public interface HandlerMapping {
|
||||
|
||||
/**
|
||||
* Name of the {@link HttpServletRequest} attribute that contains the path
|
||||
* within the handler mapping, in case of a pattern match, or the full
|
||||
* relevant URI (typically within the DispatcherServlet's mapping) else.
|
||||
* <p>Note: This attribute is not required to be supported by all
|
||||
* HandlerMapping implementations. URL-based HandlerMappings will
|
||||
* typically support it, but handlers should not necessarily expect
|
||||
* this request attribute to be present in all scenarios.
|
||||
*/
|
||||
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
|
||||
|
||||
|
||||
/**
|
||||
* Return a handler and any interceptors for this request. The choice may be made
|
||||
* on request URL, session state, or any factor the implementing class chooses.
|
||||
* <p>The returned HandlerExecutionChain contains a handler Object, rather than
|
||||
* even a tag interface, so that handlers are not constrained in any way.
|
||||
* For example, a HandlerAdapter could be written to allow another framework's
|
||||
* handler objects to be used.
|
||||
* <p>Returns <code>null</code> if no match was found. This is not an error.
|
||||
* The DispatcherServlet will query all registered HandlerMapping beans to find
|
||||
* a match, and only decide there is an error if none can find a handler.
|
||||
* @param request current HTTP request
|
||||
* @return a HandlerExecutionChain instance containing handler object and
|
||||
* any interceptors, or <code>null</code> if no mapping found
|
||||
* @throws Exception if there is an internal error
|
||||
*/
|
||||
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.support.ServletContextResourceLoader;
|
||||
|
||||
/**
|
||||
* Simple extension of {@link javax.servlet.http.HttpServlet} which treats
|
||||
* its config parameters (<code>init-param</code> entries within the
|
||||
* <code>servlet</code> tag in <code>web.xml</code>) as bean properties.
|
||||
*
|
||||
* <p>A handy superclass for any type of servlet. Type conversion of config
|
||||
* parameters is automatic, with the corresponding setter method getting
|
||||
* invoked with the converted value. It is also possible for subclasses to
|
||||
* specify required properties. Parameters without matching bean property
|
||||
* setter will simply be ignored.
|
||||
*
|
||||
* <p>This servlet leaves request handling to subclasses, inheriting the default
|
||||
* behavior of HttpServlet (<code>doGet</code>, <code>doPost</code>, etc).
|
||||
*
|
||||
* <p>This generic servlet base class has no dependency on the Spring
|
||||
* {@link org.springframework.context.ApplicationContext} concept. Simple
|
||||
* servlets usually don't load their own context but rather access service
|
||||
* beans from the Spring root application context, accessible via the
|
||||
* filter's {@link #getServletContext() ServletContext} (see
|
||||
* {@link org.springframework.web.context.support.WebApplicationContextUtils}).
|
||||
*
|
||||
* <p>The {@link FrameworkServlet} class is a more specific servlet base
|
||||
* class which loads its own application context. FrameworkServlet serves
|
||||
* as direct base class of Spring's full-fledged {@link DispatcherServlet}.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see #addRequiredProperty
|
||||
* @see #initServletBean
|
||||
* @see #doGet
|
||||
* @see #doPost
|
||||
*/
|
||||
public abstract class HttpServletBean extends HttpServlet {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* Set of required properties (Strings) that must be supplied as
|
||||
* config parameters to this servlet.
|
||||
*/
|
||||
private final Set requiredProperties = new HashSet();
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses can invoke this method to specify that this property
|
||||
* (which must match a JavaBean property they expose) is mandatory,
|
||||
* and must be supplied as a config parameter. This should be called
|
||||
* from the constructor of a subclass.
|
||||
* <p>This method is only relevant in case of traditional initialization
|
||||
* driven by a ServletConfig instance.
|
||||
* @param property name of the required property
|
||||
*/
|
||||
protected final void addRequiredProperty(String property) {
|
||||
this.requiredProperties.add(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map config parameters onto bean properties of this servlet, and
|
||||
* invoke subclass initialization.
|
||||
* @throws ServletException if bean properties are invalid (or required
|
||||
* properties are missing), or if subclass initialization fails.
|
||||
*/
|
||||
public final void init() throws ServletException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Initializing servlet '" + getServletName() + "'");
|
||||
}
|
||||
|
||||
// Set bean properties from init parameters.
|
||||
try {
|
||||
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
|
||||
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
|
||||
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
|
||||
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader));
|
||||
initBeanWrapper(bw);
|
||||
bw.setPropertyValues(pvs, true);
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
// Let subclasses do whatever initialization they like.
|
||||
initServletBean();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Servlet '" + getServletName() + "' configured successfully");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the BeanWrapper for this HttpServletBean,
|
||||
* possibly with custom editors.
|
||||
* <p>This default implementation is empty.
|
||||
* @param bw the BeanWrapper to initialize
|
||||
* @throws BeansException if thrown by BeanWrapper methods
|
||||
* @see org.springframework.beans.BeanWrapper#registerCustomEditor
|
||||
*/
|
||||
protected void initBeanWrapper(BeanWrapper bw) throws BeansException {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overridden method that simply returns <code>null</code> when no
|
||||
* ServletConfig set yet.
|
||||
* @see #getServletConfig()
|
||||
*/
|
||||
public final String getServletName() {
|
||||
return (getServletConfig() != null ? getServletConfig().getServletName() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden method that simply returns <code>null</code> when no
|
||||
* ServletConfig set yet.
|
||||
* @see #getServletConfig()
|
||||
*/
|
||||
public final ServletContext getServletContext() {
|
||||
return (getServletConfig() != null ? getServletConfig().getServletContext() : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses may override this to perform custom initialization.
|
||||
* All bean properties of this servlet will have been set before this
|
||||
* method is invoked.
|
||||
* <p>This default implementation is empty.
|
||||
* @throws ServletException if subclass initialization fails
|
||||
*/
|
||||
protected void initServletBean() throws ServletException {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* PropertyValues implementation created from ServletConfig init parameters.
|
||||
*/
|
||||
private static class ServletConfigPropertyValues extends MutablePropertyValues {
|
||||
|
||||
/**
|
||||
* Create new ServletConfigPropertyValues.
|
||||
* @param config ServletConfig we'll use to take PropertyValues from
|
||||
* @param requiredProperties set of property names we need, where
|
||||
* we can't accept default values
|
||||
* @throws ServletException if any required properties are missing
|
||||
*/
|
||||
public ServletConfigPropertyValues(ServletConfig config, Set requiredProperties)
|
||||
throws ServletException {
|
||||
|
||||
Set missingProps = (requiredProperties != null && !requiredProperties.isEmpty()) ?
|
||||
new HashSet(requiredProperties) : null;
|
||||
|
||||
Enumeration en = config.getInitParameterNames();
|
||||
while (en.hasMoreElements()) {
|
||||
String property = (String) en.nextElement();
|
||||
Object value = config.getInitParameter(property);
|
||||
addPropertyValue(new PropertyValue(property, value));
|
||||
if (missingProps != null) {
|
||||
missingProps.remove(property);
|
||||
}
|
||||
}
|
||||
|
||||
// Fail if we are still missing properties.
|
||||
if (missingProps != null && missingProps.size() > 0) {
|
||||
throw new ServletException(
|
||||
"Initialization from ServletConfig for servlet '" + config.getServletName() +
|
||||
"' failed; the following required properties were missing: " +
|
||||
StringUtils.collectionToDelimitedString(missingProps, ", "));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Interface for web-based locale resolution strategies that allows for
|
||||
* both locale resolution via the request and locale modification via
|
||||
* request and response.
|
||||
*
|
||||
* <p>This interface allows for implementations based on request, session,
|
||||
* cookies, etc. The default implementation is AcceptHeaderLocaleResolver,
|
||||
* simply using the request's locale provided by the respective HTTP header.
|
||||
*
|
||||
* <p>Use <code>RequestContext.getLocale()</code> to retrieve the current locale
|
||||
* in controllers or views, independent of the actual resolution strategy.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 27.02.2003
|
||||
* @see org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
|
||||
* @see org.springframework.web.servlet.support.RequestContext#getLocale
|
||||
*/
|
||||
public interface LocaleResolver {
|
||||
|
||||
/**
|
||||
* Resolve the current locale via the given request.
|
||||
* Should return a default locale as fallback in any case.
|
||||
* @param request the request to resolve the locale for
|
||||
* @return the current locale (never <code>null</code>)
|
||||
*/
|
||||
Locale resolveLocale(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Set the current locale to the given one.
|
||||
* @param request the request to be used for locale modification
|
||||
* @param response the response to be used for locale modification
|
||||
* @param locale the new locale, or <code>null</code> to clear the locale
|
||||
* @throws UnsupportedOperationException if the LocaleResolver implementation
|
||||
* does not support dynamic changing of the theme
|
||||
*/
|
||||
void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ui.ModelMap;
|
||||
|
||||
/**
|
||||
* Holder for both Model and View in the web MVC framework.
|
||||
* Note that these are entirely distinct. This class merely holds
|
||||
* both to make it possible for a controller to return both model
|
||||
* and view in a single return value.
|
||||
*
|
||||
* <p>Represents a model and view returned by a handler, to be resolved
|
||||
* by a DispatcherServlet. The view can take the form of a String
|
||||
* view name which will need to be resolved by a ViewResolver object;
|
||||
* alternatively a View object can be specified directly. The model
|
||||
* is a Map, allowing the use of multiple objects keyed by name.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @see DispatcherServlet
|
||||
* @see ViewResolver
|
||||
* @see HandlerAdapter#handle
|
||||
* @see org.springframework.web.servlet.mvc.Controller#handleRequest
|
||||
*/
|
||||
public class ModelAndView {
|
||||
|
||||
/** View instance or view name String */
|
||||
private Object view;
|
||||
|
||||
/** Model Map */
|
||||
private ModelMap model;
|
||||
|
||||
/**
|
||||
* Indicates whether or not this instance has been cleared with a call to {@link #clear()}.
|
||||
*/
|
||||
private boolean cleared;
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor for bean-style usage: populating bean
|
||||
* properties instead of passing in constructor arguments.
|
||||
* @see #setView(View)
|
||||
* @see #setViewName(String)
|
||||
*/
|
||||
public ModelAndView() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor when there is no model data to expose.
|
||||
* Can also be used in conjunction with <code>addObject</code>.
|
||||
* @param viewName name of the View to render, to be resolved
|
||||
* by the DispatcherServlet's ViewResolver
|
||||
* @see #addObject
|
||||
*/
|
||||
public ModelAndView(String viewName) {
|
||||
this.view = viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor when there is no model data to expose.
|
||||
* Can also be used in conjunction with <code>addObject</code>.
|
||||
* @param view View object to render
|
||||
* @see #addObject
|
||||
*/
|
||||
public ModelAndView(View view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new ModelAndView given a view name and a model.
|
||||
* @param viewName name of the View to render, to be resolved
|
||||
* by the DispatcherServlet's ViewResolver
|
||||
* @param model Map of model names (Strings) to model objects
|
||||
* (Objects). Model entries may not be <code>null</code>, but the
|
||||
* model Map may be <code>null</code> if there is no model data.
|
||||
*/
|
||||
public ModelAndView(String viewName, Map model) {
|
||||
this.view = viewName;
|
||||
if (model != null) {
|
||||
getModelMap().addAllAttributes(model);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new ModelAndView given a View object and a model.
|
||||
* <emphasis>Note: the supplied model data is copied into the internal
|
||||
* storage of this class. You should not consider to modify the supplied
|
||||
* Map after supplying it to this class</emphasis>
|
||||
* @param view View object to render
|
||||
* @param model Map of model names (Strings) to model objects
|
||||
* (Objects). Model entries may not be <code>null</code>, but the
|
||||
* model Map may be <code>null</code> if there is no model data.
|
||||
*/
|
||||
public ModelAndView(View view, Map model) {
|
||||
this.view = view;
|
||||
if (model != null) {
|
||||
getModelMap().addAllAttributes(model);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor to take a single model object.
|
||||
* @param viewName name of the View to render, to be resolved
|
||||
* by the DispatcherServlet's ViewResolver
|
||||
* @param modelName name of the single entry in the model
|
||||
* @param modelObject the single model object
|
||||
*/
|
||||
public ModelAndView(String viewName, String modelName, Object modelObject) {
|
||||
this.view = viewName;
|
||||
addObject(modelName, modelObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor to take a single model object.
|
||||
* @param view View object to render
|
||||
* @param modelName name of the single entry in the model
|
||||
* @param modelObject the single model object
|
||||
*/
|
||||
public ModelAndView(View view, String modelName, Object modelObject) {
|
||||
this.view = view;
|
||||
addObject(modelName, modelObject);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a view name for this ModelAndView, to be resolved by the
|
||||
* DispatcherServlet via a ViewResolver. Will override any
|
||||
* pre-existing view name or View.
|
||||
*/
|
||||
public void setViewName(String viewName) {
|
||||
this.view = viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the view name to be resolved by the DispatcherServlet
|
||||
* via a ViewResolver, or <code>null</code> if we are using a View object.
|
||||
*/
|
||||
public String getViewName() {
|
||||
return (this.view instanceof String ? (String) this.view : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a View object for this ModelAndView. Will override any
|
||||
* pre-existing view name or View.
|
||||
*/
|
||||
public void setView(View view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the View object, or <code>null</code> if we are using a view name
|
||||
* to be resolved by the DispatcherServlet via a ViewResolver.
|
||||
*/
|
||||
public View getView() {
|
||||
return (this.view instanceof View ? (View) this.view : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether or not this <code>ModelAndView</code> has a view, either
|
||||
* as a view name or as a direct {@link View} instance.
|
||||
*/
|
||||
public boolean hasView() {
|
||||
return (this.view != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether we use a view reference, i.e. <code>true</code>
|
||||
* if the view has been specified via a name to be resolved by the
|
||||
* DispatcherServlet via a ViewResolver.
|
||||
*/
|
||||
public boolean isReference() {
|
||||
return (this.view instanceof String);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the model map. May return <code>null</code>.
|
||||
* Called by DispatcherServlet for evaluation of the model.
|
||||
*/
|
||||
protected Map getModelInternal() {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying <code>ModelMap</code> instance (never <code>null</code>).
|
||||
*/
|
||||
public ModelMap getModelMap() {
|
||||
if (this.model == null) {
|
||||
this.model = new ModelMap();
|
||||
}
|
||||
return this.model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the model map. Never returns <code>null</code>.
|
||||
* To be called by application code for modifying the model.
|
||||
*/
|
||||
public Map getModel() {
|
||||
return getModelMap();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add an attribute to the model.
|
||||
* @param attributeName name of the object to add to the model
|
||||
* @param attributeValue object to add to the model (never <code>null</code>)
|
||||
* @see ModelMap#addAttribute(String, Object)
|
||||
* @see #getModelMap()
|
||||
*/
|
||||
public ModelAndView addObject(String attributeName, Object attributeValue) {
|
||||
getModelMap().addAttribute(attributeName, attributeValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an attribute to the model using parameter name generation.
|
||||
* @param attributeValue the object to add to the model (never <code>null</code>)
|
||||
* @see ModelMap#addAttribute(Object)
|
||||
* @see #getModelMap()
|
||||
*/
|
||||
public ModelAndView addObject(Object attributeValue) {
|
||||
getModelMap().addAttribute(attributeValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all attributes contained in the provided Map to the model.
|
||||
* @param modelMap a Map of attributeName -> attributeValue pairs
|
||||
* @see ModelMap#addAllAttributes(Map)
|
||||
* @see #getModelMap()
|
||||
*/
|
||||
public ModelAndView addAllObjects(Map modelMap) {
|
||||
getModelMap().addAllAttributes(modelMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear the state of this ModelAndView object.
|
||||
* The object will be empty afterwards.
|
||||
* <p>Can be used to suppress rendering of a given ModelAndView object
|
||||
* in the <code>postHandle</code> method of a HandlerInterceptor.
|
||||
* @see #isEmpty()
|
||||
* @see HandlerInterceptor#postHandle
|
||||
*/
|
||||
public void clear() {
|
||||
this.view = null;
|
||||
this.model = null;
|
||||
this.cleared = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this ModelAndView object is empty
|
||||
* i.e. whether it does not hold any view and does not contain a model.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return (this.view == null && this.model == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this ModelAndView object is empty as a result of a call to {@link #clear}
|
||||
* i.e. whether it does not hold any view and does not contain a model.
|
||||
* Returns <code>false</code> if any additional state was added to the instance
|
||||
* <strong>after</strong> the call to {@link #clear}.
|
||||
* @see #clear()
|
||||
*/
|
||||
public boolean wasCleared() {
|
||||
return (this.cleared && isEmpty());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return diagnostic information about this model and view.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer("ModelAndView: ");
|
||||
if (isReference()) {
|
||||
buf.append("reference to view with name '").append(this.view).append("'");
|
||||
}
|
||||
else {
|
||||
buf.append("materialized View is [").append(this.view).append(']');
|
||||
}
|
||||
buf.append("; model is ").append(this.model);
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2002-2005 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Exception to be thrown on error conditions that should forward
|
||||
* to a specific view with a specific model.
|
||||
*
|
||||
* <p>Can be thrown at any time during handler processing.
|
||||
* This includes any template methods of pre-built controllers.
|
||||
* For example, a form controller might abort to a specific error page
|
||||
* if certain parameters do not allow to proceed with the normal workflow.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 22.11.2003
|
||||
*/
|
||||
public class ModelAndViewDefiningException extends ServletException {
|
||||
|
||||
private ModelAndView modelAndView;
|
||||
|
||||
|
||||
/**
|
||||
* Create new ModelAndViewDefiningException with the given ModelAndView,
|
||||
* typically representing a specific error page.
|
||||
* @param modelAndView ModelAndView with view to forward to and model to expose
|
||||
*/
|
||||
public ModelAndViewDefiningException(ModelAndView modelAndView) {
|
||||
Assert.notNull(modelAndView, "ModelAndView must not be null in ModelAndViewDefiningException");
|
||||
this.modelAndView = modelAndView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ModelAndView that this exception contains for forwarding to.
|
||||
*/
|
||||
public ModelAndView getModelAndView() {
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Strategy interface for translating an incoming
|
||||
* {@link javax.servlet.http.HttpServletRequest} into a
|
||||
* logical view name when no view name is explicitly supplied.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface RequestToViewNameTranslator {
|
||||
|
||||
/**
|
||||
* Translate the given {@link HttpServletRequest} into a view name.
|
||||
* @param request the incoming {@link HttpServletRequest} providing
|
||||
* the context from which a view name is to be resolved
|
||||
* @return the view name (or <code>null</code> if no default found)
|
||||
* @throws Exception if view name translation fails
|
||||
*/
|
||||
String getViewName(HttpServletRequest request) throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.support.ServletContextResource;
|
||||
|
||||
/**
|
||||
* Simple servlet that can expose an internal resource, including a
|
||||
* default URL if the specified resource is not found. An alternative,
|
||||
* for example, to trying and catching exceptions when using JSP include.
|
||||
*
|
||||
* <p>A further usage of this servlet is the ability to apply last-modified
|
||||
* timestamps to quasi-static resources (typically JSPs). This can happen
|
||||
* as bridge to parameter-specified resources, or as proxy for a specific
|
||||
* target resource (or a list of specific target resources to combine).
|
||||
*
|
||||
* <p>A typical usage would map a URL like "/ResourceServlet" onto an instance
|
||||
* of this servlet, and use the "JSP include" action to include this URL,
|
||||
* with the "resource" parameter indicating the actual target path in the WAR.
|
||||
*
|
||||
* <p>The <code>defaultUrl</code> property can be set to the internal
|
||||
* resource path of a default URL, to be rendered when the target resource
|
||||
* is not found or not specified in the first place.
|
||||
*
|
||||
* <p>The "resource" parameter and the <code>defaultUrl</code> property can
|
||||
* also specify a list of target resources to combine. Those resources will be
|
||||
* included one by one to build the response. If last-modified determination
|
||||
* is active, the newest timestamp among those files will be used.
|
||||
*
|
||||
* <p>The <code>allowedResources</code> property can be set to a URL
|
||||
* pattern of resources that should be available via this servlet.
|
||||
* If not set, any target resource can be requested, including resources
|
||||
* in the WEB-INF directory!
|
||||
*
|
||||
* <p>If using this servlet for direct access rather than via includes,
|
||||
* the <code>contentType</code> property should be specified to apply a
|
||||
* proper content type. Note that a content type header in the target JSP will
|
||||
* be ignored when including the resource via a RequestDispatcher include.
|
||||
*
|
||||
* <p>To apply last-modified timestamps for the target resource, set the
|
||||
* <code>applyLastModified</code> property to true. This servlet will then
|
||||
* return the file timestamp of the target resource as last-modified value,
|
||||
* falling back to the startup time of this servlet if not retrievable.
|
||||
*
|
||||
* <p>Note that applying the last-modified timestamp in the above fashion
|
||||
* just makes sense if the target resource does not generate content that
|
||||
* depends on the HttpSession or cookies; it is just allowed to evaluate
|
||||
* request parameters.
|
||||
*
|
||||
* <p>A typical case for such last-modified usage is a JSP that just makes
|
||||
* minimal usage of basic means like includes or message resolution to
|
||||
* build quasi-static content. Regenerating such content on every request
|
||||
* is unnecessary; it can be cached as long as the file hasn't changed.
|
||||
*
|
||||
* <p>Note that this servlet will apply the last-modified timestamp if you
|
||||
* tell it to do so: It's your decision whether the content of the target
|
||||
* resource can be cached in such a fashion. Typical use cases are helper
|
||||
* resources that are not fronted by a controller, like JavaScript files
|
||||
* that are generated by a JSP (without depending on the HttpSession).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rod Johnson
|
||||
* @see #setDefaultUrl
|
||||
* @see #setAllowedResources
|
||||
* @see #setApplyLastModified
|
||||
*/
|
||||
public class ResourceServlet extends HttpServletBean {
|
||||
|
||||
/**
|
||||
* Any number of these characters are considered delimiters
|
||||
* between multiple resource paths in a single String value.
|
||||
*/
|
||||
public static final String RESOURCE_URL_DELIMITERS = ",; \t\n";
|
||||
|
||||
/**
|
||||
* Name of the parameter that must contain the actual resource path.
|
||||
*/
|
||||
public static final String RESOURCE_PARAM_NAME = "resource";
|
||||
|
||||
|
||||
private String defaultUrl;
|
||||
|
||||
private String allowedResources;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private boolean applyLastModified = false;
|
||||
|
||||
private PathMatcher pathMatcher;
|
||||
|
||||
private long startupTime;
|
||||
|
||||
|
||||
/**
|
||||
* Set the URL within the current web application from which to
|
||||
* include content if the requested path isn't found, or if none
|
||||
* is specified in the first place.
|
||||
* <p>If specifying multiple URLs, they will be included one by one
|
||||
* to build the response. If last-modified determination is active,
|
||||
* the newest timestamp among those files will be used.
|
||||
* @see #setApplyLastModified
|
||||
*/
|
||||
public void setDefaultUrl(String defaultUrl) {
|
||||
this.defaultUrl = defaultUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set allowed resources as URL pattern, e.g. "/WEB-INF/res/*.jsp",
|
||||
* The parameter can be any Ant-style pattern parsable by AntPathMatcher.
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
public void setAllowedResources(String allowedResources) {
|
||||
this.allowedResources = allowedResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content type of the target resource (typically a JSP).
|
||||
* Default is none, which is appropriate when including resources.
|
||||
* <p>For directly accessing resources, for example to leverage this
|
||||
* servlet's last-modified support, specify a content type here.
|
||||
* Note that a content type header in the target JSP will be ignored
|
||||
* when including the resource via a RequestDispatcher include.
|
||||
*/
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to apply the file timestamp of the target resource
|
||||
* as last-modified value. Default is "false".
|
||||
* <p>This is mainly intended for JSP targets that don't generate
|
||||
* session-specific or database-driven content: Such files can be
|
||||
* cached by the browser as long as the last-modified timestamp
|
||||
* of the JSP file doesn't change.
|
||||
* <p>This will only work correctly with expanded WAR files that
|
||||
* allow access to the file timestamps. Else, the startup time
|
||||
* of this servlet is returned.
|
||||
*/
|
||||
public void setApplyLastModified(boolean applyLastModified) {
|
||||
this.applyLastModified = applyLastModified;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remember the startup time, using no last-modified time before it.
|
||||
*/
|
||||
protected void initServletBean() {
|
||||
this.pathMatcher = getPathMatcher();
|
||||
this.startupTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a PathMatcher to use for matching the "allowedResources" URL pattern.
|
||||
* Default is AntPathMatcher.
|
||||
* @see #setAllowedResources
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
protected PathMatcher getPathMatcher() {
|
||||
return new AntPathMatcher();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the URL of the target resource and include it.
|
||||
* @see #determineResourceUrl
|
||||
*/
|
||||
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
// determine URL of resource to include
|
||||
String resourceUrl = determineResourceUrl(request);
|
||||
|
||||
if (resourceUrl != null) {
|
||||
try {
|
||||
doInclude(request, response, resourceUrl);
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failed to include content of resource [" + resourceUrl + "]", ex);
|
||||
}
|
||||
// Try including default URL if appropriate.
|
||||
if (!includeDefaultUrl(request, response)) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failed to include content of resource [" + resourceUrl + "]", ex);
|
||||
}
|
||||
// Try including default URL if appropriate.
|
||||
if (!includeDefaultUrl(request, response)) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no resource URL specified -> try to include default URL.
|
||||
else if (!includeDefaultUrl(request, response)) {
|
||||
throw new ServletException("No target resource URL found for request");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the URL of the target resource of this request.
|
||||
* <p>Default implementation returns the value of the "resource" parameter.
|
||||
* Can be overridden in subclasses.
|
||||
* @param request current HTTP request
|
||||
* @return the URL of the target resource, or <code>null</code> if none found
|
||||
* @see #RESOURCE_PARAM_NAME
|
||||
*/
|
||||
protected String determineResourceUrl(HttpServletRequest request) {
|
||||
return request.getParameter(RESOURCE_PARAM_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the specified default URL, if appropriate.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @return whether a default URL was included
|
||||
* @throws ServletException if thrown by the RequestDispatcher
|
||||
* @throws IOException if thrown by the RequestDispatcher
|
||||
*/
|
||||
private boolean includeDefaultUrl(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
if (this.defaultUrl == null) {
|
||||
return false;
|
||||
}
|
||||
doInclude(request, response, this.defaultUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the specified resource via the RequestDispatcher.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param resourceUrl the URL of the target resource
|
||||
* @throws ServletException if thrown by the RequestDispatcher
|
||||
* @throws IOException if thrown by the RequestDispatcher
|
||||
*/
|
||||
private void doInclude(HttpServletRequest request, HttpServletResponse response, String resourceUrl)
|
||||
throws ServletException, IOException {
|
||||
|
||||
if (this.contentType != null) {
|
||||
response.setContentType(this.contentType);
|
||||
}
|
||||
String[] resourceUrls =
|
||||
StringUtils.tokenizeToStringArray(resourceUrl, RESOURCE_URL_DELIMITERS);
|
||||
for (int i = 0; i < resourceUrls.length; i++) {
|
||||
// check whether URL matches allowed resources
|
||||
if (this.allowedResources != null && !this.pathMatcher.match(this.allowedResources, resourceUrls[i])) {
|
||||
throw new ServletException("Resource [" + resourceUrls[i] +
|
||||
"] does not match allowed pattern [" + this.allowedResources + "]");
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Including resource [" + resourceUrls[i] + "]");
|
||||
}
|
||||
RequestDispatcher rd = request.getRequestDispatcher(resourceUrls[i]);
|
||||
rd.include(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last-modified timestamp of the file that corresponds
|
||||
* to the target resource URL (i.e. typically the request ".jsp" file).
|
||||
* Will simply return -1 if "applyLastModified" is false (the default).
|
||||
* <p>Returns no last-modified date before the startup time of this servlet,
|
||||
* to allow for message resolution etc that influences JSP contents,
|
||||
* assuming that those background resources might have changed on restart.
|
||||
* <p>Returns the startup time of this servlet if the file that corresponds
|
||||
* to the target resource URL coudln't be resolved (for example, because
|
||||
* the WAR is not expanded).
|
||||
* @see #determineResourceUrl
|
||||
* @see #getFileTimestamp
|
||||
*/
|
||||
protected final long getLastModified(HttpServletRequest request) {
|
||||
if (this.applyLastModified) {
|
||||
String resourceUrl = determineResourceUrl(request);
|
||||
if (resourceUrl == null) {
|
||||
resourceUrl = this.defaultUrl;
|
||||
}
|
||||
if (resourceUrl != null) {
|
||||
String[] resourceUrls = StringUtils.tokenizeToStringArray(resourceUrl, RESOURCE_URL_DELIMITERS);
|
||||
long latestTimestamp = -1;
|
||||
for (int i = 0; i < resourceUrls.length; i++) {
|
||||
long timestamp = getFileTimestamp(resourceUrls[i]);
|
||||
if (timestamp > latestTimestamp) {
|
||||
latestTimestamp = timestamp;
|
||||
}
|
||||
}
|
||||
return (latestTimestamp > this.startupTime ? latestTimestamp : this.startupTime);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file timestamp for the given resource.
|
||||
* @param resourceUrl the URL of the resource
|
||||
* @return the file timestamp in milliseconds, or -1 if not determinable
|
||||
*/
|
||||
protected long getFileTimestamp(String resourceUrl) {
|
||||
ServletContextResource resource = new ServletContextResource(getServletContext(), resourceUrl);
|
||||
try {
|
||||
long lastModifiedTime = resource.lastModified();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Last-modified timestamp of " + resource + " is " + lastModifiedTime);
|
||||
}
|
||||
return lastModifiedTime;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.warn("Couldn't retrieve last-modified timestamp of [" + resource +
|
||||
"] - using ResourceServlet startup time");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2002-2005 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Interface for web-based theme resolution strategies that allows for
|
||||
* both theme resolution via the request and theme modification via
|
||||
* request and response.
|
||||
*
|
||||
* <p>This interface allows for implementations based on session,
|
||||
* cookies, etc. The default implementation is FixedThemeResolver,
|
||||
* simply using a configured default theme.
|
||||
*
|
||||
* <p>Note that this resolver is only responsible for determining the
|
||||
* current theme name. The Theme instance for the resolved theme name
|
||||
* gets looked up by DispatcherServlet via the respective ThemeSource,
|
||||
* i.e. the current WebApplicationContext.
|
||||
*
|
||||
* <p>Use RequestContext.getTheme() to retrieve the current theme in
|
||||
* controllers or views, independent of the actual resolution strategy.
|
||||
*
|
||||
* @author Jean-Pierre Pawlak
|
||||
* @author Juergen Hoeller
|
||||
* @since 17.06.2003
|
||||
* @see org.springframework.web.servlet.theme.FixedThemeResolver
|
||||
* @see org.springframework.ui.context.Theme
|
||||
* @see org.springframework.ui.context.ThemeSource
|
||||
* @see org.springframework.web.servlet.support.RequestContext#getTheme
|
||||
*/
|
||||
public interface ThemeResolver {
|
||||
|
||||
/**
|
||||
* Resolve the current theme name via the given request.
|
||||
* Should return a default theme as fallback in any case.
|
||||
* @param request request to be used for resolution
|
||||
* @return the current theme name
|
||||
*/
|
||||
String resolveThemeName(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Set the current theme name to the given one.
|
||||
* @param request request to be used for theme name modification
|
||||
* @param response response to be used for theme name modification
|
||||
* @param themeName the new theme name
|
||||
* @throws UnsupportedOperationException if the ThemeResolver implementation
|
||||
* does not support dynamic changing of the theme
|
||||
*/
|
||||
void setThemeName(HttpServletRequest request, HttpServletResponse response, String themeName);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2002-2005 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* MVC View for a web interaction. Implementations are responsible for rendering
|
||||
* content, and exposing the model. A single view exposes multiple model attributes.
|
||||
*
|
||||
* <p>This class and the MVC approach associated with it is discussed in Chapter 12 of
|
||||
* <a href="http://www.amazon.com/exec/obidos/tg/detail/-/0764543857/">Expert One-On-One J2EE Design and Development</a>
|
||||
* by Rod Johnson (Wrox, 2002).
|
||||
*
|
||||
* <p>View implementations may differ widely. An obvious implementation would be
|
||||
* JSP-based. Other implementations might be XSLT-based, or use an HTML generation library.
|
||||
* This interface is designed to avoid restricting the range of possible implementations.
|
||||
*
|
||||
* <p>Views should be beans. They are likely to be instantiated as beans by a ViewResolver.
|
||||
* As this interface is stateless, view implementations should be thread-safe.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @see org.springframework.web.servlet.view.AbstractView
|
||||
* @see org.springframework.web.servlet.view.InternalResourceView
|
||||
*/
|
||||
public interface View {
|
||||
|
||||
/**
|
||||
* Return the content type of the view, if predetermined.
|
||||
* <p>Can be used to check the content type upfront,
|
||||
* before the actual rendering process.
|
||||
* @return the content type String (optionally including a character set),
|
||||
* or <code>null</code> if not predetermined.
|
||||
*/
|
||||
String getContentType();
|
||||
|
||||
/**
|
||||
* Render the view given the specified model.
|
||||
* <p>The first step will be preparing the request: In the JSP case,
|
||||
* this would mean setting model objects as request attributes.
|
||||
* The second step will be the actual rendering of the view,
|
||||
* for example including the JSP via a RequestDispatcher.
|
||||
* @param model Map with name Strings as keys and corresponding model
|
||||
* objects as values (Map can also be <code>null</code> in case of empty model)
|
||||
* @param request current HTTP request
|
||||
* @param response HTTP response we are building
|
||||
* @throws Exception if rendering failed
|
||||
*/
|
||||
void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.util.NestedServletException;
|
||||
|
||||
/**
|
||||
* ViewRendererServlet is a bridge servlet, mainly for the Portlet MVC support.
|
||||
*
|
||||
* <p>For usage with Portlets, this Servlet is necessary to force the portlet container
|
||||
* to convert the PortletRequest to a ServletRequest, which it has to do when
|
||||
* including a resource via the PortletRequestDispatcher. This allows for reuse
|
||||
* of the entire Servlet-based View support even in a Portlet environment.
|
||||
*
|
||||
* <p>The actual mapping of the bridge servlet is configurable in the DispatcherPortlet,
|
||||
* via a "viewRendererUrl" property. The default is "/WEB-INF/servlet/view", which is
|
||||
* just available for internal resource dispatching.
|
||||
*
|
||||
* @author William G. Thompson, Jr.
|
||||
* @author John A. Lewis
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ViewRendererServlet extends HttpServlet {
|
||||
|
||||
/**
|
||||
* Request attribute to hold current web application context.
|
||||
* Otherwise only the global web app context is obtainable by tags etc.
|
||||
* @see org.springframework.web.servlet.support.RequestContextUtils#getWebApplicationContext
|
||||
*/
|
||||
public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE;
|
||||
|
||||
/** Name of request attribute that holds the View object */
|
||||
public static final String VIEW_ATTRIBUTE = ViewRendererServlet.class.getName() + ".VIEW";
|
||||
|
||||
/** Name of request attribute that holds the model Map */
|
||||
public static final String MODEL_ATTRIBUTE = ViewRendererServlet.class.getName() + ".MODEL";
|
||||
|
||||
|
||||
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
processRequest(request, response);
|
||||
}
|
||||
|
||||
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
processRequest(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process this request, handling exceptions.
|
||||
* The actually event handling is performed by the abstract
|
||||
* <code>renderView()</code> template method.
|
||||
* @see #renderView
|
||||
*/
|
||||
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
try {
|
||||
renderView(request, response);
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new NestedServletException("View rendering failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the View instance and model Map to render
|
||||
* and trigger actual rendering.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @throws Exception in case of any kind of processing failure
|
||||
* @see org.springframework.web.servlet.View#render
|
||||
*/
|
||||
protected void renderView(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
View view = (View) request.getAttribute(VIEW_ATTRIBUTE);
|
||||
if (view == null) {
|
||||
throw new ServletException("Could not complete render request: View is null");
|
||||
}
|
||||
Map model = (Map) request.getAttribute(MODEL_ATTRIBUTE);
|
||||
view.render(model, request, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2002-2005 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by objects that can resolve views by name.
|
||||
*
|
||||
* <p>View state doesn't change during the running of the application,
|
||||
* so implementations are free to cache views.
|
||||
*
|
||||
* <p>Implementations are encouraged to support internationalization,
|
||||
* i.e. localized view resolution.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see org.springframework.web.servlet.view.InternalResourceViewResolver
|
||||
* @see org.springframework.web.servlet.view.ResourceBundleViewResolver
|
||||
* @see org.springframework.web.servlet.view.XmlViewResolver
|
||||
*/
|
||||
public interface ViewResolver {
|
||||
|
||||
/**
|
||||
* Resolve the given view by name.
|
||||
* <p>Note: To allow for ViewResolver chaining, a ViewResolver should
|
||||
* return <code>null</code> if a view with the given name is not defined in it.
|
||||
* However, this is not required: Some ViewResolvers will always attempt
|
||||
* to build View objects with the given name, unable to return <code>null</code>
|
||||
* (rather throwing an exception when View creation failed).
|
||||
* @param viewName name of the view to resolve
|
||||
* @param locale Locale in which to resolve the view.
|
||||
* ViewResolvers that support internationalization should respect this.
|
||||
* @return the View object, or <code>null</code> if not found
|
||||
* (optional, to allow for ViewResolver chaining)
|
||||
* @throws Exception if the view cannot be resolved
|
||||
* (typically in case of problems creating an actual View object)
|
||||
*/
|
||||
View resolveViewName(String viewName, Locale locale) throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the {@link org.springframework.web.servlet.HandlerMapping}
|
||||
* interface, detecting URL mappings for handler beans through introspection of all
|
||||
* defined beans in the application context.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see #determineUrlsForHandler
|
||||
*/
|
||||
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {
|
||||
|
||||
private boolean detectHandlersInAncestorContexts = false;
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to detect handler beans in ancestor ApplicationContexts.
|
||||
* <p>Default is "false": Only handler beans in the current ApplicationContext
|
||||
* will be detected, i.e. only in the context that this HandlerMapping itself
|
||||
* is defined in (typically the current DispatcherServlet's context).
|
||||
* <p>Switch this flag on to detect handler beans in ancestor contexts
|
||||
* (typically the Spring root WebApplicationContext) as well.
|
||||
*/
|
||||
public void setDetectHandlersInAncestorContexts(boolean detectHandlersInAncestorContexts) {
|
||||
this.detectHandlersInAncestorContexts = detectHandlersInAncestorContexts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls the {@link #detectHandlers()} method in addition to the
|
||||
* superclass's initialization.
|
||||
*/
|
||||
public void initApplicationContext() throws ApplicationContextException {
|
||||
super.initApplicationContext();
|
||||
detectHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all handlers found in the current ApplicationContext.
|
||||
* <p>The actual URL determination for a handler is up to the concrete
|
||||
* {@link #determineUrlsForHandler(String)} implementation. A bean for
|
||||
* which no such URLs could be determined is simply not considered a handler.
|
||||
* @throws org.springframework.beans.BeansException if the handler couldn't be registered
|
||||
* @see #determineUrlsForHandler(String)
|
||||
*/
|
||||
protected void detectHandlers() throws BeansException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
|
||||
}
|
||||
String[] beanNames = (this.detectHandlersInAncestorContexts ?
|
||||
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
|
||||
getApplicationContext().getBeanNamesForType(Object.class));
|
||||
|
||||
// Take any bean name that we can determine URLs for.
|
||||
for (int i = 0; i < beanNames.length; i++) {
|
||||
String beanName = beanNames[i];
|
||||
String[] urls = determineUrlsForHandler(beanName);
|
||||
if (!ObjectUtils.isEmpty(urls)) {
|
||||
// URL paths found: Let's consider it a handler.
|
||||
registerHandler(urls, beanName);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Rejected bean name '" + beanNames[i] + "': no URL paths identified");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the URLs for the given handler bean.
|
||||
* @param beanName the name of the candidate bean
|
||||
* @return the URLs determined for the bean,
|
||||
* or <code>null</code> or an empty array if none
|
||||
*/
|
||||
protected abstract String[] determineUrlsForHandler(String beanName);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.context.request.WebRequestInterceptor;
|
||||
import org.springframework.web.context.support.WebApplicationObjectSupport;
|
||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link org.springframework.web.servlet.HandlerMapping}
|
||||
* implementations. Supports ordering, a default handler, and handler interceptors.
|
||||
*
|
||||
* <p>Note: This base class does <i>not</i> support exposure of the
|
||||
* {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}. Support for this attribute
|
||||
* is up to concrete subclasses, typically based on request URL mappings.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 07.04.2003
|
||||
* @see #getHandlerInternal
|
||||
* @see #setDefaultHandler
|
||||
* @see #setInterceptors
|
||||
* @see org.springframework.web.servlet.HandlerInterceptor
|
||||
*/
|
||||
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
implements HandlerMapping, Ordered {
|
||||
|
||||
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
|
||||
|
||||
private Object defaultHandler;
|
||||
|
||||
private final List interceptors = new ArrayList();
|
||||
|
||||
private HandlerInterceptor[] adaptedInterceptors;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the order value for this HandlerMapping bean.
|
||||
* <p>Default value is <code>Integer.MAX_VALUE</code>, meaning that it's non-ordered.
|
||||
* @see org.springframework.core.Ordered#getOrder()
|
||||
*/
|
||||
public final void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public final int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default handler for this handler mapping.
|
||||
* This handler will be returned if no specific mapping was found.
|
||||
* <p>Default is <code>null</code>, indicating no default handler.
|
||||
*/
|
||||
public void setDefaultHandler(Object defaultHandler) {
|
||||
this.defaultHandler = defaultHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default handler for this handler mapping,
|
||||
* or <code>null</code> if none.
|
||||
*/
|
||||
public Object getDefaultHandler() {
|
||||
return this.defaultHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interceptors to apply for all handlers mapped by this handler mapping.
|
||||
* <p>Supported interceptor types are HandlerInterceptor and WebRequestInterceptor.
|
||||
* @param interceptors array of handler interceptors, or <code>null</code> if none
|
||||
* @see #adaptInterceptor
|
||||
* @see org.springframework.web.servlet.HandlerInterceptor
|
||||
* @see org.springframework.web.context.request.WebRequestInterceptor
|
||||
*/
|
||||
public void setInterceptors(Object[] interceptors) {
|
||||
this.interceptors.addAll(Arrays.asList(interceptors));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the interceptors.
|
||||
* @see #extendInterceptors(java.util.List)
|
||||
* @see #initInterceptors()
|
||||
*/
|
||||
protected void initApplicationContext() throws BeansException {
|
||||
extendInterceptors(this.interceptors);
|
||||
initInterceptors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension hook that subclasses can override to register additional interceptors,
|
||||
* given the configured interceptors (see {@link #setInterceptors}).
|
||||
* <p>Will be invoked before {@link #initInterceptors()} adapts the specified
|
||||
* interceptors into {@link HandlerInterceptor} instances.
|
||||
* <p>The default implementation is empty.
|
||||
* @param interceptors the configured interceptor List (never <code>null</code>),
|
||||
* allowing to add further interceptors before as well as after the existing
|
||||
* interceptors
|
||||
*/
|
||||
protected void extendInterceptors(List interceptors) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the specified interceptors, adapting them where necessary.
|
||||
* @see #setInterceptors
|
||||
* @see #adaptInterceptor
|
||||
*/
|
||||
protected void initInterceptors() {
|
||||
if (!this.interceptors.isEmpty()) {
|
||||
this.adaptedInterceptors = new HandlerInterceptor[this.interceptors.size()];
|
||||
for (int i = 0; i < this.interceptors.size(); i++) {
|
||||
Object interceptor = this.interceptors.get(i);
|
||||
if (interceptor == null) {
|
||||
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
|
||||
}
|
||||
this.adaptedInterceptors[i] = adaptInterceptor(interceptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt the given interceptor object to the HandlerInterceptor interface.
|
||||
* <p>Supported interceptor types are HandlerInterceptor and WebRequestInterceptor.
|
||||
* Each given WebRequestInterceptor will be wrapped in a WebRequestHandlerInterceptorAdapter.
|
||||
* Can be overridden in subclasses.
|
||||
* @param interceptor the specified interceptor object
|
||||
* @return the interceptor wrapped as HandlerInterceptor
|
||||
* @see org.springframework.web.servlet.HandlerInterceptor
|
||||
* @see org.springframework.web.context.request.WebRequestInterceptor
|
||||
* @see WebRequestHandlerInterceptorAdapter
|
||||
*/
|
||||
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
|
||||
if (interceptor instanceof HandlerInterceptor) {
|
||||
return (HandlerInterceptor) interceptor;
|
||||
}
|
||||
else if (interceptor instanceof WebRequestInterceptor) {
|
||||
return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the adapted interceptors as HandlerInterceptor array.
|
||||
* @return the array of HandlerInterceptors, or <code>null</code> if none
|
||||
*/
|
||||
protected final HandlerInterceptor[] getAdaptedInterceptors() {
|
||||
return this.adaptedInterceptors;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look up a handler for the given request, falling back to the default
|
||||
* handler if no specific one is found.
|
||||
* @param request current HTTP request
|
||||
* @return the corresponding handler instance, or the default handler
|
||||
* @see #getHandlerInternal
|
||||
*/
|
||||
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
|
||||
Object handler = getHandlerInternal(request);
|
||||
if (handler == null) {
|
||||
handler = getDefaultHandler();
|
||||
}
|
||||
if (handler == null) {
|
||||
return null;
|
||||
}
|
||||
// Bean name or resolved handler?
|
||||
if (handler instanceof String) {
|
||||
String handlerName = (String) handler;
|
||||
handler = getApplicationContext().getBean(handlerName);
|
||||
}
|
||||
return getHandlerExecutionChain(handler, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a handler for the given request, returning <code>null</code> if no
|
||||
* specific one is found. This method is called by {@link #getHandler};
|
||||
* a <code>null</code> return value will lead to the default handler, if one is set.
|
||||
* <p>Note: This method may also return a pre-built {@link HandlerExecutionChain},
|
||||
* combining a handler object with dynamically determined interceptors.
|
||||
* Statically specified interceptors will get merged into such an existing chain.
|
||||
* @param request current HTTP request
|
||||
* @return the corresponding handler instance, or <code>null</code> if none found
|
||||
* @throws Exception if there is an internal error
|
||||
*/
|
||||
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
|
||||
|
||||
/**
|
||||
* Build a HandlerExecutionChain for the given handler, including applicable interceptors.
|
||||
* <p>The default implementation simply builds a standard HandlerExecutionChain with
|
||||
* the given handler and this handler mapping's common interceptors. Subclasses may
|
||||
* override this in order to extend/rearrange the list of interceptors.
|
||||
* <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a pre-built
|
||||
* HandlerExecutionChain. This method should handle those two cases explicitly,
|
||||
* either building a new HandlerExecutionChain or extending the existing chain.
|
||||
* <p>For simply adding an interceptor, consider calling <code>super.getHandlerExecutionChain</code>
|
||||
* and invoking {@link HandlerExecutionChain#addInterceptor} on the returned chain object.
|
||||
* @param handler the resolved handler instance (never <code>null</code>)
|
||||
* @param request current HTTP request
|
||||
* @return the HandlerExecutionChain (never <code>null</code>)
|
||||
* @see #getAdaptedInterceptors()
|
||||
*/
|
||||
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
|
||||
if (handler instanceof HandlerExecutionChain) {
|
||||
HandlerExecutionChain chain = (HandlerExecutionChain) handler;
|
||||
chain.addInterceptors(getAdaptedInterceptors());
|
||||
return chain;
|
||||
}
|
||||
else {
|
||||
return new HandlerExecutionChain(handler, getAdaptedInterceptors());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* Abstract base class for URL-mapped {@link org.springframework.web.servlet.HandlerMapping}
|
||||
* implementations. Provides infrastructure for mapping handlers to URLs and configurable
|
||||
* URL lookup. For information on the latter, see "alwaysUseFullPath" property.
|
||||
*
|
||||
* <p>Supports direct matches, e.g. a registered "/test" matches "/test", and
|
||||
* various Ant-style pattern matches, e.g. a registered "/t*" pattern matches
|
||||
* both "/test" and "/team", "/test/*" matches all paths in the "/test" directory,
|
||||
* "/test/**" matches all paths below "/test". For details, see the
|
||||
* {@link org.springframework.util.AntPathMatcher AntPathMatcher} javadoc.
|
||||
*
|
||||
* <p>Will search all path patterns to find the most exact match for the
|
||||
* current request path. The most exact match is defined as the longest
|
||||
* path pattern that matches the current request path.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 16.04.2003
|
||||
* @see #setAlwaysUseFullPath
|
||||
* @see #setUrlDecode
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
private Object rootHandler;
|
||||
|
||||
private boolean lazyInitHandlers = false;
|
||||
|
||||
private final Map handlerMap = new LinkedHashMap();
|
||||
|
||||
|
||||
/**
|
||||
* Set if URL lookup should always use the full path within the current servlet
|
||||
* context. Else, the path within the current servlet mapping is used if applicable
|
||||
* (that is, in the case of a ".../*" servlet mapping in web.xml).
|
||||
* <p>Default is "false".
|
||||
* @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
|
||||
*/
|
||||
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
|
||||
this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if context path and request URI should be URL-decoded. Both are returned
|
||||
* <i>undecoded</i> by the Servlet API, in contrast to the servlet path.
|
||||
* <p>Uses either the request encoding or the default encoding according
|
||||
* to the Servlet spec (ISO-8859-1).
|
||||
* @see org.springframework.web.util.UrlPathHelper#setUrlDecode
|
||||
*/
|
||||
public void setUrlDecode(boolean urlDecode) {
|
||||
this.urlPathHelper.setUrlDecode(urlDecode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the UrlPathHelper to use for resolution of lookup paths.
|
||||
* <p>Use this to override the default UrlPathHelper with a custom subclass,
|
||||
* or to share common UrlPathHelper settings across multiple HandlerMappings
|
||||
* and MethodNameResolvers.
|
||||
* @see org.springframework.web.servlet.mvc.multiaction.AbstractUrlMethodNameResolver#setUrlPathHelper
|
||||
*/
|
||||
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
|
||||
this.urlPathHelper = urlPathHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PathMatcher implementation to use for matching URL paths
|
||||
* against registered URL patterns. Default is AntPathMatcher.
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
||||
this.pathMatcher = pathMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PathMatcher implementation to use for matching URL paths
|
||||
* against registered URL patterns.
|
||||
*/
|
||||
public PathMatcher getPathMatcher() {
|
||||
return this.pathMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the root handler for this handler mapping, that is,
|
||||
* the handler to be registered for the root path ("/").
|
||||
* <p>Default is <code>null</code>, indicating no root handler.
|
||||
*/
|
||||
public void setRootHandler(Object rootHandler) {
|
||||
this.rootHandler = rootHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the root handler for this handler mapping (registered for "/"),
|
||||
* or <code>null</code> if none.
|
||||
*/
|
||||
public Object getRootHandler() {
|
||||
return this.rootHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to lazily initialize handlers. Only applicable to
|
||||
* singleton handlers, as prototypes are always lazily initialized.
|
||||
* Default is "false", as eager initialization allows for more efficiency
|
||||
* through referencing the controller objects directly.
|
||||
* <p>If you want to allow your controllers to be lazily initialized,
|
||||
* make them "lazy-init" and set this flag to true. Just making them
|
||||
* "lazy-init" will not work, as they are initialized through the
|
||||
* references from the handler mapping in this case.
|
||||
*/
|
||||
public void setLazyInitHandlers(boolean lazyInitHandlers) {
|
||||
this.lazyInitHandlers = lazyInitHandlers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look up a handler for the URL path of the given request.
|
||||
* @param request current HTTP request
|
||||
* @return the handler instance, or <code>null</code> if none found
|
||||
*/
|
||||
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
|
||||
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
|
||||
Object handler = lookupHandler(lookupPath, request);
|
||||
if (handler == null) {
|
||||
// We need to care for the default handler directly, since we need to
|
||||
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
|
||||
Object rawHandler = null;
|
||||
if ("/".equals(lookupPath)) {
|
||||
rawHandler = getRootHandler();
|
||||
}
|
||||
if (rawHandler == null) {
|
||||
rawHandler = getDefaultHandler();
|
||||
}
|
||||
if (rawHandler != null) {
|
||||
validateHandler(rawHandler, request);
|
||||
handler = buildPathExposingHandler(rawHandler, lookupPath);
|
||||
}
|
||||
}
|
||||
if (handler != null && logger.isDebugEnabled()) {
|
||||
logger.debug("Mapping [" + lookupPath + "] to handler '" + handler + "'");
|
||||
}
|
||||
else if (handler == null && logger.isTraceEnabled()) {
|
||||
logger.trace("No handler mapping found for [" + lookupPath + "]");
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a handler instance for the given URL path.
|
||||
* <p>Supports direct matches, e.g. a registered "/test" matches "/test",
|
||||
* and various Ant-style pattern matches, e.g. a registered "/t*" matches
|
||||
* both "/test" and "/team". For details, see the AntPathMatcher class.
|
||||
* <p>Looks for the most exact pattern, where most exact is defined as
|
||||
* the longest path pattern.
|
||||
* @param urlPath URL the bean is mapped to
|
||||
* @param request current HTTP request (to expose the path within the mapping to)
|
||||
* @return the associated handler instance, or <code>null</code> if not found
|
||||
* @see #exposePathWithinMapping
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
|
||||
// Direct match?
|
||||
Object handler = this.handlerMap.get(urlPath);
|
||||
if (handler != null) {
|
||||
validateHandler(handler, request);
|
||||
return buildPathExposingHandler(handler, urlPath);
|
||||
}
|
||||
// Pattern match?
|
||||
String bestPathMatch = null;
|
||||
for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
|
||||
String registeredPath = (String) it.next();
|
||||
if (getPathMatcher().match(registeredPath, urlPath) &&
|
||||
(bestPathMatch == null || bestPathMatch.length() < registeredPath.length())) {
|
||||
bestPathMatch = registeredPath;
|
||||
}
|
||||
}
|
||||
if (bestPathMatch != null) {
|
||||
handler = this.handlerMap.get(bestPathMatch);
|
||||
validateHandler(handler, request);
|
||||
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPathMatch, urlPath);
|
||||
return buildPathExposingHandler(handler, pathWithinMapping);
|
||||
}
|
||||
// No handler found...
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given handler against the current request.
|
||||
* <p>The default implementation is empty. Can be overridden in subclasses,
|
||||
* for example to enforce specific preconditions expressed in URL mappings.
|
||||
* @param handler the handler object to validate
|
||||
* @param request current HTTP request
|
||||
* @throws Exception if validation failed
|
||||
*/
|
||||
protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a handler object for the given raw handler, exposing the actual
|
||||
* handler as well as the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}
|
||||
* before executing the handler.
|
||||
* <p>The default implementation builds a {@link HandlerExecutionChain}
|
||||
* with a special interceptor that exposes the path attribute.
|
||||
* @param rawHandler the raw handler to expose
|
||||
* @param pathWithinMapping the path to expose before executing the handler
|
||||
* @return the final handler object
|
||||
*/
|
||||
protected Object buildPathExposingHandler(Object rawHandler, String pathWithinMapping) {
|
||||
// Bean name or resolved handler?
|
||||
if (rawHandler instanceof String) {
|
||||
String handlerName = (String) rawHandler;
|
||||
rawHandler = getApplicationContext().getBean(handlerName);
|
||||
}
|
||||
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
|
||||
chain.addInterceptor(new PathExposingHandlerInterceptor(pathWithinMapping));
|
||||
return chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose the path within the current mapping as request attribute.
|
||||
* @param pathWithinMapping the path within the current mapping
|
||||
* @param request the request to expose the path to
|
||||
* @see #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
|
||||
*/
|
||||
protected void exposePathWithinMapping(String pathWithinMapping, HttpServletRequest request) {
|
||||
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register the specified handler for the given URL paths.
|
||||
* @param urlPaths the URLs that the bean should be mapped to
|
||||
* @param beanName the name of the handler bean
|
||||
* @throws BeansException if the handler couldn't be registered
|
||||
* @throws IllegalStateException if there is a conflicting handler registered
|
||||
*/
|
||||
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
|
||||
Assert.notNull(urlPaths, "URL path array must not be null");
|
||||
for (int j = 0; j < urlPaths.length; j++) {
|
||||
registerHandler(urlPaths[j], beanName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the specified handler for the given URL path.
|
||||
* @param urlPath the URL the bean should be mapped to
|
||||
* @param handler the handler instance or handler bean name String
|
||||
* (a bean name will automatically be resolved into the corresponding handler bean)
|
||||
* @throws BeansException if the handler couldn't be registered
|
||||
* @throws IllegalStateException if there is a conflicting handler registered
|
||||
*/
|
||||
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
|
||||
Assert.notNull(urlPath, "URL path must not be null");
|
||||
Assert.notNull(handler, "Handler object must not be null");
|
||||
Object resolvedHandler = handler;
|
||||
|
||||
// Eagerly resolve handler if referencing singleton via name.
|
||||
if (!this.lazyInitHandlers && handler instanceof String) {
|
||||
String handlerName = (String) handler;
|
||||
if (getApplicationContext().isSingleton(handlerName)) {
|
||||
resolvedHandler = getApplicationContext().getBean(handlerName);
|
||||
}
|
||||
}
|
||||
|
||||
Object mappedHandler = this.handlerMap.get(urlPath);
|
||||
if (mappedHandler != null) {
|
||||
if (mappedHandler != resolvedHandler) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot map handler [" + handler + "] to URL path [" + urlPath +
|
||||
"]: There is already handler [" + resolvedHandler + "] mapped.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (urlPath.equals("/")) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Root mapping to handler [" + resolvedHandler + "]");
|
||||
}
|
||||
setRootHandler(resolvedHandler);
|
||||
}
|
||||
else if (urlPath.equals("/*")) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Default mapping to handler [" + resolvedHandler + "]");
|
||||
}
|
||||
setDefaultHandler(resolvedHandler);
|
||||
}
|
||||
else {
|
||||
this.handlerMap.put(urlPath, resolvedHandler);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapped URL path [" + urlPath + "] onto handler [" + resolvedHandler + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the registered handlers as an unmodifiable Map, with the registered path
|
||||
* as key and the handler object (or handler bean name in case of a lazy-init handler)
|
||||
* as value.
|
||||
* @see #getDefaultHandler()
|
||||
*/
|
||||
public final Map getHandlerMap() {
|
||||
return Collections.unmodifiableMap(this.handlerMap);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Special interceptor for exposing the
|
||||
* {@link AbstractUrlHandlerMapping#PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE} attribute.
|
||||
* @link AbstractUrlHandlerMapping#exposePathWithinMapping
|
||||
*/
|
||||
private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private final String pathWithinMapping;
|
||||
|
||||
public PathExposingHandlerInterceptor(String pathWithinMapping) {
|
||||
this.pathWithinMapping = pathWithinMapping;
|
||||
}
|
||||
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
exposePathWithinMapping(this.pathWithinMapping, request);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link org.springframework.web.servlet.HandlerMapping}
|
||||
* interface that map from URLs to beans with names that start with a slash ("/"),
|
||||
* similar to how Struts maps URLs to action names.
|
||||
*
|
||||
* <p>This is the default implementation used by the
|
||||
* {@link org.springframework.web.servlet.DispatcherServlet}, along with
|
||||
* {@link org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping}
|
||||
* (on Java 5 and higher). Alternatively, {@link SimpleUrlHandlerMapping} allows for
|
||||
* customizing a handler mapping declaratively.
|
||||
*
|
||||
* <p>The mapping is from URL to bean name. Thus an incoming URL "/foo" would map
|
||||
* to a handler named "/foo", or to "/foo /foo2" in case of multiple mappings to
|
||||
* a single handler. Note: In XML definitions, you'll need to use an alias
|
||||
* name="/foo" in the bean definition, as the XML id may not contain slashes.
|
||||
*
|
||||
* <p>Supports direct matches (given "/test" -> registered "/test") and "*"
|
||||
* matches (given "/test" -> registered "/t*"). Note that the default is
|
||||
* to map within the current servlet mapping if applicable; see the
|
||||
* {@link #setAlwaysUseFullPath "alwaysUseFullPath"} property for details.
|
||||
* For details on the pattern options, see the
|
||||
* {@link org.springframework.util.AntPathMatcher} javadoc.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see SimpleUrlHandlerMapping
|
||||
*/
|
||||
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
|
||||
|
||||
/**
|
||||
* Checks name and aliases of the given bean for URLs, starting with "/".
|
||||
*/
|
||||
protected String[] determineUrlsForHandler(String beanName) {
|
||||
List urls = new ArrayList();
|
||||
if (beanName.startsWith("/")) {
|
||||
urls.add(beanName);
|
||||
}
|
||||
String[] aliases = getApplicationContext().getAliases(beanName);
|
||||
for (int i = 0; i < aliases.length; i++) {
|
||||
if (aliases[i].startsWith("/")) {
|
||||
urls.add(aliases[i]);
|
||||
}
|
||||
}
|
||||
return StringUtils.toStringArray(urls);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
/**
|
||||
* {@link ServletWebRequest} subclass that is aware of
|
||||
* {@link org.springframework.web.servlet.DispatcherServlet}'s
|
||||
* request context, such as the Locale determined by the configured
|
||||
* {@link org.springframework.web.servlet.LocaleResolver}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see #getLocale()
|
||||
* @see org.springframework.web.servlet.LocaleResolver
|
||||
*/
|
||||
public class DispatcherServletWebRequest extends ServletWebRequest {
|
||||
|
||||
/**
|
||||
* Create a new DispatcherServletWebRequest instance for the given request.
|
||||
* @param request current HTTP request
|
||||
*/
|
||||
public DispatcherServletWebRequest(HttpServletRequest request) {
|
||||
super(request);
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return RequestContextUtils.getLocale(getRequest());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Abstract adapter class for the HandlerInterceptor interface,
|
||||
* for simplified implementation of pre-only/post-only interceptors.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 05.12.2003
|
||||
*/
|
||||
public abstract class HandlerInterceptorAdapter implements HandlerInterceptor {
|
||||
|
||||
/**
|
||||
* This implementation always returns <code>true</code>.
|
||||
*/
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation is empty.
|
||||
*/
|
||||
public void postHandle(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
|
||||
throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation is empty.
|
||||
*/
|
||||
public void afterCompletion(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
|
||||
throws Exception {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,446 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.web.servlet.HandlerExceptionResolver} implementation
|
||||
* that allows for mapping exception class names to view names, either for a
|
||||
* set of given handlers or for all handlers in the DispatcherServlet.
|
||||
*
|
||||
* <p>Error views are analogous to error page JSPs, but can be used with any
|
||||
* kind of exception including any checked one, with fine-granular mappings for
|
||||
* specific handlers.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 22.11.2003
|
||||
* @see org.springframework.web.servlet.DispatcherServlet
|
||||
*/
|
||||
public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, Ordered {
|
||||
|
||||
/**
|
||||
* The default name of the exception attribute: "exception".
|
||||
*/
|
||||
public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";
|
||||
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
|
||||
|
||||
private Set mappedHandlers;
|
||||
|
||||
private Class[] mappedHandlerClasses;
|
||||
|
||||
private Log warnLogger;
|
||||
|
||||
private Properties exceptionMappings;
|
||||
|
||||
private String defaultErrorView;
|
||||
|
||||
private Integer defaultStatusCode;
|
||||
|
||||
private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE;
|
||||
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the set of handlers that this exception resolver should apply to.
|
||||
* The exception mappings and the default error view will only apply
|
||||
* to the specified handlers.
|
||||
* <p>If no handlers and handler classes are set, the exception mappings
|
||||
* and the default error view will apply to all handlers. This means that
|
||||
* a specified default error view will be used as fallback for all exceptions;
|
||||
* any further HandlerExceptionResolvers in the chain will be ignored in
|
||||
* this case.
|
||||
*/
|
||||
public void setMappedHandlers(Set mappedHandlers) {
|
||||
this.mappedHandlers = mappedHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the set of classes that this exception resolver should apply to.
|
||||
* The exception mappings and the default error view will only apply
|
||||
* to handlers of the specified type; the specified types may be interfaces
|
||||
* and superclasses of handlers as well.
|
||||
* <p>If no handlers and handler classes are set, the exception mappings
|
||||
* and the default error view will apply to all handlers. This means that
|
||||
* a specified default error view will be used as fallback for all exceptions;
|
||||
* any further HandlerExceptionResolvers in the chain will be ignored in
|
||||
* this case.
|
||||
*/
|
||||
public void setMappedHandlerClasses(Class[] mappedHandlerClasses) {
|
||||
this.mappedHandlerClasses = mappedHandlerClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the log category for warn logging. The name will be passed to the
|
||||
* underlying logger implementation through Commons Logging, getting
|
||||
* interpreted as log category according to the logger's configuration.
|
||||
* <p>Default is no warn logging. Specify this setting to activate
|
||||
* warn logging into a specific category. Alternatively, override
|
||||
* the {@link #logException} method for custom logging.
|
||||
* @see org.apache.commons.logging.LogFactory#getLog(String)
|
||||
* @see org.apache.log4j.Logger#getLogger(String)
|
||||
* @see java.util.logging.Logger#getLogger(String)
|
||||
*/
|
||||
public void setWarnLogCategory(String loggerName) {
|
||||
this.warnLogger = LogFactory.getLog(loggerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mappings between exception class names and error view names.
|
||||
* The exception class name can be a substring, with no wildcard support
|
||||
* at present. A value of "ServletException" would match
|
||||
* <code>javax.servlet.ServletException</code> and subclasses, for example.
|
||||
* <p><b>NB:</b> Consider carefully how specific the pattern is, and whether
|
||||
* to include package information (which isn't mandatory). For example,
|
||||
* "Exception" will match nearly anything, and will probably hide other rules.
|
||||
* "java.lang.Exception" would be correct if "Exception" was meant to define
|
||||
* a rule for all checked exceptions. With more unusual exception names such
|
||||
* as "BaseBusinessException" there's no need to use a FQN.
|
||||
* <p>Follows the same matching algorithm as RuleBasedTransactionAttribute
|
||||
* and RollbackRuleAttribute.
|
||||
* @param mappings exception patterns (can also be fully qualified class names)
|
||||
* as keys, and error view names as values
|
||||
* @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
|
||||
* @see org.springframework.transaction.interceptor.RollbackRuleAttribute
|
||||
*/
|
||||
public void setExceptionMappings(Properties mappings) {
|
||||
this.exceptionMappings = mappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the default error view.
|
||||
* This view will be returned if no specific mapping was found.
|
||||
* <p>Default is none.
|
||||
*/
|
||||
public void setDefaultErrorView(String defaultErrorView) {
|
||||
this.defaultErrorView = defaultErrorView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default HTTP status code that this exception resolver will apply
|
||||
* if it resolves an error view.
|
||||
* <p>Note that this error code will only get applied in case of a top-level
|
||||
* request. It will not be set for an include request, since the HTTP status
|
||||
* cannot be modified from within an include.
|
||||
* <p>If not specified, no status code will be applied, either leaving this to
|
||||
* the controller or view, or keeping the servlet engine's default of 200 (OK).
|
||||
* @param defaultStatusCode HTTP status code value, for example
|
||||
* 500 (SC_INTERNAL_SERVER_ERROR) or 404 (SC_NOT_FOUND)
|
||||
* @see javax.servlet.http.HttpServletResponse#SC_INTERNAL_SERVER_ERROR
|
||||
* @see javax.servlet.http.HttpServletResponse#SC_NOT_FOUND
|
||||
*/
|
||||
public void setDefaultStatusCode(int defaultStatusCode) {
|
||||
this.defaultStatusCode = new Integer(defaultStatusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the model attribute as which the exception should
|
||||
* be exposed. Default is "exception".
|
||||
* <p>This can be either set to a different attribute name or to
|
||||
* <code>null</code> for not exposing an exception attribute at all.
|
||||
* @see #DEFAULT_EXCEPTION_ATTRIBUTE
|
||||
*/
|
||||
public void setExceptionAttribute(String exceptionAttribute) {
|
||||
this.exceptionAttribute = exceptionAttribute;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether this resolver is supposed to apply (i.e. the handler
|
||||
* matches in case of "mappedHandlers" having been specified), then
|
||||
* delegates to the {@link #doResolveException} template method.
|
||||
*/
|
||||
public ModelAndView resolveException(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
|
||||
if (shouldApplyTo(request, handler)) {
|
||||
return doResolveException(request, response, handler, ex);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this resolver is supposed to apply to the given handler.
|
||||
* <p>The default implementation checks against the specified mapped handlers
|
||||
* and handler classes, if any.
|
||||
* @param request current HTTP request
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the
|
||||
* time of the exception (for example, if multipart resolution failed)
|
||||
* @return whether this resolved should proceed with resolving the exception
|
||||
* for the given request and handler
|
||||
* @see #setMappedHandlers
|
||||
* @see #setMappedHandlerClasses
|
||||
*/
|
||||
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
|
||||
if (handler != null) {
|
||||
if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
|
||||
return true;
|
||||
}
|
||||
if (this.mappedHandlerClasses != null) {
|
||||
for (int i = 0; i < this.mappedHandlerClasses.length; i++) {
|
||||
if (this.mappedHandlerClasses[i].isInstance(handler)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Else only apply if there are no explicit handler mappings.
|
||||
return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually resolve the given exception that got thrown during on handler execution,
|
||||
* returning a ModelAndView that represents a specific error page if appropriate.
|
||||
* <p>May be overridden in subclasses, in order to apply specific exception checks.
|
||||
* Note that this template method will be invoked <i>after</i> checking whether
|
||||
* this resolved applies ("mappedHandlers" etc), so an implementation may simply
|
||||
* proceed with its actual exception handling.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or <code>null</code> if none chosen at the
|
||||
* time of the exception (for example, if multipart resolution failed)
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @return a corresponding ModelAndView to forward to, or <code>null</code> for default processing
|
||||
*/
|
||||
protected ModelAndView doResolveException(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
|
||||
// Log exception, both at debug log level and at warn level, if desired.
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
|
||||
}
|
||||
logException(ex, request);
|
||||
|
||||
// Expose ModelAndView for chosen error view.
|
||||
String viewName = determineViewName(ex, request);
|
||||
if (viewName != null) {
|
||||
// Apply HTTP status code for error views, if specified.
|
||||
// Only apply it if we're processing a top-level request.
|
||||
Integer statusCode = determineStatusCode(request, viewName);
|
||||
if (statusCode != null) {
|
||||
applyStatusCodeIfPossible(request, response, statusCode.intValue());
|
||||
}
|
||||
return getModelAndView(viewName, ex, request);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log the given exception at warn level, provided that warn logging has been
|
||||
* activated through the {@link #setWarnLogCategory "warnLogCategory"} property.
|
||||
* <p>Calls {@link #buildLogMessage} in order to determine the concrete message
|
||||
* to log. Always passes the full exception to the logger.
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @param request current HTTP request (useful for obtaining metadata)
|
||||
* @see #setWarnLogCategory
|
||||
* @see #buildLogMessage
|
||||
* @see org.apache.commons.logging.Log#warn(Object, Throwable)
|
||||
*/
|
||||
protected void logException(Exception ex, HttpServletRequest request) {
|
||||
if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
|
||||
this.warnLogger.warn(buildLogMessage(ex, request), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a log message for the given exception, occured during processing
|
||||
* the given request.
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @param request current HTTP request (useful for obtaining metadata)
|
||||
* @return the log message to use
|
||||
*/
|
||||
protected String buildLogMessage(Exception ex, HttpServletRequest request) {
|
||||
return "Handler execution resulted in exception";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the view name for the given exception, searching the
|
||||
* {@link #setExceptionMappings "exceptionMappings"}, using the
|
||||
* {@link #setDefaultErrorView "defaultErrorView"} as fallback.
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @param request current HTTP request (useful for obtaining metadata)
|
||||
* @return the resolved view name, or <code>null</code> if none found
|
||||
*/
|
||||
protected String determineViewName(Exception ex, HttpServletRequest request) {
|
||||
String viewName = null;
|
||||
// Check for specific exception mappings.
|
||||
if (this.exceptionMappings != null) {
|
||||
viewName = findMatchingViewName(this.exceptionMappings, ex);
|
||||
}
|
||||
// Return default error view else, if defined.
|
||||
if (viewName == null && this.defaultErrorView != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resolving to default view '" + this.defaultErrorView +
|
||||
"' for exception of type [" + ex.getClass().getName() + "]");
|
||||
}
|
||||
viewName = this.defaultErrorView;
|
||||
}
|
||||
return viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a matching view name in the given exception mappings.
|
||||
* @param exceptionMappings mappings between exception class names and error view names
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @return the view name, or <code>null</code> if none found
|
||||
* @see #setExceptionMappings
|
||||
*/
|
||||
protected String findMatchingViewName(Properties exceptionMappings, Exception ex) {
|
||||
String viewName = null;
|
||||
String dominantMapping = null;
|
||||
int deepest = Integer.MAX_VALUE;
|
||||
for (Enumeration names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
|
||||
String exceptionMapping = (String) names.nextElement();
|
||||
int depth = getDepth(exceptionMapping, ex);
|
||||
if (depth >= 0 && depth < deepest) {
|
||||
deepest = depth;
|
||||
dominantMapping = exceptionMapping;
|
||||
viewName = exceptionMappings.getProperty(exceptionMapping);
|
||||
}
|
||||
}
|
||||
if (viewName != null && logger.isDebugEnabled()) {
|
||||
logger.debug("Resolving to view '" + viewName + "' for exception of type [" + ex.getClass().getName() +
|
||||
"], based on exception mapping [" + dominantMapping + "]");
|
||||
}
|
||||
return viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the depth to the superclass matching.
|
||||
* <p>0 means ex matches exactly. Returns -1 if there's no match.
|
||||
* Otherwise, returns depth. Lowest depth wins.
|
||||
* <p>Follows the same algorithm as
|
||||
* {@link org.springframework.transaction.interceptor.RollbackRuleAttribute}.
|
||||
*/
|
||||
protected int getDepth(String exceptionMapping, Exception ex) {
|
||||
return getDepth(exceptionMapping, ex.getClass(), 0);
|
||||
}
|
||||
|
||||
private int getDepth(String exceptionMapping, Class exceptionClass, int depth) {
|
||||
if (exceptionClass.getName().indexOf(exceptionMapping) != -1) {
|
||||
// Found it!
|
||||
return depth;
|
||||
}
|
||||
// If we've gone as far as we can go and haven't found it...
|
||||
if (exceptionClass.equals(Throwable.class)) {
|
||||
return -1;
|
||||
}
|
||||
return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the HTTP status code to apply for the given error view.
|
||||
* <p>The default implementation always returns the specified
|
||||
* {@link #setDefaultStatusCode "defaultStatusCode"}, as a common
|
||||
* status code for all error views. Override this in a custom subclass
|
||||
* to determine a specific status code for the given view.
|
||||
* @param request current HTTP request
|
||||
* @param viewName the name of the error view
|
||||
* @return the HTTP status code to use, or <code>null</code> for the
|
||||
* servlet container's default (200 in case of a standard error view)
|
||||
* @see #setDefaultStatusCode
|
||||
* @see #applyStatusCodeIfPossible
|
||||
*/
|
||||
protected Integer determineStatusCode(HttpServletRequest request, String viewName) {
|
||||
return this.defaultStatusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified HTTP status code to the given response, if possible
|
||||
* (that is, if not executing within an include request).
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param statusCode the status code to apply
|
||||
* @see #determineStatusCode
|
||||
* @see #setDefaultStatusCode
|
||||
* @see javax.servlet.http.HttpServletResponse#setStatus
|
||||
*/
|
||||
protected void applyStatusCodeIfPossible(HttpServletRequest request, HttpServletResponse response, int statusCode) {
|
||||
if (!WebUtils.isIncludeRequest(request)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Applying HTTP status code " + statusCode);
|
||||
}
|
||||
response.setStatus(statusCode);
|
||||
request.setAttribute(WebUtils.ERROR_STATUS_CODE_ATTRIBUTE, new Integer(statusCode));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a ModelAndView for the given request, view name and exception.
|
||||
* <p>The default implementation delegates to {@link #getModelAndView(String, Exception)}.
|
||||
* @param viewName the name of the error view
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @param request current HTTP request (useful for obtaining metadata)
|
||||
* @return the ModelAndView instance
|
||||
*/
|
||||
protected ModelAndView getModelAndView(String viewName, Exception ex, HttpServletRequest request) {
|
||||
return getModelAndView(viewName, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a ModelAndView for the given view name and exception.
|
||||
* <p>The default implementation adds the specified exception attribute.
|
||||
* Can be overridden in subclasses.
|
||||
* @param viewName the name of the error view
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @return the ModelAndView instance
|
||||
* @see #setExceptionAttribute
|
||||
*/
|
||||
protected ModelAndView getModelAndView(String viewName, Exception ex) {
|
||||
ModelAndView mv = new ModelAndView(viewName);
|
||||
if (this.exceptionAttribute != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Exposing Exception as model attribute '" + this.exceptionAttribute + "'");
|
||||
}
|
||||
mv.addObject(this.exceptionAttribute, ex);
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2002-2005 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Adapter to use the Servlet interface with the generic DispatcherServlet.
|
||||
* Calls the Servlet's <code>service</code> method to handle a request.
|
||||
*
|
||||
* <p>Last-modified checking is not explicitly supported: This is typically
|
||||
* handled by the Servlet implementation itself (usually deriving from
|
||||
* the HttpServlet base class).
|
||||
*
|
||||
* <p>This adapter is not activated by default; it needs to be defined as a
|
||||
* bean in the DispatcherServlet context. It will automatically apply to
|
||||
* mapped handler beans that implement the Servlet interface then.
|
||||
*
|
||||
* <p>Note that Servlet instances defined as bean will not receive initialization
|
||||
* and destruction callbacks, unless a special post-processor such as
|
||||
* SimpleServletPostProcessor is defined in the DispatcherServlet context.
|
||||
*
|
||||
* <p><b>Alternatively, consider wrapping a Servlet with Spring's
|
||||
* ServletWrappingController.</b> This is particularly appropriate for
|
||||
* existing Servlet classes, allowing to specify Servlet initialization
|
||||
* parameters etc.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.5
|
||||
* @see javax.servlet.Servlet
|
||||
* @see javax.servlet.http.HttpServlet
|
||||
* @see SimpleServletPostProcessor
|
||||
* @see org.springframework.web.servlet.mvc.ServletWrappingController
|
||||
*/
|
||||
public class SimpleServletHandlerAdapter implements HandlerAdapter {
|
||||
|
||||
public boolean supports(Object handler) {
|
||||
return (handler instanceof Servlet);
|
||||
}
|
||||
|
||||
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
|
||||
((Servlet) handler).service(request, response);
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getLastModified(HttpServletRequest request, Object handler) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
|
||||
import org.springframework.web.context.ServletConfigAware;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.config.BeanPostProcessor}
|
||||
* that applies initialization and destruction callbacks to beans that
|
||||
* implement the {@link javax.servlet.Servlet} interface.
|
||||
*
|
||||
* <p>After initialization of the bean instance, the Servlet <code>init</code>
|
||||
* method will be called with a ServletConfig that contains the bean name
|
||||
* of the Servlet and the ServletContext that it is running in.
|
||||
*
|
||||
* <p>Before destruction of the bean instance, the Servlet <code>destroy</code>
|
||||
* will be called.
|
||||
*
|
||||
* <p><b>Note that this post-processor does not support Servlet initialization
|
||||
* parameters.</b> Bean instances that implement the Servlet interface are
|
||||
* supposed to be configured like any other Spring bean, that is, through
|
||||
* constructor arguments or bean properties.
|
||||
*
|
||||
* <p>For reuse of a Servlet implementation in a plain Servlet container
|
||||
* and as a bean in a Spring context, consider deriving from Spring's
|
||||
* {@link org.springframework.web.servlet.HttpServletBean} base class that
|
||||
* applies Servlet initialization parameters as bean properties, supporting
|
||||
* both the standard Servlet and the Spring bean initialization style.
|
||||
*
|
||||
* <p><b>Alternatively, consider wrapping a Servlet with Spring's
|
||||
* {@link org.springframework.web.servlet.mvc.ServletWrappingController}.</b>
|
||||
* This is particularly appropriate for existing Servlet classes,
|
||||
* allowing to specify Servlet initialization parameters etc.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.5
|
||||
* @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
|
||||
* @see javax.servlet.Servlet#destroy()
|
||||
* @see SimpleServletHandlerAdapter
|
||||
*/
|
||||
public class SimpleServletPostProcessor implements
|
||||
DestructionAwareBeanPostProcessor, ServletContextAware, ServletConfigAware {
|
||||
|
||||
private boolean useSharedServletConfig = true;
|
||||
|
||||
private ServletContext servletContext;
|
||||
|
||||
private ServletConfig servletConfig;
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to use the shared ServletConfig object passed in
|
||||
* through <code>setServletConfig</code>, if available.
|
||||
* <p>Default is "true". Turn this setting to "false" to pass in
|
||||
* a mock ServletConfig object with the bean name as servlet name,
|
||||
* holding the current ServletContext.
|
||||
* @see #setServletConfig
|
||||
*/
|
||||
public void setUseSharedServletConfig(boolean useSharedServletConfig) {
|
||||
this.useSharedServletConfig = useSharedServletConfig;
|
||||
}
|
||||
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
public void setServletConfig(ServletConfig servletConfig) {
|
||||
this.servletConfig = servletConfig;
|
||||
}
|
||||
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof Servlet) {
|
||||
ServletConfig config = this.servletConfig;
|
||||
if (config == null || !this.useSharedServletConfig) {
|
||||
config = new DelegatingServletConfig(beanName, this.servletContext);
|
||||
}
|
||||
try {
|
||||
((Servlet) bean).init(config);
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
throw new BeanInitializationException("Servlet.init threw exception", ex);
|
||||
}
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof Servlet) {
|
||||
((Servlet) bean).destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal implementation of the {@link ServletConfig} interface,
|
||||
* to be passed to the wrapped servlet.
|
||||
*/
|
||||
private static class DelegatingServletConfig implements ServletConfig {
|
||||
|
||||
private final String servletName;
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
public DelegatingServletConfig(String servletName, ServletContext servletContext) {
|
||||
this.servletName = servletName;
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
public String getServletName() {
|
||||
return this.servletName;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext() {
|
||||
return this.servletContext;
|
||||
}
|
||||
|
||||
public String getInitParameter(String paramName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Enumeration getInitParameterNames() {
|
||||
return Collections.enumeration(Collections.EMPTY_SET);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link org.springframework.web.servlet.HandlerMapping}
|
||||
* interface to map from URLs to request handler beans. Supports both mapping to bean
|
||||
* instances and mapping to bean names; the latter is required for non-singleton handlers.
|
||||
*
|
||||
* <p>The "urlMap" property is suitable for populating the handler map with
|
||||
* bean references, e.g. via the map element in XML bean definitions.
|
||||
*
|
||||
* <p>Mappings to bean names can be set via the "mappings" property, in a form
|
||||
* accepted by the <code>java.util.Properties</code> class, like as follows:<br>
|
||||
* <code>
|
||||
* /welcome.html=ticketController
|
||||
* /show.html=ticketController
|
||||
* </code><br>
|
||||
* The syntax is <code>PATH=HANDLER_BEAN_NAME</code>.
|
||||
* If the path doesn't begin with a slash, one is prepended.
|
||||
*
|
||||
* <p>Supports direct matches (given "/test" -> registered "/test") and "*"
|
||||
* matches (given "/test" -> registered "/t*"). Note that the default is
|
||||
* to map within the current servlet mapping if applicable; see the
|
||||
* {@link #setAlwaysUseFullPath "alwaysUseFullPath"} property for details.
|
||||
* For details on the pattern options, see the
|
||||
* {@link org.springframework.util.AntPathMatcher} javadoc.
|
||||
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see #setMappings
|
||||
* @see #setUrlMap
|
||||
* @see BeanNameUrlHandlerMapping
|
||||
*/
|
||||
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
|
||||
|
||||
private final Map urlMap = new HashMap();
|
||||
|
||||
|
||||
/**
|
||||
* Map URL paths to handler bean names.
|
||||
* This is the typical way of configuring this HandlerMapping.
|
||||
* <p>Supports direct URL matches and Ant-style pattern matches. For syntax
|
||||
* details, see the {@link org.springframework.util.AntPathMatcher} javadoc.
|
||||
* @param mappings properties with URLs as keys and bean names as values
|
||||
* @see #setUrlMap
|
||||
*/
|
||||
public void setMappings(Properties mappings) {
|
||||
this.urlMap.putAll(mappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Map with URL paths as keys and handler beans (or handler bean names)
|
||||
* as values. Convenient for population with bean references.
|
||||
* <p>Supports direct URL matches and Ant-style pattern matches. For syntax
|
||||
* details, see the {@link org.springframework.util.AntPathMatcher} javadoc.
|
||||
* @param urlMap map with URLs as keys and beans as values
|
||||
* @see #setMappings
|
||||
*/
|
||||
public void setUrlMap(Map urlMap) {
|
||||
this.urlMap.putAll(urlMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow Map access to the URL path mappings, with the option to add or
|
||||
* override specific entries.
|
||||
* <p>Useful for specifying entries directly, for example via "urlMap[myKey]".
|
||||
* This is particularly useful for adding or overriding entries in child
|
||||
* bean definitions.
|
||||
*/
|
||||
public Map getUrlMap() {
|
||||
return this.urlMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls the {@link #registerHandlers} method in addition to the
|
||||
* superclass's initialization.
|
||||
*/
|
||||
public void initApplicationContext() throws BeansException {
|
||||
super.initApplicationContext();
|
||||
registerHandlers(this.urlMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all handlers specified in the URL map for the corresponding paths.
|
||||
* @param urlMap Map with URL paths as keys and handler beans or bean names as values
|
||||
* @throws BeansException if a handler couldn't be registered
|
||||
* @throws IllegalStateException if there is a conflicting handler registered
|
||||
*/
|
||||
protected void registerHandlers(Map urlMap) throws BeansException {
|
||||
if (urlMap.isEmpty()) {
|
||||
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
|
||||
}
|
||||
else {
|
||||
Iterator it = urlMap.keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
String url = (String) it.next();
|
||||
Object handler = urlMap.get(url);
|
||||
// Prepend with slash if not already present.
|
||||
if (!url.startsWith("/")) {
|
||||
url = "/" + url;
|
||||
}
|
||||
// Remove whitespace from handler bean name.
|
||||
if (handler instanceof String) {
|
||||
handler = ((String) handler).trim();
|
||||
}
|
||||
registerHandler(url, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Interceptor that checks the authorization of the current user via the
|
||||
* user's roles, as evaluated by HttpServletRequest's isUserInRole method.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 20.06.2003
|
||||
* @see javax.servlet.http.HttpServletRequest#isUserInRole
|
||||
*/
|
||||
public class UserRoleAuthorizationInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private String[] authorizedRoles;
|
||||
|
||||
|
||||
/**
|
||||
* Set the roles that this interceptor should treat as authorized.
|
||||
* @param authorizedRoles array of role names
|
||||
*/
|
||||
public final void setAuthorizedRoles(String[] authorizedRoles) {
|
||||
this.authorizedRoles = authorizedRoles;
|
||||
}
|
||||
|
||||
|
||||
public final boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws ServletException, IOException {
|
||||
|
||||
if (this.authorizedRoles != null) {
|
||||
for (int i = 0; i < this.authorizedRoles.length; i++) {
|
||||
if (request.isUserInRole(this.authorizedRoles[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
handleNotAuthorized(request, response, handler);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a request that is not authorized according to this interceptor.
|
||||
* Default implementation sends HTTP status code 403 ("forbidden").
|
||||
* <p>This method can be overridden to write a custom message, forward or
|
||||
* redirect to some error page or login page, or throw a ServletException.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler chosen handler to execute, for type and/or instance evaluation
|
||||
* @throws javax.servlet.ServletException if there is an internal error
|
||||
* @throws java.io.IOException in case of an I/O error when writing the response
|
||||
*/
|
||||
protected void handleNotAuthorized(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws ServletException, IOException {
|
||||
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.request.WebRequestInterceptor;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Adapter that implements the Servlet HandlerInterceptor interface
|
||||
* and wraps an underlying WebRequestInterceptor.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see org.springframework.web.context.request.WebRequestInterceptor
|
||||
* @see org.springframework.web.servlet.HandlerInterceptor
|
||||
*/
|
||||
public class WebRequestHandlerInterceptorAdapter implements HandlerInterceptor {
|
||||
|
||||
private final WebRequestInterceptor requestInterceptor;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new WebRequestHandlerInterceptorAdapter for the given WebRequestInterceptor.
|
||||
* @param requestInterceptor the WebRequestInterceptor to wrap
|
||||
*/
|
||||
public WebRequestHandlerInterceptorAdapter(WebRequestInterceptor requestInterceptor) {
|
||||
Assert.notNull(requestInterceptor, "WebRequestInterceptor must not be null");
|
||||
this.requestInterceptor = requestInterceptor;
|
||||
}
|
||||
|
||||
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
|
||||
this.requestInterceptor.preHandle(new DispatcherServletWebRequest(request));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
|
||||
throws Exception {
|
||||
|
||||
this.requestInterceptor.postHandle(new DispatcherServletWebRequest(request),
|
||||
(modelAndView != null ? modelAndView.getModelMap() : null));
|
||||
}
|
||||
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
|
||||
throws Exception {
|
||||
|
||||
this.requestInterceptor.afterCompletion(new DispatcherServletWebRequest(request), ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler.metadata;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Constants;
|
||||
import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the HandlerMapping interface that recognizes
|
||||
* metadata attributes of type PathMap on application Controllers and automatically
|
||||
* wires them into the current servlet's WebApplicationContext.
|
||||
*
|
||||
* <p>The path must be mapped to the relevant Spring DispatcherServlet in /WEB-INF/web.xml.
|
||||
* It's possible to have multiple PathMap attributes on the one controller class.
|
||||
*
|
||||
* <p>Controllers instantiated by this class may have dependencies on middle tier
|
||||
* objects, expressed via JavaBean properties or constructor arguments. These will
|
||||
* be resolved automatically.
|
||||
*
|
||||
* <p>You will normally use this HandlerMapping with at most one DispatcherServlet in your
|
||||
* web application. Otherwise you'll end with one instance of the mapped controller for
|
||||
* each DispatcherServlet's context. You <i>might</i> want this -- for example, if
|
||||
* one's using a .pdf mapping and a PDF view, and another a JSP view, or if
|
||||
* using different middle tier objects, but should understand the implications. All
|
||||
* Controllers with attributes will be picked up by each DispatcherServlet's context.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @deprecated as of Spring 2.5, in favor of annotation-based request mapping.
|
||||
* To be removed in Spring 3.0.
|
||||
*/
|
||||
public abstract class AbstractPathMapHandlerMapping extends AbstractUrlHandlerMapping {
|
||||
|
||||
/** Constants instance for AutowireCapableBeanFactory */
|
||||
private static final Constants constants = new Constants(AutowireCapableBeanFactory.class);
|
||||
|
||||
private int autowireMode = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
|
||||
|
||||
private boolean dependencyCheck = true;
|
||||
|
||||
|
||||
/**
|
||||
* Set the autowire mode for handlers, by the name of the corresponding constant
|
||||
* in the AutowireCapableBeanFactory interface, e.g. "AUTOWIRE_BY_NAME".
|
||||
* @param constantName name of the constant
|
||||
* @throws java.lang.IllegalArgumentException if an invalid constant was specified
|
||||
* @see #setAutowireMode
|
||||
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_BY_NAME
|
||||
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_BY_TYPE
|
||||
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_CONSTRUCTOR
|
||||
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_AUTODETECT
|
||||
*/
|
||||
public void setAutowireModeName(String constantName) throws IllegalArgumentException {
|
||||
setAutowireMode(constants.asNumber(constantName).intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the autowire mode for handlers. This determines whether any automagical
|
||||
* detection and setting of bean references will happen.
|
||||
* <p>Default is AUTOWIRE_AUTODETECT, which means either constructor autowiring or
|
||||
* autowiring by type (depending on the constructors available in the class).
|
||||
* @param autowireMode the autowire mode to set.
|
||||
* Must be one of the constants defined in the AutowireCapableBeanFactory interface.
|
||||
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_BY_NAME
|
||||
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_BY_TYPE
|
||||
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_CONSTRUCTOR
|
||||
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_AUTODETECT
|
||||
*/
|
||||
public void setAutowireMode(int autowireMode) {
|
||||
this.autowireMode = autowireMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to perform a dependency check for objects on autowired handlers.
|
||||
* Not applicable to autowiring a constructor, thus ignored there.
|
||||
* <p>Default is "true".
|
||||
*/
|
||||
public void setDependencyCheck(boolean dependencyCheck) {
|
||||
this.dependencyCheck = dependencyCheck;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls the <code>detectAndCreateHandlers</code> method in addition
|
||||
* to the superclass's initialization.
|
||||
* @see #detectAndCreateHandlers
|
||||
*/
|
||||
public void initApplicationContext() throws BeansException {
|
||||
super.initApplicationContext();
|
||||
|
||||
if (!(getApplicationContext() instanceof ConfigurableApplicationContext)) {
|
||||
throw new IllegalStateException(
|
||||
"[" + getClass().getName() + "] needs to run in a ConfigurableApplicationContext");
|
||||
}
|
||||
ConfigurableListableBeanFactory beanFactory =
|
||||
((ConfigurableApplicationContext) getApplicationContext()).getBeanFactory();
|
||||
detectAndCreateHandlers(beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for all classes with a PathMap class attribute, instantiate them in
|
||||
* the owning ApplicationContext, and register them as MVC handlers usable
|
||||
* by the current DispatcherServlet.
|
||||
* @param beanFactory the ConfigurableListableBeanFactory to register the
|
||||
* created handler instances with
|
||||
* @throws BeansException if handler detection or creation failed
|
||||
* @see PathMap
|
||||
* @see #getClassesWithPathMapAttributes()
|
||||
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#createBean
|
||||
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerSingleton
|
||||
*/
|
||||
protected void detectAndCreateHandlers(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
try {
|
||||
Class[] handlerClasses = getClassesWithPathMapAttributes();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found " + handlerClasses.length + " attribute-targeted handlers");
|
||||
}
|
||||
|
||||
// for each Class returned by the Commons Attribute indexer
|
||||
for (int i = 0; i < handlerClasses.length; i++) {
|
||||
Class handlerClass = handlerClasses[i];
|
||||
|
||||
// Autowire the given handler class via AutowireCapableBeanFactory.
|
||||
// Either autowires a constructor or by type, depending on the
|
||||
// constructors available in the given class.
|
||||
Object handler = beanFactory.createBean(handlerClass, this.autowireMode, this.dependencyCheck);
|
||||
|
||||
// We now have an "autowired" handler, that may reference beans in the
|
||||
// application context. We now add the new handler to the factory.
|
||||
// This isn't necessary for the handler to work, but is useful if we want
|
||||
// to enumerate controllers in the factory etc.
|
||||
beanFactory.registerSingleton(handlerClass.getName(), handler);
|
||||
|
||||
// There may be multiple paths mapped to this handler.
|
||||
PathMap[] pathMaps = getPathMapAttributes(handlerClass);
|
||||
registerHandler(pathMaps, handler);
|
||||
}
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new BeanInitializationException("Could not retrieve PathMap attributes", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given handler for the URL paths indicated by the given PathMaps.
|
||||
* @param pathMaps the PathMap attributes for the handler class
|
||||
* @param handler the handler instance
|
||||
* @throws BeansException if the handler couldn't be registered
|
||||
* @throws IllegalStateException if there is a conflicting handler registered
|
||||
*/
|
||||
protected void registerHandler(PathMap[] pathMaps, Object handler) throws BeansException, IllegalStateException {
|
||||
for (int j = 0; j < pathMaps.length; j++) {
|
||||
PathMap pathMap = pathMaps[j];
|
||||
String path = pathMap.getUrl();
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
registerHandler(path, handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use an attribute index to get a Collection of Class objects
|
||||
* with the required PathMap attribute.
|
||||
* @return a array of Class objects
|
||||
*/
|
||||
protected abstract Class[] getClassesWithPathMapAttributes() throws Exception;
|
||||
|
||||
/**
|
||||
* Use Attributes API to find PathMap attributes for the given handler class.
|
||||
* We know there's at least one, as the getClassNamesWithPathMapAttributes
|
||||
* method return this class name.
|
||||
* @param handlerClass the handler class to look for
|
||||
* @return an array of PathMap objects
|
||||
*/
|
||||
protected abstract PathMap[] getPathMapAttributes(Class handlerClass) throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler.metadata;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.attributes.AttributeIndex;
|
||||
import org.apache.commons.attributes.Attributes;
|
||||
|
||||
/**
|
||||
* Subclass of AbstractPathMapHandlerMapping that recognizes Commons Attributes
|
||||
* metadata attributes of type PathMap on application Controllers and automatically
|
||||
* wires them into the current servlet's WebApplicationContext.
|
||||
*
|
||||
* <p>
|
||||
* Controllers must have class attributes of the form:
|
||||
* <code>
|
||||
* &64;org.springframework.web.servlet.handler.commonsattributes.PathMap("/path.cgi")
|
||||
* </code>
|
||||
*
|
||||
* <p>The path must be mapped to the relevant Spring DispatcherServlet in /WEB-INF/web.xml.
|
||||
* It's possible to have multiple PathMap attributes on the one controller class.
|
||||
*
|
||||
* <p>To use this feature, you must compile application classes with Commons Attributes,
|
||||
* and run the Commons Attributes indexer tool on your application classes, which must
|
||||
* be in a Jar rather than in WEB-INF/classes.
|
||||
*
|
||||
* <p>Controllers instantiated by this class may have dependencies on middle tier
|
||||
* objects, expressed via JavaBean properties or constructor arguments. These will
|
||||
* be resolved automatically.
|
||||
*
|
||||
* <p>You will normally use this HandlerMapping with at most one DispatcherServlet in
|
||||
* your web application. Otherwise you'll end with one instance of the mapped controller
|
||||
* for each DispatcherServlet's context. You <i>might</i> want this--for example, if
|
||||
* one's using a .pdf mapping and a PDF view, and another a JSP view, or if using
|
||||
* different middle tier objects, but should understand the implications. All
|
||||
* Controllers with attributes will be picked up by each DispatcherServlet's context.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @deprecated as of Spring 2.5, in favor of annotation-based request mapping.
|
||||
* To be removed in Spring 3.0.
|
||||
*/
|
||||
public class CommonsPathMapHandlerMapping extends AbstractPathMapHandlerMapping {
|
||||
|
||||
/**
|
||||
* Use Commons Attributes AttributeIndex to get a Collection of Class
|
||||
* objects with the required PathMap attribute. Protected so that it can
|
||||
* be overridden during testing.
|
||||
*/
|
||||
protected Class[] getClassesWithPathMapAttributes() throws Exception {
|
||||
AttributeIndex ai = new AttributeIndex(getClass().getClassLoader());
|
||||
Collection classes = ai.getClasses(PathMap.class);
|
||||
return (Class[]) classes.toArray(new Class[classes.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Commons Attributes to find PathMap attributes for the given class.
|
||||
* We know there's at least one, as the getClassNamesWithPathMapAttributes
|
||||
* method return this class name.
|
||||
*/
|
||||
protected PathMap[] getPathMapAttributes(Class handlerClass) {
|
||||
Collection atts = Attributes.getAttributes(handlerClass, PathMap.class);
|
||||
return (PathMap[]) atts.toArray(new PathMap[atts.size()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.handler.metadata;
|
||||
|
||||
/**
|
||||
* Attribute to be used on Controller classes to allow for automatic URL mapping
|
||||
* without web controllers being defined as beans in an XML bean definition file.
|
||||
*
|
||||
* <p>The path map should be the path in the current application, such as /foo.cgi.
|
||||
* If there is no leading "/", one will be prepended.
|
||||
*
|
||||
* <p>Application code must use the Commons Attributes indexer tool to use this option.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @deprecated as of Spring 2.5, in favor of annotation-based request mapping.
|
||||
* To be removed in Spring 3.0.
|
||||
* @@org.apache.commons.attributes.Indexed()
|
||||
*/
|
||||
public class PathMap {
|
||||
|
||||
/*
|
||||
* NB: The Indexed attribute on this class is required. Thus the Spring jar
|
||||
* must be built including a Commons Attributes attribute compilation step
|
||||
* for this class.
|
||||
*/
|
||||
|
||||
private final String url;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new PathMap attribute for the given URL.
|
||||
*/
|
||||
public PathMap(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL that this attribute indicates.
|
||||
*/
|
||||
public String getUrl() {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
This package enables automatic web controller targeting.
|
||||
|
||||
The only implementation at present is CommonsPathMapHandlerMapping, based on Commons
|
||||
Attributes source-level attributes, but metadata implementation is pluggable.
|
||||
<br>
|
||||
In this model, you don't need to map URLs onto controllers in your Spring
|
||||
XML web application context bean definition files. You merely need to
|
||||
set one or more PathMap attributes on each of your Controller classes, and
|
||||
new objects of these types will automatically be added to the relevant
|
||||
web application context.
|
||||
<br>
|
||||
Dependencies--whether expressed by via constructor arguments or JavaBean
|
||||
properties, will be resolved if possible using the middle tier definitions in
|
||||
the WebApplicationContext.
|
||||
|
||||
<p>
|
||||
To use this feature, using the Commons Attributes implementation, perform the following steps:
|
||||
<ol>
|
||||
<li>Invoke the Commons Attributes compiler on your application classes. The
|
||||
classes must be compiled into a Jar rather than the WEB-INF/classes directory.
|
||||
<li>Run the Commons Attributes attribute indexer tool on the Jar containing
|
||||
your controllers.
|
||||
<li>Use Commons Attributes syntax to define a PathMap attribute for each
|
||||
Controller you want mapped.
|
||||
<li>Define a bean of type UrlHandlerMapping in your <code>servletName-servlet.xml</code>
|
||||
bean definition file in your web application. No parameters are required.
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
You can also use other HandlerMappings, such as BeanNameHandlerMapping, in the
|
||||
same servlet XML file.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Provides standard HandlerMapping implementations,
|
||||
including abstract base classes for custom implementations.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.i18n;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.web.servlet.LocaleResolver;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link LocaleResolver} implementations.
|
||||
* Provides support for a default locale.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2.9
|
||||
*/
|
||||
public abstract class AbstractLocaleResolver implements LocaleResolver {
|
||||
|
||||
private Locale defaultLocale;
|
||||
|
||||
|
||||
/**
|
||||
* Set a default Locale that this resolver will return if no other locale found.
|
||||
*/
|
||||
public void setDefaultLocale(Locale defaultLocale) {
|
||||
this.defaultLocale = defaultLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default Locale that this resolver is supposed to fall back to, if any.
|
||||
*/
|
||||
protected Locale getDefaultLocale() {
|
||||
return this.defaultLocale;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.i18n;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.LocaleResolver;
|
||||
|
||||
/**
|
||||
* Implementation of LocaleResolver that simply uses the primary locale
|
||||
* specified in the "accept-language" header of the HTTP request (that is,
|
||||
* the locale sent by the client browser, normally that of the client's OS).
|
||||
*
|
||||
* <p>Note: Does not support <code>setLocale</code>, since the accept header
|
||||
* can only be changed through changing the client's locale settings.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 27.02.2003
|
||||
* @see javax.servlet.http.HttpServletRequest#getLocale()
|
||||
*/
|
||||
public class AcceptHeaderLocaleResolver implements LocaleResolver {
|
||||
|
||||
public Locale resolveLocale(HttpServletRequest request) {
|
||||
return request.getLocale();
|
||||
}
|
||||
|
||||
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Cannot change HTTP accept header - use a different locale resolution strategy");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.i18n;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.LocaleResolver;
|
||||
import org.springframework.web.util.CookieGenerator;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* {@link LocaleResolver} implementation that uses a cookie sent back to the user
|
||||
* in case of a custom setting, with a fallback to the specified default locale
|
||||
* or the request's accept-header locale.
|
||||
*
|
||||
* <p>This is particularly useful for stateless applications without user sessions.
|
||||
*
|
||||
* <p>Custom controllers can thus override the user's locale by calling
|
||||
* {@link #setLocale(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.util.Locale)},
|
||||
* for example responding to a certain locale change request.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Jean-Pierre Pawlak
|
||||
* @since 27.02.2003
|
||||
* @see #setDefaultLocale
|
||||
* @see #setLocale
|
||||
*/
|
||||
public class CookieLocaleResolver extends CookieGenerator implements LocaleResolver {
|
||||
|
||||
/**
|
||||
* The name of the request attribute that holds the locale.
|
||||
* <p>Only used for overriding a cookie value if the locale has been
|
||||
* changed in the course of the current request! Use
|
||||
* {@link org.springframework.web.servlet.support.RequestContext#getLocale}
|
||||
* to retrieve the current locale in controllers or views.
|
||||
* @see org.springframework.web.servlet.support.RequestContext#getLocale
|
||||
*/
|
||||
public static final String LOCALE_REQUEST_ATTRIBUTE_NAME = CookieLocaleResolver.class.getName() + ".LOCALE";
|
||||
|
||||
/**
|
||||
* The default cookie name used if none is explicitly set.
|
||||
*/
|
||||
public static final String DEFAULT_COOKIE_NAME = CookieLocaleResolver.class.getName() + ".LOCALE";
|
||||
|
||||
|
||||
private Locale defaultLocale;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of the {@link CookieLocaleResolver} class
|
||||
* using the {@link #DEFAULT_COOKIE_NAME default cookie name}.
|
||||
*/
|
||||
public CookieLocaleResolver() {
|
||||
setCookieName(DEFAULT_COOKIE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a fixed Locale that this resolver will return if no cookie found.
|
||||
*/
|
||||
public void setDefaultLocale(Locale defaultLocale) {
|
||||
this.defaultLocale = defaultLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fixed Locale that this resolver will return if no cookie found,
|
||||
* if any.
|
||||
*/
|
||||
protected Locale getDefaultLocale() {
|
||||
return this.defaultLocale;
|
||||
}
|
||||
|
||||
|
||||
public Locale resolveLocale(HttpServletRequest request) {
|
||||
// Check request for pre-parsed or preset locale.
|
||||
Locale locale = (Locale) request.getAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME);
|
||||
if (locale != null) {
|
||||
return locale;
|
||||
}
|
||||
|
||||
// Retrieve and parse cookie value.
|
||||
Cookie cookie = WebUtils.getCookie(request, getCookieName());
|
||||
if (cookie != null) {
|
||||
locale = StringUtils.parseLocaleString(cookie.getValue());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Parsed cookie value [" + cookie.getValue() + "] into locale '" + locale + "'");
|
||||
}
|
||||
if (locale != null) {
|
||||
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME, locale);
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
|
||||
return determineDefaultLocale(request);
|
||||
}
|
||||
|
||||
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
|
||||
if (locale != null) {
|
||||
// Set request attribute and add cookie.
|
||||
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME, locale);
|
||||
addCookie(response, locale.toString());
|
||||
}
|
||||
else {
|
||||
// Set request attribute to fallback locale and remove cookie.
|
||||
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME, determineDefaultLocale(request));
|
||||
removeCookie(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the default locale for the given request,
|
||||
* Called if no locale cookie has been found.
|
||||
* <p>The default implementation returns the specified default locale,
|
||||
* if any, else falls back to the request's accept-header locale.
|
||||
* @param request the request to resolve the locale for
|
||||
* @return the default locale (never <code>null</code>)
|
||||
* @see #setDefaultLocale
|
||||
* @see javax.servlet.http.HttpServletRequest#getLocale()
|
||||
*/
|
||||
protected Locale determineDefaultLocale(HttpServletRequest request) {
|
||||
Locale defaultLocale = getDefaultLocale();
|
||||
if (defaultLocale == null) {
|
||||
defaultLocale = request.getLocale();
|
||||
}
|
||||
return defaultLocale;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.i18n;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.web.servlet.LocaleResolver} implementation
|
||||
* that always returns a fixed default locale. Default is the current
|
||||
* JVM's default locale.
|
||||
*
|
||||
* <p>Note: Does not support <code>setLocale</code>, as the fixed locale
|
||||
* cannot be changed.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see #setDefaultLocale
|
||||
*/
|
||||
public class FixedLocaleResolver extends AbstractLocaleResolver {
|
||||
|
||||
/**
|
||||
* Create a default FixedLocaleResolver, exposing a configured default
|
||||
* locale (or the JVM's default locale as fallback).
|
||||
* @see #setDefaultLocale
|
||||
*/
|
||||
public FixedLocaleResolver() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a FixedLocaleResolver that exposes the given locale.
|
||||
* @param locale the locale to expose
|
||||
*/
|
||||
public FixedLocaleResolver(Locale locale) {
|
||||
setDefaultLocale(locale);
|
||||
}
|
||||
|
||||
|
||||
public Locale resolveLocale(HttpServletRequest request) {
|
||||
Locale locale = getDefaultLocale();
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
|
||||
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Cannot change fixed locale - use a different locale resolution strategy");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright 2002-2008 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.i18n;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.propertyeditors.LocaleEditor;
|
||||
import org.springframework.web.servlet.LocaleResolver;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
/**
|
||||
* Interceptor that allows for changing the current locale on every request,
|
||||
* via a configurable request parameter.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 20.06.2003
|
||||
* @see org.springframework.web.servlet.LocaleResolver
|
||||
*/
|
||||
public class LocaleChangeInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
/**
|
||||
* Default name of the locale specification parameter: "locale".
|
||||
*/
|
||||
public static final String DEFAULT_PARAM_NAME = "locale";
|
||||
|
||||
private String paramName = DEFAULT_PARAM_NAME;
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the parameter that contains a locale specification
|
||||
* in a locale change request. Default is "locale".
|
||||
*/
|
||||
public void setParamName(String paramName) {
|
||||
this.paramName = paramName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the parameter that contains a locale specification
|
||||
* in a locale change request.
|
||||
*/
|
||||
public String getParamName() {
|
||||
return this.paramName;
|
||||
}
|
||||
|
||||
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws ServletException {
|
||||
|
||||
String newLocale = request.getParameter(this.paramName);
|
||||
if (newLocale != null) {
|
||||
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
|
||||
if (localeResolver == null) {
|
||||
throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
|
||||
}
|
||||
LocaleEditor localeEditor = new LocaleEditor();
|
||||
localeEditor.setAsText(newLocale);
|
||||
localeResolver.setLocale(request, response, (Locale) localeEditor.getValue());
|
||||
}
|
||||
// Proceed in any case.
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.i18n;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Implementation of LocaleResolver that uses a locale attribute in the user's
|
||||
* session in case of a custom setting, with a fallback to the specified default
|
||||
* locale or the request's accept-header locale.
|
||||
*
|
||||
* <p>This is most appropriate if the application needs user sessions anyway,
|
||||
* that is, when the HttpSession does not have to be created for the locale.
|
||||
*
|
||||
* <p>Custom controllers can override the user's locale by calling
|
||||
* <code>setLocale</code>, e.g. responding to a locale change request.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 27.02.2003
|
||||
* @see #setDefaultLocale
|
||||
* @see #setLocale
|
||||
*/
|
||||
public class SessionLocaleResolver extends AbstractLocaleResolver {
|
||||
|
||||
/**
|
||||
* Name of the session attribute that holds the locale.
|
||||
* Only used internally by this implementation.
|
||||
* Use <code>RequestContext(Utils).getLocale()</code>
|
||||
* to retrieve the current locale in controllers or views.
|
||||
* @see org.springframework.web.servlet.support.RequestContext#getLocale
|
||||
* @see org.springframework.web.servlet.support.RequestContextUtils#getLocale
|
||||
*/
|
||||
public static final String LOCALE_SESSION_ATTRIBUTE_NAME = SessionLocaleResolver.class.getName() + ".LOCALE";
|
||||
|
||||
|
||||
public Locale resolveLocale(HttpServletRequest request) {
|
||||
Locale locale = (Locale) WebUtils.getSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME);
|
||||
if (locale == null) {
|
||||
locale = determineDefaultLocale(request);
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the default locale for the given request,
|
||||
* Called if no locale session attribute has been found.
|
||||
* <p>The default implementation returns the specified default locale,
|
||||
* if any, else falls back to the request's accept-header locale.
|
||||
* @param request the request to resolve the locale for
|
||||
* @return the default locale (never <code>null</code>)
|
||||
* @see #setDefaultLocale
|
||||
* @see javax.servlet.http.HttpServletRequest#getLocale()
|
||||
*/
|
||||
protected Locale determineDefaultLocale(HttpServletRequest request) {
|
||||
Locale defaultLocale = getDefaultLocale();
|
||||
if (defaultLocale == null) {
|
||||
defaultLocale = request.getLocale();
|
||||
}
|
||||
return defaultLocale;
|
||||
}
|
||||
|
||||
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
|
||||
WebUtils.setSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME, locale);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Locale support classes for Spring's web MVC framework.
|
||||
Provides standard LocaleResolver implementations,
|
||||
and a HandlerInterceptor for locale changes.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Abstract base class for custom command controllers.
|
||||
*
|
||||
* <p>Autopopulates a command bean from the request. For command validation,
|
||||
* a validator (property inherited from {@link BaseCommandController}) can be
|
||||
* used.
|
||||
*
|
||||
* <p>In most cases this command controller should not be used to handle form
|
||||
* submission, because functionality for forms is offered in more detail by the
|
||||
* {@link org.springframework.web.servlet.mvc.AbstractFormController} and its
|
||||
* corresponding implementations.
|
||||
*
|
||||
* <p><b><a name="config">Exposed configuration properties</a>
|
||||
* (<a href="BaseCommandController.html#config">and those defined by superclass</a>):</b><br>
|
||||
* <i>none</i> (so only those available in superclass).</p>
|
||||
*
|
||||
* <p><b><a name="workflow">Workflow
|
||||
* (<a name="BaseCommandController.html#workflow">and that defined by superclass</a>):</b><br>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see #setCommandClass
|
||||
* @see #setCommandName
|
||||
* @see #setValidator
|
||||
*/
|
||||
public abstract class AbstractCommandController extends BaseCommandController {
|
||||
|
||||
/**
|
||||
* Create a new AbstractCommandController.
|
||||
*/
|
||||
public AbstractCommandController() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AbstractCommandController.
|
||||
* @param commandClass class of the command bean
|
||||
*/
|
||||
public AbstractCommandController(Class commandClass) {
|
||||
setCommandClass(commandClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AbstractCommandController.
|
||||
* @param commandClass class of the command bean
|
||||
* @param commandName name of the command bean
|
||||
*/
|
||||
public AbstractCommandController(Class commandClass, String commandName) {
|
||||
setCommandClass(commandClass);
|
||||
setCommandName(commandName);
|
||||
}
|
||||
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
Object command = getCommand(request);
|
||||
ServletRequestDataBinder binder = bindAndValidate(request, command);
|
||||
BindException errors = new BindException(binder.getBindingResult());
|
||||
return handle(request, response, command, errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for request handling, providing a populated and validated instance
|
||||
* of the command class, and an Errors object containing binding and validation errors.
|
||||
* <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
|
||||
* with the command and the Errors instance, under the specified command name,
|
||||
* as expected by the "spring:bind" tag.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param command the populated command object
|
||||
* @param errors validation errors holder
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @see org.springframework.validation.Errors
|
||||
* @see org.springframework.validation.BindException#getModel
|
||||
*/
|
||||
protected abstract ModelAndView handle(
|
||||
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
|
||||
throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.support.WebContentGenerator;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* <p>Convenient superclass for controller implementations, using the Template
|
||||
* Method design pattern.</p>
|
||||
*
|
||||
* <p>As stated in the {@link org.springframework.web.servlet.mvc.Controller Controller}
|
||||
* interface, a lot of functionality is already provided by certain abstract
|
||||
* base controllers. The AbstractController is one of the most important
|
||||
* abstract base controller providing basic features such as the generation
|
||||
* of caching headers and the enabling or disabling of
|
||||
* supported methods (GET/POST).</p>
|
||||
*
|
||||
* <p><b><a name="workflow">Workflow
|
||||
* (<a href="Controller.html#workflow">and that defined by interface</a>):</b><br>
|
||||
* <ol>
|
||||
* <li>{@link #handleRequest(HttpServletRequest,HttpServletResponse) handleRequest()}
|
||||
* will be called by the DispatcherServlet</li>
|
||||
* <li>Inspection of supported methods (ServletException if request method
|
||||
* is not support)</li>
|
||||
* <li>If session is required, try to get it (ServletException if not found)</li>
|
||||
* <li>Set caching headers if needed according to cacheSeconds propery</li>
|
||||
* <li>Call abstract method {@link #handleRequestInternal(HttpServletRequest,HttpServletResponse) handleRequestInternal()}
|
||||
* (optionally synchronizing around the call on the HttpSession),
|
||||
* which should be implemented by extending classes to provide actual
|
||||
* functionality to return {@link org.springframework.web.servlet.ModelAndView ModelAndView} objects.</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
*
|
||||
* <p><b><a name="config">Exposed configuration properties</a>
|
||||
* (<a href="Controller.html#config">and those defined by interface</a>):</b><br>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <td><b>name</b></th>
|
||||
* <td><b>default</b></td>
|
||||
* <td><b>description</b></td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>supportedMethods</td>
|
||||
* <td>GET,POST</td>
|
||||
* <td>comma-separated (CSV) list of methods supported by this controller,
|
||||
* such as GET, POST and PUT</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>requireSession</td>
|
||||
* <td>false</td>
|
||||
* <td>whether a session should be required for requests to be able to
|
||||
* be handled by this controller. This ensures that derived controller
|
||||
* can - without fear of null pointers - call request.getSession() to
|
||||
* retrieve a session. If no session can be found while processing
|
||||
* the request, a ServletException will be thrown</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>cacheSeconds</td>
|
||||
* <td>-1</td>
|
||||
* <td>indicates the amount of seconds to include in the cache header
|
||||
* for the response following on this request. 0 (zero) will include
|
||||
* headers for no caching at all, -1 (the default) will not generate
|
||||
* <i>any headers</i> and any positive number will generate headers
|
||||
* that state the amount indicated as seconds to cache the content</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>synchronizeOnSession</td>
|
||||
* <td>false</td>
|
||||
* <td>whether the call to <code>handleRequestInternal</code> should be
|
||||
* synchronized around the HttpSession, to serialize invocations
|
||||
* from the same client. No effect if there is no HttpSession.
|
||||
* </td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see WebContentInterceptor
|
||||
*/
|
||||
public abstract class AbstractController extends WebContentGenerator implements Controller {
|
||||
|
||||
private boolean synchronizeOnSession = false;
|
||||
|
||||
|
||||
/**
|
||||
* Set if controller execution should be synchronized on the session,
|
||||
* to serialize parallel invocations from the same client.
|
||||
* <p>More specifically, the execution of the <code>handleRequestInternal</code>
|
||||
* method will get synchronized if this flag is "true". The best available
|
||||
* session mutex will be used for the synchronization; ideally, this will
|
||||
* be a mutex exposed by HttpSessionMutexListener.
|
||||
* <p>The session mutex is guaranteed to be the same object during
|
||||
* the entire lifetime of the session, available under the key defined
|
||||
* by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a
|
||||
* safe reference to synchronize on for locking on the current session.
|
||||
* <p>In many cases, the HttpSession reference itself is a safe mutex
|
||||
* as well, since it will always be the same object reference for the
|
||||
* same active logical session. However, this is not guaranteed across
|
||||
* different servlet containers; the only 100% safe way is a session mutex.
|
||||
* @see org.springframework.web.servlet.mvc.AbstractController#handleRequestInternal
|
||||
* @see org.springframework.web.util.HttpSessionMutexListener
|
||||
* @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
|
||||
*/
|
||||
public final void setSynchronizeOnSession(boolean synchronizeOnSession) {
|
||||
this.synchronizeOnSession = synchronizeOnSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether controller execution should be synchronized on the session.
|
||||
*/
|
||||
public final boolean isSynchronizeOnSession() {
|
||||
return this.synchronizeOnSession;
|
||||
}
|
||||
|
||||
|
||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
// Delegate to WebContentGenerator for checking and preparing.
|
||||
checkAndPrepare(request, response, this instanceof LastModified);
|
||||
|
||||
// Execute handleRequestInternal in synchronized block if required.
|
||||
if (this.synchronizeOnSession) {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
Object mutex = WebUtils.getSessionMutex(session);
|
||||
synchronized (mutex) {
|
||||
return handleRequestInternal(request, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return handleRequestInternal(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method. Subclasses must implement this.
|
||||
* The contract is the same as for <code>handleRequest</code>.
|
||||
* @see #handleRequest
|
||||
*/
|
||||
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,674 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.mvc;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.HttpSessionRequiredException;
|
||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* <p>Form controller that auto-populates a form bean from the request.
|
||||
* This, either using a new bean instance per request, or using the same bean
|
||||
* when the <code>sessionForm</code> property has been set to <code>true</code>.</p>
|
||||
*
|
||||
* <p>This class is the base class for both framework subclasses such as
|
||||
* {@link SimpleFormController} and {@link AbstractWizardFormController}
|
||||
* and custom form controllers that you may provide yourself.</p>
|
||||
*
|
||||
* <p>A form-input view and an after-submission view have to be provided
|
||||
* programmatically. To provide those views using configuration properties,
|
||||
* use the {@link SimpleFormController}.</p>
|
||||
*
|
||||
* <p>Subclasses need to override <code>showForm</code> to prepare the form view,
|
||||
* and <code>processFormSubmission</code> to handle submit requests. For the latter,
|
||||
* binding errors like type mismatches will be reported via the given "errors" holder.
|
||||
* For additional custom form validation, a validator (property inherited from
|
||||
* BaseCommandController) can be used, reporting via the same "errors" instance.</p>
|
||||
*
|
||||
* <p>Comparing this Controller to the Struts notion of the <code>Action</code>
|
||||
* shows us that with Spring, you can use any ordinary JavaBeans or database-
|
||||
* backed JavaBeans without having to implement a framework-specific class
|
||||
* (like Struts' <code>ActionForm</code>). More complex properties of JavaBeans
|
||||
* (Dates, Locales, but also your own application-specific or compound types)
|
||||
* can be represented and submitted to the controller, by using the notion of
|
||||
* a <code>java.beans.PropertyEditor</code>. For more information on that
|
||||
* subject, see the workflow of this controller and the explanation of the
|
||||
* {@link BaseCommandController}.</p>
|
||||
*
|
||||
* <p><b><a name="workflow">Workflow
|
||||
* (<a href="BaseCommandController.html#workflow">and that defined by superclass</a>):</b><br>
|
||||
* <ol>
|
||||
* <li><b>The controller receives a request for a new form (typically a GET).</b></li>
|
||||
* <li>Call to {@link #formBackingObject formBackingObject()} which by default,
|
||||
* returns an instance of the commandClass that has been configured
|
||||
* (see the properties the superclass exposes), but can also be overridden
|
||||
* to e.g. retrieve an object from the database (that needs to be modified
|
||||
* using the form).</li>
|
||||
* <li>Call to {@link #initBinder initBinder()} which allows you to register
|
||||
* custom editors for certain fields (often properties of non-primitive
|
||||
* or non-String types) of the command class. This will render appropriate
|
||||
* Strings for those property values, e.g. locale-specific date strings.</li>
|
||||
* <li><em>Only if <code>bindOnNewForm</code> is set to <code>true</code></em>, then
|
||||
* {@link org.springframework.web.bind.ServletRequestDataBinder ServletRequestDataBinder}
|
||||
* gets applied to populate the new form object with initial request parameters and the
|
||||
* {@link #onBindOnNewForm(HttpServletRequest, Object, BindException)} callback method is
|
||||
* called. <em>Note:</em> any defined Validators are not applied at this point, to allow
|
||||
* partial binding. However be aware that any Binder customizations applied via
|
||||
* initBinder() (such as
|
||||
* {@link org.springframework.validation.DataBinder#setRequiredFields(String[])} will
|
||||
* still apply. As such, if using bindOnNewForm=true and initBinder() customizations are
|
||||
* used to validate fields instead of using Validators, in the case that only some fields
|
||||
* will be populated for the new form, there will potentially be some bind errors for
|
||||
* missing fields in the errors object. Any view (JSP, etc.) that displays binder errors
|
||||
* needs to be intelligent and for this case take into account whether it is displaying the
|
||||
* initial form view or subsequent post results, skipping error display for the former.</li>
|
||||
* <li>Call to {@link #showForm(HttpServletRequest, HttpServletResponse, BindException) showForm()}
|
||||
* to return a View that should be rendered (typically the view that renders
|
||||
* the form). This method has to be implemented in subclasses.</li>
|
||||
* <li>The showForm() implementation will call {@link #referenceData referenceData()},
|
||||
* which you can implement to provide any relevant reference data you might need
|
||||
* when editing a form (e.g. a List of Locale objects you're going to let the
|
||||
* user select one from).</li>
|
||||
* <li>Model gets exposed and view gets rendered, to let the user fill in the form.</li>
|
||||
* <li><b>The controller receives a form submission (typically a POST).</b>
|
||||
* To use a different way of detecting a form submission, override the
|
||||
* {@link #isFormSubmission isFormSubmission} method.
|
||||
* </li>
|
||||
* <li>If <code>sessionForm</code> is not set, {@link #formBackingObject formBackingObject()}
|
||||
* is called to retrieve a form object. Otherwise, the controller tries to
|
||||
* find the command object which is already bound in the session. If it cannot
|
||||
* find the object, it does a call to {@link #handleInvalidSubmit handleInvalidSubmit}
|
||||
* which - by default - tries to create a new form object and resubmit the form.</li>
|
||||
* <li>The {@link org.springframework.web.bind.ServletRequestDataBinder ServletRequestDataBinder}
|
||||
* gets applied to populate the form object with current request parameters.
|
||||
* <li>Call to {@link #onBind onBind(HttpServletRequest, Object, Errors)} which allows
|
||||
* you to do custom processing after binding but before validation (e.g. to manually
|
||||
* bind request parameters to bean properties, to be seen by the Validator).</li>
|
||||
* <li>If <code>validateOnBinding</code> is set, a registered Validator will be invoked.
|
||||
* The Validator will check the form object properties, and register corresponding
|
||||
* errors via the given {@link org.springframework.validation.Errors Errors}</li> object.
|
||||
* <li>Call to {@link #onBindAndValidate onBindAndValidate()} which allows you
|
||||
* to do custom processing after binding and validation (e.g. to manually
|
||||
* bind request parameters, and to validate them outside a Validator).</li>
|
||||
* <li>Call {@link #processFormSubmission(HttpServletRequest, HttpServletResponse,
|
||||
* Object, BindException) processFormSubmission()} to process the submission, with
|
||||
* or without binding errors. This method has to be implemented in subclasses.</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
*
|
||||
* <p>In session form mode, a submission without an existing form object in the
|
||||
* session is considered invalid, like in case of a resubmit/reload by the browser.
|
||||
* The {@link #handleInvalidSubmit handleInvalidSubmit} method is invoked then,
|
||||
* by default trying to resubmit. It can be overridden in subclasses to show
|
||||
* corresponding messages or to redirect to a new form, in order to avoid duplicate
|
||||
* submissions. The form object in the session can be considered a transaction
|
||||
* token in that case.</p>
|
||||
*
|
||||
* <p>Note that views should never retrieve form beans from the session but always
|
||||
* from the request, as prepared by the form controller. Remember that some view
|
||||
* technologies like Velocity cannot even access a HTTP session.</p>
|
||||
*
|
||||
* <p><b><a name="config">Exposed configuration properties</a>
|
||||
* (<a href="BaseCommandController.html#config">and those defined by superclass</a>):</b><br>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <td><b>name</b></td>
|
||||
* <td><b>default</b></td>
|
||||
* <td><b>description</b></td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>bindOnNewForm</td>
|
||||
* <td>false</td>
|
||||
* <td>Indicates whether to bind servlet request parameters when
|
||||
* creating a new form. Otherwise, the parameters will only be
|
||||
* bound on form submission attempts.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>sessionForm</td>
|
||||
* <td>false</td>
|
||||
* <td>Indicates whether the form object should be kept in the session
|
||||
* when a user asks for a new form. This allows you e.g. to retrieve
|
||||
* an object from the database, let the user edit it, and then persist
|
||||
* it again. Otherwise, a new command object will be created for each
|
||||
* request (even when showing the form again after validation errors).</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* </p>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Alef Arendsen
|
||||
* @author Rob Harrop
|
||||
* @author Colin Sampaleanu
|
||||
* @see #showForm(HttpServletRequest, HttpServletResponse, BindException)
|
||||
* @see #processFormSubmission
|
||||
* @see SimpleFormController
|
||||
* @see AbstractWizardFormController
|
||||
*/
|
||||
public abstract class AbstractFormController extends BaseCommandController {
|
||||
|
||||
private boolean bindOnNewForm = false;
|
||||
|
||||
private boolean sessionForm = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new AbstractFormController.
|
||||
* <p>Subclasses should set the following properties, either in the constructor
|
||||
* or via a BeanFactory: commandName, commandClass, bindOnNewForm, sessionForm.
|
||||
* Note that "commandClass" doesn't need to be set when overriding
|
||||
* {@link #formBackingObject}, since the latter determines the class anyway.
|
||||
* <p>"cacheSeconds" is by default set to 0 (-> no caching for all form controllers).
|
||||
* @see #setCommandName
|
||||
* @see #setCommandClass
|
||||
* @see #setBindOnNewForm
|
||||
* @see #setSessionForm
|
||||
* @see #formBackingObject
|
||||
*/
|
||||
public AbstractFormController() {
|
||||
setCacheSeconds(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether request parameters should be bound to the form object
|
||||
* in case of a non-submitting request, that is, a new form.
|
||||
*/
|
||||
public final void setBindOnNewForm(boolean bindOnNewForm) {
|
||||
this.bindOnNewForm = bindOnNewForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if request parameters should be bound in case of a new form.
|
||||
*/
|
||||
public final boolean isBindOnNewForm() {
|
||||
return this.bindOnNewForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate/deactivate session form mode. In session form mode,
|
||||
* the form is stored in the session to keep the form object instance
|
||||
* between requests, instead of creating a new one on each request.
|
||||
* <p>This is necessary for either wizard-style controllers that populate a
|
||||
* single form object from multiple pages, or forms that populate a persistent
|
||||
* object that needs to be identical to allow for tracking changes.
|
||||
* <p>Please note that the {@link AbstractFormController} class (and all
|
||||
* subclasses of it unless stated to the contrary) do <i>not</i> support
|
||||
* the notion of a conversation. This is important in the context of this
|
||||
* property, because it means that there is only <i>one</i> form per session:
|
||||
* this means that if session form mode is activated and a user opens up
|
||||
* say two tabs in their browser and attempts to edit two distinct objects
|
||||
* using the same form, then the <i>shared</i> session state can potentially
|
||||
* (and most probably will) be overwritten by the last tab to be opened,
|
||||
* which can lead to errors when either of the forms in each is finally
|
||||
* submitted.
|
||||
* <p>If you need to have per-form, per-session state management (that is,
|
||||
* stateful web conversations), the recommendation is to use
|
||||
* <a href="http://www.springframework.org/webflow">Spring WebFlow</a>,
|
||||
* which has full support for conversations and has a much more flexible
|
||||
* usage model overall.
|
||||
* @param sessionForm <code>true</code> if session form mode is to be activated
|
||||
*/
|
||||
public final void setSessionForm(boolean sessionForm) {
|
||||
this.sessionForm = sessionForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if session form mode is activated.
|
||||
*/
|
||||
public final boolean isSessionForm() {
|
||||
return this.sessionForm;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles two cases: form submissions and showing a new form.
|
||||
* Delegates the decision between the two to {@link #isFormSubmission},
|
||||
* always treating requests without existing form session attribute
|
||||
* as new form when using session form mode.
|
||||
* @see #isFormSubmission
|
||||
* @see #showNewForm
|
||||
* @see #processFormSubmission
|
||||
*/
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
// Form submission or new form to show?
|
||||
if (isFormSubmission(request)) {
|
||||
// Fetch form object from HTTP session, bind, validate, process submission.
|
||||
try {
|
||||
Object command = getCommand(request);
|
||||
ServletRequestDataBinder binder = bindAndValidate(request, command);
|
||||
BindException errors = new BindException(binder.getBindingResult());
|
||||
return processFormSubmission(request, response, command, errors);
|
||||
}
|
||||
catch (HttpSessionRequiredException ex) {
|
||||
// Cannot submit a session form if no form object is in the session.
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invalid submit detected: " + ex.getMessage());
|
||||
}
|
||||
return handleInvalidSubmit(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
// New form to show: render form view.
|
||||
return showNewForm(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given request represents a form submission.
|
||||
* <p>The default implementation treats a POST request as form submission.
|
||||
* Note: If the form session attribute doesn't exist when using session form
|
||||
* mode, the request is always treated as new form by handleRequestInternal.
|
||||
* <p>Subclasses can override this to use a custom strategy, e.g. a specific
|
||||
* request parameter (assumably a hidden field or submit button name).
|
||||
* @param request current HTTP request
|
||||
* @return if the request represents a form submission
|
||||
*/
|
||||
protected boolean isFormSubmission(HttpServletRequest request) {
|
||||
return "POST".equals(request.getMethod());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the HttpSession attribute that holds the form object
|
||||
* for this form controller.
|
||||
* <p>The default implementation delegates to the {@link #getFormSessionAttributeName()}
|
||||
* variant without arguments.
|
||||
* @param request current HTTP request
|
||||
* @return the name of the form session attribute, or <code>null</code> if not in session form mode
|
||||
* @see #getFormSessionAttributeName
|
||||
* @see javax.servlet.http.HttpSession#getAttribute
|
||||
*/
|
||||
protected String getFormSessionAttributeName(HttpServletRequest request) {
|
||||
return getFormSessionAttributeName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the HttpSession attribute that holds the form object
|
||||
* for this form controller.
|
||||
* <p>Default is an internal name, of no relevance to applications, as the form
|
||||
* session attribute is not usually accessed directly. Can be overridden to use
|
||||
* an application-specific attribute name, which allows other code to access
|
||||
* the session attribute directly.
|
||||
* @return the name of the form session attribute
|
||||
* @see javax.servlet.http.HttpSession#getAttribute
|
||||
*/
|
||||
protected String getFormSessionAttributeName() {
|
||||
return getClass().getName() + ".FORM." + getCommandName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show a new form. Prepares a backing object for the current form
|
||||
* and the given request, including checking its validity.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @return the prepared form view
|
||||
* @throws Exception in case of an invalid new form object
|
||||
* @see #getErrorsForNewForm
|
||||
*/
|
||||
protected final ModelAndView showNewForm(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
logger.debug("Displaying new form");
|
||||
return showForm(request, response, getErrorsForNewForm(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a BindException instance for a new form.
|
||||
* Called by {@link #showNewForm}.
|
||||
* <p>Can be used directly when intending to show a new form but with
|
||||
* special errors registered on it (for example, on invalid submit).
|
||||
* Usually, the resulting BindException will be passed to
|
||||
* {@link #showForm(HttpServletRequest, HttpServletResponse, BindException)},
|
||||
* after registering the errors on it.
|
||||
* @param request current HTTP request
|
||||
* @return the BindException instance
|
||||
* @throws Exception in case of an invalid new form object
|
||||
* @see #showNewForm
|
||||
* @see #showForm(HttpServletRequest, HttpServletResponse, BindException)
|
||||
* @see #handleInvalidSubmit
|
||||
*/
|
||||
protected final BindException getErrorsForNewForm(HttpServletRequest request) throws Exception {
|
||||
// Create form-backing object for new form.
|
||||
Object command = formBackingObject(request);
|
||||
if (command == null) {
|
||||
throw new ServletException("Form object returned by formBackingObject() must not be null");
|
||||
}
|
||||
if (!checkCommand(command)) {
|
||||
throw new ServletException("Form object returned by formBackingObject() must match commandClass");
|
||||
}
|
||||
|
||||
// Bind without validation, to allow for prepopulating a form, and for
|
||||
// convenient error evaluation in views (on both first attempt and resubmit).
|
||||
ServletRequestDataBinder binder = createBinder(request, command);
|
||||
BindException errors = new BindException(binder.getBindingResult());
|
||||
if (isBindOnNewForm()) {
|
||||
logger.debug("Binding to new form");
|
||||
binder.bind(request);
|
||||
onBindOnNewForm(request, command, errors);
|
||||
}
|
||||
|
||||
// Return BindException object that resulted from binding.
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for custom post-processing in terms of binding for a new form.
|
||||
* Called when preparing a new form if <code>bindOnNewForm</code> is <code>true</code>.
|
||||
* <p>The default implementation delegates to <code>onBindOnNewForm(request, command)</code>.
|
||||
* @param request current HTTP request
|
||||
* @param command the command object to perform further binding on
|
||||
* @param errors validation errors holder, allowing for additional
|
||||
* custom registration of binding errors
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #onBindOnNewForm(javax.servlet.http.HttpServletRequest, Object)
|
||||
* @see #setBindOnNewForm
|
||||
*/
|
||||
protected void onBindOnNewForm(HttpServletRequest request, Object command, BindException errors)
|
||||
throws Exception {
|
||||
|
||||
onBindOnNewForm(request, command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for custom post-processing in terms of binding for a new form.
|
||||
* <p>Called by the default implementation of the
|
||||
* {@link #onBindOnNewForm(HttpServletRequest, Object, BindException)} variant
|
||||
* with all parameters, after standard binding when displaying the form view.
|
||||
* Only called if <code>bindOnNewForm</code> is set to <code>true</code>.
|
||||
* <p>The default implementation is empty.
|
||||
* @param request current HTTP request
|
||||
* @param command the command object to perform further binding on
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #onBindOnNewForm(HttpServletRequest, Object, BindException)
|
||||
* @see #setBindOnNewForm(boolean)
|
||||
*/
|
||||
protected void onBindOnNewForm(HttpServletRequest request, Object command) throws Exception {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the form object for the given request.
|
||||
* <p>Calls {@link #formBackingObject} if not in session form mode.
|
||||
* Else, retrieves the form object from the session. Note that the form object
|
||||
* gets removed from the session, but it will be re-added when showing the
|
||||
* form for resubmission.
|
||||
* @param request current HTTP request
|
||||
* @return object form to bind onto
|
||||
* @throws org.springframework.web.HttpSessionRequiredException
|
||||
* if a session was expected but no active session (or session form object) found
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #formBackingObject
|
||||
*/
|
||||
protected final Object getCommand(HttpServletRequest request) throws Exception {
|
||||
// If not in session-form mode, create a new form-backing object.
|
||||
if (!isSessionForm()) {
|
||||
return formBackingObject(request);
|
||||
}
|
||||
|
||||
// Session-form mode: retrieve form object from HTTP session attribute.
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) {
|
||||
throw new HttpSessionRequiredException("Must have session when trying to bind (in session-form mode)");
|
||||
}
|
||||
String formAttrName = getFormSessionAttributeName(request);
|
||||
Object sessionFormObject = session.getAttribute(formAttrName);
|
||||
if (sessionFormObject == null) {
|
||||
throw new HttpSessionRequiredException("Form object not found in session (in session-form mode)");
|
||||
}
|
||||
|
||||
// Remove form object from HTTP session: we might finish the form workflow
|
||||
// in this request. If it turns out that we need to show the form view again,
|
||||
// we'll re-bind the form object to the HTTP session.
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removing form session attribute [" + formAttrName + "]");
|
||||
}
|
||||
session.removeAttribute(formAttrName);
|
||||
|
||||
return currentFormObject(request, sessionFormObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a backing object for the current form from the given request.
|
||||
* <p>The properties of the form object will correspond to the form field values
|
||||
* in your form view. This object will be exposed in the model under the specified
|
||||
* command name, to be accessed under that name in the view: for example, with
|
||||
* a "spring:bind" tag. The default command name is "command".
|
||||
* <p>Note that you need to activate session form mode to reuse the form-backing
|
||||
* object across the entire form workflow. Else, a new instance of the command
|
||||
* class will be created for each submission attempt, just using this backing
|
||||
* object as template for the initial form.
|
||||
* <p>The default implementation calls {@link #createCommand()},
|
||||
* creating a new empty instance of the specified command class.
|
||||
* Subclasses can override this to provide a preinitialized backing object.
|
||||
* @param request current HTTP request
|
||||
* @return the backing object
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #setCommandName
|
||||
* @see #setCommandClass
|
||||
* @see #createCommand
|
||||
*/
|
||||
protected Object formBackingObject(HttpServletRequest request) throws Exception {
|
||||
return createCommand();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current form object to use for binding and further processing,
|
||||
* based on the passed-in form object as found in the HttpSession.
|
||||
* <p>The default implementation simply returns the session form object as-is.
|
||||
* Subclasses can override this to post-process the session form object,
|
||||
* for example reattaching it to a persistence manager.
|
||||
* @param sessionFormObject the form object retrieved from the HttpSession
|
||||
* @return the form object to use for binding and further processing
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
*/
|
||||
protected Object currentFormObject(HttpServletRequest request, Object sessionFormObject) throws Exception {
|
||||
return sessionFormObject;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare the form model and view, including reference and error data.
|
||||
* Can show a configured form page, or generate a form view programmatically.
|
||||
* <p>A typical implementation will call
|
||||
* <code>showForm(request, errors, "myView")</code>
|
||||
* to prepare the form view for a specific view name, returning the
|
||||
* ModelAndView provided there.
|
||||
* <p>For building a custom ModelAndView, call <code>errors.getModel()</code>
|
||||
* to populate the ModelAndView model with the command and the Errors instance,
|
||||
* under the specified command name, as expected by the "spring:bind" tag.
|
||||
* You also need to include the model returned by {@link #referenceData}.
|
||||
* <p>Note: If you decide to have a "formView" property specifying the
|
||||
* view name, consider using SimpleFormController.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param errors validation errors holder
|
||||
* @return the prepared form view, or <code>null</code> if handled directly
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #showForm(HttpServletRequest, BindException, String)
|
||||
* @see org.springframework.validation.Errors
|
||||
* @see org.springframework.validation.BindException#getModel
|
||||
* @see #referenceData(HttpServletRequest, Object, Errors)
|
||||
* @see SimpleFormController#setFormView
|
||||
*/
|
||||
protected abstract ModelAndView showForm(
|
||||
HttpServletRequest request, HttpServletResponse response, BindException errors)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Prepare model and view for the given form, including reference and errors.
|
||||
* <p>In session form mode: Re-puts the form object in the session when
|
||||
* returning to the form, as it has been removed by getCommand.
|
||||
* <p>Can be used in subclasses to redirect back to a specific form page.
|
||||
* @param request current HTTP request
|
||||
* @param errors validation errors holder
|
||||
* @param viewName name of the form view
|
||||
* @return the prepared form view
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
*/
|
||||
protected final ModelAndView showForm(HttpServletRequest request, BindException errors, String viewName)
|
||||
throws Exception {
|
||||
|
||||
return showForm(request, errors, viewName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare model and view for the given form, including reference and errors,
|
||||
* adding a controller-specific control model.
|
||||
* <p>In session form mode: Re-puts the form object in the session when returning
|
||||
* to the form, as it has been removed by getCommand.
|
||||
* <p>Can be used in subclasses to redirect back to a specific form page.
|
||||
* @param request current HTTP request
|
||||
* @param errors validation errors holder
|
||||
* @param viewName name of the form view
|
||||
* @param controlModel model map containing controller-specific control data
|
||||
* (e.g. current page in wizard-style controllers or special error message)
|
||||
* @return the prepared form view
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
*/
|
||||
protected final ModelAndView showForm(
|
||||
HttpServletRequest request, BindException errors, String viewName, Map controlModel)
|
||||
throws Exception {
|
||||
|
||||
// In session form mode, re-expose form object as HTTP session attribute.
|
||||
// Re-binding is necessary for proper state handling in a cluster,
|
||||
// to notify other nodes of changes in the form object.
|
||||
if (isSessionForm()) {
|
||||
String formAttrName = getFormSessionAttributeName(request);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Setting form session attribute [" + formAttrName + "] to: " + errors.getTarget());
|
||||
}
|
||||
request.getSession().setAttribute(formAttrName, errors.getTarget());
|
||||
}
|
||||
|
||||
// Fetch errors model as starting point, containing form object under
|
||||
// "commandName", and corresponding Errors instance under internal key.
|
||||
Map model = errors.getModel();
|
||||
|
||||
// Merge reference data into model, if any.
|
||||
Map referenceData = referenceData(request, errors.getTarget(), errors);
|
||||
if (referenceData != null) {
|
||||
model.putAll(referenceData);
|
||||
}
|
||||
|
||||
// Merge control attributes into model, if any.
|
||||
if (controlModel != null) {
|
||||
model.putAll(controlModel);
|
||||
}
|
||||
|
||||
// Trigger rendering of the specified view, using the final model.
|
||||
return new ModelAndView(viewName, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference data map for the given request, consisting of
|
||||
* bean name/bean instance pairs as expected by ModelAndView.
|
||||
* <p>The default implementation returns <code>null</code>.
|
||||
* Subclasses can override this to set reference data used in the view.
|
||||
* @param request current HTTP request
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @param errors validation errors holder
|
||||
* @return a Map with reference data entries, or <code>null</code> if none
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see ModelAndView
|
||||
*/
|
||||
protected Map referenceData(HttpServletRequest request, Object command, Errors errors) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process form submission request. Called by {@link #handleRequestInternal}
|
||||
* in case of a form submission, with or without binding errors. Implementations
|
||||
* need to proceed properly, typically showing a form view in case of binding
|
||||
* errors or performing a submit action else.
|
||||
* <p>Subclasses can implement this to provide custom submission handling like
|
||||
* triggering a custom action. They can also provide custom validation and call
|
||||
* {@link #showForm(HttpServletRequest, HttpServletResponse, BindException)}
|
||||
* or proceed with the submission accordingly.
|
||||
* <p>For a success view, call <code>errors.getModel()</code> to populate the
|
||||
* ModelAndView model with the command and the Errors instance, under the
|
||||
* specified command name, as expected by the "spring:bind" tag. For a form view,
|
||||
* simply return the ModelAndView object provided by
|
||||
* {@link #showForm(HttpServletRequest, HttpServletResponse, BindException)}.
|
||||
* @param request current servlet request
|
||||
* @param response current servlet response
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @param errors holder without errors (subclass can add errors if it wants to)
|
||||
* @return the prepared model and view, or <code>null</code>
|
||||
* @throws Exception in case of errors
|
||||
* @see #handleRequestInternal
|
||||
* @see #isFormSubmission
|
||||
* @see #showForm(HttpServletRequest, HttpServletResponse, BindException)
|
||||
* @see org.springframework.validation.Errors
|
||||
* @see org.springframework.validation.BindException#getModel
|
||||
*/
|
||||
protected abstract ModelAndView processFormSubmission(
|
||||
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Handle an invalid submit request, e.g. when in session form mode but no form object
|
||||
* was found in the session (like in case of an invalid resubmit by the browser).
|
||||
* <p>The default implementation simply tries to resubmit the form with a new
|
||||
* form object. This should also work if the user hit the back button, changed
|
||||
* some form data, and resubmitted the form.
|
||||
* <p>Note: To avoid duplicate submissions, you need to override this method.
|
||||
* Either show some "invalid submit" message, or call {@link #showNewForm} for
|
||||
* resetting the form (prepopulating it with the current values if "bindOnNewForm"
|
||||
* is true). In this case, the form object in the session serves as transaction token.
|
||||
* <pre>
|
||||
* protected ModelAndView handleInvalidSubmit(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
* return showNewForm(request, response);
|
||||
* }</pre>
|
||||
* You can also show a new form but with special errors registered on it:
|
||||
* <pre class="code">
|
||||
* protected ModelAndView handleInvalidSubmit(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
* BindException errors = getErrorsForNewForm(request);
|
||||
* errors.reject("duplicateFormSubmission", "Duplicate form submission");
|
||||
* return showForm(request, response, errors);
|
||||
* }</pre>
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @return a prepared view, or <code>null</code> if handled directly
|
||||
* @throws Exception in case of errors
|
||||
* @see #showNewForm
|
||||
* @see #getErrorsForNewForm
|
||||
* @see #showForm(HttpServletRequest, HttpServletResponse, BindException)
|
||||
* @see #setBindOnNewForm
|
||||
*/
|
||||
protected ModelAndView handleInvalidSubmit(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
Object command = formBackingObject(request);
|
||||
ServletRequestDataBinder binder = bindAndValidate(request, command);
|
||||
BindException errors = new BindException(binder.getBindingResult());
|
||||
return processFormSubmission(request, response, command, errors);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* Abstract base class for <code>Controllers</code> that return a view name
|
||||
* based on the request URL.
|
||||
*
|
||||
* <p>Provides infrastructure for determining view names from URLs and configurable
|
||||
* URL lookup. For information on the latter, see <code>alwaysUseFullPath</code>
|
||||
* and <code>urlDecode</code> properties.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2.6
|
||||
* @see #setAlwaysUseFullPath
|
||||
* @see #setUrlDecode
|
||||
*/
|
||||
public abstract class AbstractUrlViewController extends AbstractController {
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
|
||||
/**
|
||||
* Set if URL lookup should always use full path within current servlet
|
||||
* context. Else, the path within the current servlet mapping is used
|
||||
* if applicable (i.e. in the case of a ".../*" servlet mapping in web.xml).
|
||||
* Default is "false".
|
||||
* @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
|
||||
*/
|
||||
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
|
||||
this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if context path and request URI should be URL-decoded.
|
||||
* Both are returned <i>undecoded</i> by the Servlet API,
|
||||
* in contrast to the servlet path.
|
||||
* <p>Uses either the request encoding or the default encoding according
|
||||
* to the Servlet spec (ISO-8859-1).
|
||||
* @see org.springframework.web.util.UrlPathHelper#setUrlDecode
|
||||
*/
|
||||
public void setUrlDecode(boolean urlDecode) {
|
||||
this.urlPathHelper.setUrlDecode(urlDecode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the UrlPathHelper to use for the resolution of lookup paths.
|
||||
* <p>Use this to override the default UrlPathHelper with a custom subclass,
|
||||
* or to share common UrlPathHelper settings across multiple MethodNameResolvers
|
||||
* and HandlerMappings.
|
||||
* @see org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#setUrlPathHelper
|
||||
*/
|
||||
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
|
||||
this.urlPathHelper = urlPathHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the UrlPathHelper to use for the resolution of lookup paths.
|
||||
*/
|
||||
protected UrlPathHelper getUrlPathHelper() {
|
||||
return this.urlPathHelper;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the URL path to use for lookup and delegates to
|
||||
* {@link #getViewNameForRequest}.
|
||||
*/
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {
|
||||
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
|
||||
String viewName = getViewNameForRequest(request);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Returning view name '" + viewName + "' for lookup path [" + lookupPath + "]");
|
||||
}
|
||||
return new ModelAndView(viewName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the view to render for this request, based on the
|
||||
* given lookup path. Called by {@link #handleRequestInternal}.
|
||||
* @param request current HTTP request
|
||||
* @return a view name for this request (never <code>null</code>)
|
||||
* @see #handleRequestInternal
|
||||
* @see #setAlwaysUseFullPath
|
||||
* @see #setUrlDecode
|
||||
*/
|
||||
protected abstract String getViewNameForRequest(HttpServletRequest request);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,743 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.mvc;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Form controller for typical wizard-style workflows.
|
||||
*
|
||||
* <p>In contrast to classic forms, wizards have more than one form view page.
|
||||
* Therefore, there are various actions instead of one single submit action:
|
||||
* <ul>
|
||||
* <li>finish: trying to leave the wizard successfully, that is, perform its
|
||||
* final action, and thus requiring a valid state;
|
||||
* <li>cancel: leaving the wizard without performing its final action, and
|
||||
* thus without regard to the validity of its current state;
|
||||
* <li>page change: showing another wizard page, e.g. the next or previous
|
||||
* one, with regard to "dirty back" and "dirty forward".
|
||||
* </ul>
|
||||
*
|
||||
* <p>Finish and cancel actions can be triggered by request parameters, named
|
||||
* PARAM_FINISH ("_finish") and PARAM_CANCEL ("_cancel"), ignoring parameter
|
||||
* values to allow for HTML buttons. The target page for page changes can be
|
||||
* specified by PARAM_TARGET, appending the page number to the parameter name
|
||||
* (e.g. "_target1"). The action parameters are recognized when triggered by
|
||||
* image buttons too (via "_finish.x", "_abort.x", or "_target1.x").
|
||||
*
|
||||
* <p>The current page number will be stored in the session. It can also be
|
||||
* specified as request parameter PARAM_PAGE ("_page") in order to properly handle
|
||||
* usage of the back button in a browser: In this case, a submission will always
|
||||
* contain the correct page number, even if the user submitted from an old view.
|
||||
*
|
||||
* <p>The page can only be changed if it validates correctly, except if a
|
||||
* "dirty back" or "dirty forward" is allowed. At finish, all pages get
|
||||
* validated again to guarantee a consistent state.
|
||||
*
|
||||
* <p>Note that a validator's default validate method is not executed when using
|
||||
* this class! Rather, the {@link #validatePage} implementation should call
|
||||
* special <code>validateXXX</code> methods that the validator needs to provide,
|
||||
* validating certain pieces of the object. These can be combined to validate
|
||||
* the elements of individual pages.
|
||||
*
|
||||
* <p>Note: Page numbering starts with 0, to be able to pass an array
|
||||
* consisting of the corresponding view names to the "pages" bean property.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 25.04.2003
|
||||
* @see #setPages
|
||||
* @see #validatePage
|
||||
* @see #processFinish
|
||||
* @see #processCancel
|
||||
*/
|
||||
public abstract class AbstractWizardFormController extends AbstractFormController {
|
||||
|
||||
/**
|
||||
* Parameter triggering the finish action.
|
||||
* Can be called from any wizard page!
|
||||
*/
|
||||
public static final String PARAM_FINISH = "_finish";
|
||||
|
||||
/**
|
||||
* Parameter triggering the cancel action.
|
||||
* Can be called from any wizard page!
|
||||
*/
|
||||
public static final String PARAM_CANCEL = "_cancel";
|
||||
|
||||
/**
|
||||
* Parameter specifying the target page,
|
||||
* appending the page number to the name.
|
||||
*/
|
||||
public static final String PARAM_TARGET = "_target";
|
||||
|
||||
/**
|
||||
* Parameter specifying the current page as value. Not necessary on
|
||||
* form pages, but allows to properly handle usage of the back button.
|
||||
* @see #setPageAttribute
|
||||
*/
|
||||
public static final String PARAM_PAGE = "_page";
|
||||
|
||||
|
||||
private String[] pages;
|
||||
|
||||
private String pageAttribute;
|
||||
|
||||
private boolean allowDirtyBack = true;
|
||||
|
||||
private boolean allowDirtyForward = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new AbstractWizardFormController.
|
||||
* <p>"sessionForm" is automatically turned on, "validateOnBinding"
|
||||
* turned off, and "cacheSeconds" set to 0 by the base class
|
||||
* (-> no caching for all form controllers).
|
||||
*/
|
||||
public AbstractWizardFormController() {
|
||||
// AbstractFormController sets default cache seconds to 0.
|
||||
super();
|
||||
|
||||
// Always needs session to keep data from all pages.
|
||||
setSessionForm(true);
|
||||
|
||||
// Never validate everything on binding ->
|
||||
// wizards validate individual pages.
|
||||
setValidateOnBinding(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the wizard pages, i.e. the view names for the pages.
|
||||
* The array index is interpreted as page number.
|
||||
* @param pages view names for the pages
|
||||
*/
|
||||
public final void setPages(String[] pages) {
|
||||
if (pages == null || pages.length == 0) {
|
||||
throw new IllegalArgumentException("No wizard pages defined");
|
||||
}
|
||||
this.pages = pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wizard pages, i.e. the view names for the pages.
|
||||
* The array index corresponds to the page number.
|
||||
* <p>Note that a concrete wizard form controller might override
|
||||
* {@link #getViewName(HttpServletRequest, Object, int)} to
|
||||
* determine the view name for each page dynamically.
|
||||
* @see #getViewName(javax.servlet.http.HttpServletRequest, Object, int)
|
||||
*/
|
||||
public final String[] getPages() {
|
||||
return this.pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of wizard pages.
|
||||
* Useful to check whether the last page has been reached.
|
||||
* <p>Note that a concrete wizard form controller might override
|
||||
* {@link #getPageCount(HttpServletRequest, Object)} to determine
|
||||
* the page count dynamically. The default implementation of that extended
|
||||
* <code>getPageCount</code> variant returns the static page count as
|
||||
* determined by this <code>getPageCount()</code> method.
|
||||
* @see #getPageCount(javax.servlet.http.HttpServletRequest, Object)
|
||||
*/
|
||||
protected final int getPageCount() {
|
||||
return this.pages.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the page attribute in the model, containing
|
||||
* an Integer with the current page number.
|
||||
* <p>This will be necessary for single views rendering multiple view pages.
|
||||
* It also allows for specifying the optional "_page" parameter.
|
||||
* @param pageAttribute name of the page attribute
|
||||
* @see #PARAM_PAGE
|
||||
*/
|
||||
public final void setPageAttribute(String pageAttribute) {
|
||||
this.pageAttribute = pageAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the page attribute in the model.
|
||||
*/
|
||||
public final String getPageAttribute() {
|
||||
return this.pageAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if "dirty back" is allowed, that is, if moving to a former wizard
|
||||
* page is allowed in case of validation errors for the current page.
|
||||
* @param allowDirtyBack if "dirty back" is allowed
|
||||
*/
|
||||
public final void setAllowDirtyBack(boolean allowDirtyBack) {
|
||||
this.allowDirtyBack = allowDirtyBack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether "dirty back" is allowed.
|
||||
*/
|
||||
public final boolean isAllowDirtyBack() {
|
||||
return this.allowDirtyBack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if "dirty forward" is allowed, that is, if moving to a later wizard
|
||||
* page is allowed in case of validation errors for the current page.
|
||||
* @param allowDirtyForward if "dirty forward" is allowed
|
||||
*/
|
||||
public final void setAllowDirtyForward(boolean allowDirtyForward) {
|
||||
this.allowDirtyForward = allowDirtyForward;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether "dirty forward" is allowed.
|
||||
*/
|
||||
public final boolean isAllowDirtyForward() {
|
||||
return this.allowDirtyForward;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls page-specific onBindAndValidate method.
|
||||
*/
|
||||
protected final void onBindAndValidate(HttpServletRequest request, Object command, BindException errors)
|
||||
throws Exception {
|
||||
|
||||
onBindAndValidate(request, command, errors, getCurrentPage(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for custom post-processing in terms of binding and validation.
|
||||
* Called on each submit, after standard binding but before page-specific
|
||||
* validation of this wizard form controller.
|
||||
* <p>Note: AbstractWizardFormController does not perform standand
|
||||
* validation on binding but rather applies page-specific validation
|
||||
* on processing the form submission.
|
||||
* @param request current HTTP request
|
||||
* @param command bound command
|
||||
* @param errors Errors instance for additional custom validation
|
||||
* @param page current wizard page
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #bindAndValidate
|
||||
* @see #processFormSubmission
|
||||
* @see org.springframework.validation.Errors
|
||||
*/
|
||||
protected void onBindAndValidate(HttpServletRequest request, Object command, BindException errors, int page)
|
||||
throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Consider an explicit finish or cancel request as a form submission too.
|
||||
* @see #isFinishRequest(javax.servlet.http.HttpServletRequest)
|
||||
* @see #isCancelRequest(javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
protected boolean isFormSubmission(HttpServletRequest request) {
|
||||
return super.isFormSubmission(request) || isFinishRequest(request) || isCancelRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls page-specific referenceData method.
|
||||
*/
|
||||
protected final Map referenceData(HttpServletRequest request, Object command, Errors errors)
|
||||
throws Exception {
|
||||
|
||||
return referenceData(request, command, errors, getCurrentPage(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference data map for the given request, consisting of
|
||||
* bean name/bean instance pairs as expected by ModelAndView.
|
||||
* <p>The default implementation delegates to referenceData(HttpServletRequest, int).
|
||||
* Subclasses can override this to set reference data used in the view.
|
||||
* @param request current HTTP request
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @param errors validation errors holder
|
||||
* @param page current wizard page
|
||||
* @return a Map with reference data entries, or <code>null</code> if none
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #referenceData(HttpServletRequest, int)
|
||||
* @see ModelAndView
|
||||
*/
|
||||
protected Map referenceData(HttpServletRequest request, Object command, Errors errors, int page)
|
||||
throws Exception {
|
||||
|
||||
return referenceData(request, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference data map for the given request, consisting of
|
||||
* bean name/bean instance pairs as expected by ModelAndView.
|
||||
* <p>The default implementation returns <code>null</code>.
|
||||
* Subclasses can override this to set reference data used in the view.
|
||||
* @param request current HTTP request
|
||||
* @param page current wizard page
|
||||
* @return a Map with reference data entries, or <code>null</code> if none
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see ModelAndView
|
||||
*/
|
||||
protected Map referenceData(HttpServletRequest request, int page) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the first page as form view.
|
||||
* <p>This can be overridden in subclasses, e.g. to prepare wizard-specific
|
||||
* error views in case of an Exception.
|
||||
*/
|
||||
protected ModelAndView showForm(
|
||||
HttpServletRequest request, HttpServletResponse response, BindException errors)
|
||||
throws Exception {
|
||||
|
||||
return showPage(request, errors, getInitialPage(request, errors.getTarget()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the form model and view, including reference and error data,
|
||||
* for the given page. Can be used in {@link #processFinish} implementations,
|
||||
* to show the corresponding page in case of validation errors.
|
||||
* @param request current HTTP request
|
||||
* @param errors validation errors holder
|
||||
* @param page number of page to show
|
||||
* @return the prepared form view
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
*/
|
||||
protected final ModelAndView showPage(HttpServletRequest request, BindException errors, int page)
|
||||
throws Exception {
|
||||
|
||||
if (page >= 0 && page < getPageCount(request, errors.getTarget())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Showing wizard page " + page + " for form bean '" + getCommandName() + "'");
|
||||
}
|
||||
|
||||
// Set page session attribute, expose overriding request attribute.
|
||||
Integer pageInteger = new Integer(page);
|
||||
String pageAttrName = getPageSessionAttributeName(request);
|
||||
if (isSessionForm()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Setting page session attribute [" + pageAttrName + "] to: " + pageInteger);
|
||||
}
|
||||
request.getSession().setAttribute(pageAttrName, pageInteger);
|
||||
}
|
||||
request.setAttribute(pageAttrName, pageInteger);
|
||||
|
||||
// Set page request attribute for evaluation by views.
|
||||
Map controlModel = new HashMap();
|
||||
if (this.pageAttribute != null) {
|
||||
controlModel.put(this.pageAttribute, new Integer(page));
|
||||
}
|
||||
String viewName = getViewName(request, errors.getTarget(), page);
|
||||
return showForm(request, errors, viewName, controlModel);
|
||||
}
|
||||
|
||||
else {
|
||||
throw new ServletException("Invalid wizard page number: " + page);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the page count for this wizard form controller.
|
||||
* The default implementation delegates to {@link #getPageCount()}.
|
||||
* <p>Can be overridden to dynamically adapt the page count.
|
||||
* @param request current HTTP request
|
||||
* @param command the command object as returned by formBackingObject
|
||||
* @return the current page count
|
||||
* @see #getPageCount
|
||||
*/
|
||||
protected int getPageCount(HttpServletRequest request, Object command) {
|
||||
return getPageCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the view for the specified page of this wizard form controller.
|
||||
* <p>The default implementation takes the view name from the {@link #getPages()} array.
|
||||
* <p>Can be overridden to dynamically switch the page view or to return view names
|
||||
* for dynamically defined pages.
|
||||
* @param request current HTTP request
|
||||
* @param command the command object as returned by formBackingObject
|
||||
* @param page the current page number
|
||||
* @return the current page count
|
||||
* @see #getPageCount
|
||||
*/
|
||||
protected String getViewName(HttpServletRequest request, Object command, int page) {
|
||||
return getPages()[page];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the initial page of the wizard, that is, the page shown at wizard startup.
|
||||
* <p>The default implementation delegates to {@link #getInitialPage(HttpServletRequest)}.
|
||||
* @param request current HTTP request
|
||||
* @param command the command object as returned by formBackingObject
|
||||
* @return the initial page number
|
||||
* @see #getInitialPage(HttpServletRequest)
|
||||
* @see #formBackingObject
|
||||
*/
|
||||
protected int getInitialPage(HttpServletRequest request, Object command) {
|
||||
return getInitialPage(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the initial page of the wizard, that is, the page shown at wizard startup.
|
||||
* <p>The default implementation returns 0 for first page.
|
||||
* @param request current HTTP request
|
||||
* @return the initial page number
|
||||
*/
|
||||
protected int getInitialPage(HttpServletRequest request) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the HttpSession attribute that holds the page object
|
||||
* for this wizard form controller.
|
||||
* <p>The default implementation delegates to the {@link #getPageSessionAttributeName()}
|
||||
* variant without arguments.
|
||||
* @param request current HTTP request
|
||||
* @return the name of the form session attribute, or <code>null</code> if not in session form mode
|
||||
* @see #getPageSessionAttributeName
|
||||
* @see #getFormSessionAttributeName(javax.servlet.http.HttpServletRequest)
|
||||
* @see javax.servlet.http.HttpSession#getAttribute
|
||||
*/
|
||||
protected String getPageSessionAttributeName(HttpServletRequest request) {
|
||||
return getPageSessionAttributeName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the HttpSession attribute that holds the page object
|
||||
* for this wizard form controller.
|
||||
* <p>Default is an internal name, of no relevance to applications, as the form
|
||||
* session attribute is not usually accessed directly. Can be overridden to use
|
||||
* an application-specific attribute name, which allows other code to access
|
||||
* the session attribute directly.
|
||||
* @return the name of the page session attribute
|
||||
* @see #getFormSessionAttributeName
|
||||
* @see javax.servlet.http.HttpSession#getAttribute
|
||||
*/
|
||||
protected String getPageSessionAttributeName() {
|
||||
return getClass().getName() + ".PAGE." + getCommandName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an invalid submit request, e.g. when in session form mode but no form object
|
||||
* was found in the session (like in case of an invalid resubmit by the browser).
|
||||
* <p>The default implementation for wizard form controllers simply shows the initial page
|
||||
* of a new wizard form. If you want to show some "invalid submit" message, you need
|
||||
* to override this method.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @return a prepared view, or <code>null</code> if handled directly
|
||||
* @throws Exception in case of errors
|
||||
* @see #showNewForm
|
||||
* @see #setBindOnNewForm
|
||||
*/
|
||||
protected ModelAndView handleInvalidSubmit(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
return showNewForm(request, response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply wizard workflow: finish, cancel, page change.
|
||||
*/
|
||||
protected final ModelAndView processFormSubmission(
|
||||
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
|
||||
throws Exception {
|
||||
|
||||
int currentPage = getCurrentPage(request);
|
||||
// Remove page session attribute, provide copy as request attribute.
|
||||
String pageAttrName = getPageSessionAttributeName(request);
|
||||
if (isSessionForm()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removing page session attribute [" + pageAttrName + "]");
|
||||
}
|
||||
request.getSession().removeAttribute(pageAttrName);
|
||||
}
|
||||
request.setAttribute(pageAttrName, new Integer(currentPage));
|
||||
|
||||
// cancel?
|
||||
if (isCancelRequest(request)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cancelling wizard for form bean '" + getCommandName() + "'");
|
||||
}
|
||||
return processCancel(request, response, command, errors);
|
||||
}
|
||||
|
||||
// finish?
|
||||
if (isFinishRequest(request)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Finishing wizard for form bean '" + getCommandName() + "'");
|
||||
}
|
||||
return validatePagesAndFinish(request, response, command, errors, currentPage);
|
||||
}
|
||||
|
||||
// Normal submit: validate current page and show specified target page.
|
||||
if (!suppressValidation(request, command, errors)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Validating wizard page " + currentPage + " for form bean '" + getCommandName() + "'");
|
||||
}
|
||||
validatePage(command, errors, currentPage, false);
|
||||
}
|
||||
|
||||
// Give subclasses a change to perform custom post-procession
|
||||
// of the current page and its command object.
|
||||
postProcessPage(request, command, errors, currentPage);
|
||||
|
||||
int targetPage = getTargetPage(request, command, errors, currentPage);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Target page " + targetPage + " requested");
|
||||
}
|
||||
if (targetPage != currentPage) {
|
||||
if (!errors.hasErrors() || (this.allowDirtyBack && targetPage < currentPage) ||
|
||||
(this.allowDirtyForward && targetPage > currentPage)) {
|
||||
// Allowed to go to target page.
|
||||
return showPage(request, errors, targetPage);
|
||||
}
|
||||
}
|
||||
|
||||
// Show current page again.
|
||||
return showPage(request, errors, currentPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current page number. Used by {@link #processFormSubmission}.
|
||||
* <p>The default implementation checks the page session attribute.
|
||||
* Subclasses can override this for customized page determination.
|
||||
* @param request current HTTP request
|
||||
* @return the current page number
|
||||
* @see #getPageSessionAttributeName()
|
||||
*/
|
||||
protected int getCurrentPage(HttpServletRequest request) {
|
||||
// Check for overriding attribute in request.
|
||||
String pageAttrName = getPageSessionAttributeName(request);
|
||||
Integer pageAttr = (Integer) request.getAttribute(pageAttrName);
|
||||
if (pageAttr != null) {
|
||||
return pageAttr.intValue();
|
||||
}
|
||||
// Check for explicit request parameter.
|
||||
String pageParam = request.getParameter(PARAM_PAGE);
|
||||
if (pageParam != null) {
|
||||
return Integer.parseInt(pageParam);
|
||||
}
|
||||
// Check for original attribute in session.
|
||||
if (isSessionForm()) {
|
||||
pageAttr = (Integer) request.getSession().getAttribute(pageAttrName);
|
||||
if (pageAttr != null) {
|
||||
return pageAttr.intValue();
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Page attribute [" + pageAttrName + "] neither found in session nor in request");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the incoming request is a request to finish the
|
||||
* processing of the current form.
|
||||
* <p>By default, this method returns <code>true</code> if a parameter
|
||||
* matching the "_finish" key is present in the request, otherwise it
|
||||
* returns <code>false</code>. Subclasses may override this method
|
||||
* to provide custom logic to detect a finish request.
|
||||
* <p>The parameter is recognized both when sent as a plain parameter
|
||||
* ("_finish") or when triggered by an image button ("_finish.x").
|
||||
* @param request current HTTP request
|
||||
* @return whether the request indicates to finish form processing
|
||||
* @see #PARAM_FINISH
|
||||
*/
|
||||
protected boolean isFinishRequest(HttpServletRequest request) {
|
||||
return WebUtils.hasSubmitParameter(request, PARAM_FINISH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the incoming request is a request to cancel the
|
||||
* processing of the current form.
|
||||
* <p>By default, this method returns <code>true</code> if a parameter
|
||||
* matching the "_cancel" key is present in the request, otherwise it
|
||||
* returns <code>false</code>. Subclasses may override this method
|
||||
* to provide custom logic to detect a cancel request.
|
||||
* <p>The parameter is recognized both when sent as a plain parameter
|
||||
* ("_cancel") or when triggered by an image button ("_cancel.x").
|
||||
* @return whether the request indicates to cancel form processing
|
||||
* @param request current HTTP request
|
||||
* @see #PARAM_CANCEL
|
||||
*/
|
||||
protected boolean isCancelRequest(HttpServletRequest request) {
|
||||
return WebUtils.hasSubmitParameter(request, PARAM_CANCEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the target page specified in the request.
|
||||
* <p>The default implementation delegates to {@link #getTargetPage(HttpServletRequest, int)}.
|
||||
* Subclasses can override this for customized target page determination.
|
||||
* @param request current HTTP request
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @param errors validation errors holder
|
||||
* @param currentPage the current page, to be returned as fallback
|
||||
* if no target page specified
|
||||
* @return the page specified in the request, or current page if not found
|
||||
* @see #getTargetPage(HttpServletRequest, int)
|
||||
*/
|
||||
protected int getTargetPage(HttpServletRequest request, Object command, Errors errors, int currentPage) {
|
||||
return getTargetPage(request, currentPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the target page specified in the request.
|
||||
* <p>The default implementation examines "_target" parameter (e.g. "_target1").
|
||||
* Subclasses can override this for customized target page determination.
|
||||
* @param request current HTTP request
|
||||
* @param currentPage the current page, to be returned as fallback
|
||||
* if no target page specified
|
||||
* @return the page specified in the request, or current page if not found
|
||||
* @see #PARAM_TARGET
|
||||
*/
|
||||
protected int getTargetPage(HttpServletRequest request, int currentPage) {
|
||||
return WebUtils.getTargetPage(request, PARAM_TARGET, currentPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate all pages and process finish.
|
||||
* If there are page validation errors, show the corresponding view page.
|
||||
*/
|
||||
private ModelAndView validatePagesAndFinish(
|
||||
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors,
|
||||
int currentPage) throws Exception {
|
||||
|
||||
// In case of binding errors -> show current page.
|
||||
if (errors.hasErrors()) {
|
||||
return showPage(request, errors, currentPage);
|
||||
}
|
||||
|
||||
if (!suppressValidation(request, command, errors)) {
|
||||
// In case of remaining errors on a page -> show the page.
|
||||
for (int page = 0; page < getPageCount(request, command); page++) {
|
||||
validatePage(command, errors, page, true);
|
||||
if (errors.hasErrors()) {
|
||||
return showPage(request, errors, page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No remaining errors -> proceed with finish.
|
||||
return processFinish(request, response, command, errors);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Template method for custom validation logic for individual pages.
|
||||
* The default implementation calls {@link #validatePage(Object, Errors, int)}.
|
||||
* <p>Implementations will typically call fine-granular <code>validateXXX</code>
|
||||
* methods of this instance's Validator, combining them to validation of the
|
||||
* corresponding pages. The Validator's default <code>validate</code> method
|
||||
* will not be called by a wizard form controller!
|
||||
* @param command form object with the current wizard state
|
||||
* @param errors validation errors holder
|
||||
* @param page number of page to validate
|
||||
* @param finish whether this method is called during final revalidation on finish
|
||||
* (else, it is called for validating the current page)
|
||||
* @see #validatePage(Object, Errors, int)
|
||||
* @see org.springframework.validation.Validator#validate
|
||||
*/
|
||||
protected void validatePage(Object command, Errors errors, int page, boolean finish) {
|
||||
validatePage(command, errors, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for custom validation logic for individual pages.
|
||||
* The default implementation is empty.
|
||||
* <p>Implementations will typically call fine-granular validateXXX methods of this
|
||||
* instance's validator, combining them to validation of the corresponding pages.
|
||||
* The validator's default <code>validate</code> method will not be called by a
|
||||
* wizard form controller!
|
||||
* @param command form object with the current wizard state
|
||||
* @param errors validation errors holder
|
||||
* @param page number of page to validate
|
||||
* @see org.springframework.validation.Validator#validate
|
||||
*/
|
||||
protected void validatePage(Object command, Errors errors, int page) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the given page after binding and validation, potentially
|
||||
* updating its command object. The passed-in request might contain special
|
||||
* parameters sent by the page.
|
||||
* <p>Only invoked when displaying another page or the same page again,
|
||||
* not when finishing or cancelling.
|
||||
* @param request current HTTP request
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @param errors validation errors holder
|
||||
* @param page number of page to post-process
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
*/
|
||||
protected void postProcessPage(HttpServletRequest request, Object command, Errors errors, int page)
|
||||
throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for processing the final action of this wizard.
|
||||
* <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
|
||||
* with the command and the Errors instance, under the specified command name,
|
||||
* as expected by the "spring:bind" tag.
|
||||
* <p>You can call the {@link #showPage} method to return back to the wizard,
|
||||
* in case of last-minute validation errors having been found that you would
|
||||
* like to present to the user within the original wizard form.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param command form object with the current wizard state
|
||||
* @param errors validation errors holder
|
||||
* @return the finish view
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see org.springframework.validation.Errors
|
||||
* @see org.springframework.validation.BindException#getModel
|
||||
* @see #showPage(javax.servlet.http.HttpServletRequest, org.springframework.validation.BindException, int)
|
||||
*/
|
||||
protected abstract ModelAndView processFinish(
|
||||
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Template method for processing the cancel action of this wizard.
|
||||
* <p>The default implementation throws a ServletException, saying that a cancel
|
||||
* operation is not supported by this controller. Thus, you do not need to
|
||||
* implement this template method if you do not support a cancel operation.
|
||||
* <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
|
||||
* with the command and the Errors instance, under the specified command name,
|
||||
* as expected by the "spring:bind" tag.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param command form object with the current wizard state
|
||||
* @param errors Errors instance containing errors
|
||||
* @return the cancellation view
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see org.springframework.validation.Errors
|
||||
* @see org.springframework.validation.BindException#getModel
|
||||
*/
|
||||
protected ModelAndView processCancel(
|
||||
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
|
||||
throws Exception {
|
||||
|
||||
throw new ServletException(
|
||||
"Wizard form controller class [" + getClass().getName() + "] does not support a cancel operation");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,591 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.PropertyEditorRegistrar;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingErrorProcessor;
|
||||
import org.springframework.validation.MessageCodesResolver;
|
||||
import org.springframework.validation.ValidationUtils;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.bind.ServletRequestDataBinder;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
/**
|
||||
* <p>Controller implementation which creates an object (the command object) on
|
||||
* receipt of a request and attempts to populate this object with request parameters.</p>
|
||||
*
|
||||
* <p>This controller is the base for all controllers wishing to populate
|
||||
* JavaBeans based on request parameters, validate the content of such
|
||||
* JavaBeans using {@link org.springframework.validation.Validator Validators}
|
||||
* and use custom editors (in the form of
|
||||
* {@link java.beans.PropertyEditor PropertyEditors}) to transform
|
||||
* objects into strings and vice versa, for example. Three notions are mentioned here:</p>
|
||||
*
|
||||
* <p><b>Command class:</b><br>
|
||||
* An instance of the command class will be created for each request and populated
|
||||
* with request parameters. A command class can basically be any Java class; the only
|
||||
* requirement is a no-arg constructor. The command class should preferably be a
|
||||
* JavaBean in order to be able to populate bean properties with request parameters.</p>
|
||||
*
|
||||
* <p><b>Populating using request parameters and PropertyEditors:</b><br>
|
||||
* Upon receiving a request, any BaseCommandController will attempt to fill the
|
||||
* command object using the request parameters. This is done using the typical
|
||||
* and well-known JavaBeans property notation. When a request parameter named
|
||||
* <code>'firstName'</code> exists, the framework will attempt to call
|
||||
* <code>setFirstName([value])</code> passing the value of the parameter. Nested properties
|
||||
* are of course supported. For instance a parameter named <code>'address.city'</code>
|
||||
* will result in a <code>getAddress().setCity([value])</code> call on the
|
||||
* command class.</p>
|
||||
*
|
||||
* <p>It's important to realise that you are not limited to String arguments in
|
||||
* your JavaBeans. Using the PropertyEditor-notion as supplied by the
|
||||
* java.beans package, you will be able to transform Strings to Objects and
|
||||
* the other way around. For instance <code>setLocale(Locale loc)</code> is
|
||||
* perfectly possible for a request parameter named <code>locale</code> having
|
||||
* a value of <code>en</code>, as long as you register the appropriate
|
||||
* PropertyEditor in the Controller (see {@link #initBinder initBinder()}
|
||||
* for more information on that matter.</p>
|
||||
*
|
||||
* <p><b>Validators:</b>
|
||||
* After the controller has successfully populated the command object with
|
||||
* parameters from the request, it will use any configured validators to
|
||||
* validate the object. Validation results will be put in a
|
||||
* {@link org.springframework.validation.Errors Errors} object which can be
|
||||
* used in a View to render any input problems.</p>
|
||||
*
|
||||
* <p><b><a name="workflow">Workflow
|
||||
* (<a href="AbstractController.html#workflow">and that defined by superclass</a>):</b><br>
|
||||
* Since this class is an abstract base class for more specific implementation,
|
||||
* it does not override the handleRequestInternal() method and also has no
|
||||
* actual workflow. Implementing classes like
|
||||
* {@link AbstractFormController AbstractFormController},
|
||||
* {@link AbstractCommandController AbstractcommandController},
|
||||
* {@link SimpleFormController SimpleFormController} and
|
||||
* {@link AbstractWizardFormController AbstractWizardFormController}
|
||||
* provide actual functionality and workflow.
|
||||
* More information on workflow performed by superclasses can be found
|
||||
* <a href="AbstractController.html#workflow">here</a>.</p>
|
||||
*
|
||||
* <p><b><a name="config">Exposed configuration properties</a>
|
||||
* (<a href="AbstractController.html#config">and those defined by superclass</a>):</b><br>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <td><b>name</b></th>
|
||||
* <td><b>default</b></td>
|
||||
* <td><b>description</b></td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>commandName</td>
|
||||
* <td>command</td>
|
||||
* <td>the name to use when binding the instantiated command class
|
||||
* to the request</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>commandClass</td>
|
||||
* <td><i>null</i></td>
|
||||
* <td>the class to use upon receiving a request and which to fill
|
||||
* using the request parameters. What object is used and whether
|
||||
* or not it should be created is defined by extending classes
|
||||
* and their configuration properties and methods.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>validators</td>
|
||||
* <td><i>null</i></td>
|
||||
* <td>Array of Validator beans. The validator will be called at appropriate
|
||||
* places in the workflow of subclasses (have a look at those for more info)
|
||||
* to validate the command object.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>validator</td>
|
||||
* <td><i>null</i></td>
|
||||
* <td>Short-form property for setting only one Validator bean (usually passed in
|
||||
* using a <ref bean="beanId"/> property.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>validateOnBinding</td>
|
||||
* <td>true</td>
|
||||
* <td>Indicates whether or not to validate the command object after the
|
||||
* object has been populated with request parameters.</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* </p>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public abstract class BaseCommandController extends AbstractController {
|
||||
|
||||
/** Default command name used for binding command objects: "command" */
|
||||
public static final String DEFAULT_COMMAND_NAME = "command";
|
||||
|
||||
|
||||
private String commandName = DEFAULT_COMMAND_NAME;
|
||||
|
||||
private Class commandClass;
|
||||
|
||||
private Validator[] validators;
|
||||
|
||||
private boolean validateOnBinding = true;
|
||||
|
||||
private MessageCodesResolver messageCodesResolver;
|
||||
|
||||
private BindingErrorProcessor bindingErrorProcessor;
|
||||
|
||||
private PropertyEditorRegistrar[] propertyEditorRegistrars;
|
||||
|
||||
private WebBindingInitializer webBindingInitializer;
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the command in the model.
|
||||
* The command object will be included in the model under this name.
|
||||
*/
|
||||
public final void setCommandName(String commandName) {
|
||||
this.commandName = commandName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the command in the model.
|
||||
*/
|
||||
public final String getCommandName() {
|
||||
return this.commandName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the command class for this controller.
|
||||
* An instance of this class gets populated and validated on each request.
|
||||
*/
|
||||
public final void setCommandClass(Class commandClass) {
|
||||
this.commandClass = commandClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the command class for this controller.
|
||||
*/
|
||||
public final Class getCommandClass() {
|
||||
return this.commandClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the primary Validator for this controller. The Validator
|
||||
* must support the specified command class. If there are one
|
||||
* or more existing validators set already when this method is
|
||||
* called, only the specified validator will be kept. Use
|
||||
* {@link #setValidators(Validator[])} to set multiple validators.
|
||||
*/
|
||||
public final void setValidator(Validator validator) {
|
||||
this.validators = new Validator[] {validator};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the primary Validator for this controller.
|
||||
*/
|
||||
public final Validator getValidator() {
|
||||
return (this.validators != null && this.validators.length > 0 ? this.validators[0] : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Validators for this controller.
|
||||
* The Validator must support the specified command class.
|
||||
*/
|
||||
public final void setValidators(Validator[] validators) {
|
||||
this.validators = validators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Validators for this controller.
|
||||
*/
|
||||
public final Validator[] getValidators() {
|
||||
return this.validators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the Validator should get applied when binding.
|
||||
*/
|
||||
public final void setValidateOnBinding(boolean validateOnBinding) {
|
||||
this.validateOnBinding = validateOnBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the Validator should get applied when binding.
|
||||
*/
|
||||
public final boolean isValidateOnBinding() {
|
||||
return this.validateOnBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the strategy to use for resolving errors into message codes.
|
||||
* Applies the given strategy to all data binders used by this controller.
|
||||
* <p>Default is <code>null</code>, i.e. using the default strategy of
|
||||
* the data binder.
|
||||
* @see #createBinder
|
||||
* @see org.springframework.validation.DataBinder#setMessageCodesResolver
|
||||
*/
|
||||
public final void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) {
|
||||
this.messageCodesResolver = messageCodesResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the strategy to use for resolving errors into message codes (if any).
|
||||
*/
|
||||
public final MessageCodesResolver getMessageCodesResolver() {
|
||||
return this.messageCodesResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the strategy to use for processing binding errors, that is,
|
||||
* required field errors and <code>PropertyAccessException</code>s.
|
||||
* <p>Default is <code>null</code>, that is, using the default strategy
|
||||
* of the data binder.
|
||||
* @see #createBinder
|
||||
* @see org.springframework.validation.DataBinder#setBindingErrorProcessor
|
||||
*/
|
||||
public final void setBindingErrorProcessor(BindingErrorProcessor bindingErrorProcessor) {
|
||||
this.bindingErrorProcessor = bindingErrorProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the strategy to use for processing binding errors (if any).
|
||||
*/
|
||||
public final BindingErrorProcessor getBindingErrorProcessor() {
|
||||
return this.bindingErrorProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a single PropertyEditorRegistrar to be applied
|
||||
* to every DataBinder that this controller uses.
|
||||
* <p>Allows for factoring out the registration of PropertyEditors
|
||||
* to separate objects, as an alternative to {@link #initBinder}.
|
||||
* @see #initBinder
|
||||
*/
|
||||
public final void setPropertyEditorRegistrar(PropertyEditorRegistrar propertyEditorRegistrar) {
|
||||
this.propertyEditorRegistrars = new PropertyEditorRegistrar[] {propertyEditorRegistrar};
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify multiple PropertyEditorRegistrars to be applied
|
||||
* to every DataBinder that this controller uses.
|
||||
* <p>Allows for factoring out the registration of PropertyEditors
|
||||
* to separate objects, as an alternative to {@link #initBinder}.
|
||||
* @see #initBinder
|
||||
*/
|
||||
public final void setPropertyEditorRegistrars(PropertyEditorRegistrar[] propertyEditorRegistrars) {
|
||||
this.propertyEditorRegistrars = propertyEditorRegistrars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PropertyEditorRegistrars (if any) to be applied
|
||||
* to every DataBinder that this controller uses.
|
||||
*/
|
||||
public final PropertyEditorRegistrar[] getPropertyEditorRegistrars() {
|
||||
return this.propertyEditorRegistrars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a WebBindingInitializer which will apply pre-configured
|
||||
* configuration to every DataBinder that this controller uses.
|
||||
* <p>Allows for factoring out the entire binder configuration
|
||||
* to separate objects, as an alternative to {@link #initBinder}.
|
||||
*/
|
||||
public final void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
|
||||
this.webBindingInitializer = webBindingInitializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the WebBindingInitializer (if any) which will apply pre-configured
|
||||
* configuration to every DataBinder that this controller uses.
|
||||
*/
|
||||
public final WebBindingInitializer getWebBindingInitializer() {
|
||||
return this.webBindingInitializer;
|
||||
}
|
||||
|
||||
|
||||
protected void initApplicationContext() {
|
||||
if (this.validators != null) {
|
||||
for (int i = 0; i < this.validators.length; i++) {
|
||||
if (this.commandClass != null && !this.validators[i].supports(this.commandClass))
|
||||
throw new IllegalArgumentException("Validator [" + this.validators[i] +
|
||||
"] does not support command class [" +
|
||||
this.commandClass.getName() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a command object for the given request.
|
||||
* <p>The default implementation calls {@link #createCommand}.
|
||||
* Subclasses can override this.
|
||||
* @param request current HTTP request
|
||||
* @return object command to bind onto
|
||||
* @throws Exception if the command object could not be obtained
|
||||
* @see #createCommand
|
||||
*/
|
||||
protected Object getCommand(HttpServletRequest request) throws Exception {
|
||||
return createCommand();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new command instance for the command class of this controller.
|
||||
* <p>This implementation uses <code>BeanUtils.instantiateClass</code>,
|
||||
* so the command needs to have a no-arg constructor (supposed to be
|
||||
* public, but not required to).
|
||||
* @return the new command instance
|
||||
* @throws Exception if the command object could not be instantiated
|
||||
* @see org.springframework.beans.BeanUtils#instantiateClass(Class)
|
||||
*/
|
||||
protected final Object createCommand() throws Exception {
|
||||
if (this.commandClass == null) {
|
||||
throw new IllegalStateException("Cannot create command without commandClass being set - " +
|
||||
"either set commandClass or (in a form controller) override formBackingObject");
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Creating new command of class [" + this.commandClass.getName() + "]");
|
||||
}
|
||||
return BeanUtils.instantiateClass(this.commandClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given command object is a valid for this controller,
|
||||
* i.e. its command class.
|
||||
* @param command the command object to check
|
||||
* @return if the command object is valid for this controller
|
||||
*/
|
||||
protected final boolean checkCommand(Object command) {
|
||||
return (this.commandClass == null || this.commandClass.isInstance(command));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind the parameters of the given request to the given command object.
|
||||
* @param request current HTTP request
|
||||
* @param command the command to bind onto
|
||||
* @return the ServletRequestDataBinder instance for additional custom validation
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
*/
|
||||
protected final ServletRequestDataBinder bindAndValidate(HttpServletRequest request, Object command)
|
||||
throws Exception {
|
||||
|
||||
ServletRequestDataBinder binder = createBinder(request, command);
|
||||
BindException errors = new BindException(binder.getBindingResult());
|
||||
if (!suppressBinding(request)) {
|
||||
binder.bind(request);
|
||||
onBind(request, command, errors);
|
||||
if (this.validators != null && isValidateOnBinding() && !suppressValidation(request, command, errors)) {
|
||||
for (int i = 0; i < this.validators.length; i++) {
|
||||
ValidationUtils.invokeValidator(this.validators[i], command, errors);
|
||||
}
|
||||
}
|
||||
onBindAndValidate(request, command, errors);
|
||||
}
|
||||
return binder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to suppress binding for the given request.
|
||||
* <p>The default implementation always returns "false". Can be overridden
|
||||
* in subclasses to suppress validation, for example, if a special
|
||||
* request parameter is set.
|
||||
* @param request current HTTP request
|
||||
* @return whether to suppress binding for the given request
|
||||
* @see #suppressValidation
|
||||
*/
|
||||
protected boolean suppressBinding(HttpServletRequest request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new binder instance for the given command and request.
|
||||
* <p>Called by {@link #bindAndValidate}. Can be overridden to plug in
|
||||
* custom ServletRequestDataBinder instances.
|
||||
* <p>The default implementation creates a standard ServletRequestDataBinder
|
||||
* and invokes {@link #prepareBinder} and {@link #initBinder}.
|
||||
* <p>Note that neither {@link #prepareBinder} nor {@link #initBinder} will
|
||||
* be invoked automatically if you override this method! Call those methods
|
||||
* at appropriate points of your overridden method.
|
||||
* @param request current HTTP request
|
||||
* @param command the command to bind onto
|
||||
* @return the new binder instance
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #bindAndValidate
|
||||
* @see #prepareBinder
|
||||
* @see #initBinder
|
||||
*/
|
||||
protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object command)
|
||||
throws Exception {
|
||||
|
||||
ServletRequestDataBinder binder = new ServletRequestDataBinder(command, getCommandName());
|
||||
prepareBinder(binder);
|
||||
initBinder(request, binder);
|
||||
return binder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the given binder, applying the specified MessageCodesResolver,
|
||||
* BindingErrorProcessor and PropertyEditorRegistrars (if any).
|
||||
* Called by {@link #createBinder}.
|
||||
* @param binder the new binder instance
|
||||
* @see #createBinder
|
||||
* @see #setMessageCodesResolver
|
||||
* @see #setBindingErrorProcessor
|
||||
*/
|
||||
protected final void prepareBinder(ServletRequestDataBinder binder) {
|
||||
if (useDirectFieldAccess()) {
|
||||
binder.initDirectFieldAccess();
|
||||
}
|
||||
if (this.messageCodesResolver != null) {
|
||||
binder.setMessageCodesResolver(this.messageCodesResolver);
|
||||
}
|
||||
if (this.bindingErrorProcessor != null) {
|
||||
binder.setBindingErrorProcessor(this.bindingErrorProcessor);
|
||||
}
|
||||
if (this.propertyEditorRegistrars != null) {
|
||||
for (int i = 0; i < this.propertyEditorRegistrars.length; i++) {
|
||||
this.propertyEditorRegistrars[i].registerCustomEditors(binder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether to use direct field access instead of bean property access.
|
||||
* Applied by {@link #prepareBinder}.
|
||||
* <p>Default is "false". Can be overridden in subclasses.
|
||||
* @return whether to use direct field access (<code>true</code>)
|
||||
* or bean property access (<code>false</code>)
|
||||
* @see #prepareBinder
|
||||
* @see org.springframework.validation.DataBinder#initDirectFieldAccess()
|
||||
*/
|
||||
protected boolean useDirectFieldAccess() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the given binder instance, for example with custom editors.
|
||||
* Called by {@link #createBinder}.
|
||||
* <p>This method allows you to register custom editors for certain fields of your
|
||||
* command class. For instance, you will be able to transform Date objects into a
|
||||
* String pattern and back, in order to allow your JavaBeans to have Date properties
|
||||
* and still be able to set and display them in an HTML interface.
|
||||
* <p>The default implementation is empty.
|
||||
* @param request current HTTP request
|
||||
* @param binder the new binder instance
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #createBinder
|
||||
* @see org.springframework.validation.DataBinder#registerCustomEditor
|
||||
* @see org.springframework.beans.propertyeditors.CustomDateEditor
|
||||
*/
|
||||
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
|
||||
if (this.webBindingInitializer != null) {
|
||||
this.webBindingInitializer.initBinder(binder, new ServletWebRequest(request));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for custom post-processing in terms of binding.
|
||||
* Called on each submit, after standard binding but before validation.
|
||||
* <p>The default implementation delegates to {@link #onBind(HttpServletRequest, Object)}.
|
||||
* @param request current HTTP request
|
||||
* @param command the command object to perform further binding on
|
||||
* @param errors validation errors holder, allowing for additional
|
||||
* custom registration of binding errors
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #bindAndValidate
|
||||
* @see #onBind(HttpServletRequest, Object)
|
||||
*/
|
||||
protected void onBind(HttpServletRequest request, Object command, BindException errors) throws Exception {
|
||||
onBind(request, command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for custom post-processing in terms of binding.
|
||||
* <p>Called by the default implementation of the
|
||||
* {@link #onBind(HttpServletRequest, Object, BindException)} variant
|
||||
* with all parameters, after standard binding but before validation.
|
||||
* <p>The default implementation is empty.
|
||||
* @param request current HTTP request
|
||||
* @param command the command object to perform further binding on
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #onBind(HttpServletRequest, Object, BindException)
|
||||
*/
|
||||
protected void onBind(HttpServletRequest request, Object command) throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to suppress validation for the given request.
|
||||
* <p>The default implementation delegates to {@link #suppressValidation(HttpServletRequest, Object)}.
|
||||
* @param request current HTTP request
|
||||
* @param command the command object to validate
|
||||
* @param errors validation errors holder, allowing for additional
|
||||
* custom registration of binding errors
|
||||
* @return whether to suppress validation for the given request
|
||||
*/
|
||||
protected boolean suppressValidation(HttpServletRequest request, Object command, BindException errors) {
|
||||
return suppressValidation(request, command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to suppress validation for the given request.
|
||||
* <p>Called by the default implementation of the
|
||||
* {@link #suppressValidation(HttpServletRequest, Object, BindException)} variant
|
||||
* with all parameters.
|
||||
* <p>The default implementation delegates to {@link #suppressValidation(HttpServletRequest)}.
|
||||
* @param request current HTTP request
|
||||
* @param command the command object to validate
|
||||
* @return whether to suppress validation for the given request
|
||||
*/
|
||||
protected boolean suppressValidation(HttpServletRequest request, Object command) {
|
||||
return suppressValidation(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to suppress validation for the given request.
|
||||
* <p>Called by the default implementation of the
|
||||
* {@link #suppressValidation(HttpServletRequest, Object)} variant
|
||||
* with all parameters.
|
||||
* <p>The default implementation is empty.
|
||||
* @param request current HTTP request
|
||||
* @return whether to suppress validation for the given request
|
||||
* @deprecated as of Spring 2.0.4, in favor of the
|
||||
* {@link #suppressValidation(HttpServletRequest, Object)} variant
|
||||
*/
|
||||
protected boolean suppressValidation(HttpServletRequest request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for custom post-processing in terms of binding and validation.
|
||||
* Called on each submit, after standard binding and validation,
|
||||
* but before error evaluation.
|
||||
* <p>The default implementation is empty.
|
||||
* @param request current HTTP request
|
||||
* @param command the command object, still allowing for further binding
|
||||
* @param errors validation errors holder, allowing for additional
|
||||
* custom validation
|
||||
* @throws Exception in case of invalid state or arguments
|
||||
* @see #bindAndValidate
|
||||
* @see org.springframework.validation.Errors
|
||||
*/
|
||||
protected void onBindAndValidate(HttpServletRequest request, Object command, BindException errors)
|
||||
throws Exception {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* <p>Extension of <code>SimpleFormController</code> that supports "cancellation"
|
||||
* of form processing. By default, this controller looks for a given parameter in the
|
||||
* request, identified by the <code>cancelParamKey<code>. If this parameter is present,
|
||||
* then the controller will return the configured <code>cancelView</code>, otherwise
|
||||
* processing is passed back to the superclass.</p>
|
||||
*
|
||||
* <p><b><a name="workflow">Workflow
|
||||
* (<a href="SimpleFormController.html#workflow">in addition to the superclass</a>):</b><br>
|
||||
* <ol>
|
||||
* <li>Call to {@link #processFormSubmission processFormSubmission} which calls
|
||||
* {@link #isCancelRequest} to see if the incoming request is to cancel the
|
||||
* current form entry. By default, {@link #isCancelRequest} returns <code>true</code>
|
||||
* if the configured <code>cancelParamKey</code> exists in the request.
|
||||
* This behavior can be overridden in subclasses.</li>
|
||||
* <li>If {@link #isCancelRequest} returns <code>false</code>, then the controller
|
||||
* will delegate all processing back to {@link SimpleFormController SimpleFormController},
|
||||
* otherwise it will call the {@link #onCancel} version with all parameters.
|
||||
* By default, that method will delegate to the {@link #onCancel} version with just
|
||||
* the command object, which will in turn simply return the configured
|
||||
* <code>cancelView</code>. This behavior can be overridden in subclasses.</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
*
|
||||
* <p>Thanks to Erwin Bolwidt for submitting the original prototype
|
||||
* of such a cancellable form controller!</p>
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2.3
|
||||
* @see #setCancelParamKey
|
||||
* @see #setCancelView
|
||||
* @see #isCancelRequest(javax.servlet.http.HttpServletRequest)
|
||||
* @see #onCancel(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, Object)
|
||||
*/
|
||||
public class CancellableFormController extends SimpleFormController {
|
||||
|
||||
/**
|
||||
* Default parameter triggering the cancel action.
|
||||
* Can be called even with validation errors on the form.
|
||||
*/
|
||||
private static final String PARAM_CANCEL = "_cancel";
|
||||
|
||||
|
||||
private String cancelParamKey = PARAM_CANCEL;
|
||||
|
||||
private String cancelView;
|
||||
|
||||
|
||||
/**
|
||||
* Set the key of the request parameter used to identify a cancel request.
|
||||
* Default is "_cancel".
|
||||
* <p>The parameter is recognized both when sent as a plain parameter
|
||||
* ("_cancel") or when triggered by an image button ("_cancel.x").
|
||||
*/
|
||||
public final void setCancelParamKey(String cancelParamKey) {
|
||||
this.cancelParamKey = cancelParamKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the request parameter used to identify a cancel request.
|
||||
*/
|
||||
public final String getCancelParamKey() {
|
||||
return this.cancelParamKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the cancel view.
|
||||
*/
|
||||
public final void setCancelView(String cancelView) {
|
||||
this.cancelView = cancelView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the cancel view.
|
||||
*/
|
||||
public final String getCancelView() {
|
||||
return this.cancelView;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Consider an explicit cancel request as a form submission too.
|
||||
* @see #isCancelRequest(javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
protected boolean isFormSubmission(HttpServletRequest request) {
|
||||
return super.isFormSubmission(request) || isCancelRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppress validation for an explicit cancel request too.
|
||||
* @see #isCancelRequest(javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
protected boolean suppressValidation(HttpServletRequest request, Object command) {
|
||||
return super.suppressValidation(request, command) || isCancelRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation first checks to see if the incoming is a cancel request,
|
||||
* through a call to {@link #isCancelRequest}. If so, control is passed to
|
||||
* {@link #onCancel}; otherwise, control is passed up to
|
||||
* {@link SimpleFormController#processFormSubmission}.
|
||||
* @see #isCancelRequest
|
||||
* @see #onCancel(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, Object)
|
||||
* @see SimpleFormController#processFormSubmission
|
||||
*/
|
||||
protected ModelAndView processFormSubmission(
|
||||
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
|
||||
throws Exception {
|
||||
|
||||
if (isCancelRequest(request)) {
|
||||
return onCancel(request, response, command);
|
||||
}
|
||||
else {
|
||||
return super.processFormSubmission(request, response, command, errors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the incoming request is a request to cancel the
|
||||
* processing of the current form.
|
||||
* <p>By default, this method returns <code>true</code> if a parameter
|
||||
* matching the configured <code>cancelParamKey</code> is present in
|
||||
* the request, otherwise it returns <code>false</code>. Subclasses may
|
||||
* override this method to provide custom logic to detect a cancel request.
|
||||
* <p>The parameter is recognized both when sent as a plain parameter
|
||||
* ("_cancel") or when triggered by an image button ("_cancel.x").
|
||||
* @param request current HTTP request
|
||||
* @see #setCancelParamKey
|
||||
* @see #PARAM_CANCEL
|
||||
*/
|
||||
protected boolean isCancelRequest(HttpServletRequest request) {
|
||||
return WebUtils.hasSubmitParameter(request, getCancelParamKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for handling a cancel request. Called if {@link #isCancelRequest}
|
||||
* returns <code>true</code>.
|
||||
* <p>Default implementation delegates to <code>onCancel(Object)</code> to return
|
||||
* the configured <code>cancelView</code>. Subclasses may override either of the two
|
||||
* methods to build a custom {@link ModelAndView ModelAndView} that may contain model
|
||||
* parameters used in the cancel view.
|
||||
* <p>If you simply want to move the user to a new view and you don't want to add
|
||||
* additional model parameters, use {@link #setCancelView(String)} rather than
|
||||
* overriding an <code>onCancel</code> method.
|
||||
* @param request current servlet request
|
||||
* @param response current servlet response
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @return the prepared model and view, or <code>null</code>
|
||||
* @throws Exception in case of errors
|
||||
* @see #isCancelRequest(javax.servlet.http.HttpServletRequest)
|
||||
* @see #onCancel(Object)
|
||||
* @see #setCancelView
|
||||
*/
|
||||
protected ModelAndView onCancel(HttpServletRequest request, HttpServletResponse response, Object command)
|
||||
throws Exception {
|
||||
|
||||
return onCancel(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple <code>onCancel</code> version. Called by the default implementation
|
||||
* of the <code>onCancel</code> version with all parameters.
|
||||
* <p>Default implementation returns eturns the configured <code>cancelView</code>.
|
||||
* Subclasses may override this method to build a custom {@link ModelAndView ModelAndView}
|
||||
* that may contain model parameters used in the cancel view.
|
||||
* <p>If you simply want to move the user to a new view and you don't want to add
|
||||
* additional model parameters, use {@link #setCancelView(String)} rather than
|
||||
* overriding an <code>onCancel</code> method.
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @return the prepared model and view, or <code>null</code>
|
||||
* @throws Exception in case of errors
|
||||
* @see #onCancel(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, Object)
|
||||
* @see #setCancelView
|
||||
*/
|
||||
protected ModelAndView onCancel(Object command) throws Exception {
|
||||
return new ModelAndView(getCancelView());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright 2002-2006 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Base Controller interface, representing a component that receives
|
||||
* <code>HttpServletRequest</code> and <code>HttpServletResponse</code>
|
||||
* instances just like a <code>HttpServlet</code> but is able to
|
||||
* participate in an MVC workflow. Controllers are comparable to the
|
||||
* notion of a Struts <code>Action</code>.
|
||||
*
|
||||
* <p>Any implementation of the Controller interface should be a
|
||||
* <i>reusable, thread-safe</i> class, capable of handling multiple
|
||||
* HTTP requests throughout the lifecycle of an application. To be able to
|
||||
* configure a Controller easily, Controller implementations are encouraged
|
||||
* to be (and usually are) JavaBeans.
|
||||
* </p>
|
||||
*
|
||||
* <p><b><a name="workflow">Workflow</a></b></p>
|
||||
*
|
||||
* <p>
|
||||
* After a <cde>DispatcherServlet</code> has received a request and has
|
||||
* done its work to resolve locales, themes and suchlike, it then tries
|
||||
* to resolve a Controller, using a
|
||||
* {@link org.springframework.web.servlet.HandlerMapping HandlerMapping}.
|
||||
* When a Controller has been found to handle the request, the
|
||||
* {@link #handleRequest(HttpServletRequest, HttpServletResponse) handleRequest}
|
||||
* method of the located Controller will be invoked; the located Controller
|
||||
* is then responsible for handling the actual request and - if applicable -
|
||||
* returning an appropriate
|
||||
* {@link org.springframework.web.servlet.ModelAndView ModelAndView}.
|
||||
* So actually, this method is the main entrypoint for the
|
||||
* {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet}
|
||||
* which delegates requests to controllers. This method - and also this interface -
|
||||
* should preferrably not be implemented by custom controllers <i>directly</i>, since
|
||||
* abstract controller also provided by this package already provide a lot of
|
||||
* functionality for typical use cases in web applications. A few examples of
|
||||
* those controllers:
|
||||
* {@link AbstractController AbstractController},
|
||||
* {@link AbstractCommandController AbstractCommandController},
|
||||
* {@link SimpleFormController SimpleFormController}.</p>
|
||||
*
|
||||
* <p>So basically any <i>direct</i> implementation of the Controller interface
|
||||
* just handles HttpServletRequests and should return a ModelAndView, to be further
|
||||
* interpreted by the DispatcherServlet. Any additional functionality such as
|
||||
* optional validation, form handling, etc should be obtained through extending
|
||||
* one of the abstract controller classes mentioned above.</p>
|
||||
*
|
||||
* <p><b>Notes on design and testing</b></p>
|
||||
*
|
||||
* <p>The Controller interface is explicitly designed to operate on HttpServletRequest
|
||||
* and HttpServletResponse objects, just like an HttpServlet. It does not aim to
|
||||
* decouple itself from the Servlet API, in contrast to, for example, WebWork, JSF or Tapestry.
|
||||
* Instead, the full power of the Servlet API is available, allowing Controllers to be
|
||||
* general-purpose: a Controller is able to not only handle web user interface
|
||||
* requests but also to process remoting protocols or to generate reports on demand.</p>
|
||||
*
|
||||
* <p>Controllers can easily be tested by passing in mock objects for the
|
||||
* HttpServletRequest and HttpServletResponse objects as parameters to the
|
||||
* {@link #handleRequest(HttpServletRequest, HttpServletResponse) handleRequest}
|
||||
* method. As a convenience, Spring ships with a set of Servlet API mocks
|
||||
* that are suitable for testing any kind of web components, but are particularly
|
||||
* suitable for testing Spring web controllers. In contrast to a Struts Action,
|
||||
* there is no need to mock the ActionServlet or any other infrastructure;
|
||||
* HttpServletRequest and HttpServletResponse are sufficient.</p>
|
||||
*
|
||||
* <p>If Controllers need to be aware of specific environment references, they can
|
||||
* choose to implement specific awareness interfaces, just like any other bean in a
|
||||
* Spring (web) application context can do, for example:</p>
|
||||
* <ul>
|
||||
* <li><code>org.springframework.context.ApplicationContextAware</code></li>
|
||||
* <li><code>org.springframework.context.ResourceLoaderAware</code></li>
|
||||
* <li><code>org.springframework.web.context.ServletContextAware</code></li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Such environment references can easily be passed in testing environments,
|
||||
* through the corresponding setters defined in the respective awareness interfaces.
|
||||
* In general, it is recommended to keep the dependencies as minimal as possible:
|
||||
* for example, if all you need is resource loading, implement ResourceLoaderAware only.
|
||||
* Alternatively, derive from the WebApplicationObjectSupport base class, which gives
|
||||
* you all those references through convenient accessors - but requires an
|
||||
* ApplicationContext reference on initialization.
|
||||
*
|
||||
* <p>Controllers can optionally implement the LastModified interface.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see LastModified
|
||||
* @see SimpleControllerHandlerAdapter
|
||||
* @see AbstractController
|
||||
* @see AbstractCommandController
|
||||
* @see SimpleFormController
|
||||
* @see org.springframework.mock.web.MockHttpServletRequest
|
||||
* @see org.springframework.mock.web.MockHttpServletResponse
|
||||
* @see org.springframework.context.ApplicationContextAware
|
||||
* @see org.springframework.context.ResourceLoaderAware
|
||||
* @see org.springframework.web.context.ServletContextAware
|
||||
* @see org.springframework.web.context.support.WebApplicationObjectSupport
|
||||
*/
|
||||
public interface Controller {
|
||||
|
||||
/**
|
||||
* Process the request and return a ModelAndView object which the DispatcherServlet
|
||||
* will render. A <code>null</code> return value is not an error: It indicates that
|
||||
* this object completed request processing itself, thus there is no ModelAndView
|
||||
* to render.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @return a ModelAndView to render, or <code>null</code> if handled directly
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2002-2007 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.springframework.web.servlet.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.HttpRequestHandler;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Adapter to use the plain {@link org.springframework.web.HttpRequestHandler}
|
||||
* interface with the generic {@link org.springframework.web.servlet.DispatcherServlet}.
|
||||
* Supports handlers that implement the {@link LastModified} interface.
|
||||
*
|
||||
* <p>This is an SPI class, not used directly by application code.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see org.springframework.web.servlet.DispatcherServlet
|
||||
* @see org.springframework.web.HttpRequestHandler
|
||||
* @see LastModified
|
||||
* @see SimpleControllerHandlerAdapter
|
||||
*/
|
||||
public class HttpRequestHandlerAdapter implements HandlerAdapter {
|
||||
|
||||
public boolean supports(Object handler) {
|
||||
return (handler instanceof HttpRequestHandler);
|
||||
}
|
||||
|
||||
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
|
||||
((HttpRequestHandler) handler).handleRequest(request, response);
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getLastModified(HttpServletRequest request, Object handler) {
|
||||
if (handler instanceof LastModified) {
|
||||
return ((LastModified) handler).getLastModified(request);
|
||||
}
|
||||
return -1L;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue