Rename modules {org.springframework.*=>spring-*}
This renaming more intuitively expresses the relationship between
subprojects and the JAR artifacts they produce.
Tracking history across these renames is possible, but it requires
use of the --follow flag to `git log`, for example
$ git log spring-aop/src/main/java/org/springframework/aop/Advisor.java
will show history up until the renaming event, where
$ git log --follow spring-aop/src/main/java/org/springframework/aop/Advisor.java
will show history for all changes to the file, before and after the
renaming.
See http://chrisbeams.com/git-diff-across-renamed-directories
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
# 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.annotation.AnnotationMethodHandlerAdapter
|
||||
|
||||
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
|
||||
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
|
||||
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
|
||||
|
||||
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
|
||||
|
||||
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
|
||||
|
||||
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.DefaultFlashMapManager
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.HashMap;
|
||||
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A FlashMap provides a way for one request to store attributes intended for
|
||||
* use in another. This is most commonly needed when redirecting from one URL
|
||||
* to another -- e.g. the Post/Redirect/Get pattern. A FlashMap is saved before
|
||||
* the redirect (typically in the session) and is made available after the
|
||||
* redirect and removed immediately.
|
||||
*
|
||||
* <p>A FlashMap can be set up with a request path and request parameters to
|
||||
* help identify the target request. Without this information, a FlashMap is
|
||||
* made available to the next request, which may or may not be the intended
|
||||
* recipient. On a redirect, the target URL is known and for example
|
||||
* {@code org.springframework.web.servlet.view.RedirectView} has the
|
||||
* opportunity to automatically update the current FlashMap with target
|
||||
* URL information.
|
||||
*
|
||||
* <p>Annotated controllers will usually not use this type directly.
|
||||
* See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes}
|
||||
* for an overview of using flash attributes in annotated controllers.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*
|
||||
* @see FlashMapManager
|
||||
*/
|
||||
public final class FlashMap extends HashMap<String, Object> implements Comparable<FlashMap> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String targetRequestPath;
|
||||
|
||||
private final MultiValueMap<String, String> targetRequestParams = new LinkedMultiValueMap<String, String>();
|
||||
|
||||
private long expirationStartTime;
|
||||
|
||||
private int timeToLive;
|
||||
|
||||
private final int createdBy;
|
||||
|
||||
/**
|
||||
* Create a new instance with an id uniquely identifying the creator of
|
||||
* this FlashMap.
|
||||
* @param createdBy identifies the FlashMapManager instance that created
|
||||
* and will manage this FlashMap instance (e.g. via a hashCode)
|
||||
*/
|
||||
public FlashMap(int createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*/
|
||||
public FlashMap() {
|
||||
this.createdBy = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a URL path to help identify the target request for this FlashMap.
|
||||
* The path may be absolute (e.g. /application/resource) or relative to the
|
||||
* current request (e.g. ../resource).
|
||||
* @param path the URI path
|
||||
*/
|
||||
public void setTargetRequestPath(String path) {
|
||||
this.targetRequestPath = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the target URL path or {@code null}.
|
||||
*/
|
||||
public String getTargetRequestPath() {
|
||||
return targetRequestPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide request parameters identifying the request for this FlashMap.
|
||||
* Null or empty keys and values are skipped.
|
||||
* @param params a Map with the names and values of expected parameters.
|
||||
*/
|
||||
public FlashMap addTargetRequestParams(MultiValueMap<String, String> params) {
|
||||
if (params != null) {
|
||||
for (String key : params.keySet()) {
|
||||
for (String value : params.get(key)) {
|
||||
addTargetRequestParam(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a request parameter identifying the request for this FlashMap.
|
||||
* @param name the expected parameter name, skipped if {@code null}
|
||||
* @param value the expected parameter value, skipped if {@code null}
|
||||
*/
|
||||
public FlashMap addTargetRequestParam(String name, String value) {
|
||||
if (StringUtils.hasText(name) && StringUtils.hasText(value)) {
|
||||
this.targetRequestParams.add(name, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parameters identifying the target request, or an empty map.
|
||||
*/
|
||||
public MultiValueMap<String, String> getTargetRequestParams() {
|
||||
return targetRequestParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the expiration period for this instance.
|
||||
* @param timeToLive the number of seconds before expiration
|
||||
*/
|
||||
public void startExpirationPeriod(int timeToLive) {
|
||||
this.expirationStartTime = System.currentTimeMillis();
|
||||
this.timeToLive = timeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this instance has expired depending on the amount of elapsed
|
||||
* time since the call to {@link #startExpirationPeriod}.
|
||||
*/
|
||||
public boolean isExpired() {
|
||||
if (this.expirationStartTime != 0) {
|
||||
return (System.currentTimeMillis() - this.expirationStartTime) > this.timeToLive * 1000;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given id matches the id of the creator of this FlashMap.
|
||||
*/
|
||||
public boolean isCreatedBy(int createdBy) {
|
||||
return this.createdBy == createdBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two FlashMaps and prefer the one that specifies a target URL
|
||||
* path or has more target URL parameters. Before comparing FlashMap
|
||||
* instances ensure that they match a given request.
|
||||
*/
|
||||
public int compareTo(FlashMap other) {
|
||||
int thisUrlPath = (this.targetRequestPath != null) ? 1 : 0;
|
||||
int otherUrlPath = (other.targetRequestPath != null) ? 1 : 0;
|
||||
if (thisUrlPath != otherUrlPath) {
|
||||
return otherUrlPath - thisUrlPath;
|
||||
}
|
||||
else {
|
||||
return other.targetRequestParams.size() - this.targetRequestParams.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[Attributes=").append(super.toString());
|
||||
sb.append(", targetRequestPath=").append(this.targetRequestPath);
|
||||
sb.append(", targetRequestParams=").append(this.targetRequestParams).append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A strategy interface for storing, retrieving, and managing {@code FlashMap}
|
||||
* instances. See {@link FlashMap} for a general overview of flash attributes.
|
||||
*
|
||||
* <p>A FlashMapManager is invoked at the beginning and at the end of requests.
|
||||
* For each request it retrieves an "input" FlashMap with attributes passed
|
||||
* from a previous request (if any) and creates an "output" FlashMap with
|
||||
* attributes to pass to a subsequent request. "Input" and "output" FlashMap
|
||||
* instances are exposed as request attributes and are accessible via methods
|
||||
* in {@code org.springframework.web.servlet.support.RequestContextUtils}.
|
||||
*
|
||||
* <p>Annotated controllers will usually not use this FlashMap directly.
|
||||
* See {@code org.springframework.web.servlet.mvc.support.RedirectAttributes}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*
|
||||
* @see FlashMap
|
||||
*/
|
||||
public interface FlashMapManager {
|
||||
|
||||
/**
|
||||
* Name of request attribute that holds a read-only
|
||||
* {@code Map<String, Object>} with "input" flash attributes if any.
|
||||
* @see org.springframework.web.servlet.support.RequestContextUtils#getInputFlashMap(HttpServletRequest)
|
||||
*/
|
||||
String INPUT_FLASH_MAP_ATTRIBUTE = FlashMapManager.class.getName() + ".INPUT_FLASH_MAP";
|
||||
|
||||
/**
|
||||
* Name of request attribute that holds the "output" {@link FlashMap} with
|
||||
* attributes to save for a subsequent request.
|
||||
* @see org.springframework.web.servlet.support.RequestContextUtils#getOutputFlashMap(HttpServletRequest)
|
||||
*/
|
||||
String OUTPUT_FLASH_MAP_ATTRIBUTE = FlashMapManager.class.getName() + ".OUTPUT_FLASH_MAP";
|
||||
|
||||
/**
|
||||
* Perform the following tasks unless the {@link #OUTPUT_FLASH_MAP_ATTRIBUTE}
|
||||
* request attribute exists:
|
||||
* <ol>
|
||||
* <li>Find the "input" FlashMap, expose it under the request attribute
|
||||
* {@link #INPUT_FLASH_MAP_ATTRIBUTE}, and remove it from underlying storage.
|
||||
* <li>Create the "output" FlashMap and expose it under the request
|
||||
* attribute {@link #OUTPUT_FLASH_MAP_ATTRIBUTE}.
|
||||
* <li>Clean expired FlashMap instances.
|
||||
* </ol>
|
||||
* @param request the current request
|
||||
* @param response the current response
|
||||
*/
|
||||
void requestStarted(HttpServletRequest request, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* Start the expiration period of the "output" FlashMap save it in the
|
||||
* underlying storage.
|
||||
* <p>The "output" FlashMap should not be saved if it is empty or if it was
|
||||
* not created by the current FlashMapManager instance.
|
||||
* @param request the current request
|
||||
* @param response the current response
|
||||
*/
|
||||
void requestCompleted(HttpServletRequest request, HttpServletResponse response);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,993 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
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.context.i18n.LocaleContext;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.context.i18n.SimpleLocaleContext;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
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
|
||||
* 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
|
||||
* XmlWebApplicationContext}, if not found. Note that, with the default
|
||||
* {@code FrameworkServlet}, a custom context class needs to implement the
|
||||
* {@link org.springframework.web.context.ConfigurableWebApplicationContext
|
||||
* ConfigurableWebApplicationContext} SPI.
|
||||
*
|
||||
* <p>Accepts an optional "contextInitializerClasses" servlet init-param that
|
||||
* specifies one or more {@link org.springframework.context.ApplicationContextInitializer
|
||||
* ApplicationContextInitializer} classes. The managed web application context will be
|
||||
* delegated to these initializers, allowing for additional programmatic configuration,
|
||||
* e.g. adding property sources or activating profiles against the {@linkplain
|
||||
* org.springframework.context.ConfigurableApplicationContext#getEnvironment() context's
|
||||
* environment}. See also {@link org.springframework.web.context.ContextLoader} which
|
||||
* supports a "contextInitializerClasses" context-param with identical semantics for
|
||||
* the "root" web application context.
|
||||
*
|
||||
* <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.
|
||||
*
|
||||
* <p>As of Spring 3.1, {@code FrameworkServlet} may now be injected with a web
|
||||
* application context, rather than creating its own internally. This is useful in Servlet
|
||||
* 3.0+ environments, which support programmatic registration of servlet instances. See
|
||||
* {@link #FrameworkServlet(WebApplicationContext)} Javadoc for details.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @author Chris Beams
|
||||
* @see #doService
|
||||
* @see #setContextClass
|
||||
* @see #setContextConfigLocation
|
||||
* @see #setContextInitializerClasses
|
||||
* @see #setNamespace
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class FrameworkServlet extends HttpServletBean {
|
||||
|
||||
/**
|
||||
* 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.";
|
||||
|
||||
/**
|
||||
* Any number of these characters are considered delimiters between
|
||||
* multiple values in a single init-param String value.
|
||||
*/
|
||||
private static final String INIT_PARAM_DELIMITERS = ",; \t\n";
|
||||
|
||||
|
||||
/** ServletContext attribute to find the WebApplicationContext in */
|
||||
private String contextAttribute;
|
||||
|
||||
/** WebApplicationContext implementation class to create */
|
||||
private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
|
||||
|
||||
/** WebApplicationContext id to assign */
|
||||
private String contextId;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Expose LocaleContext and RequestAttributes as inheritable for child threads? */
|
||||
private boolean threadContextInheritable = false;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Comma-delimited ApplicationContextInitializer classnames set through init param */
|
||||
private String contextInitializerClasses;
|
||||
|
||||
/** Actual ApplicationContextInitializer instances to apply to the context */
|
||||
private ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
|
||||
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code FrameworkServlet} that will create its own internal web
|
||||
* application context based on defaults and values provided through servlet
|
||||
* init-params. Typically used in Servlet 2.5 or earlier environments, where the only
|
||||
* option for servlet registration is through {@code web.xml} which requires the use
|
||||
* of a no-arg constructor.
|
||||
* <p>Calling {@link #setContextConfigLocation} (init-param 'contextConfigLocation')
|
||||
* will dictate which XML files will be loaded by the
|
||||
* {@linkplain #DEFAULT_CONTEXT_CLASS default XmlWebApplicationContext}
|
||||
* <p>Calling {@link #setContextClass} (init-param 'contextClass') overrides the
|
||||
* default {@code XmlWebApplicationContext} and allows for specifying an alternative class,
|
||||
* such as {@code AnnotationConfigWebApplicationContext}.
|
||||
* <p>Calling {@link #setContextInitializerClasses} (init-param 'contextInitializerClasses')
|
||||
* indicates which {@link ApplicationContextInitializer} classes should be used to
|
||||
* further configure the internal application context prior to refresh().
|
||||
* @see #FrameworkServlet(WebApplicationContext)
|
||||
*/
|
||||
public FrameworkServlet() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code FrameworkServlet} with the given web application context. This
|
||||
* constructor is useful in Servlet 3.0+ environments where instance-based registration
|
||||
* of servlets is possible through the {@link ServletContext#addServlet} API.
|
||||
* <p>Using this constructor indicates that the following properties / init-params
|
||||
* will be ignored:
|
||||
* <ul>
|
||||
* <li>{@link #setContextClass(Class)} / 'contextClass'</li>
|
||||
* <li>{@link #setContextConfigLocation(String)} / 'contextConfigLocation'</li>
|
||||
* <li>{@link #setContextAttribute(String)} / 'contextAttribute'</li>
|
||||
* <li>{@link #setNamespace(String)} / 'namespace'</li>
|
||||
* </ul>
|
||||
* <p>The given web application context may or may not yet be {@linkplain
|
||||
* ConfigurableApplicationContext#refresh() refreshed}. If it (a) is an implementation
|
||||
* of {@link ConfigurableWebApplicationContext} and (b) has <strong>not</strong>
|
||||
* already been refreshed (the recommended approach), then the following will occur:
|
||||
* <ul>
|
||||
* <li>If the given context does not already have a {@linkplain
|
||||
* ConfigurableApplicationContext#setParent parent}, the root application context
|
||||
* will be set as the parent.</li>
|
||||
* <li>If the given context has not already been assigned an {@linkplain
|
||||
* ConfigurableApplicationContext#setId id}, one will be assigned to it</li>
|
||||
* <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to
|
||||
* the application context</li>
|
||||
* <li>{@link #postProcessWebApplicationContext} will be called</li>
|
||||
* <li>Any {@link ApplicationContextInitializer}s specified through the
|
||||
* "contextInitializerClasses" init-param or through the {@link
|
||||
* #setContextInitializers} property will be applied.</li>
|
||||
* <li>{@link ConfigurableApplicationContext#refresh refresh()} will be called</li>
|
||||
* </ul>
|
||||
* If the context has already been refreshed or does not implement
|
||||
* {@code ConfigurableWebApplicationContext}, none of the above will occur under the
|
||||
* assumption that the user has performed these actions (or not) per his or her
|
||||
* specific needs.
|
||||
* <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
|
||||
* @param webApplicationContext the context to use
|
||||
* @see #initWebApplicationContext
|
||||
* @see #configureAndRefreshWebApplicationContext
|
||||
* @see org.springframework.web.WebApplicationInitializer
|
||||
*/
|
||||
public FrameworkServlet(WebApplicationContext webApplicationContext) {
|
||||
this.webApplicationContext = webApplicationContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a custom WebApplicationContext id,
|
||||
* to be used as serialization id for the underlying BeanFactory.
|
||||
*/
|
||||
public void setContextId(String contextId) {
|
||||
this.contextId = contextId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the custom WebApplicationContext id, if any.
|
||||
*/
|
||||
public String getContextId() {
|
||||
return this.contextId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the set of fully-qualified {@link ApplicationContextInitializer} class
|
||||
* names, per the optional "contextInitializerClasses" servlet init-param.
|
||||
* @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext)
|
||||
* @see #applyInitializers(ConfigurableWebApplicationContext)
|
||||
*/
|
||||
public void setContextInitializerClasses(String contextInitializerClasses) {
|
||||
this.contextInitializerClasses = contextInitializerClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify which {@link ApplicationContextInitializer} instances should be used
|
||||
* to initialize the application context used by this {@code FrameworkServlet}.
|
||||
* @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext)
|
||||
* @see #applyInitializers(ConfigurableWebApplicationContext)
|
||||
*/
|
||||
public void setContextInitializers(ApplicationContextInitializer<ConfigurableApplicationContext>... contextInitializers) {
|
||||
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : contextInitializers) {
|
||||
this.contextInitializers.add(initializer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 to expose the LocaleContext and RequestAttributes as inheritable
|
||||
* for child threads (using an {@link java.lang.InheritableThreadLocal}).
|
||||
* <p>Default is "false", to avoid side effects on spawned background threads.
|
||||
* Switch this to "true" to enable inheritance for custom child threads which
|
||||
* are spawned during request processing and only used for this request
|
||||
* (that is, ending after their initial task, without reuse of the thread).
|
||||
* <p><b>WARNING:</b> Do not use inheritance for child threads if you are
|
||||
* accessing a thread pool which is configured to potentially add new threads
|
||||
* on demand (e.g. a JDK {@link java.util.concurrent.ThreadPoolExecutor}),
|
||||
* since this will expose the inherited context to such a pooled thread.
|
||||
*/
|
||||
public void setThreadContextInheritable(boolean threadContextInheritable) {
|
||||
this.threadContextInheritable = threadContextInheritable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 if your controllers happen to not set the 'Allow' header
|
||||
* (as required for an OPTIONS response).
|
||||
*/
|
||||
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 if your controllers happen to not generate a response
|
||||
* of content type 'message/http' (as required for a TRACE response).
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@Override
|
||||
protected final void initServletBean() throws ServletException {
|
||||
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 (RuntimeException 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
|
||||
* @see #FrameworkServlet(WebApplicationContext)
|
||||
* @see #setContextClass
|
||||
* @see #setContextConfigLocation
|
||||
*/
|
||||
protected WebApplicationContext initWebApplicationContext() {
|
||||
WebApplicationContext rootContext =
|
||||
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
|
||||
WebApplicationContext wac = null;
|
||||
|
||||
if (this.webApplicationContext != null) {
|
||||
// A context instance was injected at construction time -> use it
|
||||
wac = this.webApplicationContext;
|
||||
if (wac instanceof ConfigurableWebApplicationContext) {
|
||||
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
|
||||
if (!cwac.isActive()) {
|
||||
// The context has not yet been refreshed -> provide services such as
|
||||
// setting the parent context, setting the application context id, etc
|
||||
if (cwac.getParent() == null) {
|
||||
// The context instance was injected without an explicit parent -> set
|
||||
// the root application context (if any; may be null) as the parent
|
||||
cwac.setParent(rootContext);
|
||||
}
|
||||
configureAndRefreshWebApplicationContext(cwac);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wac == null) {
|
||||
// No context instance was injected at construction time -> see if one
|
||||
// has been registered in the servlet context. If one exists, it is assumed
|
||||
// that the parent context (if any) has already been set and that the
|
||||
// user has performed any initialization such as setting the context id
|
||||
wac = findWebApplicationContext();
|
||||
}
|
||||
if (wac == null) {
|
||||
// No context instance is defined for this servlet -> create a local one
|
||||
wac = createWebApplicationContext(rootContext);
|
||||
}
|
||||
|
||||
if (!this.refreshEventReceived) {
|
||||
// Either the context is not a ConfigurableApplicationContext with refresh
|
||||
// support or the context injected at construction time had already been
|
||||
// refreshed -> trigger 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
|
||||
* @see org.springframework.web.context.support.XmlWebApplicationContext
|
||||
*/
|
||||
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
|
||||
Class<?> contextClass = getContextClass();
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Servlet with name '" + getServletName() +
|
||||
"' will try to create custom WebApplicationContext context of class '" +
|
||||
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
|
||||
}
|
||||
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
|
||||
throw new ApplicationContextException(
|
||||
"Fatal initialization error in servlet with name '" + getServletName() +
|
||||
"': custom WebApplicationContext class [" + contextClass.getName() +
|
||||
"] is not of type ConfigurableWebApplicationContext");
|
||||
}
|
||||
ConfigurableWebApplicationContext wac =
|
||||
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
|
||||
|
||||
wac.setParent(parent);
|
||||
wac.setConfigLocation(getContextConfigLocation());
|
||||
|
||||
configureAndRefreshWebApplicationContext(wac);
|
||||
|
||||
return wac;
|
||||
}
|
||||
|
||||
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
|
||||
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
|
||||
// The application context id is still set to its original default value
|
||||
// -> assign a more useful id based on available information
|
||||
if (this.contextId != null) {
|
||||
wac.setId(this.contextId);
|
||||
}
|
||||
else {
|
||||
// Generate default id...
|
||||
ServletContext sc = getServletContext();
|
||||
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
|
||||
// Servlet <= 2.4: resort to name specified in web.xml, if any.
|
||||
String servletContextName = sc.getServletContextName();
|
||||
if (servletContextName != null) {
|
||||
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +
|
||||
"." + getServletName());
|
||||
}
|
||||
else {
|
||||
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getServletName());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Servlet 2.5's getContextPath available!
|
||||
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
|
||||
ObjectUtils.getDisplayString(sc.getContextPath()) + "/" + getServletName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wac.setServletContext(getServletContext());
|
||||
wac.setServletConfig(getServletConfig());
|
||||
wac.setNamespace(getNamespace());
|
||||
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
|
||||
|
||||
postProcessWebApplicationContext(wac);
|
||||
|
||||
applyInitializers(wac);
|
||||
|
||||
wac.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Delegates to #createWebApplicationContext(ApplicationContext).
|
||||
* @param parent the parent WebApplicationContext to use, or <code>null</code> if none
|
||||
* @return the WebApplicationContext for this servlet
|
||||
* @see org.springframework.web.context.support.XmlWebApplicationContext
|
||||
* @see #createWebApplicationContext(ApplicationContext)
|
||||
*/
|
||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
||||
return createWebApplicationContext((ApplicationContext) parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate the WebApplicationContext before it is refreshed to any
|
||||
* {@link ApplicationContextInitializer} instances specified by the
|
||||
* "contextInitializerClasses" servlet init-param.
|
||||
* <p>See also {@link #postProcessWebApplicationContext}, which is designed to allow
|
||||
* subclasses (as opposed to end-users) to modify the application context, and is
|
||||
* called immediately after this method.
|
||||
* @param wac the configured WebApplicationContext (not refreshed yet)
|
||||
* @see #createWebApplicationContext
|
||||
* @see #postProcessWebApplicationContext
|
||||
* @see ConfigurableApplicationContext#refresh()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void applyInitializers(ConfigurableApplicationContext wac) {
|
||||
if (this.contextInitializerClasses != null) {
|
||||
String[] initializerClassNames =
|
||||
StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS);
|
||||
for (String initializerClassName : initializerClassNames) {
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext> initializer;
|
||||
try {
|
||||
Class<?> initializerClass = ClassUtils.forName(initializerClassName, wac.getClassLoader());
|
||||
initializer = BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Could not instantiate class [%s] specified via " +
|
||||
"'contextInitializerClasses' init-param", initializerClassName), ex);
|
||||
}
|
||||
this.contextInitializers.add(initializer);
|
||||
}
|
||||
}
|
||||
Collections.sort(this.contextInitializers, new AnnotationAwareOrderComparator());
|
||||
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
|
||||
initializer.initialize(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.
|
||||
* <p>Note that this method is designed to allow subclasses to modify the application
|
||||
* context, while {@link #initializeWebApplicationContext} is designed to allow
|
||||
* end-users to modify the context through the use of
|
||||
* {@link ApplicationContextInitializer}s.
|
||||
* @param wac the configured WebApplicationContext (not refreshed yet)
|
||||
* @see #createWebApplicationContext
|
||||
* @see #initializeWebApplicationContext
|
||||
* @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
|
||||
*/
|
||||
protected void initFrameworkServlet() throws ServletException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh this servlet's application context, as well as the
|
||||
* dependent state of the servlet.
|
||||
* @see #getWebApplicationContext()
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#refresh()
|
||||
*/
|
||||
public void refresh() {
|
||||
WebApplicationContext wac = getWebApplicationContext();
|
||||
if (!(wac instanceof ConfigurableApplicationContext)) {
|
||||
throw new IllegalStateException("WebApplicationContext does not support refresh: " + wac);
|
||||
}
|
||||
((ConfigurableApplicationContext) wac).refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback that receives refresh events from this servlet's WebApplicationContext.
|
||||
* <p>The default implementation calls {@link #onRefresh},
|
||||
* triggering a refresh of this servlet's context-dependent state.
|
||||
* @param event the incoming ApplicationContext event
|
||||
*/
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
this.refreshEventReceived = true;
|
||||
onRefresh(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
|
||||
* @see #refresh()
|
||||
*/
|
||||
protected void onRefresh(ApplicationContext context) {
|
||||
// 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
|
||||
*/
|
||||
@Override
|
||||
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
processRequest(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate POST requests to {@link #processRequest}.
|
||||
* @see #doService
|
||||
*/
|
||||
@Override
|
||||
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
processRequest(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate PUT requests to {@link #processRequest}.
|
||||
* @see #doService
|
||||
*/
|
||||
@Override
|
||||
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
processRequest(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate DELETE requests to {@link #processRequest}.
|
||||
* @see #doService
|
||||
*/
|
||||
@Override
|
||||
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 otherwise,
|
||||
* and also if there is still no 'Allow' header set after dispatching.
|
||||
* @see #doService
|
||||
*/
|
||||
@Override
|
||||
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
if (this.dispatchOptionsRequest) {
|
||||
processRequest(request, response);
|
||||
if (response.containsHeader("Allow")) {
|
||||
// Proper OPTIONS response coming from a handler - we're done.
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.doOptions(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate TRACE requests to {@link #processRequest}, if desired.
|
||||
* <p>Applies HttpServlet's standard TRACE processing otherwise.
|
||||
* @see #doService
|
||||
*/
|
||||
@Override
|
||||
protected void doTrace(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
if (this.dispatchTraceRequest) {
|
||||
processRequest(request, response);
|
||||
if ("message/http".equals(response.getContentType())) {
|
||||
// Proper TRACE response coming from a handler - we're done.
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.doTrace(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;
|
||||
|
||||
// Expose current LocaleResolver and request as LocaleContext.
|
||||
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
|
||||
LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);
|
||||
|
||||
// Expose current RequestAttributes to current thread.
|
||||
RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
ServletRequestAttributes requestAttributes = null;
|
||||
if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) {
|
||||
requestAttributes = new ServletRequestAttributes(request);
|
||||
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Bound request context to thread: " + request);
|
||||
}
|
||||
|
||||
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 {
|
||||
// Clear request attributes and reset thread-bound context.
|
||||
LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
|
||||
if (requestAttributes != null) {
|
||||
RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
|
||||
requestAttributes.requestCompleted();
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Cleared thread-bound request context: " + request);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a LocaleContext for the given request, exposing the request's
|
||||
* primary locale as current locale.
|
||||
* @param request current HTTP request
|
||||
* @return the corresponding LocaleContext
|
||||
*/
|
||||
protected LocaleContext buildLocaleContext(HttpServletRequest request) {
|
||||
return new SimpleLocaleContext(request.getLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
|
||||
if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) this.webApplicationContext).close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ApplicationListener endpoint that receives events from this servlet's WebApplicationContext
|
||||
* only, delegating to <code>onApplicationEvent</code> on the FrameworkServlet instance.
|
||||
*/
|
||||
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
FrameworkServlet.this.onApplicationEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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 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 {@link 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 <code>Object</code>. This is to enable
|
||||
* handlers from other frameworks to be integrated with this framework without
|
||||
* custom coding, as well as to allow for annotation handler objects that do
|
||||
* not obey any specific Java interface.
|
||||
*
|
||||
* <p>This interface is not intended for application developers. It is available
|
||||
* to handlers who want to develop their own web workflow.
|
||||
*
|
||||
* <p>Note: HandlerAdaptger implementators may 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.web.servlet.mvc.SimpleControllerHandlerAdapter
|
||||
* @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,53 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.
|
||||
* <p>The returned ModelAndView may be {@linkplain ModelAndView#isEmpty() empty}
|
||||
* to indicate that the exception has been resolved successfully but that no view
|
||||
* should be rendered, for instance by setting a status code.
|
||||
* @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,132 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.Arrays;
|
||||
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<HandlerInterceptor> 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<HandlerInterceptor>();
|
||||
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();
|
||||
this.interceptorList.addAll(Arrays.asList(interceptors));
|
||||
}
|
||||
}
|
||||
|
||||
private void initInterceptorList() {
|
||||
if (this.interceptorList == null) {
|
||||
this.interceptorList = new ArrayList<HandlerInterceptor>();
|
||||
}
|
||||
if (this.interceptors != null) {
|
||||
this.interceptorList.addAll(Arrays.asList(this.interceptors));
|
||||
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 = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
|
||||
}
|
||||
return this.interceptors;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to the handler's <code>toString()</code>.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this.handler == null) {
|
||||
return "HandlerExecutionChain with no handler";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]");
|
||||
if (!CollectionUtils.isEmpty(this.interceptorList)) {
|
||||
sb.append(" and ").append(this.interceptorList.size()).append(" interceptor");
|
||||
if (this.interceptorList.size() > 1) {
|
||||
sb.append("s");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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>!
|
||||
* <p>As with the {@code postHandle} method, the method will be invoked on each
|
||||
* interceptor in the chain in reverse order, so the first interceptor will be
|
||||
* the last to be invoked.
|
||||
* @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,120 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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";
|
||||
|
||||
/**
|
||||
* Name of the {@link HttpServletRequest} attribute that contains the
|
||||
* best matching pattern within the handler mapping.
|
||||
* <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 BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
|
||||
|
||||
/**
|
||||
* Name of the boolean {@link HttpServletRequest} attribute that indicates
|
||||
* whether type-level mappings should be inspected.
|
||||
* <p>Note: This attribute is not required to be supported by all
|
||||
* HandlerMapping implementations.
|
||||
*/
|
||||
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
|
||||
|
||||
/**
|
||||
* Name of the {@link HttpServletRequest} attribute that contains the URI
|
||||
* templates map, mapping variable names to values.
|
||||
* <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 URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
|
||||
|
||||
/**
|
||||
* Name of the {@link HttpServletRequest} attribute that contains the set of producible MediaTypes
|
||||
* applicable to the mapped handler.
|
||||
* <p>Note: This attribute is not required to be supported by all HandlerMapping implementations.
|
||||
* Handlers should not necessarily expect this request attribute to be present in all scenarios.
|
||||
*/
|
||||
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
|
||||
|
||||
/**
|
||||
* 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,231 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
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.StandardServletEnvironment;
|
||||
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
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class HttpServletBean extends HttpServlet implements EnvironmentAware {
|
||||
|
||||
/** 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<String> requiredProperties = new HashSet<String>();
|
||||
|
||||
private Environment environment = new StandardServletEnvironment();
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
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, this.environment));
|
||||
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()
|
||||
*/
|
||||
@Override
|
||||
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()
|
||||
*/
|
||||
@Override
|
||||
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 {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>Any environment set here overrides the {@link StandardServletEnvironment}
|
||||
* provided by default.
|
||||
*/
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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<String> requiredProperties)
|
||||
throws ServletException {
|
||||
|
||||
Set<String> missingProps = (requiredProperties != null && !requiredProperties.isEmpty()) ?
|
||||
new HashSet<String>(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,304 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* 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 = false;
|
||||
|
||||
|
||||
/**
|
||||
* 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<String, ?> 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<String, ?> 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<String, Object> 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<String, Object> 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<String, ?> 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 && CollectionUtils.isEmpty(this.model));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p>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.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("ModelAndView: ");
|
||||
if (isReference()) {
|
||||
sb.append("reference to view with name '").append(this.view).append("'");
|
||||
}
|
||||
else {
|
||||
sb.append("materialized View is [").append(this.view).append(']');
|
||||
}
|
||||
sb.append("; model is ").append(this.model);
|
||||
return sb.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,345 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
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,33 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Provides additional information about a View such as whether it
|
||||
* performs redirects.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface SmartView extends View {
|
||||
|
||||
/**
|
||||
* Whether the view performs a redirect.
|
||||
*/
|
||||
boolean isRedirectView();
|
||||
|
||||
}
|
||||
@@ -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,84 @@
|
||||
/*
|
||||
* 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.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
|
||||
* @author Arjen Poutsma
|
||||
* @see org.springframework.web.servlet.view.AbstractView
|
||||
* @see org.springframework.web.servlet.view.InternalResourceView
|
||||
*/
|
||||
public interface View {
|
||||
|
||||
/**
|
||||
* Name of the {@link HttpServletRequest} attribute that contains the response status code.
|
||||
* <p>Note: This attribute is not required to be supported by all View implementations.
|
||||
*/
|
||||
String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
|
||||
|
||||
/**
|
||||
* Name of the {@link HttpServletRequest} attribute that contains a Map with path variables.
|
||||
* The map consists of String-based URI template variable names as keys and their corresponding
|
||||
* Object-based values -- extracted from segments of the URL and type converted.
|
||||
*
|
||||
* <p>Note: This attribute is not required to be supported by all View implementations.
|
||||
*/
|
||||
String PATH_VARIABLES = View.class.getName() + ".pathVariables";
|
||||
|
||||
/**
|
||||
* 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<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.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";
|
||||
|
||||
|
||||
@Override
|
||||
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
processRequest(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
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<String, Object> model = (Map<String, Object>) request.getAttribute(MODEL_ATTRIBUTE);
|
||||
view.render(model, request, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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,362 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.web.HttpRequestHandler;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ServletWebArgumentResolverAdapter;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* A {@link BeanDefinitionParser} that provides the configuration for the
|
||||
* {@code <annotation-driven/>} MVC namespace element.
|
||||
*
|
||||
* <p>This class registers the following {@link HandlerMapping}s:</p>
|
||||
* <ul>
|
||||
* <li>{@link RequestMappingHandlerMapping}
|
||||
* ordered at 0 for mapping requests to annotated controller methods.
|
||||
* <li>{@link BeanNameUrlHandlerMapping}
|
||||
* ordered at 2 to map URL paths to controller bean names.
|
||||
* </ul>
|
||||
*
|
||||
* <p><strong>Note:</strong> Additional HandlerMappings may be registered
|
||||
* as a result of using the {@code <view-controller>} or the
|
||||
* {@code <resources>} MVC namespace elements.
|
||||
*
|
||||
* <p>This class registers the following {@link HandlerAdapter}s:
|
||||
* <ul>
|
||||
* <li>{@link RequestMappingHandlerAdapter}
|
||||
* for processing requests with annotated controller methods.
|
||||
* <li>{@link HttpRequestHandlerAdapter}
|
||||
* for processing requests with {@link HttpRequestHandler}s.
|
||||
* <li>{@link SimpleControllerHandlerAdapter}
|
||||
* for processing requests with interface-based {@link Controller}s.
|
||||
* </ul>
|
||||
*
|
||||
* <p>This class registers the following {@link HandlerExceptionResolver}s:
|
||||
* <ul>
|
||||
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions
|
||||
* through @{@link ExceptionHandler} methods.
|
||||
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated
|
||||
* with @{@link ResponseStatus}.
|
||||
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
|
||||
* exception types
|
||||
* </ul>
|
||||
*
|
||||
* <p>Both the {@link RequestMappingHandlerAdapter} and the
|
||||
* {@link ExceptionHandlerExceptionResolver} are configured with default
|
||||
* instances of the following kind, unless custom instances are provided:
|
||||
* <ul>
|
||||
* <li>A {@link DefaultFormattingConversionService}
|
||||
* <li>A {@link LocalValidatorFactoryBean} if a JSR-303 implementation is
|
||||
* available on the classpath
|
||||
* <li>A range of {@link HttpMessageConverter}s depending on what 3rd party
|
||||
* libraries are available on the classpath.
|
||||
* </ul>
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.0
|
||||
*/
|
||||
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final boolean jsr303Present = ClassUtils.isPresent(
|
||||
"javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
private static final boolean jaxb2Present =
|
||||
ClassUtils.isPresent("javax.xml.bind.Binder", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
private static final boolean jacksonPresent =
|
||||
ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) &&
|
||||
ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
private static boolean romePresent =
|
||||
ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
|
||||
parserContext.pushContainingComponent(compDefinition);
|
||||
|
||||
RootBeanDefinition methodMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
|
||||
methodMappingDef.setSource(source);
|
||||
methodMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
methodMappingDef.getPropertyValues().add("order", 0);
|
||||
String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(methodMappingDef);
|
||||
|
||||
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
|
||||
RuntimeBeanReference validator = getValidator(element, source, parserContext);
|
||||
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element, source, parserContext);
|
||||
|
||||
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
|
||||
bindingDef.setSource(source);
|
||||
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
bindingDef.getPropertyValues().add("conversionService", conversionService);
|
||||
bindingDef.getPropertyValues().add("validator", validator);
|
||||
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
|
||||
|
||||
ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
|
||||
ManagedList<?> argumentResolvers = getArgumentResolvers(element, source, parserContext);
|
||||
ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, source, parserContext);
|
||||
|
||||
RootBeanDefinition methodAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
|
||||
methodAdapterDef.setSource(source);
|
||||
methodAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
methodAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
|
||||
methodAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
|
||||
if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {
|
||||
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));
|
||||
methodAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
|
||||
}
|
||||
if (argumentResolvers != null) {
|
||||
methodAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
|
||||
}
|
||||
if (returnValueHandlers != null) {
|
||||
methodAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
|
||||
}
|
||||
String methodAdapterName = parserContext.getReaderContext().registerWithGeneratedName(methodAdapterDef);
|
||||
|
||||
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
|
||||
csInterceptorDef.setSource(source);
|
||||
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
|
||||
RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
|
||||
mappedCsInterceptorDef.setSource(source);
|
||||
mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
|
||||
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
|
||||
String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef);
|
||||
|
||||
RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
|
||||
methodExceptionResolver.setSource(source);
|
||||
methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
|
||||
methodExceptionResolver.getPropertyValues().add("order", 0);
|
||||
String methodExceptionResolverName =
|
||||
parserContext.getReaderContext().registerWithGeneratedName(methodExceptionResolver);
|
||||
|
||||
RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
|
||||
responseStatusExceptionResolver.setSource(source);
|
||||
responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
responseStatusExceptionResolver.getPropertyValues().add("order", 1);
|
||||
String responseStatusExceptionResolverName =
|
||||
parserContext.getReaderContext().registerWithGeneratedName(responseStatusExceptionResolver);
|
||||
|
||||
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
|
||||
defaultExceptionResolver.setSource(source);
|
||||
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
defaultExceptionResolver.getPropertyValues().add("order", 2);
|
||||
String defaultExceptionResolverName =
|
||||
parserContext.getReaderContext().registerWithGeneratedName(defaultExceptionResolver);
|
||||
|
||||
parserContext.registerComponent(new BeanComponentDefinition(methodMappingDef, methodMappingName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(methodAdapterDef, methodAdapterName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExceptionResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
|
||||
|
||||
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
|
||||
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
|
||||
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private RuntimeBeanReference getConversionService(Element element, Object source, ParserContext parserContext) {
|
||||
RuntimeBeanReference conversionServiceRef;
|
||||
if (element.hasAttribute("conversion-service")) {
|
||||
conversionServiceRef = new RuntimeBeanReference(element.getAttribute("conversion-service"));
|
||||
}
|
||||
else {
|
||||
RootBeanDefinition conversionDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);
|
||||
conversionDef.setSource(source);
|
||||
conversionDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String conversionName = parserContext.getReaderContext().registerWithGeneratedName(conversionDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(conversionDef, conversionName));
|
||||
conversionServiceRef = new RuntimeBeanReference(conversionName);
|
||||
}
|
||||
return conversionServiceRef;
|
||||
}
|
||||
|
||||
private RuntimeBeanReference getValidator(Element element, Object source, ParserContext parserContext) {
|
||||
if (element.hasAttribute("validator")) {
|
||||
return new RuntimeBeanReference(element.getAttribute("validator"));
|
||||
}
|
||||
else if (jsr303Present) {
|
||||
RootBeanDefinition validatorDef = new RootBeanDefinition(LocalValidatorFactoryBean.class);
|
||||
validatorDef.setSource(source);
|
||||
validatorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String validatorName = parserContext.getReaderContext().registerWithGeneratedName(validatorDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(validatorDef, validatorName));
|
||||
return new RuntimeBeanReference(validatorName);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private RuntimeBeanReference getMessageCodesResolver(Element element, Object source, ParserContext parserContext) {
|
||||
if (element.hasAttribute("message-codes-resolver")) {
|
||||
return new RuntimeBeanReference(element.getAttribute("message-codes-resolver"));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ManagedList<?> getArgumentResolvers(Element element, Object source, ParserContext parserContext) {
|
||||
Element resolversElement = DomUtils.getChildElementByTagName(element, "argument-resolvers");
|
||||
if (resolversElement != null) {
|
||||
ManagedList<BeanDefinitionHolder> argumentResolvers = extractBeanSubElements(resolversElement, parserContext);
|
||||
return wrapWebArgumentResolverBeanDefs(argumentResolvers);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ManagedList<?> getReturnValueHandlers(Element element, Object source, ParserContext parserContext) {
|
||||
Element handlersElement = DomUtils.getChildElementByTagName(element, "return-value-handlers");
|
||||
if (handlersElement != null) {
|
||||
return extractBeanSubElements(handlersElement, parserContext);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ManagedList<?> getMessageConverters(Element element, Object source, ParserContext parserContext) {
|
||||
Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
|
||||
ManagedList<? super Object> messageConverters = new ManagedList<Object>();
|
||||
if (convertersElement != null) {
|
||||
messageConverters.setSource(source);
|
||||
for (Element converter : DomUtils.getChildElementsByTagName(convertersElement, "bean")) {
|
||||
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(converter);
|
||||
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(converter, beanDef);
|
||||
messageConverters.add(beanDef);
|
||||
}
|
||||
}
|
||||
|
||||
if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-defaults"))) {
|
||||
messageConverters.setSource(source);
|
||||
messageConverters.add(createConverterBeanDefinition(ByteArrayHttpMessageConverter.class, source));
|
||||
|
||||
RootBeanDefinition stringConverterDef = createConverterBeanDefinition(StringHttpMessageConverter.class,
|
||||
source);
|
||||
stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
|
||||
messageConverters.add(stringConverterDef);
|
||||
|
||||
messageConverters.add(createConverterBeanDefinition(ResourceHttpMessageConverter.class, source));
|
||||
messageConverters.add(createConverterBeanDefinition(SourceHttpMessageConverter.class, source));
|
||||
messageConverters.add(createConverterBeanDefinition(XmlAwareFormHttpMessageConverter.class, source));
|
||||
if (jaxb2Present) {
|
||||
messageConverters
|
||||
.add(createConverterBeanDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
|
||||
}
|
||||
if (jacksonPresent) {
|
||||
messageConverters.add(createConverterBeanDefinition(MappingJacksonHttpMessageConverter.class, source));
|
||||
}
|
||||
if (romePresent) {
|
||||
messageConverters.add(createConverterBeanDefinition(AtomFeedHttpMessageConverter.class, source));
|
||||
messageConverters.add(createConverterBeanDefinition(RssChannelHttpMessageConverter.class, source));
|
||||
}
|
||||
}
|
||||
return messageConverters;
|
||||
}
|
||||
|
||||
private RootBeanDefinition createConverterBeanDefinition(Class<? extends HttpMessageConverter> converterClass,
|
||||
Object source) {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(converterClass);
|
||||
beanDefinition.setSource(source);
|
||||
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
private ManagedList<BeanDefinitionHolder> extractBeanSubElements(Element parentElement, ParserContext parserContext) {
|
||||
ManagedList<BeanDefinitionHolder> list = new ManagedList<BeanDefinitionHolder>();
|
||||
list.setSource(parserContext.extractSource(parentElement));
|
||||
for (Element beanElement : DomUtils.getChildElementsByTagName(parentElement, "bean")) {
|
||||
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(beanElement);
|
||||
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(beanElement, beanDef);
|
||||
list.add(beanDef);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private ManagedList<BeanDefinitionHolder> wrapWebArgumentResolverBeanDefs(List<BeanDefinitionHolder> beanDefs) {
|
||||
ManagedList<BeanDefinitionHolder> result = new ManagedList<BeanDefinitionHolder>();
|
||||
|
||||
for (BeanDefinitionHolder beanDef : beanDefs) {
|
||||
String className = beanDef.getBeanDefinition().getBeanClassName();
|
||||
Class<?> clazz = ClassUtils.resolveClassName(className, ClassUtils.getDefaultClassLoader());
|
||||
|
||||
if (WebArgumentResolver.class.isAssignableFrom(clazz)) {
|
||||
RootBeanDefinition adapter = new RootBeanDefinition(ServletWebArgumentResolverAdapter.class);
|
||||
adapter.getConstructorArgumentValues().addIndexedArgumentValue(0, beanDef);
|
||||
result.add(new BeanDefinitionHolder(adapter, beanDef.getBeanName() + "Adapter"));
|
||||
} else {
|
||||
result.add(beanDef);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} that parses a {@code default-servlet-handler} element to
|
||||
* register a {@link DefaultServletHttpRequestHandler}. Will also register a
|
||||
* {@link SimpleUrlHandlerMapping} for mapping resource requests, and a
|
||||
* {@link HttpRequestHandlerAdapter}.
|
||||
*
|
||||
* @author Jeremy Grelle
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.0.4
|
||||
*/
|
||||
class DefaultServletHandlerBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
String defaultServletName = element.getAttribute("default-servlet-name");
|
||||
RootBeanDefinition defaultServletHandlerDef = new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
|
||||
defaultServletHandlerDef.setSource(source);
|
||||
defaultServletHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
if (StringUtils.hasText(defaultServletName)) {
|
||||
defaultServletHandlerDef.getPropertyValues().add("defaultServletName", defaultServletName);
|
||||
}
|
||||
String defaultServletHandlerName = parserContext.getReaderContext().generateBeanName(defaultServletHandlerDef);
|
||||
parserContext.getRegistry().registerBeanDefinition(defaultServletHandlerName, defaultServletHandlerDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(defaultServletHandlerDef, defaultServletHandlerName));
|
||||
|
||||
Map<String, String> urlMap = new ManagedMap<String, String>();
|
||||
urlMap.put("/**", defaultServletHandlerName);
|
||||
|
||||
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
|
||||
handlerMappingDef.setSource(source);
|
||||
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
|
||||
|
||||
String handlerMappingBeanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
|
||||
parserContext.getRegistry().registerBeanDefinition(handlerMappingBeanName, handlerMappingDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, handlerMappingBeanName));
|
||||
|
||||
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
|
||||
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a {@code interceptors} element to register
|
||||
* a set of {@link MappedInterceptor} definitions.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
class InterceptorsBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
|
||||
parserContext.pushContainingComponent(compDefinition);
|
||||
|
||||
List<Element> interceptors = DomUtils.getChildElementsByTagName(element, new String[] { "bean", "ref", "interceptor" });
|
||||
for (Element interceptor : interceptors) {
|
||||
RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
|
||||
mappedInterceptorDef.setSource(parserContext.extractSource(interceptor));
|
||||
mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
String[] pathPatterns;
|
||||
Object interceptorBean;
|
||||
if ("interceptor".equals(interceptor.getLocalName())) {
|
||||
List<Element> paths = DomUtils.getChildElementsByTagName(interceptor, "mapping");
|
||||
pathPatterns = new String[paths.size()];
|
||||
for (int i = 0; i < paths.size(); i++) {
|
||||
pathPatterns[i] = paths.get(i).getAttribute("path");
|
||||
}
|
||||
Element beanElem = DomUtils.getChildElementsByTagName(interceptor, new String[] { "bean", "ref"}).get(0);
|
||||
interceptorBean = parserContext.getDelegate().parsePropertySubElement(beanElem, null);
|
||||
}
|
||||
else {
|
||||
pathPatterns = null;
|
||||
interceptorBean = parserContext.getDelegate().parsePropertySubElement(interceptor, null);
|
||||
}
|
||||
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, pathPatterns);
|
||||
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, interceptorBean);
|
||||
|
||||
String beanName = parserContext.getReaderContext().registerWithGeneratedName(mappedInterceptorDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, beanName));
|
||||
}
|
||||
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.NamespaceHandler;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
|
||||
/**
|
||||
* {@link NamespaceHandler} for Spring MVC configuration namespace.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Jeremy Grelle
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
|
||||
|
||||
/**
|
||||
* Convenience methods for use in MVC namespace BeanDefinitionParsers.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
abstract class MvcNamespaceUtils {
|
||||
|
||||
private static final String BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME =
|
||||
BeanNameUrlHandlerMapping.class.getName();
|
||||
|
||||
private static final String SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME =
|
||||
SimpleControllerHandlerAdapter.class.getName();
|
||||
|
||||
private static final String HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME =
|
||||
HttpRequestHandlerAdapter.class.getName();
|
||||
|
||||
public static void registerDefaultComponents(ParserContext parserContext, Object source) {
|
||||
registerBeanNameUrlHandlerMapping(parserContext, source);
|
||||
registerHttpRequestHandlerAdapter(parserContext, source);
|
||||
registerSimpleControllerHandlerAdapter(parserContext, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an {@link HttpRequestHandlerAdapter} under a well-known
|
||||
* name unless already registered.
|
||||
*/
|
||||
private static void registerBeanNameUrlHandlerMapping(ParserContext parserContext, Object source) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME)){
|
||||
RootBeanDefinition beanNameMappingDef = new RootBeanDefinition(BeanNameUrlHandlerMapping.class);
|
||||
beanNameMappingDef.setSource(source);
|
||||
beanNameMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
beanNameMappingDef.getPropertyValues().add("order", 2); // consistent with WebMvcConfigurationSupport
|
||||
parserContext.getRegistry().registerBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME, beanNameMappingDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(beanNameMappingDef, BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an {@link HttpRequestHandlerAdapter} under a well-known
|
||||
* name unless already registered.
|
||||
*/
|
||||
private static void registerHttpRequestHandlerAdapter(ParserContext parserContext, Object source) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME)) {
|
||||
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(HttpRequestHandlerAdapter.class);
|
||||
handlerAdapterDef.setSource(source);
|
||||
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
parserContext.getRegistry().registerBeanDefinition(HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link SimpleControllerHandlerAdapter} under a well-known
|
||||
* name unless already registered.
|
||||
*/
|
||||
private static void registerSimpleControllerHandlerAdapter(ParserContext parserContext, Object source) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME)) {
|
||||
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(SimpleControllerHandlerAdapter.class);
|
||||
handlerAdapterDef.setSource(source);
|
||||
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
parserContext.getRegistry().registerBeanDefinition(SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, SIMPLE_CONTROLLER_HANDLER_ADAPTER_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a
|
||||
* {@code resources} element to register a {@link ResourceHttpRequestHandler}.
|
||||
* Will also register a {@link SimpleUrlHandlerMapping} for mapping resource requests,
|
||||
* and a {@link HttpRequestHandlerAdapter}.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Jeremy Grelle
|
||||
* @since 3.0.4
|
||||
*/
|
||||
class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
String resourceHandlerName = registerResourceHandler(parserContext, element, source);
|
||||
if (resourceHandlerName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, String> urlMap = new ManagedMap<String, String>();
|
||||
String resourceRequestPath = element.getAttribute("mapping");
|
||||
if (!StringUtils.hasText(resourceRequestPath)) {
|
||||
parserContext.getReaderContext().error("The 'mapping' attribute is required.", parserContext.extractSource(element));
|
||||
return null;
|
||||
}
|
||||
urlMap.put(resourceRequestPath, resourceHandlerName);
|
||||
|
||||
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
|
||||
handlerMappingDef.setSource(source);
|
||||
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
|
||||
|
||||
String order = element.getAttribute("order");
|
||||
// use a default of near-lowest precedence, still allowing for even lower precedence in other mappings
|
||||
handlerMappingDef.getPropertyValues().add("order", StringUtils.hasText(order) ? order : Ordered.LOWEST_PRECEDENCE - 1);
|
||||
|
||||
String beanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
|
||||
parserContext.getRegistry().registerBeanDefinition(beanName, handlerMappingDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, beanName));
|
||||
|
||||
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
|
||||
// Register HttpRequestHandlerAdapter
|
||||
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String registerResourceHandler(ParserContext parserContext, Element element, Object source) {
|
||||
String locationAttr = element.getAttribute("location");
|
||||
if (!StringUtils.hasText(locationAttr)) {
|
||||
parserContext.getReaderContext().error("The 'location' attribute is required.", parserContext.extractSource(element));
|
||||
return null;
|
||||
}
|
||||
|
||||
RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class);
|
||||
resourceHandlerDef.setSource(source);
|
||||
resourceHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
resourceHandlerDef.getPropertyValues().add("locations", StringUtils.commaDelimitedListToStringArray(locationAttr));
|
||||
|
||||
String cacheSeconds = element.getAttribute("cache-period");
|
||||
if (StringUtils.hasText(cacheSeconds)) {
|
||||
resourceHandlerDef.getPropertyValues().add("cacheSeconds", cacheSeconds);
|
||||
}
|
||||
|
||||
String beanName = parserContext.getReaderContext().generateBeanName(resourceHandlerDef);
|
||||
parserContext.getRegistry().registerBeanDefinition(beanName, resourceHandlerDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(resourceHandlerDef, beanName));
|
||||
return beanName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a
|
||||
* {@code view-controller} element to register a {@link ParameterizableViewController}.
|
||||
* Will also register a {@link SimpleUrlHandlerMapping} for view controllers.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Christian Dupuis
|
||||
* @since 3.0
|
||||
*/
|
||||
class ViewControllerBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String HANDLER_MAPPING_BEAN_NAME =
|
||||
"org.springframework.web.servlet.config.viewControllerHandlerMapping";
|
||||
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
// Register SimpleUrlHandlerMapping for view controllers
|
||||
BeanDefinition handlerMappingDef = registerHandlerMapping(parserContext, source);
|
||||
|
||||
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
|
||||
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
|
||||
|
||||
// Create view controller bean definition
|
||||
RootBeanDefinition viewControllerDef = new RootBeanDefinition(ParameterizableViewController.class);
|
||||
viewControllerDef.setSource(source);
|
||||
if (element.hasAttribute("view-name")) {
|
||||
viewControllerDef.getPropertyValues().add("viewName", element.getAttribute("view-name"));
|
||||
}
|
||||
Map<String, BeanDefinition> urlMap;
|
||||
if (handlerMappingDef.getPropertyValues().contains("urlMap")) {
|
||||
urlMap = (Map<String, BeanDefinition>) handlerMappingDef.getPropertyValues().getPropertyValue("urlMap").getValue();
|
||||
}
|
||||
else {
|
||||
urlMap = new ManagedMap<String, BeanDefinition>();
|
||||
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
|
||||
}
|
||||
urlMap.put(element.getAttribute("path"), viewControllerDef);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private BeanDefinition registerHandlerMapping(ParserContext parserContext, Object source) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(HANDLER_MAPPING_BEAN_NAME)) {
|
||||
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
|
||||
handlerMappingDef.setSource(source);
|
||||
handlerMappingDef.getPropertyValues().add("order", "1");
|
||||
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
parserContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
|
||||
return handlerMappingDef;
|
||||
}
|
||||
else {
|
||||
return parserContext.getRegistry().getBeanDefinition(HANDLER_MAPPING_BEAN_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.HttpRequestHandler;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Configures a request handler for serving static resources by forwarding the request to the Servlet container's
|
||||
* "default" Servlet. This is indended to be used when the Spring MVC {@link DispatcherServlet} is mapped to "/"
|
||||
* thus overriding the Servlet container's default handling of static resources. Since this handler is configured
|
||||
* at the lowest precedence, effectively it allows all other handler mappings to handle the request, and if none
|
||||
* of them do, this handler can forward it to the "default" Servlet.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*
|
||||
* @see DefaultServletHttpRequestHandler
|
||||
*/
|
||||
public class DefaultServletHandlerConfigurer {
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private DefaultServletHttpRequestHandler handler;
|
||||
|
||||
/**
|
||||
* Create a {@link DefaultServletHandlerConfigurer} instance.
|
||||
* @param servletContext the ServletContext to use to configure the underlying DefaultServletHttpRequestHandler.
|
||||
*/
|
||||
public DefaultServletHandlerConfigurer(ServletContext servletContext) {
|
||||
Assert.notNull(servletContext, "A ServletContext is required to configure default servlet handling");
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable forwarding to the "default" Servlet. When this method is used the {@link DefaultServletHttpRequestHandler}
|
||||
* will try to auto-detect the "default" Servlet name. Alternatively, you can specify the name of the default
|
||||
* Servlet via {@link #enable(String)}.
|
||||
* @see DefaultServletHttpRequestHandler
|
||||
*/
|
||||
public void enable() {
|
||||
enable(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable forwarding to the "default" Servlet identified by the given name.
|
||||
* This is useful when the default Servlet cannot be auto-detected, for example when it has been manually configured.
|
||||
* @see DefaultServletHttpRequestHandler
|
||||
*/
|
||||
public void enable(String defaultServletName) {
|
||||
handler = new DefaultServletHttpRequestHandler();
|
||||
handler.setDefaultServletName(defaultServletName);
|
||||
handler.setServletContext(servletContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a handler mapping instance ordered at {@link Integer#MAX_VALUE} containing the
|
||||
* {@link DefaultServletHttpRequestHandler} instance mapped to {@code "/**"}; or {@code null} if
|
||||
* default servlet handling was not been enabled.
|
||||
*/
|
||||
protected AbstractHandlerMapping getHandlerMapping() {
|
||||
if (handler == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, HttpRequestHandler> urlMap = new HashMap<String, HttpRequestHandler>();
|
||||
urlMap.put("/**", handler);
|
||||
|
||||
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
|
||||
handlerMapping.setOrder(Integer.MAX_VALUE);
|
||||
handlerMapping.setUrlMap(urlMap);
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
|
||||
/**
|
||||
* Extends {@link WebMvcConfigurationSupport} with the ability to detect beans
|
||||
* of type {@link WebMvcConfigurer} and give them a chance to customize the
|
||||
* provided configuration by delegating to them at the appropriate times.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*
|
||||
* @see EnableWebMvc
|
||||
*/
|
||||
@Configuration
|
||||
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
|
||||
|
||||
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setConfigurers(List<WebMvcConfigurer> configurers) {
|
||||
if (configurers == null || configurers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this.configurers.addWebMvcConfigurers(configurers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void addInterceptors(InterceptorRegistry registry) {
|
||||
configurers.addInterceptors(registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void addViewControllers(ViewControllerRegistry registry) {
|
||||
configurers.addViewControllers(registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
configurers.addResourceHandlers(registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
|
||||
configurers.configureDefaultServletHandling(configurer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
configurers.addArgumentResolvers(argumentResolvers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
|
||||
configurers.addReturnValueHandlers(returnValueHandlers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
configurers.configureMessageConverters(converters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void addFormatters(FormatterRegistry registry) {
|
||||
configurers.addFormatters(registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Validator getValidator() {
|
||||
return configurers.getValidator();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
|
||||
configurers.configureHandlerExceptionResolvers(exceptionResolvers);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.config.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;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Add this annotation to an {@code @Configuration} class to have the Spring MVC
|
||||
* configuration defined in {@link WebMvcConfigurationSupport} imported:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableWebMvc
|
||||
* @ComponentScan(basePackageClasses = { MyConfiguration.class })
|
||||
* public class MyWebConfiguration {
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
* <p>Customize the imported configuration by implementing the
|
||||
* {@link WebMvcConfigurer} interface or more likely by extending the
|
||||
* {@link WebMvcConfigurerAdapter} base class and overriding individual methods:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableWebMvc
|
||||
* @ComponentScan(basePackageClasses = { MyConfiguration.class })
|
||||
* public class MyConfiguration extends WebMvcConfigurerAdapter {
|
||||
*
|
||||
* @Override
|
||||
* public void addFormatters(FormatterRegistry formatterRegistry) {
|
||||
* formatterRegistry.addConverter(new MyConverter());
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
* converters.add(new MyHttpMessageConverter());
|
||||
* }
|
||||
*
|
||||
* // More overridden methods ...
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>If the customization options of {@link WebMvcConfigurer} do not expose
|
||||
* something you need to configure, consider removing the {@code @EnableWebMvc}
|
||||
* annotation and extending directly from {@link WebMvcConfigurationSupport}
|
||||
* overriding selected {@code @Bean} methods:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @ComponentScan(basePackageClasses = { MyConfiguration.class })
|
||||
* public class MyConfiguration extends WebMvcConfigurationSupport {
|
||||
*
|
||||
* @Override
|
||||
* public void addFormatters(FormatterRegistry formatterRegistry) {
|
||||
* formatterRegistry.addConverter(new MyConverter());
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
|
||||
* // Create or delegate to "super" to create and
|
||||
* // customize properties of RequestMapingHandlerAdapter
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @see WebMvcConfigurer
|
||||
* @see WebMvcConfigurerAdapter
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import(DelegatingWebMvcConfiguration.class)
|
||||
public @interface EnableWebMvc {
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
|
||||
/**
|
||||
* Encapsulates a {@link HandlerInterceptor} and an optional list of URL patterns.
|
||||
* Results in the creation of a {@link MappedInterceptor} if URL patterns are provided.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Keith Donald
|
||||
* @since 3.1
|
||||
*/
|
||||
public class InterceptorRegistration {
|
||||
|
||||
private final HandlerInterceptor interceptor;
|
||||
|
||||
private final List<String> pathPatterns = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* Creates an {@link InterceptorRegistration} instance.
|
||||
*/
|
||||
public InterceptorRegistration(HandlerInterceptor interceptor) {
|
||||
Assert.notNull(interceptor, "Interceptor is required");
|
||||
this.interceptor = interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one or more URL patterns to which the registered interceptor should apply to.
|
||||
* If no URL patterns are provided, the interceptor applies to all paths.
|
||||
*/
|
||||
public void addPathPatterns(String... pathPatterns) {
|
||||
this.pathPatterns.addAll(Arrays.asList(pathPatterns));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying interceptor. If URL patterns are provided the returned type is
|
||||
* {@link MappedInterceptor}; otherwise {@link HandlerInterceptor}.
|
||||
*/
|
||||
protected Object getInterceptor() {
|
||||
if (pathPatterns.isEmpty()) {
|
||||
return interceptor;
|
||||
}
|
||||
return new MappedInterceptor(pathPatterns.toArray(new String[pathPatterns.size()]), interceptor);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.context.request.WebRequestInterceptor;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter;
|
||||
|
||||
/**
|
||||
* Stores and provides access to a list of interceptors. For each interceptor you can optionally
|
||||
* specify one or more URL patterns it applies to.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Keith Donald
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
public class InterceptorRegistry {
|
||||
|
||||
private final List<InterceptorRegistration> registrations = new ArrayList<InterceptorRegistration>();
|
||||
|
||||
/**
|
||||
* Adds the provided {@link HandlerInterceptor}.
|
||||
* @param interceptor the interceptor to add
|
||||
* @return An {@link InterceptorRegistration} that allows you optionally configure the
|
||||
* registered interceptor further for example adding URL patterns it should apply to.
|
||||
*/
|
||||
public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {
|
||||
InterceptorRegistration registration = new InterceptorRegistration(interceptor);
|
||||
registrations.add(registration);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the provided {@link WebRequestInterceptor}.
|
||||
* @param interceptor the interceptor to add
|
||||
* @return An {@link InterceptorRegistration} that allows you optionally configure the
|
||||
* registered interceptor further for example adding URL patterns it should apply to.
|
||||
*/
|
||||
public InterceptorRegistration addWebRequestInterceptor(WebRequestInterceptor interceptor) {
|
||||
WebRequestHandlerInterceptorAdapter adapted = new WebRequestHandlerInterceptorAdapter(interceptor);
|
||||
InterceptorRegistration registration = new InterceptorRegistration(adapted);
|
||||
registrations.add(registration);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all registered interceptors.
|
||||
*/
|
||||
protected List<Object> getInterceptors() {
|
||||
List<Object> interceptors = new ArrayList<Object>();
|
||||
for (InterceptorRegistration registration : registrations) {
|
||||
interceptors.add(registration.getInterceptor());
|
||||
}
|
||||
return interceptors ;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Encapsulates information required to create a resource handlers.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Keith Donald
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ResourceHandlerRegistration {
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final String[] pathPatterns;
|
||||
|
||||
private final List<Resource> locations = new ArrayList<Resource>();
|
||||
|
||||
private Integer cachePeriod;
|
||||
|
||||
/**
|
||||
* Create a {@link ResourceHandlerRegistration} instance.
|
||||
* @param resourceLoader a resource loader for turning a String location into a {@link Resource}
|
||||
* @param pathPatterns one or more resource URL path patterns
|
||||
*/
|
||||
public ResourceHandlerRegistration(ResourceLoader resourceLoader, String... pathPatterns) {
|
||||
Assert.notEmpty(pathPatterns, "At least one path pattern is required for resource handling.");
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.pathPatterns = pathPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more resource locations from which to serve static content. Each location must point to a valid
|
||||
* directory. Multiple locations may be specified as a comma-separated list, and the locations will be checked
|
||||
* for a given resource in the order specified.
|
||||
* <p>For example, {{@code "/"}, {@code "classpath:/META-INF/public-web-resources/"}} allows resources to
|
||||
* be served both from the web application root and from any JAR on the classpath that contains a
|
||||
* {@code /META-INF/public-web-resources/} directory, with resources in the web application root taking precedence.
|
||||
* @return the same {@link ResourceHandlerRegistration} instance for chained method invocation
|
||||
*/
|
||||
public ResourceHandlerRegistration addResourceLocations(String...resourceLocations) {
|
||||
for (String location : resourceLocations) {
|
||||
this.locations.add(resourceLoader.getResource(location));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the cache period for the resources served by the resource handler, in seconds. The default is to not
|
||||
* send any cache headers but to rely on last-modified timestamps only. Set to 0 in order to send cache headers
|
||||
* that prevent caching, or to a positive number of seconds to send cache headers with the given max-age value.
|
||||
* @param cachePeriod the time to cache resources in seconds
|
||||
* @return the same {@link ResourceHandlerRegistration} instance for chained method invocation
|
||||
*/
|
||||
public ResourceHandlerRegistration setCachePeriod(Integer cachePeriod) {
|
||||
this.cachePeriod = cachePeriod;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL path patterns for the resource handler.
|
||||
*/
|
||||
protected String[] getPathPatterns() {
|
||||
return pathPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link ResourceHttpRequestHandler} instance.
|
||||
*/
|
||||
protected ResourceHttpRequestHandler getRequestHandler() {
|
||||
Assert.isTrue(!CollectionUtils.isEmpty(locations), "At least one location is required for resource handling.");
|
||||
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
|
||||
requestHandler.setLocations(locations);
|
||||
if (cachePeriod != null) {
|
||||
requestHandler.setCacheSeconds(cachePeriod);
|
||||
}
|
||||
return requestHandler;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.HttpRequestHandler;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Stores registrations of resource handlers for serving static resources such as images, css files and others
|
||||
* through Spring MVC including setting cache headers optimized for efficient loading in a web browser.
|
||||
* Resources can be served out of locations under web application root, from the classpath, and others.
|
||||
*
|
||||
* <p>To create a resource handler, use {@link #addResourceHandler(String...)} providing the URL path patterns
|
||||
* for which the handler should be invoked to serve static resources (e.g. {@code "/resources/**"}).
|
||||
*
|
||||
* <p>Then use additional methods on the returned {@link ResourceHandlerRegistration} to add one or more
|
||||
* locations from which to serve static content from (e.g. {{@code "/"},
|
||||
* {@code "classpath:/META-INF/public-web-resources/"}}) or to specify a cache period for served resources.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*
|
||||
* @see DefaultServletHandlerConfigurer
|
||||
*/
|
||||
public class ResourceHandlerRegistry {
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final List<ResourceHandlerRegistration> registrations = new ArrayList<ResourceHandlerRegistration>();
|
||||
|
||||
private int order = Integer.MAX_VALUE -1;
|
||||
|
||||
public ResourceHandlerRegistry(ApplicationContext applicationContext, ServletContext servletContext) {
|
||||
Assert.notNull(applicationContext, "ApplicationContext is required");
|
||||
this.applicationContext = applicationContext;
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a resource handler for serving static resources based on the specified URL path patterns.
|
||||
* The handler will be invoked for every incoming request that matches to one of the specified path patterns.
|
||||
* @return A {@link ResourceHandlerRegistration} to use to further configure the registered resource handler.
|
||||
*/
|
||||
public ResourceHandlerRegistration addResourceHandler(String... pathPatterns) {
|
||||
ResourceHandlerRegistration registration = new ResourceHandlerRegistration(applicationContext, pathPatterns);
|
||||
registrations.add(registration);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the order to use for resource handling relative to other {@link HandlerMapping}s configured in
|
||||
* the Spring MVC application context. The default value used is {@code Integer.MAX_VALUE-1}.
|
||||
*/
|
||||
public ResourceHandlerRegistry setOrder(int order) {
|
||||
this.order = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a handler mapping with the mapped resource handlers; or {@code null} in case of no registrations.
|
||||
*/
|
||||
protected AbstractHandlerMapping getHandlerMapping() {
|
||||
if (registrations.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, HttpRequestHandler> urlMap = new LinkedHashMap<String, HttpRequestHandler>();
|
||||
for (ResourceHandlerRegistration registration : registrations) {
|
||||
for (String pathPattern : registration.getPathPatterns()) {
|
||||
ResourceHttpRequestHandler requestHandler = registration.getRequestHandler();
|
||||
requestHandler.setServletContext(servletContext);
|
||||
requestHandler.setApplicationContext(applicationContext);
|
||||
urlMap.put(pathPattern, requestHandler);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
|
||||
handlerMapping.setOrder(order);
|
||||
handlerMapping.setUrlMap(urlMap);
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.RequestToViewNameTranslator;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
/**
|
||||
* Encapsulates information required to create a view controller.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Keith Donald
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ViewControllerRegistration {
|
||||
|
||||
private final String urlPath;
|
||||
|
||||
private String viewName;
|
||||
|
||||
/**
|
||||
* Creates a {@link ViewControllerRegistration} with the given URL path. When a request matches
|
||||
* to the given URL path this view controller will process it.
|
||||
*/
|
||||
public ViewControllerRegistration(String urlPath) {
|
||||
Assert.notNull(urlPath, "A URL path is required to create a view controller.");
|
||||
this.urlPath = urlPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the view name to use for this view controller. This field is optional. If not specified the
|
||||
* view controller will return a {@code null} view name, which will be resolved through the configured
|
||||
* {@link RequestToViewNameTranslator}. By default that means "/foo/bar" would resolve to "foo/bar".
|
||||
*/
|
||||
public void setViewName(String viewName) {
|
||||
this.viewName = viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL path for the view controller.
|
||||
*/
|
||||
protected String getUrlPath() {
|
||||
return urlPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view controllers.
|
||||
*/
|
||||
protected Object getViewController() {
|
||||
ParameterizableViewController controller = new ParameterizableViewController();
|
||||
controller.setViewName(viewName);
|
||||
return controller;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
|
||||
/**
|
||||
* Stores registrations of view controllers. A view controller does nothing more than return a specified
|
||||
* view name. It saves you from having to write a controller when you want to forward the request straight
|
||||
* through to a view such as a JSP.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Keith Donald
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ViewControllerRegistry {
|
||||
|
||||
private final List<ViewControllerRegistration> registrations = new ArrayList<ViewControllerRegistration>();
|
||||
|
||||
private int order = 1;
|
||||
|
||||
public ViewControllerRegistration addViewController(String urlPath) {
|
||||
ViewControllerRegistration registration = new ViewControllerRegistration(urlPath);
|
||||
registrations.add(registration);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the order to use for ViewControllers mappings relative to other {@link HandlerMapping}s
|
||||
* configured in the Spring MVC application context. The default value for view controllers is 1,
|
||||
* which is 1 higher than the value used for annotated controllers.
|
||||
*/
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a handler mapping with the mapped ViewControllers; or {@code null} in case of no registrations.
|
||||
*/
|
||||
protected AbstractHandlerMapping getHandlerMapping() {
|
||||
if (registrations.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, Object> urlMap = new LinkedHashMap<String, Object>();
|
||||
for (ViewControllerRegistration registration : registrations) {
|
||||
urlMap.put(registration.getUrlPath(), registration.getViewController());
|
||||
}
|
||||
|
||||
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
|
||||
handlerMapping.setOrder(order);
|
||||
handlerMapping.setUrlMap(urlMap);
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.xml.transform.Source;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
|
||||
import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.web.HttpRequestHandler;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
|
||||
import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
|
||||
import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite;
|
||||
import org.springframework.web.servlet.mvc.Controller;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||
|
||||
/**
|
||||
* This is the main class providing the configuration behind the MVC Java config.
|
||||
* It is typically imported by adding {@link EnableWebMvc @EnableWebMvc} to an
|
||||
* application {@link Configuration @Configuration} class. An alternative more
|
||||
* advanced option is to extend directly from this class and override methods as
|
||||
* necessary remembering to add {@link Configuration @Configuration} to the
|
||||
* subclass and {@link Bean @Bean} to overridden {@link Bean @Bean} methods.
|
||||
* For more details see the Javadoc of {@link EnableWebMvc @EnableWebMvc}.
|
||||
*
|
||||
* <p>This class registers the following {@link HandlerMapping}s:</p>
|
||||
* <ul>
|
||||
* <li>{@link RequestMappingHandlerMapping}
|
||||
* ordered at 0 for mapping requests to annotated controller methods.
|
||||
* <li>{@link HandlerMapping}
|
||||
* ordered at 1 to map URL paths directly to view names.
|
||||
* <li>{@link BeanNameUrlHandlerMapping}
|
||||
* ordered at 2 to map URL paths to controller bean names.
|
||||
* <li>{@link HandlerMapping}
|
||||
* ordered at {@code Integer.MAX_VALUE-1} to serve static resource requests.
|
||||
* <li>{@link HandlerMapping}
|
||||
* ordered at {@code Integer.MAX_VALUE} to forward requests to the default servlet.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Registers these {@link HandlerAdapter}s:
|
||||
* <ul>
|
||||
* <li>{@link RequestMappingHandlerAdapter}
|
||||
* for processing requests with annotated controller methods.
|
||||
* <li>{@link HttpRequestHandlerAdapter}
|
||||
* for processing requests with {@link HttpRequestHandler}s.
|
||||
* <li>{@link SimpleControllerHandlerAdapter}
|
||||
* for processing requests with interface-based {@link Controller}s.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Registers a {@link HandlerExceptionResolverComposite} with this chain of
|
||||
* exception resolvers:
|
||||
* <ul>
|
||||
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions
|
||||
* through @{@link ExceptionHandler} methods.
|
||||
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated
|
||||
* with @{@link ResponseStatus}.
|
||||
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
|
||||
* exception types
|
||||
* </ul>
|
||||
*
|
||||
* <p>Both the {@link RequestMappingHandlerAdapter} and the
|
||||
* {@link ExceptionHandlerExceptionResolver} are configured with default
|
||||
* instances of the following kind, unless custom instances are provided:
|
||||
* <ul>
|
||||
* <li>A {@link DefaultFormattingConversionService}
|
||||
* <li>A {@link LocalValidatorFactoryBean} if a JSR-303 implementation is
|
||||
* available on the classpath
|
||||
* <li>A range of {@link HttpMessageConverter}s depending on the 3rd party
|
||||
* libraries available on the classpath.
|
||||
* </ul>
|
||||
*
|
||||
* @see EnableWebMvc
|
||||
* @see WebMvcConfigurer
|
||||
* @see WebMvcConfigurerAdapter
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
|
||||
|
||||
private ServletContext servletContext;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private List<Object> interceptors;
|
||||
|
||||
private List<HttpMessageConverter<?>> messageConverters;
|
||||
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping
|
||||
* requests to annotated controllers.
|
||||
*/
|
||||
@Bean
|
||||
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
|
||||
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
|
||||
handlerMapping.setOrder(0);
|
||||
handlerMapping.setInterceptors(getInterceptors());
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide access to the shared handler interceptors used to configure
|
||||
* {@link HandlerMapping} instances with. This method cannot be overridden,
|
||||
* use {@link #addInterceptors(InterceptorRegistry)} instead.
|
||||
*/
|
||||
protected final Object[] getInterceptors() {
|
||||
if (interceptors == null) {
|
||||
InterceptorRegistry registry = new InterceptorRegistry();
|
||||
addInterceptors(registry);
|
||||
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
|
||||
interceptors = registry.getInterceptors();
|
||||
}
|
||||
return interceptors.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to add Spring MVC interceptors for
|
||||
* pre- and post-processing of controller invocation.
|
||||
* @see InterceptorRegistry
|
||||
*/
|
||||
protected void addInterceptors(InterceptorRegistry registry) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a handler mapping ordered at 1 to map URL paths directly to
|
||||
* view names. To configure view controllers, override
|
||||
* {@link #addViewControllers}.
|
||||
*/
|
||||
@Bean
|
||||
public HandlerMapping viewControllerHandlerMapping() {
|
||||
ViewControllerRegistry registry = new ViewControllerRegistry();
|
||||
addViewControllers(registry);
|
||||
|
||||
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
|
||||
handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping();
|
||||
handlerMapping.setInterceptors(getInterceptors());
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to add view controllers.
|
||||
* @see ViewControllerRegistry
|
||||
*/
|
||||
protected void addViewControllers(ViewControllerRegistry registry) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL
|
||||
* paths to controller bean names.
|
||||
*/
|
||||
@Bean
|
||||
public BeanNameUrlHandlerMapping beanNameHandlerMapping() {
|
||||
BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
|
||||
mapping.setOrder(2);
|
||||
mapping.setInterceptors(getInterceptors());
|
||||
return mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
|
||||
* resource handlers. To configure resource handling, override
|
||||
* {@link #addResourceHandlers}.
|
||||
*/
|
||||
@Bean
|
||||
public HandlerMapping resourceHandlerMapping() {
|
||||
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(applicationContext, servletContext);
|
||||
addResourceHandlers(registry);
|
||||
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
|
||||
handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping();
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to add resource handlers for serving static resources.
|
||||
* @see ResourceHandlerRegistry
|
||||
*/
|
||||
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a handler mapping ordered at Integer.MAX_VALUE with a mapped
|
||||
* default servlet handler. To configure "default" Servlet handling,
|
||||
* override {@link #configureDefaultServletHandling}.
|
||||
*/
|
||||
@Bean
|
||||
public HandlerMapping defaultServletHandlerMapping() {
|
||||
DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext);
|
||||
configureDefaultServletHandling(configurer);
|
||||
AbstractHandlerMapping handlerMapping = configurer.getHandlerMapping();
|
||||
handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping();
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to configure "default" Servlet handling.
|
||||
* @see DefaultServletHandlerConfigurer
|
||||
*/
|
||||
protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link RequestMappingHandlerAdapter} for processing requests
|
||||
* through annotated controller methods. Consider overriding one of these
|
||||
* other more fine-grained methods:
|
||||
* <ul>
|
||||
* <li>{@link #addArgumentResolvers} for adding custom argument resolvers.
|
||||
* <li>{@link #addReturnValueHandlers} for adding custom return value handlers.
|
||||
* <li>{@link #configureMessageConverters} for adding custom message converters.
|
||||
* </ul>
|
||||
*/
|
||||
@Bean
|
||||
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
|
||||
ConfigurableWebBindingInitializer webBindingInitializer = new ConfigurableWebBindingInitializer();
|
||||
webBindingInitializer.setConversionService(mvcConversionService());
|
||||
webBindingInitializer.setValidator(mvcValidator());
|
||||
|
||||
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<HandlerMethodArgumentResolver>();
|
||||
addArgumentResolvers(argumentResolvers);
|
||||
|
||||
List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<HandlerMethodReturnValueHandler>();
|
||||
addReturnValueHandlers(returnValueHandlers);
|
||||
|
||||
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
|
||||
adapter.setMessageConverters(getMessageConverters());
|
||||
adapter.setWebBindingInitializer(webBindingInitializer);
|
||||
adapter.setCustomArgumentResolvers(argumentResolvers);
|
||||
adapter.setCustomReturnValueHandlers(returnValueHandlers);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom {@link HandlerMethodArgumentResolver}s to use in addition to
|
||||
* the ones registered by default.
|
||||
* <p>Custom argument resolvers are invoked before built-in resolvers
|
||||
* except for those that rely on the presence of annotations (e.g.
|
||||
* {@code @RequestParameter}, {@code @PathVariable}, etc.).
|
||||
* The latter can be customized by configuring the
|
||||
* {@link RequestMappingHandlerAdapter} directly.
|
||||
* @param argumentResolvers the list of custom converters;
|
||||
* initially an empty list.
|
||||
*/
|
||||
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom {@link HandlerMethodReturnValueHandler}s in addition to the
|
||||
* ones registered by default.
|
||||
* <p>Custom return value handlers are invoked before built-in ones except
|
||||
* for those that rely on the presence of annotations (e.g.
|
||||
* {@code @ResponseBody}, {@code @ModelAttribute}, etc.).
|
||||
* The latter can be customized by configuring the
|
||||
* {@link RequestMappingHandlerAdapter} directly.
|
||||
* @param returnValueHandlers the list of custom handlers;
|
||||
* initially an empty list.
|
||||
*/
|
||||
protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the shared {@link HttpMessageConverter}s used by the
|
||||
* {@link RequestMappingHandlerAdapter} and the
|
||||
* {@link ExceptionHandlerExceptionResolver}.
|
||||
* This method cannot be overridden.
|
||||
* Use {@link #configureMessageConverters(List)} instead.
|
||||
* Also see {@link #addDefaultHttpMessageConverters(List)} that can be
|
||||
* used to add default message converters.
|
||||
*/
|
||||
protected final List<HttpMessageConverter<?>> getMessageConverters() {
|
||||
if (messageConverters == null) {
|
||||
messageConverters = new ArrayList<HttpMessageConverter<?>>();
|
||||
configureMessageConverters(messageConverters);
|
||||
if (messageConverters.isEmpty()) {
|
||||
addDefaultHttpMessageConverters(messageConverters);
|
||||
}
|
||||
}
|
||||
return messageConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to add custom {@link HttpMessageConverter}s to use
|
||||
* with the {@link RequestMappingHandlerAdapter} and the
|
||||
* {@link ExceptionHandlerExceptionResolver}. Adding converters to the
|
||||
* list turns off the default converters that would otherwise be registered
|
||||
* by default. Also see {@link #addDefaultHttpMessageConverters(List)} that
|
||||
* can be used to add default message converters.
|
||||
* @param converters a list to add message converters to;
|
||||
* initially an empty list.
|
||||
*/
|
||||
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of default HttpMessageConverter instances to the given list.
|
||||
* Subclasses can call this method from {@link #configureMessageConverters(List)}.
|
||||
* @param messageConverters the list to add the default message converters to
|
||||
*/
|
||||
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
|
||||
stringConverter.setWriteAcceptCharset(false);
|
||||
|
||||
messageConverters.add(new ByteArrayHttpMessageConverter());
|
||||
messageConverters.add(stringConverter);
|
||||
messageConverters.add(new ResourceHttpMessageConverter());
|
||||
messageConverters.add(new SourceHttpMessageConverter<Source>());
|
||||
messageConverters.add(new XmlAwareFormHttpMessageConverter());
|
||||
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
if (ClassUtils.isPresent("javax.xml.bind.Binder", classLoader)) {
|
||||
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
|
||||
}
|
||||
if (ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", classLoader)) {
|
||||
messageConverters.add(new MappingJacksonHttpMessageConverter());
|
||||
}
|
||||
if (ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", classLoader)) {
|
||||
messageConverters.add(new AtomFeedHttpMessageConverter());
|
||||
messageConverters.add(new RssChannelHttpMessageConverter());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link FormattingConversionService} for use with annotated
|
||||
* controller methods and the {@code spring:eval} JSP tag.
|
||||
* Also see {@link #addFormatters} as an alternative to overriding this method.
|
||||
*/
|
||||
@Bean
|
||||
public FormattingConversionService mvcConversionService() {
|
||||
FormattingConversionService conversionService = new DefaultFormattingConversionService();
|
||||
addFormatters(conversionService);
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to add custom {@link Converter}s and {@link Formatter}s.
|
||||
*/
|
||||
protected void addFormatters(FormatterRegistry registry) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a global {@link Validator} instance for example for validating
|
||||
* {@code @ModelAttribute} and {@code @RequestBody} method arguments.
|
||||
* Delegates to {@link #getValidator()} first and if that returns {@code null}
|
||||
* checks the classpath for the presence of a JSR-303 implementations
|
||||
* before creating a {@code LocalValidatorFactoryBean}.If a JSR-303
|
||||
* implementation is not available, a no-op {@link Validator} is returned.
|
||||
*/
|
||||
@Bean
|
||||
public Validator mvcValidator() {
|
||||
Validator validator = getValidator();
|
||||
if (validator == null) {
|
||||
if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
|
||||
Class<?> clazz;
|
||||
try {
|
||||
String className = "org.springframework.validation.beanvalidation.LocalValidatorFactoryBean";
|
||||
clazz = ClassUtils.forName(className, WebMvcConfigurationSupport.class.getClassLoader());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new BeanInitializationException("Could not find default validator");
|
||||
} catch (LinkageError e) {
|
||||
throw new BeanInitializationException("Could not find default validator");
|
||||
}
|
||||
validator = (Validator) BeanUtils.instantiate(clazz);
|
||||
}
|
||||
else {
|
||||
validator = new Validator() {
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
public void validate(Object target, Errors errors) {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to provide a custom {@link Validator}.
|
||||
*/
|
||||
protected Validator getValidator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link HttpRequestHandlerAdapter} for processing requests
|
||||
* with {@link HttpRequestHandler}s.
|
||||
*/
|
||||
@Bean
|
||||
public HttpRequestHandlerAdapter httpRequestHandlerAdapter() {
|
||||
return new HttpRequestHandlerAdapter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link SimpleControllerHandlerAdapter} for processing requests
|
||||
* with interface-based controllers.
|
||||
*/
|
||||
@Bean
|
||||
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
|
||||
return new SimpleControllerHandlerAdapter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link HandlerExceptionResolverComposite} containing a list
|
||||
* of exception resolvers obtained either through
|
||||
* {@link #configureHandlerExceptionResolvers(List)} or through
|
||||
* {@link #addDefaultHandlerExceptionResolvers(List)}.
|
||||
* <p><strong>Note:</strong> This method cannot be made final due to CGLib
|
||||
* constraints. Rather than overriding it, consider overriding
|
||||
* {@link #configureHandlerExceptionResolvers(List)}, which allows
|
||||
* providing a list of resolvers.
|
||||
*/
|
||||
@Bean
|
||||
public HandlerExceptionResolver handlerExceptionResolver() throws Exception {
|
||||
List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<HandlerExceptionResolver>();
|
||||
configureHandlerExceptionResolvers(exceptionResolvers);
|
||||
|
||||
if (exceptionResolvers.isEmpty()) {
|
||||
addDefaultHandlerExceptionResolvers(exceptionResolvers);
|
||||
}
|
||||
|
||||
HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
|
||||
composite.setOrder(0);
|
||||
composite.setExceptionResolvers(exceptionResolvers);
|
||||
return composite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to configure the list of
|
||||
* {@link HandlerExceptionResolver}s to use. Adding resolvers to the list
|
||||
* turns off the default resolvers that would otherwise be registered by
|
||||
* default. Also see {@link #addDefaultHandlerExceptionResolvers(List)}
|
||||
* that can be used to add the default exception resolvers.
|
||||
* @param exceptionResolvers a list to add exception resolvers to;
|
||||
* initially an empty list.
|
||||
*/
|
||||
protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A method available to subclasses for adding default
|
||||
* {@link HandlerExceptionResolver}s.
|
||||
* <p>Adds the following exception resolvers:
|
||||
* <ul>
|
||||
* <li>{@link ExceptionHandlerExceptionResolver}
|
||||
* for handling exceptions through @{@link ExceptionHandler} methods.
|
||||
* <li>{@link ResponseStatusExceptionResolver}
|
||||
* for exceptions annotated with @{@link ResponseStatus}.
|
||||
* <li>{@link DefaultHandlerExceptionResolver}
|
||||
* for resolving known Spring exception types
|
||||
* </ul>
|
||||
*/
|
||||
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
|
||||
ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver = new ExceptionHandlerExceptionResolver();
|
||||
exceptionHandlerExceptionResolver.setMessageConverters(getMessageConverters());
|
||||
exceptionHandlerExceptionResolver.afterPropertiesSet();
|
||||
|
||||
exceptionResolvers.add(exceptionHandlerExceptionResolver);
|
||||
exceptionResolvers.add(new ResponseStatusExceptionResolver());
|
||||
exceptionResolvers.add(new DefaultHandlerExceptionResolver());
|
||||
}
|
||||
|
||||
private final static class EmptyHandlerMapping extends AbstractHandlerMapping {
|
||||
|
||||
@Override
|
||||
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
/**
|
||||
* Defines callback methods to customize the Java-based configuration for
|
||||
* Spring MVC enabled via {@code @EnableWebMvc}.
|
||||
*
|
||||
* <p>{@code @EnableWebMvc}-annotated configuration classes may implement
|
||||
* this interface to be called back and given a chance to customize the
|
||||
* default configuration. Consider extending {@link WebMvcConfigurerAdapter},
|
||||
* which provides a stub implementation of all interface methods.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Keith Donald
|
||||
* @author David Syer
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* Add {@link Converter}s and {@link Formatter}s in addition to the ones
|
||||
* registered by default.
|
||||
*/
|
||||
void addFormatters(FormatterRegistry registry);
|
||||
|
||||
/**
|
||||
* Configure the {@link HttpMessageConverter}s to use in argument resolvers
|
||||
* and return value handlers that support reading and/or writing to the
|
||||
* body of the request and response. If no message converters are added to
|
||||
* the list, default converters are added instead.
|
||||
* @param converters initially an empty list of converters
|
||||
*/
|
||||
void configureMessageConverters(List<HttpMessageConverter<?>> converters);
|
||||
|
||||
/**
|
||||
* Provide a custom {@link Validator} instead of the one created by default.
|
||||
* The default implementation, assuming JSR-303 is on the classpath, is:
|
||||
* {@link org.springframework.validation.beanvalidation.LocalValidatorFactoryBean}.
|
||||
* Leave the return value as {@code null} to keep the default.
|
||||
*/
|
||||
Validator getValidator();
|
||||
|
||||
/**
|
||||
* Add resolvers to support custom controller method argument types.
|
||||
* <p>This does not override the built-in support for resolving handler
|
||||
* method arguments. To customize the built-in support for argument
|
||||
* resolution, configure {@link RequestMappingHandlerAdapter} directly.
|
||||
* @param argumentResolvers initially an empty list
|
||||
*/
|
||||
void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers);
|
||||
|
||||
/**
|
||||
* Add handlers to support custom controller method return value types.
|
||||
* <p>Using this option does not override the built-in support for handling
|
||||
* return values. To customize the built-in support for handling return
|
||||
* values, configure RequestMappingHandlerAdapter directly.
|
||||
* @param returnValueHandlers initially an empty list
|
||||
*/
|
||||
void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers);
|
||||
|
||||
/**
|
||||
* Configure the {@link HandlerExceptionResolver}s to handle unresolved
|
||||
* controller exceptions. If no resolvers are added to the list, default
|
||||
* exception resolvers are added instead.
|
||||
* @param exceptionResolvers initially an empty list
|
||||
*/
|
||||
void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers);
|
||||
|
||||
/**
|
||||
* Add Spring MVC lifecycle interceptors for pre- and post-processing of
|
||||
* controller method invocations. Interceptors can be registered to apply
|
||||
* to all requests or be limited to a subset of URL patterns.
|
||||
*/
|
||||
void addInterceptors(InterceptorRegistry registry);
|
||||
|
||||
/**
|
||||
* Add view controllers to create a direct mapping between a URL path and
|
||||
* view name without the need for a controller in between.
|
||||
*/
|
||||
void addViewControllers(ViewControllerRegistry registry);
|
||||
|
||||
/**
|
||||
* Add handlers to serve static resources such as images, js, and, css
|
||||
* files from specific locations under web application root, the classpath,
|
||||
* and others.
|
||||
*/
|
||||
void addResourceHandlers(ResourceHandlerRegistry registry);
|
||||
|
||||
/**
|
||||
* Configure a handler to delegate unhandled requests by forwarding to the
|
||||
* Servlet container's "default" servlet. A common use case for this is when
|
||||
* the {@link DispatcherServlet} is mapped to "/" thus overriding the
|
||||
* Servlet container's default handling of static resources.
|
||||
*/
|
||||
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
|
||||
/**
|
||||
* An convenient base class with empty method implementations of {@link WebMvcConfigurer}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation is empty.
|
||||
*/
|
||||
public void addFormatters(FormatterRegistry registry) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation is empty.
|
||||
*/
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation returns {@code null}
|
||||
*/
|
||||
public Validator getValidator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation is empty.
|
||||
*/
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation is empty.
|
||||
*/
|
||||
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation is empty.
|
||||
*/
|
||||
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation is empty.
|
||||
*/
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation is empty.
|
||||
*/
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation is empty.
|
||||
*/
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation is empty.
|
||||
*/
|
||||
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
|
||||
/**
|
||||
* An {@link WebMvcConfigurer} implementation that delegates to other {@link WebMvcConfigurer} instances.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
class WebMvcConfigurerComposite implements WebMvcConfigurer {
|
||||
|
||||
private final List<WebMvcConfigurer> delegates = new ArrayList<WebMvcConfigurer>();
|
||||
|
||||
public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
|
||||
if (configurers != null) {
|
||||
this.delegates.addAll(configurers);
|
||||
}
|
||||
}
|
||||
|
||||
public void addFormatters(FormatterRegistry registry) {
|
||||
for (WebMvcConfigurer delegate : delegates) {
|
||||
delegate.addFormatters(registry);
|
||||
}
|
||||
}
|
||||
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
for (WebMvcConfigurer delegate : delegates) {
|
||||
delegate.configureMessageConverters(converters);
|
||||
}
|
||||
}
|
||||
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
for (WebMvcConfigurer delegate : delegates) {
|
||||
delegate.addArgumentResolvers(argumentResolvers);
|
||||
}
|
||||
}
|
||||
|
||||
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
|
||||
for (WebMvcConfigurer delegate : delegates) {
|
||||
delegate.addReturnValueHandlers(returnValueHandlers);
|
||||
}
|
||||
}
|
||||
|
||||
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
|
||||
for (WebMvcConfigurer delegate : delegates) {
|
||||
delegate.configureHandlerExceptionResolvers(exceptionResolvers);
|
||||
}
|
||||
}
|
||||
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
for (WebMvcConfigurer delegate : delegates) {
|
||||
delegate.addInterceptors(registry);
|
||||
}
|
||||
}
|
||||
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
for (WebMvcConfigurer delegate : delegates) {
|
||||
delegate.addViewControllers(registry);
|
||||
}
|
||||
}
|
||||
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
for (WebMvcConfigurer delegate : delegates) {
|
||||
delegate.addResourceHandlers(registry);
|
||||
}
|
||||
}
|
||||
|
||||
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
|
||||
for (WebMvcConfigurer delegate : delegates) {
|
||||
delegate.configureDefaultServletHandling(configurer);
|
||||
}
|
||||
}
|
||||
|
||||
public Validator getValidator() {
|
||||
Map<WebMvcConfigurer, Validator> validators = new HashMap<WebMvcConfigurer, Validator>();
|
||||
for (WebMvcConfigurer delegate : delegates) {
|
||||
Validator validator = delegate.getValidator();
|
||||
if (validator != null) {
|
||||
validators.put(delegate, validator);
|
||||
}
|
||||
}
|
||||
if (validators.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
else if (validators.size() == 1) {
|
||||
return validators.values().iterator().next();
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"Multiple custom validators provided from [" + validators.keySet() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Annotation-based setup for Spring MVC.
|
||||
*
|
||||
*/
|
||||
package org.springframework.web.servlet.config.annotation;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Defines the XML configuration namespace for Spring MVC.
|
||||
*
|
||||
*/
|
||||
package org.springframework.web.servlet.config;
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.
|
||||
*/
|
||||
@Override
|
||||
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 (String beanName : beanNames) {
|
||||
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 '" + beanName + "': 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,244 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.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;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link HandlerExceptionResolver} implementations.
|
||||
*
|
||||
* <p>Provides a set of mapped handlers that the resolver should map to,
|
||||
* and the {@link Ordered} implementation.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
|
||||
|
||||
private static final String HEADER_PRAGMA = "Pragma";
|
||||
|
||||
private static final String HEADER_EXPIRES = "Expires";
|
||||
|
||||
private static final String HEADER_CACHE_CONTROL = "Cache-Control";
|
||||
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
private Set mappedHandlers;
|
||||
|
||||
private Class[] mappedHandlerClasses;
|
||||
|
||||
private Log warnLogger;
|
||||
|
||||
private boolean preventResponseCaching = false;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to prevent HTTP response caching for any view resolved
|
||||
* by this HandlerExceptionResolver.
|
||||
* <p>Default is "false". Switch this to "true" in order to automatically
|
||||
* generate HTTP response headers that suppress response caching.
|
||||
*/
|
||||
public void setPreventResponseCaching(boolean preventResponseCaching) {
|
||||
this.preventResponseCaching = preventResponseCaching;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)) {
|
||||
// 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);
|
||||
prepareResponse(ex, response);
|
||||
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 (Class handlerClass : this.mappedHandlerClasses) {
|
||||
if (handlerClass.isInstance(handler)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Else only apply if there are no explicit handler mappings.
|
||||
return (this.mappedHandlers == null && this.mappedHandlerClasses == 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";
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the response for the exceptional case.
|
||||
* <p>The default implementation prevents the response from being cached,
|
||||
* if the {@link #setPreventResponseCaching "preventResponseCaching"} property
|
||||
* has been set to "true".
|
||||
* @param ex the exception that got thrown during handler execution
|
||||
* @param response current HTTP response
|
||||
* @see #preventCaching
|
||||
*/
|
||||
protected void prepareResponse(Exception ex, HttpServletResponse response) {
|
||||
if (this.preventResponseCaching) {
|
||||
preventCaching(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the response from being cached, through setting corresponding
|
||||
* HTTP headers. See <code>http://www.mnot.net/cache_docs</code>.
|
||||
* @param response current HTTP response
|
||||
*/
|
||||
protected void preventCaching(HttpServletResponse response) {
|
||||
response.setHeader(HEADER_PRAGMA, "no-cache");
|
||||
response.setDateHeader(HEADER_EXPIRES, 1L);
|
||||
response.setHeader(HEADER_CACHE_CONTROL, "no-cache");
|
||||
response.addHeader(HEADER_CACHE_CONTROL, "no-store");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 abstract ModelAndView doResolveException(HttpServletRequest request,
|
||||
HttpServletResponse response, Object handler, Exception ex);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.PathMatcher;
|
||||
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;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link org.springframework.web.servlet.HandlerMapping}
|
||||
* implementations. Supports ordering, a default handler, handler interceptors,
|
||||
* including handler interceptors mapped by path patterns.
|
||||
*
|
||||
* <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
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 07.04.2003
|
||||
* @see #getHandlerInternal
|
||||
* @see #setDefaultHandler
|
||||
* @see #setAlwaysUseFullPath
|
||||
* @see #setUrlDecode
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
* @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 UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
private final List<Object> interceptors = new ArrayList<Object>();
|
||||
|
||||
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
|
||||
|
||||
private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
*/
|
||||
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
|
||||
this.urlPathHelper = urlPathHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the UrlPathHelper implementation to use for resolution of lookup paths.
|
||||
*/
|
||||
public UrlPathHelper getUrlPathHelper() {
|
||||
return 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 interceptors to apply for all handlers mapped by this handler mapping.
|
||||
* <p>Supported interceptor types are HandlerInterceptor, WebRequestInterceptor, and MappedInterceptor.
|
||||
* Mapped interceptors apply only to request URLs that match its path patterns.
|
||||
* Mapped interceptor beans are also detected by type during initialization.
|
||||
* @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()
|
||||
*/
|
||||
@Override
|
||||
protected void initApplicationContext() throws BeansException {
|
||||
extendInterceptors(this.interceptors);
|
||||
detectMappedInterceptors(this.mappedInterceptors);
|
||||
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<Object> interceptors) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects beans of type {@link MappedInterceptor} and adds them to the list of mapped interceptors.
|
||||
* This is done in addition to any {@link MappedInterceptor}s that may have been provided via
|
||||
* {@link #setInterceptors(Object[])}. Subclasses can override this method to change that.
|
||||
*
|
||||
* @param mappedInterceptors an empty list to add MappedInterceptor types to
|
||||
*/
|
||||
protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) {
|
||||
mappedInterceptors.addAll(
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
getApplicationContext(),MappedInterceptor.class, true, false).values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the specified interceptors, checking for {@link MappedInterceptor}s and adapting
|
||||
* HandlerInterceptors where necessary.
|
||||
* @see #setInterceptors
|
||||
* @see #adaptInterceptor
|
||||
*/
|
||||
protected void initInterceptors() {
|
||||
if (!this.interceptors.isEmpty()) {
|
||||
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");
|
||||
}
|
||||
if (interceptor instanceof MappedInterceptor) {
|
||||
mappedInterceptors.add((MappedInterceptor) interceptor);
|
||||
}
|
||||
else {
|
||||
adaptedInterceptors.add(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() {
|
||||
int count = adaptedInterceptors.size();
|
||||
return (count > 0) ? adaptedInterceptors.toArray(new HandlerInterceptor[count]) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all configured {@link MappedInterceptor}s as an array.
|
||||
* @return the array of {@link MappedInterceptor}s, or <code>null</code> if none
|
||||
*/
|
||||
protected final MappedInterceptor[] getMappedInterceptors() {
|
||||
int count = mappedInterceptors.size();
|
||||
return (count > 0) ? mappedInterceptors.toArray(new MappedInterceptor[count]) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, the handler mapping's common interceptors, and any {@link MappedInterceptor}s
|
||||
* matching to the current request URL. 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) {
|
||||
HandlerExecutionChain chain =
|
||||
(handler instanceof HandlerExecutionChain) ?
|
||||
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
|
||||
|
||||
chain.addInterceptors(getAdaptedInterceptors());
|
||||
|
||||
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
|
||||
for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
|
||||
if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
|
||||
chain.addInterceptor(mappedInterceptor.getInterceptor());
|
||||
}
|
||||
}
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Abstract base class for
|
||||
* {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver}
|
||||
* implementations that support handling exceptions from handlers of type {@link HandlerMethod}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {
|
||||
|
||||
/**
|
||||
* Checks if the handler is a {@link HandlerMethod} instance and performs the check against the bean
|
||||
* instance it contains. If the provided handler is not an instance of {@link HandlerMethod},
|
||||
* {@code false} is returned instead.
|
||||
*/
|
||||
@Override
|
||||
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
|
||||
if (handler == null) {
|
||||
return super.shouldApplyTo(request, handler);
|
||||
}
|
||||
else if (handler instanceof HandlerMethod) {
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
handler = handlerMethod.getBean();
|
||||
return super.shouldApplyTo(request, handler);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ModelAndView doResolveException(
|
||||
HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception ex) {
|
||||
|
||||
return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 handlerMethod the executed handler method, 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 abstract ModelAndView doResolveHandlerMethodException(
|
||||
HttpServletRequest request, HttpServletResponse response,
|
||||
HandlerMethod handlerMethod, Exception ex);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link HandlerMapping} implementations that define a
|
||||
* mapping between a request and a {@link HandlerMethod}.
|
||||
*
|
||||
* <p>For each registered handler method, a unique mapping is maintained with
|
||||
* subclasses defining the details of the mapping type {@code <T>}.
|
||||
*
|
||||
* @param <T> The mapping for a {@link HandlerMethod} containing the conditions
|
||||
* needed to match the handler method to incoming request.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping {
|
||||
|
||||
private boolean detectHandlerMethodsInAncestorContexts = false;
|
||||
|
||||
private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();
|
||||
|
||||
private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();
|
||||
|
||||
/**
|
||||
* Whether to detect handler methods in beans in ancestor ApplicationContexts.
|
||||
* <p>Default is "false": Only beans in the current ApplicationContext are
|
||||
* considered, 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 setDetectHandlerMethodsInAncestorContexts(boolean detectHandlerMethodsInAncestorContexts) {
|
||||
this.detectHandlerMethodsInAncestorContexts = detectHandlerMethodsInAncestorContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a map with all handler methods and their mappings.
|
||||
*/
|
||||
public Map<T, HandlerMethod> getHandlerMethods() {
|
||||
return Collections.unmodifiableMap(handlerMethods);
|
||||
}
|
||||
|
||||
/**
|
||||
* ApplicationContext initialization and handler method detection.
|
||||
*/
|
||||
@Override
|
||||
public void initApplicationContext() throws ApplicationContextException {
|
||||
super.initApplicationContext();
|
||||
initHandlerMethods();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan beans in the ApplicationContext, detect and register handler methods.
|
||||
* @see #isHandler(Class)
|
||||
* @see #getMappingForMethod(Method, Class)
|
||||
* @see #handlerMethodsInitialized(Map)
|
||||
*/
|
||||
protected void initHandlerMethods() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
|
||||
}
|
||||
|
||||
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
|
||||
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
|
||||
getApplicationContext().getBeanNamesForType(Object.class));
|
||||
|
||||
for (String beanName : beanNames) {
|
||||
if (isHandler(getApplicationContext().getType(beanName))){
|
||||
detectHandlerMethods(beanName);
|
||||
}
|
||||
}
|
||||
handlerMethodsInitialized(getHandlerMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given type is a handler with handler methods.
|
||||
* @param beanType the type of the bean being checked
|
||||
* @return "true" if this a handler type, "false" otherwise.
|
||||
*/
|
||||
protected abstract boolean isHandler(Class<?> beanType);
|
||||
|
||||
/**
|
||||
* Invoked after all handler methods have been detected.
|
||||
* @param handlerMethods a read-only map with handler methods and mappings.
|
||||
*/
|
||||
protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for handler methods in a handler.
|
||||
* @param handler the bean name of a handler or a handler instance
|
||||
*/
|
||||
protected void detectHandlerMethods(final Object handler) {
|
||||
Class<?> handlerType = (handler instanceof String) ?
|
||||
getApplicationContext().getType((String) handler) : handler.getClass();
|
||||
|
||||
final Class<?> userType = ClassUtils.getUserClass(handlerType);
|
||||
|
||||
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
|
||||
public boolean matches(Method method) {
|
||||
return getMappingForMethod(method, userType) != null;
|
||||
}
|
||||
});
|
||||
|
||||
for (Method method : methods) {
|
||||
T mapping = getMappingForMethod(method, userType);
|
||||
registerHandlerMethod(handler, method, mapping);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the mapping for a handler method. A method for which no
|
||||
* mapping can be provided is not a handler method.
|
||||
*
|
||||
* @param method the method to provide a mapping for
|
||||
* @param handlerType the handler type, possibly a sub-type of the method's
|
||||
* declaring class
|
||||
* @return the mapping, or {@code null} if the method is not mapped
|
||||
*/
|
||||
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
|
||||
|
||||
/**
|
||||
* Register a handler method and its unique mapping.
|
||||
*
|
||||
* @param handler the bean name of the handler or the handler instance
|
||||
* @param method the method to register
|
||||
* @param mapping the mapping conditions associated with the handler method
|
||||
* @throws IllegalStateException if another method was already registered
|
||||
* under the same mapping
|
||||
*/
|
||||
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
|
||||
HandlerMethod handlerMethod;
|
||||
if (handler instanceof String) {
|
||||
String beanName = (String) handler;
|
||||
handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method);
|
||||
}
|
||||
else {
|
||||
handlerMethod = new HandlerMethod(handler, method);
|
||||
}
|
||||
|
||||
HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
|
||||
if (oldHandlerMethod != null && !oldHandlerMethod.equals(handlerMethod)) {
|
||||
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + handlerMethod.getBean()
|
||||
+ "' bean method \n" + handlerMethod + "\nto " + mapping + ": There is already '"
|
||||
+ oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
|
||||
}
|
||||
|
||||
handlerMethods.put(mapping, handlerMethod);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
|
||||
}
|
||||
|
||||
Set<String> patterns = getMappingPathPatterns(mapping);
|
||||
for (String pattern : patterns) {
|
||||
if (!getPathMatcher().isPattern(pattern)) {
|
||||
urlMap.add(pattern, mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and return the URL paths contained in a mapping.
|
||||
*/
|
||||
protected abstract Set<String> getMappingPathPatterns(T mapping);
|
||||
|
||||
/**
|
||||
* Look up a handler method for the given request.
|
||||
*/
|
||||
@Override
|
||||
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
|
||||
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking up handler method for path " + lookupPath);
|
||||
}
|
||||
|
||||
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (handlerMethod != null) {
|
||||
logger.debug("Returning handler method [" + handlerMethod + "]");
|
||||
}
|
||||
else {
|
||||
logger.debug("Did not find handler method for [" + lookupPath + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the best-matching handler method for the current request.
|
||||
* If multiple matches are found, the best match is selected.
|
||||
*
|
||||
* @param lookupPath mapping lookup path within the current servlet mapping
|
||||
* @param request the current request
|
||||
* @return the best-matching handler method, or {@code null} if no match
|
||||
*
|
||||
* @see #handleMatch(Object, String, HttpServletRequest)
|
||||
* @see #handleNoMatch(Set, String, HttpServletRequest)
|
||||
*/
|
||||
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
|
||||
List<T> mappings = urlMap.get(lookupPath);
|
||||
if (mappings == null) {
|
||||
mappings = new ArrayList<T>(handlerMethods.keySet());
|
||||
}
|
||||
|
||||
List<Match> matches = new ArrayList<Match>();
|
||||
|
||||
for (T mapping : mappings) {
|
||||
T match = getMatchingMapping(mapping, request);
|
||||
if (match != null) {
|
||||
matches.add(new Match(match, handlerMethods.get(mapping)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!matches.isEmpty()) {
|
||||
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
|
||||
Collections.sort(matches, comparator);
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
|
||||
}
|
||||
|
||||
Match bestMatch = matches.get(0);
|
||||
if (matches.size() > 1) {
|
||||
Match secondBestMatch = matches.get(1);
|
||||
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
|
||||
Method m1 = bestMatch.handlerMethod.getMethod();
|
||||
Method m2 = secondBestMatch.handlerMethod.getMethod();
|
||||
throw new IllegalStateException(
|
||||
"Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
|
||||
m1 + ", " + m2 + "}");
|
||||
}
|
||||
}
|
||||
|
||||
handleMatch(bestMatch.mapping, lookupPath, request);
|
||||
return bestMatch.handlerMethod;
|
||||
}
|
||||
else {
|
||||
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a mapping matches the current request and return a (potentially
|
||||
* new) mapping with conditions relevant to the current request.
|
||||
*
|
||||
* @param mapping the mapping to get a match for
|
||||
* @param request the current HTTP servlet request
|
||||
* @return the match, or {@code null} if the mapping doesn't match
|
||||
*/
|
||||
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Return a comparator for sorting matching mappings.
|
||||
* The returned comparator should sort 'better' matches higher.
|
||||
* @param request the current request
|
||||
* @return the comparator, never {@code null}
|
||||
*/
|
||||
protected abstract Comparator<T> getMappingComparator(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Invoked when a matching mapping is found.
|
||||
* @param mapping the matching mapping
|
||||
* @param lookupPath mapping lookup path within the current servlet mapping
|
||||
* @param request the current request
|
||||
*/
|
||||
protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
|
||||
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when no matching mapping is not found.
|
||||
* @param mappings all registered mappings
|
||||
* @param lookupPath mapping lookup path within the current servlet mapping
|
||||
* @param request the current request
|
||||
* @throws ServletException in case of errors
|
||||
*/
|
||||
protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
|
||||
throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary container for a mapping matched to a request.
|
||||
*/
|
||||
private class Match {
|
||||
|
||||
private final T mapping;
|
||||
|
||||
private final HandlerMethod handlerMethod;
|
||||
|
||||
private Match(T mapping, HandlerMethod handlerMethod) {
|
||||
this.mapping = mapping;
|
||||
this.handlerMethod = handlerMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mapping.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private class MatchComparator implements Comparator<Match> {
|
||||
|
||||
private final Comparator<T> comparator;
|
||||
|
||||
public MatchComparator(Comparator<T> comparator) {
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
public int compare(Match match1, Match match2) {
|
||||
return comparator.compare(match1.mapping, match2.mapping);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @author Arjen Poutsma
|
||||
* @since 16.04.2003
|
||||
*/
|
||||
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
|
||||
|
||||
private Object rootHandler;
|
||||
|
||||
private boolean lazyInitHandlers = false;
|
||||
|
||||
private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
|
||||
String lookupPath = getUrlPathHelper().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) {
|
||||
// Bean name or resolved handler?
|
||||
if (rawHandler instanceof String) {
|
||||
String handlerName = (String) rawHandler;
|
||||
rawHandler = getApplicationContext().getBean(handlerName);
|
||||
}
|
||||
validateHandler(rawHandler, request);
|
||||
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
|
||||
}
|
||||
}
|
||||
if (handler != null && logger.isDebugEnabled()) {
|
||||
logger.debug("Mapping [" + lookupPath + "] to " + 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) {
|
||||
// Bean name or resolved handler?
|
||||
if (handler instanceof String) {
|
||||
String handlerName = (String) handler;
|
||||
handler = getApplicationContext().getBean(handlerName);
|
||||
}
|
||||
validateHandler(handler, request);
|
||||
return buildPathExposingHandler(handler, urlPath, urlPath, null);
|
||||
}
|
||||
// Pattern match?
|
||||
List<String> matchingPatterns = new ArrayList<String>();
|
||||
for (String registeredPattern : this.handlerMap.keySet()) {
|
||||
if (getPathMatcher().match(registeredPattern, urlPath)) {
|
||||
matchingPatterns.add(registeredPattern);
|
||||
}
|
||||
}
|
||||
String bestPatternMatch = null;
|
||||
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
|
||||
if (!matchingPatterns.isEmpty()) {
|
||||
Collections.sort(matchingPatterns, patternComparator);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
|
||||
}
|
||||
bestPatternMatch = matchingPatterns.get(0);
|
||||
}
|
||||
if (bestPatternMatch != null) {
|
||||
handler = this.handlerMap.get(bestPatternMatch);
|
||||
// Bean name or resolved handler?
|
||||
if (handler instanceof String) {
|
||||
String handlerName = (String) handler;
|
||||
handler = getApplicationContext().getBean(handlerName);
|
||||
}
|
||||
validateHandler(handler, request);
|
||||
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
|
||||
|
||||
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
|
||||
// for all of them
|
||||
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
|
||||
for (String matchingPattern : matchingPatterns) {
|
||||
if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
|
||||
uriTemplateVariables
|
||||
.putAll(getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath));
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
|
||||
}
|
||||
return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
|
||||
}
|
||||
// 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, the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}, as well as
|
||||
* the {@link #URI_TEMPLATE_VARIABLES_ATTRIBUTE} before executing the handler.
|
||||
* <p>The default implementation builds a {@link HandlerExecutionChain}
|
||||
* with a special interceptor that exposes the path attribute and uri template variables
|
||||
* @param rawHandler the raw handler to expose
|
||||
* @param pathWithinMapping the path to expose before executing the handler
|
||||
* @param uriTemplateVariables the URI template variables, can be <code>null</code> if no variables found
|
||||
* @return the final handler object
|
||||
*/
|
||||
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
|
||||
String pathWithinMapping, Map<String, String> uriTemplateVariables) {
|
||||
|
||||
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
|
||||
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
|
||||
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
|
||||
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
|
||||
}
|
||||
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 bestMatchingPattern, String pathWithinMapping, HttpServletRequest request) {
|
||||
request.setAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatchingPattern);
|
||||
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose the URI templates variables as request attribute.
|
||||
* @param uriTemplateVariables the URI template variables
|
||||
* @param request the request to expose the path to
|
||||
* @see #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
|
||||
*/
|
||||
protected void exposeUriTemplateVariables(Map<String, String> uriTemplateVariables, HttpServletRequest request) {
|
||||
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (String urlPath : urlPaths) {
|
||||
registerHandler(urlPath, 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 " + getHandlerDescription(handler) + " to URL path [" + urlPath +
|
||||
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (urlPath.equals("/")) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Root mapping to " + getHandlerDescription(handler));
|
||||
}
|
||||
setRootHandler(resolvedHandler);
|
||||
}
|
||||
else if (urlPath.equals("/*")) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Default mapping to " + getHandlerDescription(handler));
|
||||
}
|
||||
setDefaultHandler(resolvedHandler);
|
||||
}
|
||||
else {
|
||||
this.handlerMap.put(urlPath, resolvedHandler);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getHandlerDescription(Object handler) {
|
||||
return "handler " + (handler instanceof String ? "'" + handler + "'" : "of type [" + handler.getClass() + "]");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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<String, Object> getHandlerMap() {
|
||||
return Collections.unmodifiableMap(this.handlerMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this handler mapping support type-level mappings. Default to {@code false}.
|
||||
*/
|
||||
protected boolean supportsTypeLevelMappings() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Special interceptor for exposing the
|
||||
* {@link AbstractUrlHandlerMapping#PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE} attribute.
|
||||
* @see AbstractUrlHandlerMapping#exposePathWithinMapping
|
||||
*/
|
||||
private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private final String bestMatchingPattern;
|
||||
|
||||
private final String pathWithinMapping;
|
||||
|
||||
public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) {
|
||||
this.bestMatchingPattern = bestMatchingPattern;
|
||||
this.pathWithinMapping = pathWithinMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
|
||||
request.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Special interceptor for exposing the
|
||||
* {@link AbstractUrlHandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE} attribute.
|
||||
* @see AbstractUrlHandlerMapping#exposePathWithinMapping
|
||||
*/
|
||||
private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private final Map<String, String> uriTemplateVariables;
|
||||
|
||||
public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) {
|
||||
this.uriTemplateVariables = uriTemplateVariables;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
exposeUriTemplateVariables(this.uriTemplateVariables, request);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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 "/".
|
||||
*/
|
||||
@Override
|
||||
protected String[] determineUrlsForHandler(String beanName) {
|
||||
List<String> urls = new ArrayList<String>();
|
||||
if (beanName.startsWith("/")) {
|
||||
urls.add(beanName);
|
||||
}
|
||||
String[] aliases = getApplicationContext().getAliases(beanName);
|
||||
for (String alias : aliases) {
|
||||
if (alias.startsWith("/")) {
|
||||
urls.add(alias);
|
||||
}
|
||||
}
|
||||
return StringUtils.toStringArray(urls);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Interceptor that places the configured {@link ConversionService} in request scope
|
||||
* so it's available during request processing. The request attribute name is
|
||||
* "org.springframework.core.convert.ConversionService", the value of
|
||||
* <code>ConversionService.class.getName()</code>.
|
||||
*
|
||||
* <p>Mainly for use within JSP tags such as the spring:eval tag.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.0.1
|
||||
*/
|
||||
public class ConversionServiceExposingInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConversionServiceExposingInterceptor}.
|
||||
* @param conversionService the conversion service to export to request scope when this interceptor is invoked
|
||||
*/
|
||||
public ConversionServiceExposingInterceptor(ConversionService conversionService) {
|
||||
Assert.notNull(conversionService, "The ConversionService may not be null");
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws ServletException, IOException {
|
||||
|
||||
request.setAttribute(ConversionService.class.getName(), this.conversionService);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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 javax.servlet.http.HttpServletResponse;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DispatcherServletWebRequest instance for the given request and response.
|
||||
* @param request current HTTP request
|
||||
* @param request current HTTP response
|
||||
*/
|
||||
public DispatcherServletWebRequest(HttpServletRequest request, HttpServletResponse response) {
|
||||
super(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
return RequestContextUtils.getLocale(getRequest());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* A {@link HandlerExceptionResolver} that delegates to a list of other {@link HandlerExceptionResolver}s.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class HandlerExceptionResolverComposite implements HandlerExceptionResolver, Ordered {
|
||||
|
||||
private List<HandlerExceptionResolver> resolvers;
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of exception resolvers to delegate to.
|
||||
*/
|
||||
public void setExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
|
||||
this.resolvers = exceptionResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of exception resolvers to delegate to.
|
||||
*/
|
||||
public List<HandlerExceptionResolver> getExceptionResolvers() {
|
||||
return Collections.unmodifiableList(resolvers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the exception by iterating over the list of configured exception resolvers.
|
||||
* The first one to return a ModelAndView instance wins. Otherwise {@code null} is returned.
|
||||
*/
|
||||
public ModelAndView resolveException(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler,
|
||||
Exception ex) {
|
||||
if (resolvers != null) {
|
||||
for (HandlerExceptionResolver handlerExceptionResolver : resolvers) {
|
||||
ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
|
||||
if (mav != null) {
|
||||
return mav;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,91 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.util.PathMatcher;
|
||||
import org.springframework.web.context.request.WebRequestInterceptor;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
/**
|
||||
* Holds information about a HandlerInterceptor mapped to a path into the application.
|
||||
* Provides a method to match a request path to the mapped path patterns.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class MappedInterceptor {
|
||||
|
||||
private final String[] pathPatterns;
|
||||
|
||||
private final HandlerInterceptor interceptor;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MappedInterceptor instance.
|
||||
* @param pathPatterns the path patterns to map with a {@code null} value matching to all paths
|
||||
* @param interceptor the HandlerInterceptor instance to map to the given patterns
|
||||
*/
|
||||
public MappedInterceptor(String[] pathPatterns, HandlerInterceptor interceptor) {
|
||||
this.pathPatterns = pathPatterns;
|
||||
this.interceptor = interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MappedInterceptor instance.
|
||||
* @param pathPatterns the path patterns to map with a {@code null} value matching to all paths
|
||||
* @param interceptor the WebRequestInterceptor instance to map to the given patterns
|
||||
*/
|
||||
public MappedInterceptor(String[] pathPatterns, WebRequestInterceptor interceptor) {
|
||||
this.pathPatterns = pathPatterns;
|
||||
this.interceptor = new WebRequestHandlerInterceptorAdapter(interceptor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The path into the application the interceptor is mapped to.
|
||||
*/
|
||||
public String[] getPathPatterns() {
|
||||
return this.pathPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual Interceptor reference.
|
||||
*/
|
||||
public HandlerInterceptor getInterceptor() {
|
||||
return this.interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the interceptor applies to the given request path.
|
||||
* @param lookupPath the current request path
|
||||
* @param pathMatcher a path matcher for path pattern matching
|
||||
*/
|
||||
public boolean matches(String lookupPath, PathMatcher pathMatcher) {
|
||||
if (pathPatterns == null) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
for (String pathPattern : pathPatterns) {
|
||||
if (pathMatcher.match(pathPattern, lookupPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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 java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
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
|
||||
* @author Arjen Poutsma
|
||||
* @since 22.11.2003
|
||||
* @see org.springframework.web.servlet.DispatcherServlet
|
||||
*/
|
||||
public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
|
||||
|
||||
/** The default name of the exception attribute: "exception". */
|
||||
public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";
|
||||
|
||||
private Properties exceptionMappings;
|
||||
|
||||
private String defaultErrorView;
|
||||
|
||||
private Integer defaultStatusCode;
|
||||
|
||||
private Map<String, Integer> statusCodes = new HashMap<String, Integer>();
|
||||
|
||||
private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE;
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param mappings exception patterns (can also be fully qualified class names) as keys,
|
||||
* and error view names as values
|
||||
*/
|
||||
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 HTTP status code that this exception resolver will apply for a given resolved error view. Keys are
|
||||
* view names; values are status codes.
|
||||
* <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, the default status code will be applied.
|
||||
* @see #setDefaultStatusCode(int)
|
||||
*/
|
||||
public void setStatusCodes(Properties statusCodes) {
|
||||
for (Enumeration<?> enumeration = statusCodes.propertyNames(); enumeration.hasMoreElements();) {
|
||||
String viewName = (String) enumeration.nextElement();
|
||||
Integer statusCode = new Integer(statusCodes.getProperty(viewName));
|
||||
this.statusCodes.put(viewName, statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An alternative to {@link #setStatusCodes(Properties)} for use with
|
||||
* Java-based configuration.
|
||||
*/
|
||||
public void addStatusCode(String viewName, int statusCode) {
|
||||
this.statusCodes.put(viewName, statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP status codes provided via {@link #setStatusCodes(Properties)}.
|
||||
* Keys are view names; values are status codes.
|
||||
*/
|
||||
public Map<String, Integer> getStatusCodesAsMap() {
|
||||
return Collections.unmodifiableMap(statusCodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default HTTP status code that this exception resolver will apply if it resolves an error view and if there
|
||||
* is no status code mapping defined.
|
||||
* <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
|
||||
* ({@link HttpServletResponse#SC_INTERNAL_SERVER_ERROR}) or 404 ({@link HttpServletResponse#SC_NOT_FOUND})
|
||||
* @see #setStatusCodes(Properties)
|
||||
*/
|
||||
public void setDefaultStatusCode(int defaultStatusCode) {
|
||||
this.defaultStatusCode = 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
protected ModelAndView doResolveException(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler,
|
||||
Exception ex) {
|
||||
|
||||
// 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);
|
||||
}
|
||||
return getModelAndView(viewName, ex, request);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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().contains(exceptionMapping)) {
|
||||
// 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 returns the status code for the given view name (specified through the
|
||||
* {@link #setStatusCodes(Properties) statusCodes} property), or falls back to the
|
||||
* {@link #setDefaultStatusCode defaultStatusCode} if there is no match.
|
||||
* <p>Override this in a custom subclass to customize this behavior.
|
||||
* @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) {
|
||||
if (this.statusCodes.containsKey(viewName)) {
|
||||
return this.statusCodes.get(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 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, 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-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.Enumeration;
|
||||
import java.util.HashSet;
|
||||
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<String> getInitParameterNames() {
|
||||
return Collections.enumeration(new HashSet<String>());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* 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<String, Object> urlMap = new HashMap<String, Object>();
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String, ?> 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<String, ?> getUrlMap() {
|
||||
return this.urlMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls the {@link #registerHandlers} method in addition to the
|
||||
* superclass's initialization.
|
||||
*/
|
||||
@Override
|
||||
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<String, Object> urlMap) throws BeansException {
|
||||
if (urlMap.isEmpty()) {
|
||||
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
|
||||
}
|
||||
else {
|
||||
for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
|
||||
String url = entry.getKey();
|
||||
Object handler = entry.getValue();
|
||||
// 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-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.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;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws ServletException, IOException {
|
||||
|
||||
if (this.authorizedRoles != null) {
|
||||
for (String role : this.authorizedRoles) {
|
||||
if (request.isUserInRole(role)) {
|
||||
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-2010 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, response));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
|
||||
throws Exception {
|
||||
|
||||
this.requestInterceptor.postHandle(new DispatcherServletWebRequest(request, response),
|
||||
(modelAndView != null && !modelAndView.wasCleared() ? modelAndView.getModelMap() : null));
|
||||
}
|
||||
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
|
||||
throws Exception {
|
||||
|
||||
this.requestInterceptor.afterCompletion(new DispatcherServletWebRequest(request, response), ex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Provides standard HandlerMapping implementations,
|
||||
* including abstract base classes for custom implementations.
|
||||
*
|
||||
*/
|
||||
package org.springframework.web.servlet.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.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,79 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 javax.servlet.ServletException;
|
||||
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.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;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
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?");
|
||||
}
|
||||
localeResolver.setLocale(request, response, StringUtils.parseLocaleString(newLocale));
|
||||
}
|
||||
// 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,10 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Locale support classes for Spring's web MVC framework.
|
||||
* Provides standard LocaleResolver implementations,
|
||||
* and a HandlerInterceptor for locale changes.
|
||||
*
|
||||
*/
|
||||
package org.springframework.web.servlet.i18n;
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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
|
||||
* @deprecated as of Spring 3.0, in favor of annotated controllers
|
||||
*/
|
||||
@Deprecated
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
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 the cacheSeconds property</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,678 @@
|
||||
/*
|
||||
* 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
|
||||
* @deprecated as of Spring 3.0, in favor of annotated controllers
|
||||
*/
|
||||
@Deprecated
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
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,114 @@
|
||||
/*
|
||||
* 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.servlet.support.RequestContextUtils;
|
||||
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}. Also adds the content of
|
||||
* {@link RequestContextUtils#getInputFlashMap} to the model.
|
||||
*/
|
||||
@Override
|
||||
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, RequestContextUtils.getInputFlashMap(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,751 @@
|
||||
/*
|
||||
* 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
|
||||
* @deprecated as of Spring 3.0, in favor of annotated controllers
|
||||
*/
|
||||
@Deprecated
|
||||
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.
|
||||
*/
|
||||
@Override
|
||||
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)
|
||||
*/
|
||||
@Override
|
||||
protected boolean isFormSubmission(HttpServletRequest request) {
|
||||
return super.isFormSubmission(request) || isFinishRequest(request) || isCancelRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls page-specific referenceData method.
|
||||
*/
|
||||
@Override
|
||||
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.
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
protected ModelAndView handleInvalidSubmit(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
return showNewForm(request, response);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply wizard workflow: finish, cancel, page change.
|
||||
*/
|
||||
@Override
|
||||
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,595 @@
|
||||
/*
|
||||
* 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
|
||||
* @deprecated as of Spring 3.0, in favor of annotated controllers
|
||||
*/
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Deprecated
|
||||
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,210 @@
|
||||
/*
|
||||
* 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)
|
||||
* @deprecated as of Spring 3.0, in favor of annotated controllers
|
||||
*/
|
||||
@Deprecated
|
||||
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)
|
||||
*/
|
||||
@Override
|
||||
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)
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
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,124 @@
|
||||
/*
|
||||
* 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.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.</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 {@link LastModified} interface.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see LastModified
|
||||
* @see SimpleControllerHandlerAdapter
|
||||
* @see AbstractController
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Supports last-modified HTTP requests to facilitate content caching.
|
||||
* Same contract as for the Servlet API's <code>getLastModified</code> method.
|
||||
*
|
||||
* <p>Delegated to by a {@link org.springframework.web.servlet.HandlerAdapter#getLastModified}
|
||||
* implementation. By default, any Controller or HttpRequestHandler within Spring's
|
||||
* default framework can implement this interface to enable last-modified checking.
|
||||
*
|
||||
* <p><b>Note:</b> Alternative handler implementation approaches have different
|
||||
* last-modified handling styles. For example, Spring 2.5's annotated controller
|
||||
* approach (using <code>@RequestMapping</code>) provides last-modified support
|
||||
* through the {@link org.springframework.web.context.request.WebRequest#checkNotModified}
|
||||
* method, allowing for last-modified checking within the main handler method.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see javax.servlet.http.HttpServlet#getLastModified
|
||||
* @see Controller
|
||||
* @see SimpleControllerHandlerAdapter
|
||||
* @see org.springframework.web.HttpRequestHandler
|
||||
* @see HttpRequestHandlerAdapter
|
||||
*/
|
||||
public interface LastModified {
|
||||
|
||||
/**
|
||||
* Same contract as for HttpServlet's <code>getLastModified</code> method.
|
||||
* Invoked <b>before</b> request processing.
|
||||
* <p>The return value will be sent to the HTTP client as Last-Modified header,
|
||||
* and compared with If-Modified-Since headers that the client sends back.
|
||||
* The content will only get regenerated if there has been a modification.
|
||||
* @param request current HTTP request
|
||||
* @return the time the underlying resource was last modified, or -1
|
||||
* meaning that the content must always be regenerated
|
||||
* @see org.springframework.web.servlet.HandlerAdapter#getLastModified
|
||||
* @see javax.servlet.http.HttpServlet#getLastModified
|
||||
*/
|
||||
long getLastModified(HttpServletRequest request);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
/**
|
||||
* <p>Trivial controller that always returns a named view. The view
|
||||
* can be configured using an exposed configuration property. This
|
||||
* controller offers an alternative to sending a request straight to a view
|
||||
* such as a JSP. The advantage here is that the client is not exposed to
|
||||
* the concrete view technology but rather just to the controller URL;
|
||||
* the concrete view will be determined by the ViewResolver.
|
||||
*
|
||||
* <p>An alternative to the ParameterizableViewController is a
|
||||
* {@link org.springframework.web.servlet.mvc.multiaction.MultiActionController MultiActionController},
|
||||
* which can define a variety of handler methods that just return a plain
|
||||
* ModelAndView instance for a given view name.
|
||||
*
|
||||
* <p><b><a name="workflow">Workflow
|
||||
* (<a href="AbstractController.html#workflow">and that defined by superclass</a>):</b><br>
|
||||
* <ol>
|
||||
* <li>Request is received by the controller</li>
|
||||
* <li>call to {@link #handleRequestInternal handleRequestInternal} which
|
||||
* just returns the view, named by the configuration property
|
||||
* <code>viewName</code>. Nothing more, nothing less</li>
|
||||
* </ol>
|
||||
* </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></td>
|
||||
* <td><b>default</b></td>
|
||||
* <td><b>description</b></td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>viewName</td>
|
||||
* <td><i>null</i></td>
|
||||
* <td>the name of the view the viewResolver will use to forward to
|
||||
* (if this property is not set, a null view name will be returned
|
||||
* directing the caller to calculate the view name from the current request)</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* </p>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class ParameterizableViewController extends AbstractController {
|
||||
|
||||
private String viewName;
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the view to delegate to.
|
||||
*/
|
||||
public void setViewName(String viewName) {
|
||||
this.viewName = viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the view to delegate to.
|
||||
*/
|
||||
public String getViewName() {
|
||||
return this.viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a ModelAndView object with the specified view name.
|
||||
* The content of {@link RequestContextUtils#getInputFlashMap} is also added to the model.
|
||||
* @see #getViewName()
|
||||
*/
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
return new ModelAndView(getViewName(), RequestContextUtils.getInputFlashMap(request));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Spring Controller implementation that forwards to a named servlet,
|
||||
* i.e. the "servlet-name" in web.xml rather than a URL path mapping.
|
||||
* A target servlet doesn't even need a "servlet-mapping" in web.xml
|
||||
* in the first place: A "servlet" declaration is sufficient.
|
||||
*
|
||||
* <p>Useful to invoke an existing servlet via Spring's dispatching infrastructure,
|
||||
* for example to apply Spring HandlerInterceptors to its requests. This will work
|
||||
* even in a minimal Servlet container that does not support Servlet filters.
|
||||
*
|
||||
* <p><b>Example:</b> web.xml, mapping all "/myservlet" requests to a Spring dispatcher.
|
||||
* Also defines a custom "myServlet", but <i>without</i> servlet mapping.
|
||||
*
|
||||
* <pre>
|
||||
* <servlet>
|
||||
* <servlet-name>myServlet</servlet-name>
|
||||
* <servlet-class>mypackage.TestServlet</servlet-class>
|
||||
* </servlet>
|
||||
*
|
||||
* <servlet>
|
||||
* <servlet-name>myDispatcher</servlet-name>
|
||||
* <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
* </servlet>
|
||||
*
|
||||
* <servlet-mapping>
|
||||
* <servlet-name>myDispatcher</servlet-name>
|
||||
* <url-pattern>/myservlet</url-pattern>
|
||||
* </servlet-mapping></pre>
|
||||
*
|
||||
* <b>Example:</b> myDispatcher-servlet.xml, in turn forwarding "/myservlet" to your
|
||||
* servlet (identified by servlet name). All such requests will go through the
|
||||
* configured HandlerInterceptor chain (e.g. an OpenSessionInViewInterceptor).
|
||||
* From the servlet point of view, everything will work as usual.
|
||||
*
|
||||
* <pre>
|
||||
* <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
|
||||
* <property name="interceptors">
|
||||
* <list>
|
||||
* <ref bean="openSessionInViewInterceptor"/>
|
||||
* </list>
|
||||
* </property>
|
||||
* <property name="mappings">
|
||||
* <props>
|
||||
* <prop key="/myservlet">myServletForwardingController</prop>
|
||||
* </props>
|
||||
* </property>
|
||||
* </bean>
|
||||
*
|
||||
* <bean id="myServletForwardingController" class="org.springframework.web.servlet.mvc.ServletForwardingController">
|
||||
* <property name="servletName"><value>myServlet</value></property>
|
||||
* </bean></pre>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.1
|
||||
* @see ServletWrappingController
|
||||
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor
|
||||
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
|
||||
* @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewInterceptor
|
||||
* @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewFilter
|
||||
*/
|
||||
public class ServletForwardingController extends AbstractController implements BeanNameAware {
|
||||
|
||||
private String servletName;
|
||||
|
||||
private String beanName;
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the servlet to forward to,
|
||||
* i.e. the "servlet-name" of the target servlet in web.xml.
|
||||
* <p>Default is the bean name of this controller.
|
||||
*/
|
||||
public void setServletName(String servletName) {
|
||||
this.servletName = servletName;
|
||||
}
|
||||
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
if (this.servletName == null) {
|
||||
this.servletName = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
RequestDispatcher rd = getServletContext().getNamedDispatcher(this.servletName);
|
||||
if (rd == null) {
|
||||
throw new ServletException("No servlet with name '" + this.servletName + "' defined in web.xml");
|
||||
}
|
||||
// If already included, include again, else forward.
|
||||
if (useInclude(request, response)) {
|
||||
rd.include(request, response);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Included servlet [" + this.servletName +
|
||||
"] in ServletForwardingController '" + this.beanName + "'");
|
||||
}
|
||||
}
|
||||
else {
|
||||
rd.forward(request, response);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Forwarded to servlet [" + this.servletName +
|
||||
"] in ServletForwardingController '" + this.beanName + "'");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether to use RequestDispatcher's <code>include</code> or
|
||||
* <code>forward</code> method.
|
||||
* <p>Performs a check whether an include URI attribute is found in the request,
|
||||
* indicating an include request, and whether the response has already been committed.
|
||||
* In both cases, an include will be performed, as a forward is not possible anymore.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @return <code>true</code> for include, <code>false</code> for forward
|
||||
* @see javax.servlet.RequestDispatcher#forward
|
||||
* @see javax.servlet.RequestDispatcher#include
|
||||
* @see javax.servlet.ServletResponse#isCommitted
|
||||
* @see org.springframework.web.util.WebUtils#isIncludeRequest
|
||||
*/
|
||||
protected boolean useInclude(HttpServletRequest request, HttpServletResponse response) {
|
||||
return (WebUtils.isIncludeRequest(request) || response.isCommitted());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.Enumeration;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Spring Controller implementation that wraps a servlet instance which it manages
|
||||
* internally. Such a wrapped servlet is not known outside of this controller;
|
||||
* its entire lifecycle is covered here (in contrast to {@link ServletForwardingController}).
|
||||
*
|
||||
* <p>Useful to invoke an existing servlet via Spring's dispatching infrastructure,
|
||||
* for example to apply Spring HandlerInterceptors to its requests.
|
||||
*
|
||||
* <p>Note that Struts has a special requirement in that it parses <code>web.xml</code>
|
||||
* to find its servlet mapping. Therefore, you need to specify the DispatcherServlet's
|
||||
* servlet name as "servletName" on this controller, so that Struts finds the
|
||||
* DispatcherServlet's mapping (thinking that it refers to the ActionServlet).
|
||||
*
|
||||
* <p><b>Example:</b> a DispatcherServlet XML context, forwarding "*.do" to the Struts
|
||||
* ActionServlet wrapped by a ServletWrappingController. All such requests will go
|
||||
* through the configured HandlerInterceptor chain (e.g. an OpenSessionInViewInterceptor).
|
||||
* From the Struts point of view, everything will work as usual.
|
||||
*
|
||||
* <pre>
|
||||
* <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
|
||||
* <property name="interceptors">
|
||||
* <list>
|
||||
* <ref bean="openSessionInViewInterceptor"/>
|
||||
* </list>
|
||||
* </property>
|
||||
* <property name="mappings">
|
||||
* <props>
|
||||
* <prop key="*.do">strutsWrappingController</prop>
|
||||
* </props>
|
||||
* </property>
|
||||
* </bean>
|
||||
*
|
||||
* <bean id="strutsWrappingController" class="org.springframework.web.servlet.mvc.ServletWrappingController">
|
||||
* <property name="servletClass">
|
||||
* <value>org.apache.struts.action.ActionServlet</value>
|
||||
* </property>
|
||||
* <property name="servletName">
|
||||
* <value>action</value>
|
||||
* </property>
|
||||
* <property name="initParameters">
|
||||
* <props>
|
||||
* <prop key="config">/WEB-INF/struts-config.xml</prop>
|
||||
* </props>
|
||||
* </property>
|
||||
* </bean></pre>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.1
|
||||
* @see ServletForwardingController
|
||||
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor
|
||||
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
|
||||
* @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewInterceptor
|
||||
* @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewFilter
|
||||
*/
|
||||
public class ServletWrappingController extends AbstractController
|
||||
implements BeanNameAware, InitializingBean, DisposableBean {
|
||||
|
||||
private Class servletClass;
|
||||
|
||||
private String servletName;
|
||||
|
||||
private Properties initParameters = new Properties();
|
||||
|
||||
private String beanName;
|
||||
|
||||
private Servlet servletInstance;
|
||||
|
||||
|
||||
/**
|
||||
* Set the class of the servlet to wrap.
|
||||
* Needs to implement <code>javax.servlet.Servlet</code>.
|
||||
* @see javax.servlet.Servlet
|
||||
*/
|
||||
public void setServletClass(Class servletClass) {
|
||||
this.servletClass = servletClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the servlet to wrap.
|
||||
* Default is the bean name of this controller.
|
||||
*/
|
||||
public void setServletName(String servletName) {
|
||||
this.servletName = servletName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify init parameters for the servlet to wrap,
|
||||
* as name-value pairs.
|
||||
*/
|
||||
public void setInitParameters(Properties initParameters) {
|
||||
this.initParameters = initParameters;
|
||||
}
|
||||
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the wrapped Servlet instance.
|
||||
* @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.servletClass == null) {
|
||||
throw new IllegalArgumentException("servletClass is required");
|
||||
}
|
||||
if (!Servlet.class.isAssignableFrom(this.servletClass)) {
|
||||
throw new IllegalArgumentException("servletClass [" + this.servletClass.getName() +
|
||||
"] needs to implement interface [javax.servlet.Servlet]");
|
||||
}
|
||||
if (this.servletName == null) {
|
||||
this.servletName = this.beanName;
|
||||
}
|
||||
this.servletInstance = (Servlet) this.servletClass.newInstance();
|
||||
this.servletInstance.init(new DelegatingServletConfig());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoke the the wrapped Servlet instance.
|
||||
* @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
|
||||
*/
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
this.servletInstance.service(request, response);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destroy the wrapped Servlet instance.
|
||||
* @see javax.servlet.Servlet#destroy()
|
||||
*/
|
||||
public void destroy() {
|
||||
this.servletInstance.destroy();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal implementation of the ServletConfig interface, to be passed
|
||||
* to the wrapped servlet. Delegates to ServletWrappingController fields
|
||||
* and methods to provide init parameters and other environment info.
|
||||
*/
|
||||
private class DelegatingServletConfig implements ServletConfig {
|
||||
|
||||
public String getServletName() {
|
||||
return servletName;
|
||||
}
|
||||
|
||||
public ServletContext getServletContext() {
|
||||
return ServletWrappingController.this.getServletContext();
|
||||
}
|
||||
|
||||
public String getInitParameter(String paramName) {
|
||||
return initParameters.getProperty(paramName);
|
||||
}
|
||||
|
||||
public Enumeration getInitParameterNames() {
|
||||
return initParameters.keys();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* Adapter to use the plain {@link Controller} workflow 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 Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see org.springframework.web.servlet.DispatcherServlet
|
||||
* @see Controller
|
||||
* @see LastModified
|
||||
* @see HttpRequestHandlerAdapter
|
||||
*/
|
||||
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
|
||||
|
||||
public boolean supports(Object handler) {
|
||||
return (handler instanceof Controller);
|
||||
}
|
||||
|
||||
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
|
||||
return ((Controller) handler).handleRequest(request, response);
|
||||
}
|
||||
|
||||
public long getLastModified(HttpServletRequest request, Object handler) {
|
||||
if (handler instanceof LastModified) {
|
||||
return ((LastModified) handler).getLastModified(request);
|
||||
}
|
||||
return -1L;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
* 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 org.springframework.validation.BindException;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* <p>Concrete FormController implementation that provides configurable
|
||||
* form and success views, and an onSubmit chain for convenient overriding.
|
||||
* Automatically resubmits to the form view in case of validation errors,
|
||||
* and renders the success view in case of a valid submission.</p>
|
||||
*
|
||||
* <p>The workflow of this Controller does not differ much from the one described
|
||||
* in the {@link AbstractFormController AbstractFormController}. The difference
|
||||
* is that you do not need to implement {@link #showForm showForm} and
|
||||
* {@link #processFormSubmission processFormSubmission}: A form view and a
|
||||
* success view can be configured declaratively.</p>
|
||||
*
|
||||
* <p><b><a name="workflow">Workflow
|
||||
* (<a href="AbstractFormController.html#workflow">in addition to the superclass</a>):</b><br>
|
||||
* <ol>
|
||||
* <li>Call to {@link #processFormSubmission processFormSubmission} which inspects
|
||||
* the {@link org.springframework.validation.Errors Errors} object to see if
|
||||
* any errors have occurred during binding and validation.</li>
|
||||
* <li>If errors occured, the controller will return the configured formView,
|
||||
* showing the form again (possibly rendering according error messages).</li>
|
||||
* <li>If {@link #isFormChangeRequest isFormChangeRequest} is overridden and returns
|
||||
* true for the given request, the controller will return the formView too.
|
||||
* In that case, the controller will also suppress validation. Before returning the formView,
|
||||
* the controller will invoke {@link #onFormChange}, giving sub-classes a chance
|
||||
* to make modification to the command object.
|
||||
* This is intended for requests that change the structure of the form,
|
||||
* which should not cause validation and show the form in any case.</li>
|
||||
* <li>If no errors occurred, the controller will call
|
||||
* {@link #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException) onSubmit}
|
||||
* using all parameters, which in case of the default implementation delegates to
|
||||
* {@link #onSubmit(Object, BindException) onSubmit} with just the command object.
|
||||
* The default implementation of the latter method will return the configured
|
||||
* <code>successView</code>. Consider implementing {@link #doSubmitAction} doSubmitAction
|
||||
* for simply performing a submit action and rendering the success view.</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
*
|
||||
* <p>The submit behavior can be customized by overriding one of the
|
||||
* {@link #onSubmit onSubmit} methods. Submit actions can also perform
|
||||
* custom validation if necessary (typically database-driven checks), calling
|
||||
* {@link #showForm(HttpServletRequest, HttpServletResponse, BindException) showForm}
|
||||
* in case of validation errors to show the form view again.</p>
|
||||
*
|
||||
* <p><b><a name="config">Exposed configuration properties</a>
|
||||
* (<a href="AbstractFormController.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>formView</td>
|
||||
* <td><i>null</i></td>
|
||||
* <td>Indicates what view to use when the user asks for a new form
|
||||
* or when validation errors have occurred on form submission.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>successView</td>
|
||||
* <td><i>null</i></td>
|
||||
* <td>Indicates what view to use when successful form submissions have
|
||||
* occurred. Such a success view could e.g. display a submission summary.
|
||||
* More sophisticated actions can be implemented by overriding one of
|
||||
* the {@link #onSubmit(Object) onSubmit()} methods.</td>
|
||||
* </tr>
|
||||
* <table>
|
||||
* </p>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @since 05.05.2003
|
||||
* @deprecated as of Spring 3.0, in favor of annotated controllers
|
||||
*/
|
||||
@Deprecated
|
||||
public class SimpleFormController extends AbstractFormController {
|
||||
|
||||
private String formView;
|
||||
|
||||
private String successView;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SimpleFormController.
|
||||
* <p>Subclasses should set the following properties, either in the constructor
|
||||
* or via a BeanFactory: commandName, commandClass, sessionForm, formView,
|
||||
* successView. Note that commandClass doesn't need to be set when overriding
|
||||
* <code>formBackingObject</code>, as this determines the class anyway.
|
||||
* @see #setCommandClass
|
||||
* @see #setCommandName
|
||||
* @see #setSessionForm
|
||||
* @see #setFormView
|
||||
* @see #setSuccessView
|
||||
* @see #formBackingObject
|
||||
*/
|
||||
public SimpleFormController() {
|
||||
// AbstractFormController sets default cache seconds to 0.
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the view that should be used for form display.
|
||||
*/
|
||||
public final void setFormView(String formView) {
|
||||
this.formView = formView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the view that should be used for form display.
|
||||
*/
|
||||
public final String getFormView() {
|
||||
return this.formView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the view that should be shown on successful submit.
|
||||
*/
|
||||
public final void setSuccessView(String successView) {
|
||||
this.successView = successView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the view that should be shown on successful submit.
|
||||
*/
|
||||
public final String getSuccessView() {
|
||||
return this.successView;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This implementation shows the configured form view, delegating to the analogous
|
||||
* {@link #showForm(HttpServletRequest, HttpServletResponse, BindException, Map)}
|
||||
* variant with a "controlModel" argument.
|
||||
* <p>Can be called within
|
||||
* {@link #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)}
|
||||
* implementations, to redirect back to the form in case of custom validation errors
|
||||
* (errors not determined by the validator).
|
||||
* <p>Can be overridden in subclasses to show a custom view, writing directly
|
||||
* to the response or preparing the response before rendering a view.
|
||||
* <p>If calling showForm with a custom control model in subclasses, it's preferable
|
||||
* to override the analogous showForm version with a controlModel argument
|
||||
* (which will handle both standard form showing and custom form showing then).
|
||||
* @see #setFormView
|
||||
* @see #showForm(HttpServletRequest, HttpServletResponse, BindException, Map)
|
||||
*/
|
||||
@Override
|
||||
protected ModelAndView showForm(
|
||||
HttpServletRequest request, HttpServletResponse response, BindException errors)
|
||||
throws Exception {
|
||||
|
||||
return showForm(request, response, errors, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation shows the configured form view.
|
||||
* <p>Can be called within
|
||||
* {@link #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)}
|
||||
* implementations, to redirect back to the form in case of custom validation errors
|
||||
* (errors not determined by the validator).
|
||||
* <p>Can be overridden in subclasses to show a custom view, writing directly
|
||||
* to the response or preparing the response before rendering a view.
|
||||
* @param request current HTTP request
|
||||
* @param errors validation errors holder
|
||||
* @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
|
||||
* @see #setFormView
|
||||
*/
|
||||
protected ModelAndView showForm(
|
||||
HttpServletRequest request, HttpServletResponse response, BindException errors, Map controlModel)
|
||||
throws Exception {
|
||||
|
||||
return showForm(request, errors, getFormView(), controlModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference data map for the given request and command,
|
||||
* consisting of bean name/bean instance pairs as expected by ModelAndView.
|
||||
* <p>The default implementation delegates to {@link #referenceData(HttpServletRequest)}.
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
protected Map referenceData(HttpServletRequest request, Object command, Errors errors) throws Exception {
|
||||
return referenceData(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference data map for the given request.
|
||||
* Called by the {@link #referenceData(HttpServletRequest, Object, Errors)}
|
||||
* variant with all parameters.
|
||||
* <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
|
||||
* @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, Object, Errors)
|
||||
* @see ModelAndView
|
||||
*/
|
||||
protected Map referenceData(HttpServletRequest request) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This implementation calls
|
||||
* {@link #showForm(HttpServletRequest, HttpServletResponse, BindException)}
|
||||
* in case of errors, and delegates to the full
|
||||
* {@link #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)}'s
|
||||
* variant else.
|
||||
* <p>This can only be overridden to check for an action that should be executed
|
||||
* without respect to binding errors, like a cancel action. To just handle successful
|
||||
* submissions without binding errors, override one of the <code>onSubmit</code>
|
||||
* methods or {@link #doSubmitAction}.
|
||||
* @see #showForm(HttpServletRequest, HttpServletResponse, BindException)
|
||||
* @see #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)
|
||||
* @see #onSubmit(Object, BindException)
|
||||
* @see #onSubmit(Object)
|
||||
* @see #doSubmitAction(Object)
|
||||
*/
|
||||
@Override
|
||||
protected ModelAndView processFormSubmission(
|
||||
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
|
||||
throws Exception {
|
||||
|
||||
if (errors.hasErrors()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Data binding errors: " + errors.getErrorCount());
|
||||
}
|
||||
return showForm(request, response, errors);
|
||||
}
|
||||
else if (isFormChangeRequest(request, command)) {
|
||||
logger.debug("Detected form change request -> routing request to onFormChange");
|
||||
onFormChange(request, response, command, errors);
|
||||
return showForm(request, response, errors);
|
||||
}
|
||||
else {
|
||||
logger.debug("No errors -> processing submit");
|
||||
return onSubmit(request, response, command, errors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation delegates to {@link #isFormChangeRequest(HttpServletRequest, Object)}:
|
||||
* A form change request changes the appearance of the form and should not get
|
||||
* validated but just show the new form.
|
||||
* @see #isFormChangeRequest
|
||||
*/
|
||||
@Override
|
||||
protected boolean suppressValidation(HttpServletRequest request, Object command) {
|
||||
return isFormChangeRequest(request, command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given request is a form change request.
|
||||
* A form change request changes the appearance of the form
|
||||
* and should always show the new form, without validation.
|
||||
* <p>Gets called by {@link #suppressValidation} and {@link #processFormSubmission}.
|
||||
* Consequently, this single method determines to suppress validation
|
||||
* <i>and</i> to show the form view in any case.
|
||||
* <p>The default implementation delegates to
|
||||
* {@link #isFormChangeRequest(javax.servlet.http.HttpServletRequest)}.
|
||||
* @param request current HTTP request
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @return whether the given request is a form change request
|
||||
* @see #suppressValidation
|
||||
* @see #processFormSubmission
|
||||
*/
|
||||
protected boolean isFormChangeRequest(HttpServletRequest request, Object command) {
|
||||
return isFormChangeRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simpler <code>isFormChangeRequest</code> variant, called by the full
|
||||
* variant {@link #isFormChangeRequest(HttpServletRequest, Object)}.
|
||||
* <p>The default implementation returns <code>false</code.
|
||||
* @param request current HTTP request
|
||||
* @return whether the given request is a form change request
|
||||
* @see #suppressValidation
|
||||
* @see #processFormSubmission
|
||||
*/
|
||||
protected boolean isFormChangeRequest(HttpServletRequest request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during form submission if
|
||||
* {@link #isFormChangeRequest(javax.servlet.http.HttpServletRequest)}
|
||||
* returns <code>true</code>. Allows subclasses to implement custom logic
|
||||
* to modify the command object to directly modify data in the form.
|
||||
* <p>The default implementation delegates to
|
||||
* {@link #onFormChange(HttpServletRequest, HttpServletResponse, Object, BindException)}.
|
||||
* @param request current servlet request
|
||||
* @param response current servlet response
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @param errors validation errors holder, allowing for additional
|
||||
* custom validation
|
||||
* @throws Exception in case of errors
|
||||
* @see #isFormChangeRequest(HttpServletRequest)
|
||||
* @see #onFormChange(HttpServletRequest, HttpServletResponse, Object)
|
||||
*/
|
||||
protected void onFormChange(
|
||||
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
|
||||
throws Exception {
|
||||
|
||||
onFormChange(request, response, command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simpler <code>onFormChange</code> variant, called by the full variant
|
||||
* {@link #onFormChange(HttpServletRequest, HttpServletResponse, Object, BindException)}.
|
||||
* <p>The default implementation is empty.
|
||||
* @param request current servlet request
|
||||
* @param response current servlet response
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @throws Exception in case of errors
|
||||
* @see #onFormChange(HttpServletRequest, HttpServletResponse, Object, BindException)
|
||||
*/
|
||||
protected void onFormChange(HttpServletRequest request, HttpServletResponse response, Object command)
|
||||
throws Exception {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Submit callback with all parameters. Called in case of submit without errors
|
||||
* reported by the registered validator, or on every submit if no validator.
|
||||
* <p>The default implementation delegates to {@link #onSubmit(Object, BindException)}.
|
||||
* For simply performing a submit action and rendering the specified success
|
||||
* view, consider implementing {@link #doSubmitAction} rather than an
|
||||
* <code>onSubmit</code> variant.
|
||||
* <p>Subclasses can override this to provide custom submission handling like storing
|
||||
* the object to the database. Implementations can also perform custom validation and
|
||||
* call showForm to return to the form. Do <i>not</i> implement multiple onSubmit
|
||||
* methods: In that case, just this method will be called by the controller.
|
||||
* <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 servlet request
|
||||
* @param response current servlet response
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @param errors Errors instance 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 #onSubmit(Object, BindException)
|
||||
* @see #doSubmitAction
|
||||
* @see #showForm
|
||||
* @see org.springframework.validation.Errors
|
||||
* @see org.springframework.validation.BindException#getModel
|
||||
*/
|
||||
protected ModelAndView onSubmit(
|
||||
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
|
||||
throws Exception {
|
||||
|
||||
return onSubmit(command, errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simpler <code>onSubmit</code> variant.
|
||||
* Called by the default implementation of the
|
||||
* {@link #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)}
|
||||
* variant with all parameters.
|
||||
* <p>The default implementation calls {@link #onSubmit(Object)}, using the
|
||||
* returned ModelAndView if actually implemented in a subclass. Else, the
|
||||
* default behavior will apply: rendering the success view with the command
|
||||
* and Errors instance as model.
|
||||
* <p>Subclasses can override this to provide custom submission handling that
|
||||
* does not need request and response.
|
||||
* <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 command form object with request parameters bound onto it
|
||||
* @param errors Errors instance without errors
|
||||
* @return the prepared model and view
|
||||
* @throws Exception in case of errors
|
||||
* @see #onSubmit(HttpServletRequest, HttpServletResponse, Object, BindException)
|
||||
* @see #onSubmit(Object)
|
||||
* @see #setSuccessView
|
||||
* @see org.springframework.validation.Errors
|
||||
* @see org.springframework.validation.BindException#getModel
|
||||
*/
|
||||
protected ModelAndView onSubmit(Object command, BindException errors) throws Exception {
|
||||
ModelAndView mv = onSubmit(command);
|
||||
if (mv != null) {
|
||||
// simplest onSubmit variant implemented in custom subclass
|
||||
return mv;
|
||||
}
|
||||
else {
|
||||
// default behavior: render success view
|
||||
if (getSuccessView() == null) {
|
||||
throw new ServletException("successView isn't set");
|
||||
}
|
||||
return new ModelAndView(getSuccessView(), errors.getModel());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplest <code>onSubmit</code> variant. Called by the default implementation
|
||||
* of the {@link #onSubmit(Object, BindException)} variant.
|
||||
* <p>This implementation calls {@link #doSubmitAction(Object)} and returns
|
||||
* <code>null</code> as ModelAndView, making the calling <code>onSubmit</code>
|
||||
* method perform its default rendering of the success view.
|
||||
* <p>Subclasses can override this to provide custom submission handling
|
||||
* that just depends on the command object. It's preferable to use either
|
||||
* {@link #onSubmit(Object, BindException)} or {@link #doSubmitAction(Object)},
|
||||
* though: Use the former when you want to build your own ModelAndView; use the
|
||||
* latter when you want to perform an action and forward to the successView.
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @return the prepared model and view, or <code>null</code> for default
|
||||
* (that is, rendering the configured "successView")
|
||||
* @throws Exception in case of errors
|
||||
* @see #onSubmit(Object, BindException)
|
||||
* @see #doSubmitAction
|
||||
* @see #setSuccessView
|
||||
*/
|
||||
protected ModelAndView onSubmit(Object command) throws Exception {
|
||||
doSubmitAction(command);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for submit actions. Called by the default implementation
|
||||
* of the simplest {@link #onSubmit(Object)} variant.
|
||||
* <p><b>This is the preferred submit callback to implement if you want to
|
||||
* perform an action (like storing changes to the database) and then render
|
||||
* the success view with the command and Errors instance as model.</b>
|
||||
* You don't need to care about the success ModelAndView here.
|
||||
* @param command form object with request parameters bound onto it
|
||||
* @throws Exception in case of errors
|
||||
* @see #onSubmit(Object)
|
||||
* @see #setSuccessView
|
||||
*/
|
||||
protected void doSubmitAction(Object command) throws Exception {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.mvc;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* Simple <code>Controller</code> implementation that transforms the virtual
|
||||
* path of a URL into a view name and returns that view.
|
||||
*
|
||||
* <p>Can optionally prepend a {@link #setPrefix prefix} and/or append a
|
||||
* {@link #setSuffix suffix} to build the viewname from the URL filename.
|
||||
*
|
||||
* <p>Find below some examples:
|
||||
*
|
||||
* <ol>
|
||||
* <li><code>"/index" -> "index"</code></li>
|
||||
* <li><code>"/index.html" -> "index"</code></li>
|
||||
* <li><code>"/index.html"</code> + prefix <code>"pre_"</code> and suffix <code>"_suf" -> "pre_index_suf"</code></li>
|
||||
* <li><code>"/products/view.html" -> "products/view"</code></li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>Thanks to David Barri for suggesting prefix/suffix support!
|
||||
*
|
||||
* @author Alef Arendsen
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @see #setPrefix
|
||||
* @see #setSuffix
|
||||
*/
|
||||
public class UrlFilenameViewController extends AbstractUrlViewController {
|
||||
|
||||
private String prefix = "";
|
||||
|
||||
private String suffix = "";
|
||||
|
||||
/** Request URL path String --> view name String */
|
||||
private final Map<String, String> viewNameCache = new ConcurrentHashMap<String, String>();
|
||||
|
||||
|
||||
/**
|
||||
* Set the prefix to prepend to the request URL filename
|
||||
* to build a view name.
|
||||
*/
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = (prefix != null ? prefix : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prefix to prepend to the request URL filename.
|
||||
*/
|
||||
protected String getPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the suffix to append to the request URL filename
|
||||
* to build a view name.
|
||||
*/
|
||||
public void setSuffix(String suffix) {
|
||||
this.suffix = (suffix != null ? suffix : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the suffix to append to the request URL filename.
|
||||
*/
|
||||
protected String getSuffix() {
|
||||
return this.suffix;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns view name based on the URL filename,
|
||||
* with prefix/suffix applied when appropriate.
|
||||
* @see #extractViewNameFromUrlPath
|
||||
* @see #setPrefix
|
||||
* @see #setSuffix
|
||||
*/
|
||||
@Override
|
||||
protected String getViewNameForRequest(HttpServletRequest request) {
|
||||
String uri = extractOperableUrl(request);
|
||||
return getViewNameForUrlPath(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a URL path from the given request,
|
||||
* suitable for view name extraction.
|
||||
* @param request current HTTP request
|
||||
* @return the URL to use for view name extraction
|
||||
*/
|
||||
protected String extractOperableUrl(HttpServletRequest request) {
|
||||
String urlPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(urlPath)) {
|
||||
urlPath = getUrlPathHelper().getLookupPathForRequest(request);
|
||||
}
|
||||
return urlPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns view name based on the URL filename,
|
||||
* with prefix/suffix applied when appropriate.
|
||||
* @param uri the request URI; for example <code>"/index.html"</code>
|
||||
* @return the extracted URI filename; for example <code>"index"</code>
|
||||
* @see #extractViewNameFromUrlPath
|
||||
* @see #postProcessViewName
|
||||
*/
|
||||
protected String getViewNameForUrlPath(String uri) {
|
||||
String viewName = this.viewNameCache.get(uri);
|
||||
if (viewName == null) {
|
||||
viewName = extractViewNameFromUrlPath(uri);
|
||||
viewName = postProcessViewName(viewName);
|
||||
this.viewNameCache.put(uri, viewName);
|
||||
}
|
||||
return viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the URL filename from the given request URI.
|
||||
* @param uri the request URI; for example <code>"/index.html"</code>
|
||||
* @return the extracted URI filename; for example <code>"index"</code>
|
||||
*/
|
||||
protected String extractViewNameFromUrlPath(String uri) {
|
||||
int start = (uri.charAt(0) == '/' ? 1 : 0);
|
||||
int lastIndex = uri.lastIndexOf(".");
|
||||
int end = (lastIndex < 0 ? uri.length() : lastIndex);
|
||||
return uri.substring(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the full view name based on the given view name
|
||||
* as indicated by the URL path.
|
||||
* <p>The default implementation simply applies prefix and suffix.
|
||||
* This can be overridden, for example, to manipulate upper case
|
||||
* / lower case, etc.
|
||||
* @param viewName the original view name, as indicated by the URL path
|
||||
* @return the full view name to use
|
||||
* @see #getPrefix()
|
||||
* @see #getSuffix()
|
||||
*/
|
||||
protected String postProcessViewName(String viewName) {
|
||||
return getPrefix() + viewName + getSuffix();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.support.WebContentGenerator;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* Interceptor that checks and prepares request and response. Checks for supported
|
||||
* methods and a required session, and applies the specified number of cache seconds.
|
||||
* See superclass bean properties for configuration options.
|
||||
*
|
||||
* <p>All the settings supported by this interceptor can also be set on AbstractController.
|
||||
* This interceptor is mainly intended for applying checks and preparations to a set of
|
||||
* controllers mapped by a HandlerMapping.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 27.11.2003
|
||||
* @see AbstractController
|
||||
*/
|
||||
public class WebContentInterceptor extends WebContentGenerator implements HandlerInterceptor {
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
private Map<String, Integer> cacheMappings = new HashMap<String, Integer>();
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
|
||||
public WebContentInterceptor() {
|
||||
// no restriction of HTTP methods by default,
|
||||
// in particular for use with annotated controllers
|
||||
super(false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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".
|
||||
* <p>Only relevant for the "cacheMappings" setting.
|
||||
* @see #setCacheMappings
|
||||
* @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).
|
||||
* <p>Only relevant for the "cacheMappings" setting.
|
||||
* @see #setCacheMappings
|
||||
* @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.
|
||||
* <p>Only relevant for the "cacheMappings" setting.
|
||||
* @see #setCacheMappings
|
||||
* @see org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#setUrlPathHelper
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map specific URL paths to specific cache seconds.
|
||||
* <p>Overrides the default cache seconds setting of this interceptor.
|
||||
* Can specify "-1" to exclude a URL path from default caching.
|
||||
* <p>Supports direct matches, e.g. a registered "/test" matches "/test",
|
||||
* and a various Ant-style pattern matches, e.g. a registered "/t*" matches
|
||||
* both "/test" and "/team". For details, see the AntPathMatcher javadoc.
|
||||
* @param cacheMappings a mapping between URL paths (as keys) and
|
||||
* cache seconds (as values, need to be integer-parsable)
|
||||
* @see #setCacheSeconds
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
public void setCacheMappings(Properties cacheMappings) {
|
||||
this.cacheMappings.clear();
|
||||
Enumeration propNames = cacheMappings.propertyNames();
|
||||
while (propNames.hasMoreElements()) {
|
||||
String path = (String) propNames.nextElement();
|
||||
this.cacheMappings.put(path, Integer.valueOf(cacheMappings.getProperty(path)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PathMatcher implementation to use for matching URL paths
|
||||
* against registered URL patterns, for determining cache mappings.
|
||||
* Default is AntPathMatcher.
|
||||
* @see #setCacheMappings
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
||||
this.pathMatcher = pathMatcher;
|
||||
}
|
||||
|
||||
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws ServletException {
|
||||
|
||||
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking up cache seconds for [" + lookupPath + "]");
|
||||
}
|
||||
|
||||
Integer cacheSeconds = lookupCacheSeconds(lookupPath);
|
||||
if (cacheSeconds != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Applying " + cacheSeconds + " cache seconds to [" + lookupPath + "]");
|
||||
}
|
||||
checkAndPrepare(request, response, cacheSeconds, handler instanceof LastModified);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Applying default cache seconds to [" + lookupPath + "]");
|
||||
}
|
||||
checkAndPrepare(request, response, handler instanceof LastModified);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a cache seconds value 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.
|
||||
* @param urlPath URL the bean is mapped to
|
||||
* @return the associated cache seconds, or <code>null</code> if not found
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
protected Integer lookupCacheSeconds(String urlPath) {
|
||||
// direct match?
|
||||
Integer cacheSeconds = this.cacheMappings.get(urlPath);
|
||||
if (cacheSeconds == null) {
|
||||
// pattern match?
|
||||
for (String registeredPath : this.cacheMappings.keySet()) {
|
||||
if (this.pathMatcher.match(registeredPath, urlPath)) {
|
||||
cacheSeconds = this.cacheMappings.get(registeredPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return cacheSeconds;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.core.ExceptionDepthComparator;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link org.springframework.web.servlet.HandlerExceptionResolver} interface that handles
|
||||
* exceptions through the {@link ExceptionHandler} annotation.
|
||||
*
|
||||
* <p>This exception resolver is enabled by default in the {@link org.springframework.web.servlet.DispatcherServlet}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
|
||||
|
||||
// dummy method placeholder
|
||||
private static final Method NO_METHOD_FOUND = ClassUtils.getMethodIfAvailable(System.class, "currentTimeMillis", null);
|
||||
|
||||
private final Map<Class<?>, Map<Class<? extends Throwable>, Method>> exceptionHandlerCache =
|
||||
new ConcurrentHashMap<Class<?>, Map<Class<? extends Throwable>, Method>>();
|
||||
|
||||
private WebArgumentResolver[] customArgumentResolvers;
|
||||
|
||||
private HttpMessageConverter<?>[] messageConverters =
|
||||
new HttpMessageConverter[] {new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter(),
|
||||
new SourceHttpMessageConverter(), new XmlAwareFormHttpMessageConverter()};
|
||||
|
||||
|
||||
/**
|
||||
* Set a custom ArgumentResolvers to use for special method parameter types.
|
||||
* <p>Such a custom ArgumentResolver will kick in first, having a chance to resolve
|
||||
* an argument value before the standard argument handling kicks in.
|
||||
*/
|
||||
public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
|
||||
this.customArgumentResolvers = new WebArgumentResolver[]{argumentResolver};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more custom ArgumentResolvers to use for special method parameter types.
|
||||
* <p>Any such custom ArgumentResolver will kick in first, having a chance to resolve
|
||||
* an argument value before the standard argument handling kicks in.
|
||||
*/
|
||||
public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
|
||||
this.customArgumentResolvers = argumentResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message body converters to use.
|
||||
* <p>These converters are used to convert from and to HTTP requests and responses.
|
||||
*/
|
||||
public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) {
|
||||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected ModelAndView doResolveException(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
|
||||
if (handler != null) {
|
||||
Method handlerMethod = findBestExceptionHandlerMethod(handler, ex);
|
||||
if (handlerMethod != null) {
|
||||
ServletWebRequest webRequest = new ServletWebRequest(request, response);
|
||||
try {
|
||||
Object[] args = resolveHandlerArguments(handlerMethod, handler, webRequest, ex);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invoking request handler method: " + handlerMethod);
|
||||
}
|
||||
Object retVal = doInvokeMethod(handlerMethod, handler, args);
|
||||
return getModelAndView(handlerMethod, retVal, webRequest);
|
||||
}
|
||||
catch (Exception invocationEx) {
|
||||
logger.error("Invoking request method resulted in exception : " + handlerMethod, invocationEx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the handler method that matches the thrown exception best.
|
||||
* @param handler the handler object
|
||||
* @param thrownException the exception to be handled
|
||||
* @return the best matching method; or <code>null</code> if none is found
|
||||
*/
|
||||
private Method findBestExceptionHandlerMethod(Object handler, final Exception thrownException) {
|
||||
final Class<?> handlerType = ClassUtils.getUserClass(handler);
|
||||
final Class<? extends Throwable> thrownExceptionType = thrownException.getClass();
|
||||
Method handlerMethod = null;
|
||||
|
||||
Map<Class<? extends Throwable>, Method> handlers = exceptionHandlerCache.get(handlerType);
|
||||
|
||||
if (handlers != null) {
|
||||
handlerMethod = handlers.get(thrownExceptionType);
|
||||
if (handlerMethod != null) {
|
||||
return (handlerMethod == NO_METHOD_FOUND ? null : handlerMethod);
|
||||
}
|
||||
}
|
||||
else {
|
||||
handlers = new ConcurrentHashMap<Class<? extends Throwable>, Method>();
|
||||
exceptionHandlerCache.put(handlerType, handlers);
|
||||
}
|
||||
|
||||
final Map<Class<? extends Throwable>, Method> resolverMethods = handlers;
|
||||
|
||||
ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) {
|
||||
method = ClassUtils.getMostSpecificMethod(method, handlerType);
|
||||
List<Class<? extends Throwable>> handledExceptions = getHandledExceptions(method);
|
||||
for (Class<? extends Throwable> handledException : handledExceptions) {
|
||||
if (handledException.isAssignableFrom(thrownExceptionType)) {
|
||||
if (!resolverMethods.containsKey(handledException)) {
|
||||
resolverMethods.put(handledException, method);
|
||||
}
|
||||
else {
|
||||
Method oldMappedMethod = resolverMethods.get(handledException);
|
||||
if (!oldMappedMethod.equals(method)) {
|
||||
throw new IllegalStateException(
|
||||
"Ambiguous exception handler mapped for " + handledException + "]: {" +
|
||||
oldMappedMethod + ", " + method + "}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
handlerMethod = getBestMatchingMethod(resolverMethods, thrownException);
|
||||
handlers.put(thrownExceptionType, (handlerMethod == null ? NO_METHOD_FOUND : handlerMethod));
|
||||
return handlerMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the exception classes handled by the given method.
|
||||
* <p>The default implementation looks for exceptions in the {@linkplain ExceptionHandler#value() annotation},
|
||||
* or - if that annotation element is empty - any exceptions listed in the method parameters if the method
|
||||
* is annotated with {@code @ExceptionHandler}.
|
||||
* @param method the method
|
||||
* @return the handled exceptions
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected List<Class<? extends Throwable>> getHandledExceptions(Method method) {
|
||||
List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();
|
||||
ExceptionHandler exceptionHandler = AnnotationUtils.findAnnotation(method, ExceptionHandler.class);
|
||||
if (exceptionHandler != null) {
|
||||
if (!ObjectUtils.isEmpty(exceptionHandler.value())) {
|
||||
result.addAll(Arrays.asList(exceptionHandler.value()));
|
||||
}
|
||||
else {
|
||||
for (Class<?> param : method.getParameterTypes()) {
|
||||
if (Throwable.class.isAssignableFrom(param)) {
|
||||
result.add((Class<? extends Throwable>) param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the best matching method. Uses the {@link DepthComparator}.
|
||||
*/
|
||||
private Method getBestMatchingMethod(
|
||||
Map<Class<? extends Throwable>, Method> resolverMethods, Exception thrownException) {
|
||||
|
||||
if (!resolverMethods.isEmpty()) {
|
||||
Class<? extends Throwable> closestMatch =
|
||||
ExceptionDepthComparator.findClosestMatch(resolverMethods.keySet(), thrownException);
|
||||
return resolverMethods.get(closestMatch);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the arguments for the given method. Delegates to {@link #resolveCommonArgument}.
|
||||
*/
|
||||
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
|
||||
NativeWebRequest webRequest, Exception thrownException) throws Exception {
|
||||
|
||||
Class[] paramTypes = handlerMethod.getParameterTypes();
|
||||
Object[] args = new Object[paramTypes.length];
|
||||
Class<?> handlerType = handler.getClass();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
MethodParameter methodParam = new MethodParameter(handlerMethod, i);
|
||||
GenericTypeResolver.resolveParameterType(methodParam, handlerType);
|
||||
Class paramType = methodParam.getParameterType();
|
||||
Object argValue = resolveCommonArgument(methodParam, webRequest, thrownException);
|
||||
if (argValue != WebArgumentResolver.UNRESOLVED) {
|
||||
args[i] = argValue;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unsupported argument [" + paramType.getName() +
|
||||
"] for @ExceptionHandler method: " + handlerMethod);
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves common method arguments. Delegates to registered {@link #setCustomArgumentResolver(WebArgumentResolver)
|
||||
* argumentResolvers} first, then checking {@link #resolveStandardArgument}.
|
||||
* @param methodParameter the method parameter
|
||||
* @param webRequest the request
|
||||
* @param thrownException the exception thrown
|
||||
* @return the argument value, or {@link WebArgumentResolver#UNRESOLVED}
|
||||
*/
|
||||
protected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest,
|
||||
Exception thrownException) 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, thrownException);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves standard method arguments. The default implementation handles {@link NativeWebRequest},
|
||||
* {@link ServletRequest}, {@link ServletResponse}, {@link HttpSession}, {@link Principal},
|
||||
* {@link Locale}, request {@link InputStream}, request {@link Reader}, response {@link OutputStream},
|
||||
* response {@link Writer}, and the given {@code thrownException}.
|
||||
* @param parameterType the method parameter type
|
||||
* @param webRequest the request
|
||||
* @param thrownException the exception thrown
|
||||
* @return the argument value, or {@link WebArgumentResolver#UNRESOLVED}
|
||||
*/
|
||||
protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest,
|
||||
Exception thrownException) throws Exception {
|
||||
|
||||
if (parameterType.isInstance(thrownException)) {
|
||||
return thrownException;
|
||||
}
|
||||
else if (WebRequest.class.isAssignableFrom(parameterType)) {
|
||||
return webRequest;
|
||||
}
|
||||
|
||||
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
|
||||
|
||||
if (ServletRequest.class.isAssignableFrom(parameterType)) {
|
||||
return request;
|
||||
}
|
||||
else if (ServletResponse.class.isAssignableFrom(parameterType)) {
|
||||
return response;
|
||||
}
|
||||
else if (HttpSession.class.isAssignableFrom(parameterType)) {
|
||||
return request.getSession();
|
||||
}
|
||||
else if (Principal.class.isAssignableFrom(parameterType)) {
|
||||
return request.getUserPrincipal();
|
||||
}
|
||||
else if (Locale.class.equals(parameterType)) {
|
||||
return RequestContextUtils.getLocale(request);
|
||||
}
|
||||
else if (InputStream.class.isAssignableFrom(parameterType)) {
|
||||
return request.getInputStream();
|
||||
}
|
||||
else if (Reader.class.isAssignableFrom(parameterType)) {
|
||||
return request.getReader();
|
||||
}
|
||||
else if (OutputStream.class.isAssignableFrom(parameterType)) {
|
||||
return response.getOutputStream();
|
||||
}
|
||||
else if (Writer.class.isAssignableFrom(parameterType)) {
|
||||
return response.getWriter();
|
||||
}
|
||||
else {
|
||||
return WebArgumentResolver.UNRESOLVED;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ModelAndView getModelAndView(Method handlerMethod, Object returnValue, ServletWebRequest webRequest)
|
||||
throws Exception {
|
||||
|
||||
ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
|
||||
if (responseStatusAnn != null) {
|
||||
HttpStatus responseStatus = responseStatusAnn.value();
|
||||
String reason = responseStatusAnn.reason();
|
||||
if (!StringUtils.hasText(reason)) {
|
||||
webRequest.getResponse().setStatus(responseStatus.value());
|
||||
}
|
||||
else {
|
||||
webRequest.getResponse().sendError(responseStatus.value(), reason);
|
||||
}
|
||||
}
|
||||
|
||||
if (returnValue != null && AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
|
||||
return handleResponseBody(returnValue, webRequest);
|
||||
}
|
||||
|
||||
if (returnValue instanceof ModelAndView) {
|
||||
return (ModelAndView) returnValue;
|
||||
}
|
||||
else if (returnValue instanceof Model) {
|
||||
return new ModelAndView().addAllObjects(((Model) returnValue).asMap());
|
||||
}
|
||||
else if (returnValue instanceof Map) {
|
||||
return new ModelAndView().addAllObjects((Map) returnValue);
|
||||
}
|
||||
else if (returnValue instanceof View) {
|
||||
return new ModelAndView((View) returnValue);
|
||||
}
|
||||
else if (returnValue instanceof String) {
|
||||
return new ModelAndView((String) returnValue);
|
||||
}
|
||||
else if (returnValue == null) {
|
||||
return new ModelAndView();
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ModelAndView handleResponseBody(Object returnValue, ServletWebRequest webRequest)
|
||||
throws ServletException, IOException {
|
||||
|
||||
HttpInputMessage inputMessage = new ServletServerHttpRequest(webRequest.getRequest());
|
||||
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
|
||||
if (acceptedMediaTypes.isEmpty()) {
|
||||
acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
|
||||
}
|
||||
MediaType.sortByQualityValue(acceptedMediaTypes);
|
||||
HttpOutputMessage outputMessage = new ServletServerHttpResponse(webRequest.getResponse());
|
||||
Class<?> returnValueType = returnValue.getClass();
|
||||
if (this.messageConverters != null) {
|
||||
for (MediaType acceptedMediaType : acceptedMediaTypes) {
|
||||
for (HttpMessageConverter messageConverter : this.messageConverters) {
|
||||
if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
|
||||
messageConverter.write(returnValue, acceptedMediaType, outputMessage);
|
||||
return new ModelAndView();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not find HttpMessageConverter that supports return type [" + returnValueType + "] and " +
|
||||
acceptedMediaTypes);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.bind.UnsatisfiedServletRequestParameterException;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link org.springframework.web.servlet.HandlerMapping}
|
||||
* interface that maps handlers based on HTTP paths expressed through the
|
||||
* {@link RequestMapping} annotation at the type or method level.
|
||||
*
|
||||
* <p>Registered by default in {@link org.springframework.web.servlet.DispatcherServlet}
|
||||
* on Java 5+. <b>NOTE:</b> If you define custom HandlerMapping beans in your
|
||||
* DispatcherServlet context, you need to add a DefaultAnnotationHandlerMapping bean
|
||||
* explicitly, since custom HandlerMapping beans replace the default mapping strategies.
|
||||
* Defining a DefaultAnnotationHandlerMapping also allows for registering custom
|
||||
* interceptors:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
|
||||
* <property name="interceptors">
|
||||
* ...
|
||||
* </property>
|
||||
* </bean></pre>
|
||||
*
|
||||
* Annotated controllers are usually marked with the {@link Controller} stereotype
|
||||
* at the type level. This is not strictly necessary when {@link RequestMapping} is
|
||||
* applied at the type level (since such a handler usually implements the
|
||||
* {@link org.springframework.web.servlet.mvc.Controller} interface). However,
|
||||
* {@link Controller} is required for detecting {@link RequestMapping} annotations
|
||||
* at the method level if {@link RequestMapping} is not present at the type level.
|
||||
*
|
||||
* <p><b>NOTE:</b> Method-level mappings are only allowed to narrow the mapping
|
||||
* expressed at the class level (if any). HTTP paths need to uniquely map onto
|
||||
* specific handler beans, with any given HTTP path 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>The {@link AnnotationMethodHandlerAdapter} is responsible for processing
|
||||
* annotated handler methods, as mapped by this HandlerMapping. For
|
||||
* {@link RequestMapping} at the type level, specific HandlerAdapters such as
|
||||
* {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter} apply.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @since 2.5
|
||||
* @see RequestMapping
|
||||
* @see AnnotationMethodHandlerAdapter
|
||||
*/
|
||||
public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping {
|
||||
|
||||
private boolean useDefaultSuffixPattern = true;
|
||||
|
||||
private final Map<Class, RequestMapping> cachedMappings = new HashMap<Class, RequestMapping>();
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to register paths using the default suffix pattern as well:
|
||||
* i.e. whether "/users" should be registered as "/users.*" and "/users/" too.
|
||||
* <p>Default is "true". Turn this convention off if you intend to interpret
|
||||
* your <code>@RequestMapping</code> paths strictly.
|
||||
* <p>Note that paths which include a ".xxx" suffix or end with "/" already will not be
|
||||
* transformed using the default suffix pattern in any case.
|
||||
*/
|
||||
public void setUseDefaultSuffixPattern(boolean useDefaultSuffixPattern) {
|
||||
this.useDefaultSuffixPattern = useDefaultSuffixPattern;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks for presence of the {@link org.springframework.web.bind.annotation.RequestMapping}
|
||||
* annotation on the handler class and on any of its methods.
|
||||
*/
|
||||
@Override
|
||||
protected String[] determineUrlsForHandler(String beanName) {
|
||||
ApplicationContext context = getApplicationContext();
|
||||
Class<?> handlerType = context.getType(beanName);
|
||||
RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
|
||||
if (mapping != null) {
|
||||
// @RequestMapping found at type level
|
||||
this.cachedMappings.put(handlerType, mapping);
|
||||
Set<String> urls = new LinkedHashSet<String>();
|
||||
String[] typeLevelPatterns = mapping.value();
|
||||
if (typeLevelPatterns.length > 0) {
|
||||
// @RequestMapping specifies paths at type level
|
||||
String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);
|
||||
for (String typeLevelPattern : typeLevelPatterns) {
|
||||
if (!typeLevelPattern.startsWith("/")) {
|
||||
typeLevelPattern = "/" + typeLevelPattern;
|
||||
}
|
||||
boolean hasEmptyMethodLevelMappings = false;
|
||||
for (String methodLevelPattern : methodLevelPatterns) {
|
||||
if (methodLevelPattern == null) {
|
||||
hasEmptyMethodLevelMappings = true;
|
||||
}
|
||||
else {
|
||||
String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
|
||||
addUrlsForPath(urls, combinedPattern);
|
||||
}
|
||||
}
|
||||
if (hasEmptyMethodLevelMappings ||
|
||||
org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {
|
||||
addUrlsForPath(urls, typeLevelPattern);
|
||||
}
|
||||
}
|
||||
return StringUtils.toStringArray(urls);
|
||||
}
|
||||
else {
|
||||
// actual paths specified by @RequestMapping at method level
|
||||
return determineUrlsForHandlerMethods(handlerType, false);
|
||||
}
|
||||
}
|
||||
else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
|
||||
// @RequestMapping to be introspected at method level
|
||||
return determineUrlsForHandlerMethods(handlerType, false);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive URL mappings from the handler's method-level mappings.
|
||||
* @param handlerType the handler type to introspect
|
||||
* @param hasTypeLevelMapping whether the method-level mappings are nested
|
||||
* within a type-level mapping
|
||||
* @return the array of mapped URLs
|
||||
*/
|
||||
protected String[] determineUrlsForHandlerMethods(Class<?> handlerType, final boolean hasTypeLevelMapping) {
|
||||
String[] subclassResult = determineUrlsForHandlerMethods(handlerType);
|
||||
if (subclassResult != null) {
|
||||
return subclassResult;
|
||||
}
|
||||
|
||||
final Set<String> urls = new LinkedHashSet<String>();
|
||||
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
|
||||
handlerTypes.add(handlerType);
|
||||
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
|
||||
for (Class<?> currentHandlerType : handlerTypes) {
|
||||
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) {
|
||||
RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
|
||||
if (mapping != null) {
|
||||
String[] mappedPatterns = mapping.value();
|
||||
if (mappedPatterns.length > 0) {
|
||||
for (String mappedPattern : mappedPatterns) {
|
||||
if (!hasTypeLevelMapping && !mappedPattern.startsWith("/")) {
|
||||
mappedPattern = "/" + mappedPattern;
|
||||
}
|
||||
addUrlsForPath(urls, mappedPattern);
|
||||
}
|
||||
}
|
||||
else if (hasTypeLevelMapping) {
|
||||
// empty method-level RequestMapping
|
||||
urls.add(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, ReflectionUtils.USER_DECLARED_METHODS);
|
||||
}
|
||||
return StringUtils.toStringArray(urls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive URL mappings from the handler's method-level mappings.
|
||||
* @param handlerType the handler type to introspect
|
||||
* @return the array of mapped URLs
|
||||
*/
|
||||
protected String[] determineUrlsForHandlerMethods(Class<?> handlerType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add URLs and/or URL patterns for the given path.
|
||||
* @param urls the Set of URLs for the current bean
|
||||
* @param path the currently introspected path
|
||||
*/
|
||||
protected void addUrlsForPath(Set<String> urls, String path) {
|
||||
urls.add(path);
|
||||
if (this.useDefaultSuffixPattern && path.indexOf('.') == -1 && !path.endsWith("/")) {
|
||||
urls.add(path + ".*");
|
||||
urls.add(path + "/");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate the given annotated handler against the current request.
|
||||
* @see #validateMapping
|
||||
*/
|
||||
@Override
|
||||
protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {
|
||||
RequestMapping mapping = this.cachedMappings.get(handler.getClass());
|
||||
if (mapping == null) {
|
||||
mapping = AnnotationUtils.findAnnotation(handler.getClass(), RequestMapping.class);
|
||||
}
|
||||
if (mapping != null) {
|
||||
validateMapping(mapping, request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given type-level mapping metadata against the current request,
|
||||
* checking HTTP request method and parameter conditions.
|
||||
* @param mapping the mapping metadata to validate
|
||||
* @param request current HTTP request
|
||||
* @throws Exception if validation failed
|
||||
*/
|
||||
protected void validateMapping(RequestMapping mapping, HttpServletRequest request) throws Exception {
|
||||
RequestMethod[] mappedMethods = mapping.method();
|
||||
if (!ServletAnnotationMappingUtils.checkRequestMethod(mappedMethods, request)) {
|
||||
String[] supportedMethods = new String[mappedMethods.length];
|
||||
for (int i = 0; i < mappedMethods.length; i++) {
|
||||
supportedMethods[i] = mappedMethods[i].name();
|
||||
}
|
||||
throw new HttpRequestMethodNotSupportedException(request.getMethod(), supportedMethods);
|
||||
}
|
||||
|
||||
String[] mappedParams = mapping.params();
|
||||
if (!ServletAnnotationMappingUtils.checkParameters(mappedParams, request)) {
|
||||
throw new UnsatisfiedServletRequestParameterException(mappedParams, request.getParameterMap());
|
||||
}
|
||||
|
||||
String[] mappedHeaders = mapping.headers();
|
||||
if (!ServletAnnotationMappingUtils.checkHeaders(mappedHeaders, request)) {
|
||||
throw new ServletRequestBindingException("Header conditions \"" +
|
||||
StringUtils.arrayToDelimitedString(mappedHeaders, ", ") +
|
||||
"\" not met for actual request");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsTypeLevelMappings() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* SPI for resolving custom return values from a specific handler method. Typically implemented to detect special return
|
||||
* types, resolving well-known result values for them.
|
||||
*
|
||||
* <p>A typical implementation could look like as follows:
|
||||
*
|
||||
* <pre class="code">
|
||||
* public class MyModelAndViewResolver implements ModelAndViewResolver {
|
||||
*
|
||||
* public ModelAndView resolveModelAndView(Method handlerMethod,
|
||||
* Class handlerType,
|
||||
* Object returnValue,
|
||||
* ExtendedModelMap implicitModel,
|
||||
* NativeWebRequest webRequest) {
|
||||
* if (returnValue instanceof MySpecialRetVal.class)) {
|
||||
* return new MySpecialRetVal(returnValue);
|
||||
* }
|
||||
* return UNRESOLVED;
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#setCustomModelAndViewResolvers
|
||||
* @see org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter#setCustomModelAndViewResolvers
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface ModelAndViewResolver {
|
||||
|
||||
/** Marker to be returned when the resolver does not know how to handle the given method parameter. */
|
||||
ModelAndView UNRESOLVED = new ModelAndView();
|
||||
|
||||
ModelAndView resolveModelAndView(Method handlerMethod,
|
||||
Class handlerType,
|
||||
Object returnValue,
|
||||
ExtendedModelMap implicitModel,
|
||||
NativeWebRequest webRequest);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.annotation;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver}
|
||||
* interface that uses the {@link ResponseStatus @ResponseStatus} annotation to map exceptions to HTTP status codes.
|
||||
*
|
||||
* <p>This exception resolver is enabled by default in the {@link org.springframework.web.servlet.DispatcherServlet}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver {
|
||||
|
||||
@Override
|
||||
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception ex) {
|
||||
|
||||
ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
|
||||
if (responseStatus != null) {
|
||||
try {
|
||||
return resolveResponseStatus(responseStatus, request, response, handler, ex);
|
||||
}
|
||||
catch (Exception resolveEx) {
|
||||
logger.warn("Handling of @ResponseStatus resulted in Exception", resolveEx);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles {@link ResponseStatus @ResponseStatus} annotation. <p>Default implementation send a
|
||||
* response error using {@link HttpServletResponse#sendError(int)}, or {@link HttpServletResponse#sendError(int,
|
||||
* String)} if the annotation has a {@linkplain ResponseStatus#reason() reason}. Returns an empty ModelAndView.
|
||||
* @param responseStatus the annotation
|
||||
* @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 resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
|
||||
HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
|
||||
int statusCode = responseStatus.value().value();
|
||||
String reason = responseStatus.reason();
|
||||
if (!StringUtils.hasLength(reason)) {
|
||||
response.sendError(statusCode);
|
||||
}
|
||||
else {
|
||||
response.sendError(statusCode, reason);
|
||||
}
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.annotation;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* Helper class for annotation-based request mapping.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @since 2.5.2
|
||||
*/
|
||||
abstract class ServletAnnotationMappingUtils {
|
||||
|
||||
/**
|
||||
* Check whether the given request matches the specified request methods.
|
||||
* @param methods the HTTP request methods to check against
|
||||
* @param request the current HTTP request to check
|
||||
*/
|
||||
public static boolean checkRequestMethod(RequestMethod[] methods, HttpServletRequest request) {
|
||||
if (ObjectUtils.isEmpty(methods)) {
|
||||
return true;
|
||||
}
|
||||
for (RequestMethod method : methods) {
|
||||
if (method.name().equals(request.getMethod())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given request matches the specified parameter conditions.
|
||||
* @param params the parameter conditions, following
|
||||
* {@link org.springframework.web.bind.annotation.RequestMapping#params() RequestMapping.#params()}
|
||||
* @param request the current HTTP request to check
|
||||
*/
|
||||
public static boolean checkParameters(String[] params, HttpServletRequest request) {
|
||||
if (!ObjectUtils.isEmpty(params)) {
|
||||
for (String param : params) {
|
||||
int separator = param.indexOf('=');
|
||||
if (separator == -1) {
|
||||
if (param.startsWith("!")) {
|
||||
if (WebUtils.hasSubmitParameter(request, param.substring(1))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!WebUtils.hasSubmitParameter(request, param)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
boolean negated = separator > 0 && param.charAt(separator - 1) == '!';
|
||||
String key = !negated ? param.substring(0, separator) : param.substring(0, separator - 1);
|
||||
String value = param.substring(separator + 1);
|
||||
boolean match = value.equals(request.getParameter(key));
|
||||
if (negated) {
|
||||
match = !match;
|
||||
}
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given request matches the specified header conditions.
|
||||
* @param headers the header conditions, following
|
||||
* {@link org.springframework.web.bind.annotation.RequestMapping#headers() RequestMapping.headers()}
|
||||
* @param request the current HTTP request to check
|
||||
*/
|
||||
public static boolean checkHeaders(String[] headers, HttpServletRequest request) {
|
||||
if (!ObjectUtils.isEmpty(headers)) {
|
||||
for (String header : headers) {
|
||||
int separator = header.indexOf('=');
|
||||
if (separator == -1) {
|
||||
if (header.startsWith("!")) {
|
||||
if (request.getHeader(header.substring(1)) != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (request.getHeader(header) == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
boolean negated = (separator > 0 && header.charAt(separator - 1) == '!');
|
||||
String key = !negated ? header.substring(0, separator) : header.substring(0, separator - 1);
|
||||
String value = header.substring(separator + 1);
|
||||
if (isMediaTypeHeader(key)) {
|
||||
List<MediaType> requestMediaTypes = MediaType.parseMediaTypes(request.getHeader(key));
|
||||
List<MediaType> valueMediaTypes = MediaType.parseMediaTypes(value);
|
||||
boolean found = false;
|
||||
for (Iterator<MediaType> valIter = valueMediaTypes.iterator(); valIter.hasNext() && !found;) {
|
||||
MediaType valueMediaType = valIter.next();
|
||||
for (Iterator<MediaType> reqIter = requestMediaTypes.iterator();
|
||||
reqIter.hasNext() && !found;) {
|
||||
MediaType requestMediaType = reqIter.next();
|
||||
if (valueMediaType.includes(requestMediaType)) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (negated) {
|
||||
found = !found;
|
||||
}
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
boolean match = value.equals(request.getHeader(key));
|
||||
if (negated) {
|
||||
match = !match;
|
||||
}
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isMediaTypeHeader(String headerName) {
|
||||
return "Accept".equalsIgnoreCase(headerName) || "Content-Type".equalsIgnoreCase(headerName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Support package for annotation-based Servlet MVC controllers.
|
||||
*
|
||||
*/
|
||||
package org.springframework.web.servlet.mvc.annotation;
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.condition;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* Supports media type expressions as described in:
|
||||
* {@link RequestMapping#consumes()} and {@link RequestMapping#produces()}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
abstract class AbstractMediaTypeExpression implements Comparable<AbstractMediaTypeExpression>, MediaTypeExpression {
|
||||
|
||||
private final MediaType mediaType;
|
||||
|
||||
private final boolean isNegated;
|
||||
|
||||
AbstractMediaTypeExpression(String expression) {
|
||||
if (expression.startsWith("!")) {
|
||||
isNegated = true;
|
||||
expression = expression.substring(1);
|
||||
}
|
||||
else {
|
||||
isNegated = false;
|
||||
}
|
||||
this.mediaType = MediaType.parseMediaType(expression);
|
||||
}
|
||||
|
||||
AbstractMediaTypeExpression(MediaType mediaType, boolean negated) {
|
||||
this.mediaType = mediaType;
|
||||
isNegated = negated;
|
||||
}
|
||||
|
||||
public MediaType getMediaType() {
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
public boolean isNegated() {
|
||||
return isNegated;
|
||||
}
|
||||
|
||||
public final boolean match(HttpServletRequest request) {
|
||||
boolean match = matchMediaType(request);
|
||||
return !isNegated ? match : !match;
|
||||
}
|
||||
|
||||
protected abstract boolean matchMediaType(HttpServletRequest request);
|
||||
|
||||
public int compareTo(AbstractMediaTypeExpression other) {
|
||||
return MediaType.SPECIFICITY_COMPARATOR.compare(this.getMediaType(), other.getMediaType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && getClass().equals(obj.getClass())) {
|
||||
AbstractMediaTypeExpression other = (AbstractMediaTypeExpression) obj;
|
||||
return (this.mediaType.equals(other.mediaType)) && (this.isNegated == other.isNegated);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mediaType.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (isNegated) {
|
||||
builder.append('!');
|
||||
}
|
||||
builder.append(mediaType.toString());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.condition;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Supports "name=value" style expressions as described in:
|
||||
* {@link org.springframework.web.bind.annotation.RequestMapping#params()} and
|
||||
* {@link org.springframework.web.bind.annotation.RequestMapping#headers()}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.1
|
||||
*/
|
||||
abstract class AbstractNameValueExpression<T> implements NameValueExpression<T> {
|
||||
|
||||
protected final String name;
|
||||
|
||||
protected final T value;
|
||||
|
||||
protected final boolean isNegated;
|
||||
|
||||
AbstractNameValueExpression(String expression) {
|
||||
int separator = expression.indexOf('=');
|
||||
if (separator == -1) {
|
||||
this.isNegated = expression.startsWith("!");
|
||||
this.name = isNegated ? expression.substring(1) : expression;
|
||||
this.value = null;
|
||||
}
|
||||
else {
|
||||
this.isNegated = (separator > 0) && (expression.charAt(separator - 1) == '!');
|
||||
this.name = isNegated ? expression.substring(0, separator - 1) : expression.substring(0, separator);
|
||||
this.value = parseValue(expression.substring(separator + 1));
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public boolean isNegated() {
|
||||
return this.isNegated;
|
||||
}
|
||||
|
||||
protected abstract T parseValue(String valueExpression);
|
||||
|
||||
public final boolean match(HttpServletRequest request) {
|
||||
boolean isMatch;
|
||||
if (this.value != null) {
|
||||
isMatch = matchValue(request);
|
||||
}
|
||||
else {
|
||||
isMatch = matchName(request);
|
||||
}
|
||||
return isNegated ? !isMatch : isMatch;
|
||||
}
|
||||
|
||||
protected abstract boolean matchName(HttpServletRequest request);
|
||||
|
||||
protected abstract boolean matchValue(HttpServletRequest request);
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof AbstractNameValueExpression) {
|
||||
AbstractNameValueExpression<?> other = (AbstractNameValueExpression<?>) obj;
|
||||
return ((this.name.equalsIgnoreCase(other.name)) &&
|
||||
(this.value != null ? this.value.equals(other.value) : other.value == null) &&
|
||||
this.isNegated == other.isNegated);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name.hashCode();
|
||||
result = 31 * result + (value != null ? value.hashCode() : 0);
|
||||
result = 31 * result + (isNegated ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (value != null) {
|
||||
builder.append(name);
|
||||
if (isNegated) {
|
||||
builder.append('!');
|
||||
}
|
||||
builder.append('=');
|
||||
builder.append(value);
|
||||
}
|
||||
else {
|
||||
if (isNegated) {
|
||||
builder.append('!');
|
||||
}
|
||||
builder.append(name);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.condition;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* A base class for {@link RequestCondition} types providing implementations of
|
||||
* {@link #equals(Object)}, {@link #hashCode()}, and {@link #toString()}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractRequestCondition<T extends AbstractRequestCondition<T>> implements RequestCondition<T> {
|
||||
|
||||
/**
|
||||
* Return the discrete items a request condition is composed of.
|
||||
* For example URL patterns, HTTP request methods, param expressions, etc.
|
||||
* @return a collection of objects, never {@code null}
|
||||
*/
|
||||
protected abstract Collection<?> getContent();
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o != null && getClass().equals(o.getClass())) {
|
||||
AbstractRequestCondition<?> other = (AbstractRequestCondition<?>) o;
|
||||
return getContent().equals(other.getContent());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getContent().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("[");
|
||||
for (Iterator<?> iterator = getContent().iterator(); iterator.hasNext();) {
|
||||
Object expression = iterator.next();
|
||||
builder.append(expression.toString());
|
||||
if (iterator.hasNext()) {
|
||||
builder.append(getToStringInfix());
|
||||
}
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* The notation to use when printing discrete items of content.
|
||||
* For example " || " for URL patterns or " && " for param expressions.
|
||||
*/
|
||||
protected abstract String getToStringInfix();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition.HeaderExpression;
|
||||
|
||||
/**
|
||||
* A logical disjunction (' || ') request condition to match a request's
|
||||
* 'Content-Type' header to a list of media type expressions. Two kinds of
|
||||
* media type expressions are supported, which are described in
|
||||
* {@link RequestMapping#consumes()} and {@link RequestMapping#headers()}
|
||||
* where the header name is 'Content-Type'. Regardless of which syntax is
|
||||
* used, the semantics are the same.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class ConsumesRequestCondition extends AbstractRequestCondition<ConsumesRequestCondition> {
|
||||
|
||||
private final List<ConsumeMediaTypeExpression> expressions;
|
||||
|
||||
/**
|
||||
* Creates a new instance from 0 or more "consumes" expressions.
|
||||
* @param consumes expressions with the syntax described in
|
||||
* {@link RequestMapping#consumes()}; if 0 expressions are provided,
|
||||
* the condition will match to every request.
|
||||
*/
|
||||
public ConsumesRequestCondition(String... consumes) {
|
||||
this(consumes, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with "consumes" and "header" expressions.
|
||||
* "Header" expressions where the header name is not 'Content-Type' or have
|
||||
* no header value defined are ignored. If 0 expressions are provided in
|
||||
* total, the condition will match to every request
|
||||
* @param consumes as described in {@link RequestMapping#consumes()}
|
||||
* @param headers as described in {@link RequestMapping#headers()}
|
||||
*/
|
||||
public ConsumesRequestCondition(String[] consumes, String[] headers) {
|
||||
this(parseExpressions(consumes, headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor accepting parsed media type expressions.
|
||||
*/
|
||||
private ConsumesRequestCondition(Collection<ConsumeMediaTypeExpression> expressions) {
|
||||
this.expressions = new ArrayList<ConsumeMediaTypeExpression>(expressions);
|
||||
Collections.sort(this.expressions);
|
||||
}
|
||||
|
||||
private static Set<ConsumeMediaTypeExpression> parseExpressions(String[] consumes, String[] headers) {
|
||||
Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<ConsumeMediaTypeExpression>();
|
||||
if (headers != null) {
|
||||
for (String header : headers) {
|
||||
HeaderExpression expr = new HeaderExpression(header);
|
||||
if ("Content-Type".equalsIgnoreCase(expr.name)) {
|
||||
for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
|
||||
result.add(new ConsumeMediaTypeExpression(mediaType, expr.isNegated));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (consumes != null) {
|
||||
for (String consume : consumes) {
|
||||
result.add(new ConsumeMediaTypeExpression(consume));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contained MediaType expressions.
|
||||
*/
|
||||
public Set<MediaTypeExpression> getExpressions() {
|
||||
return new LinkedHashSet<MediaTypeExpression>(this.expressions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the media types for this condition excluding negated expressions.
|
||||
*/
|
||||
public Set<MediaType> getConsumableMediaTypes() {
|
||||
Set<MediaType> result = new LinkedHashSet<MediaType>();
|
||||
for (ConsumeMediaTypeExpression expression : this.expressions) {
|
||||
if (!expression.isNegated()) {
|
||||
result.add(expression.getMediaType());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the condition has any media type expressions.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.expressions.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<ConsumeMediaTypeExpression> getContent() {
|
||||
return this.expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " || ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "other" instance if it has any expressions; returns "this"
|
||||
* instance otherwise. Practically that means a method-level "consumes"
|
||||
* overrides a type-level "consumes" condition.
|
||||
*/
|
||||
public ConsumesRequestCondition combine(ConsumesRequestCondition other) {
|
||||
return !other.expressions.isEmpty() ? other : this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any of the contained media type expressions match the given
|
||||
* request 'Content-Type' header and returns an instance that is guaranteed
|
||||
* to contain matching expressions only. The match is performed via
|
||||
* {@link MediaType#includes(MediaType)}.
|
||||
*
|
||||
* @param request the current request
|
||||
*
|
||||
* @return the same instance if the condition contains no expressions;
|
||||
* or a new condition with matching expressions only;
|
||||
* or {@code null} if no expressions match.
|
||||
*/
|
||||
public ConsumesRequestCondition getMatchingCondition(HttpServletRequest request) {
|
||||
if (isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<ConsumeMediaTypeExpression>(expressions);
|
||||
for (Iterator<ConsumeMediaTypeExpression> iterator = result.iterator(); iterator.hasNext();) {
|
||||
ConsumeMediaTypeExpression expression = iterator.next();
|
||||
if (!expression.match(request)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
return (result.isEmpty()) ? null : new ConsumesRequestCondition(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns:
|
||||
* <ul>
|
||||
* <li>0 if the two conditions have the same number of expressions
|
||||
* <li>Less than 0 if "this" has more or more specific media type expressions
|
||||
* <li>Greater than 0 if "other" has more or more specific media type expressions
|
||||
* </ul>
|
||||
*
|
||||
* <p>It is assumed that both instances have been obtained via
|
||||
* {@link #getMatchingCondition(HttpServletRequest)} and each instance contains
|
||||
* the matching consumable media type expression only or is otherwise empty.
|
||||
*/
|
||||
public int compareTo(ConsumesRequestCondition other, HttpServletRequest request) {
|
||||
if (this.expressions.isEmpty() && other.expressions.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
else if (this.expressions.isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
else if (other.expressions.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return this.expressions.get(0).compareTo(other.expressions.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and matches a single media type expression to a request's 'Content-Type' header.
|
||||
*/
|
||||
static class ConsumeMediaTypeExpression extends AbstractMediaTypeExpression {
|
||||
|
||||
ConsumeMediaTypeExpression(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
ConsumeMediaTypeExpression(MediaType mediaType, boolean negated) {
|
||||
super(mediaType, negated);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchMediaType(HttpServletRequest request) {
|
||||
MediaType contentType = StringUtils.hasLength(request.getContentType()) ?
|
||||
MediaType.parseMediaType(request.getContentType()) :
|
||||
MediaType.APPLICATION_OCTET_STREAM ;
|
||||
return getMediaType().includes(contentType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.condition;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* A logical conjunction (' && ') request condition that matches a request against
|
||||
* a set of header expressions with syntax defined in {@link RequestMapping#headers()}.
|
||||
*
|
||||
* <p>Expressions passed to the constructor with header names 'Accept' or
|
||||
* 'Content-Type' are ignored. See {@link ConsumesRequestCondition} and
|
||||
* {@link ProducesRequestCondition} for those.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class HeadersRequestCondition extends AbstractRequestCondition<HeadersRequestCondition> {
|
||||
|
||||
private final Set<HeaderExpression> expressions;
|
||||
|
||||
/**
|
||||
* Create a new instance from the given header expressions. Expressions with
|
||||
* header names 'Accept' or 'Content-Type' are ignored. See {@link ConsumesRequestCondition}
|
||||
* and {@link ProducesRequestCondition} for those.
|
||||
* @param headers media type expressions with syntax defined in {@link RequestMapping#headers()};
|
||||
* if 0, the condition will match to every request.
|
||||
*/
|
||||
public HeadersRequestCondition(String... headers) {
|
||||
this(parseExpressions(headers));
|
||||
}
|
||||
|
||||
private HeadersRequestCondition(Collection<HeaderExpression> conditions) {
|
||||
this.expressions = Collections.unmodifiableSet(new LinkedHashSet<HeaderExpression>(conditions));
|
||||
}
|
||||
|
||||
private static Collection<HeaderExpression> parseExpressions(String... headers) {
|
||||
Set<HeaderExpression> expressions = new LinkedHashSet<HeaderExpression>();
|
||||
if (headers != null) {
|
||||
for (String header : headers) {
|
||||
HeaderExpression expr = new HeaderExpression(header);
|
||||
if ("Accept".equalsIgnoreCase(expr.name) || "Content-Type".equalsIgnoreCase(expr.name)) {
|
||||
continue;
|
||||
}
|
||||
expressions.add(expr);
|
||||
}
|
||||
}
|
||||
return expressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contained request header expressions.
|
||||
*/
|
||||
public Set<NameValueExpression<String>> getExpressions() {
|
||||
return new LinkedHashSet<NameValueExpression<String>>(this.expressions);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<HeaderExpression> getContent() {
|
||||
return this.expressions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getToStringInfix() {
|
||||
return " && ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance with the union of the header expressions
|
||||
* from "this" and the "other" instance.
|
||||
*/
|
||||
public HeadersRequestCondition combine(HeadersRequestCondition other) {
|
||||
Set<HeaderExpression> set = new LinkedHashSet<HeaderExpression>(this.expressions);
|
||||
set.addAll(other.expressions);
|
||||
return new HeadersRequestCondition(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "this" instance if the request matches all expressions;
|
||||
* or {@code null} otherwise.
|
||||
*/
|
||||
public HeadersRequestCondition getMatchingCondition(HttpServletRequest request) {
|
||||
for (HeaderExpression expression : expressions) {
|
||||
if (!expression.match(request)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns:
|
||||
* <ul>
|
||||
* <li>0 if the two conditions have the same number of header expressions
|
||||
* <li>Less than 0 if "this" instance has more header expressions
|
||||
* <li>Greater than 0 if the "other" instance has more header expressions
|
||||
* </ul>
|
||||
*
|
||||
* <p>It is assumed that both instances have been obtained via
|
||||
* {@link #getMatchingCondition(HttpServletRequest)} and each instance
|
||||
* contains the matching header expression only or is otherwise empty.
|
||||
*/
|
||||
public int compareTo(HeadersRequestCondition other, HttpServletRequest request) {
|
||||
return other.expressions.size() - this.expressions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and matches a single header expression to a request.
|
||||
*/
|
||||
static class HeaderExpression extends AbstractNameValueExpression<String> {
|
||||
|
||||
public HeaderExpression(String expression) {
|
||||
super(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseValue(String valueExpression) {
|
||||
return valueExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchName(HttpServletRequest request) {
|
||||
return request.getHeader(name) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchValue(HttpServletRequest request) {
|
||||
return value.equals(request.getHeader(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name.toLowerCase().hashCode();
|
||||
result = 31 * result + (value != null ? value.hashCode() : 0);
|
||||
result = 31 * result + (isNegated ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.condition;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* A contract for media type expressions (e.g. "text/plain", "!text/plain") as
|
||||
* defined in the {@code @RequestMapping} annotation for "consumes" and
|
||||
* "produces" conditions.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*
|
||||
* @see RequestMapping#consumes()
|
||||
* @see RequestMapping#produces()
|
||||
*/
|
||||
public interface MediaTypeExpression {
|
||||
|
||||
MediaType getMediaType();
|
||||
|
||||
boolean isNegated();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.condition;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
|
||||
/**
|
||||
* A contract for {@code "name!=value"} style expression used to specify request
|
||||
* parameters and request header conditions in {@code @RequestMapping}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*
|
||||
* @see RequestMapping#params()
|
||||
* @see RequestMapping#headers()
|
||||
*/
|
||||
public interface NameValueExpression<T> {
|
||||
|
||||
String getName();
|
||||
|
||||
T getValue();
|
||||
|
||||
boolean isNegated();
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user