Sync with 3.1.x
* 3.1.x: (61 commits) Compensate for changes in JDK 7 Introspector Avoid 'type mismatch' errors in ExtendedBeanInfo Polish ExtendedBeanInfo and tests Infer AnnotationAttributes method return types Minor fix in MVC reference doc chapter Hibernate 4.1 etc TypeDescriptor equals implementation accepts annotations in any order "setBasenames" uses varargs now (for programmatic setup; SPR-9106) @ActiveProfiles mechanism works with @ImportResource as well (SPR-8992 polishing clarified Resource's "getFilename" method to consistently return null substituteNamedParameters detects and unwraps SqlParameterValue object Replace spaces with tabs Consider security in ClassUtils#getMostSpecificMethod Adding null check for username being null. Improvements for registering custom SQL exception translators in app c SPR-7680 Adding QueryTimeoutException to the DataAccessException hiera Minor polish in WebMvcConfigurationSupport Detect overridden boolean getters in ExtendedBeanInfo Polish ExtendedBeanInfoTests ...
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -450,6 +450,14 @@ public class MediaType implements Comparable<MediaType> {
|
||||
return this.parameters.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all generic parameter values.
|
||||
* @return a read-only map, possibly empty, never <code>null</code>
|
||||
*/
|
||||
public Map<String, String> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether this {@code MediaType} includes the given media type.
|
||||
* <p>For instance, {@code text/*} includes {@code text/plain} and {@code text/html}, and {@code application/*+xml}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -25,15 +25,19 @@ import java.io.Writer;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -90,14 +94,31 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
|
||||
public HttpHeaders getHeaders() {
|
||||
if (this.headers == null) {
|
||||
this.headers = new HttpHeaders();
|
||||
for (Enumeration headerNames = this.servletRequest.getHeaderNames(); headerNames.hasMoreElements();) {
|
||||
for (Enumeration<?> headerNames = this.servletRequest.getHeaderNames(); headerNames.hasMoreElements();) {
|
||||
String headerName = (String) headerNames.nextElement();
|
||||
for (Enumeration headerValues = this.servletRequest.getHeaders(headerName);
|
||||
for (Enumeration<?> headerValues = this.servletRequest.getHeaders(headerName);
|
||||
headerValues.hasMoreElements();) {
|
||||
String headerValue = (String) headerValues.nextElement();
|
||||
this.headers.add(headerName, headerValue);
|
||||
}
|
||||
}
|
||||
// HttpServletRequest exposes some headers as properties: we should include those if not already present
|
||||
if (this.headers.getContentType() == null && this.servletRequest.getContentType() != null) {
|
||||
MediaType contentType = MediaType.parseMediaType(this.servletRequest.getContentType());
|
||||
this.headers.setContentType(contentType);
|
||||
}
|
||||
if (this.headers.getContentType() != null && this.headers.getContentType().getCharSet() == null &&
|
||||
this.servletRequest.getCharacterEncoding() != null) {
|
||||
MediaType oldContentType = this.headers.getContentType();
|
||||
Charset charSet = Charset.forName(this.servletRequest.getCharacterEncoding());
|
||||
Map<String, String> params = new HashMap<String, String>(oldContentType.getParameters());
|
||||
params.put("charset", charSet.toString());
|
||||
MediaType newContentType = new MediaType(oldContentType.getType(), oldContentType.getSubtype(), params);
|
||||
this.headers.setContentType(newContentType);
|
||||
}
|
||||
if (this.headers.getContentLength() == -1 && this.servletRequest.getContentLength() != -1) {
|
||||
this.headers.setContentLength(this.servletRequest.getContentLength());
|
||||
}
|
||||
}
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -83,6 +83,14 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
|
||||
this.servletResponse.addHeader(headerName, headerValue);
|
||||
}
|
||||
}
|
||||
// HttpServletResponse exposes some headers as properties: we should include those if not already present
|
||||
if (this.servletResponse.getContentType() == null && this.headers.getContentType() != null) {
|
||||
this.servletResponse.setContentType(this.headers.getContentType().toString());
|
||||
}
|
||||
if (this.servletResponse.getCharacterEncoding() == null && this.headers.getContentType() != null &&
|
||||
this.headers.getContentType().getCharSet() != null) {
|
||||
this.servletResponse.setCharacterEncoding(this.headers.getContentType().getCharSet().name());
|
||||
}
|
||||
this.headersWritten = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,17 +24,18 @@ import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation for mapping web requests onto specific handler classes and/or
|
||||
* handler methods. Provides consistent style between Servlet and Portlet
|
||||
* handler methods. Provides a consistent style between Servlet and Portlet
|
||||
* environments, with the semantics adapting to the concrete environment.
|
||||
*
|
||||
* <p><b>NOTE:</b> Method-level mappings are only allowed to narrow the mapping
|
||||
* expressed at the class level (if any). In the Servlet case, an HTTP path needs to
|
||||
* uniquely map onto one specific handler bean (not spread across multiple handler beans);
|
||||
* the remaining mapping parameters and conditions are effectively assertions only.
|
||||
* In the Portlet case, a portlet mode in combination with specific parameter conditions
|
||||
* needs to uniquely map onto one specific handler bean, with all conditions evaluated
|
||||
* for mapping purposes. It is strongly recommended to co-locate related handler methods
|
||||
* into the same bean and therefore keep the mappings simple and intuitive.
|
||||
* <p><b>NOTE:</b> The set of features supported for Servlets is a superset
|
||||
* of the set of features supported for Portlets. The places where this applies
|
||||
* are marked with the label "Servlet-only" in this source file. For Servlet
|
||||
* environments there are some further distinctions depending on whether an
|
||||
* application is configured with {@literal "@MVC 3.0"} or
|
||||
* {@literal "@MVC 3.1"} support classes. The places where this applies are
|
||||
* marked with {@literal "@MVC 3.1-only"} in this source file. For more
|
||||
* details see the note on the new support classes added in Spring MVC 3.1
|
||||
* further below.
|
||||
*
|
||||
* <p>Handler methods which are annotated with this annotation are allowed
|
||||
* to have very flexible signatures. They may have arguments of the following
|
||||
@@ -71,8 +72,8 @@ import java.lang.annotation.Target;
|
||||
* <li>{@link java.io.OutputStream} / {@link java.io.Writer} for generating
|
||||
* the response's content. This will be the raw OutputStream/Writer as
|
||||
* exposed by the Servlet/Portlet API.
|
||||
* <li>{@link PathVariable @PathVariable} annotated parameters for access to
|
||||
* URI template values (i.e. /hotels/{hotel}). Variable values will be
|
||||
* <li>{@link PathVariable @PathVariable} annotated parameters (Servlet-only)
|
||||
* for access to URI template values (i.e. /hotels/{hotel}). Variable values will be
|
||||
* converted to the declared method argument type. By default, the URI template
|
||||
* will match against the regular expression {@code [^\.]*} (i.e. any character
|
||||
* other than period), but this can be changed by specifying another regular
|
||||
@@ -90,30 +91,43 @@ import java.lang.annotation.Target;
|
||||
* {@link org.springframework.util.MultiValueMap MultiValueMap<String, String>}, or
|
||||
* {@link org.springframework.http.HttpHeaders HttpHeaders} method parameter to
|
||||
* gain access to all request headers.
|
||||
* <li>{@link RequestBody @RequestBody} annotated parameters for access to
|
||||
* the Servlet request HTTP contents. The request stream will be
|
||||
* converted to the declared method argument type using
|
||||
* <li>{@link RequestBody @RequestBody} annotated parameters (Servlet-only)
|
||||
* for access to the Servlet request HTTP contents. The request stream will be
|
||||
* converted to the declared method argument type using
|
||||
* {@linkplain org.springframework.http.converter.HttpMessageConverter message
|
||||
* converters}. Such parameters may optionally be annotated with {@code @Valid}.
|
||||
* <li>{@link RequestPart @RequestPart} annotated parameters for access to the content
|
||||
* converters}. Such parameters may optionally be annotated with {@code @Valid}
|
||||
* but do not support access to validation results through a
|
||||
* {@link org.springframework.validation.Errors} /
|
||||
* {@link org.springframework.validation.BindingResult} argument.
|
||||
* Instead a {@link org.springframework.web.servlet.mvc.method.annotation.MethodArgumentNotValidException}
|
||||
* exception is raised.
|
||||
* <li>{@link RequestPart @RequestPart} annotated parameters
|
||||
* (Servlet-only, {@literal @MVC 3.1-only})
|
||||
* for access to the content
|
||||
* of a part of "multipart/form-data" request. The request part stream will be
|
||||
* converted to the declared method argument type using
|
||||
* converted to the declared method argument type using
|
||||
* {@linkplain org.springframework.http.converter.HttpMessageConverter message
|
||||
* converters}. Such parameters may optionally be annotated with {@code @Valid}.
|
||||
* converters}. Such parameters may optionally be annotated with {@code @Valid}
|
||||
* but do not support access to validation results through a
|
||||
* {@link org.springframework.validation.Errors} /
|
||||
* {@link org.springframework.validation.BindingResult} argument.
|
||||
* Instead a {@link org.springframework.web.servlet.mvc.method.annotation.MethodArgumentNotValidException}
|
||||
* exception is raised.
|
||||
* <li>{@link org.springframework.http.HttpEntity HttpEntity<?>} parameters
|
||||
* for access to the Servlet request HTTP headers and contents. The request stream will be
|
||||
* converted to the entity body using
|
||||
* (Servlet-only) for access to the Servlet request HTTP headers and contents.
|
||||
* The request stream will be converted to the entity body using
|
||||
* {@linkplain org.springframework.http.converter.HttpMessageConverter message
|
||||
* converters}.
|
||||
* <li>{@link java.util.Map} / {@link org.springframework.ui.Model} /
|
||||
* {@link org.springframework.ui.ModelMap} for enriching the implicit model
|
||||
* that will be exposed to the web view.
|
||||
* <li>{@link org.springframework.web.servlet.mvc.support.RedirectAttributes}
|
||||
* to specify the exact set of attributes to use in case of a redirect
|
||||
* and also to add flash attributes (attributes stored temporarily on the
|
||||
* server-side to make them available to the request after the redirect).
|
||||
* {@code RedirectAttributes} is used instead of the implicit model if the
|
||||
* method returns a "redirect:" prefixed view name or {@code RedirectView}.
|
||||
* <li>{@link org.springframework.web.servlet.mvc.support.RedirectAttributes}
|
||||
* (Servlet-only, {@literal @MVC 3.1-only}) to specify the exact set of attributes
|
||||
* to use in case of a redirect and also to add flash attributes (attributes
|
||||
* stored temporarily on the server-side to make them available to the request
|
||||
* after the redirect). {@code RedirectAttributes} is used instead of the
|
||||
* implicit model if the method returns a "redirect:" prefixed view name or
|
||||
* {@code RedirectView}.
|
||||
* <li>Command/form objects to bind parameters to: as bean properties or fields,
|
||||
* with customizable type conversion, depending on {@link InitBinder} methods
|
||||
* and/or the HandlerAdapter configuration - see the "webBindingInitializer"
|
||||
@@ -130,9 +144,10 @@ import java.lang.annotation.Target;
|
||||
* for marking form processing as complete (triggering the cleanup of session
|
||||
* attributes that have been indicated by the {@link SessionAttributes} annotation
|
||||
* at the handler type level).
|
||||
* <li>{@link org.springframework.web.util.UriComponentsBuilder} a builder for
|
||||
* preparing a URL relative to the current request's host, port, scheme, context
|
||||
* path, and the literal part of the servlet mapping.
|
||||
* <li>{@link org.springframework.web.util.UriComponentsBuilder}
|
||||
* (Servlet-only, {@literal @MVC 3.1-only})
|
||||
* for preparing a URL relative to the current request's host, port, scheme,
|
||||
* context path, and the literal part of the servlet mapping.
|
||||
* </ul>
|
||||
*
|
||||
* <p>The following return types are supported for handler methods:
|
||||
@@ -160,15 +175,15 @@ import java.lang.annotation.Target;
|
||||
* The handler method may also programmatically enrich the model by
|
||||
* declaring a {@link org.springframework.ui.ModelMap} argument
|
||||
* (see above).
|
||||
* <li>{@link ResponseBody @ResponseBody} annotated methods for access to
|
||||
* the Servlet response HTTP contents. The return value will be converted
|
||||
* to the response stream using
|
||||
* <li>{@link ResponseBody @ResponseBody} annotated methods (Servlet-only)
|
||||
* for access to the Servlet response HTTP contents. The return value will
|
||||
* be converted to the response stream using
|
||||
* {@linkplain org.springframework.http.converter.HttpMessageConverter message
|
||||
* converters}.
|
||||
* <li>A {@link org.springframework.http.HttpEntity HttpEntity<?>} or
|
||||
* {@link org.springframework.http.ResponseEntity ResponseEntity<?>} object
|
||||
* to access to the Servlet response HTTP headers and contents. The entity body will
|
||||
* be converted to the response stream using
|
||||
* (Servlet-only) to access to the Servlet response HTTP headers and contents.
|
||||
* The entity body will be converted to the response stream using
|
||||
* {@linkplain org.springframework.http.converter.HttpMessageConverter message
|
||||
* converters}.
|
||||
* <li><code>void</code> if the method handles the response itself (by
|
||||
@@ -187,16 +202,24 @@ import java.lang.annotation.Target;
|
||||
* {@link ModelAttribute} annotated reference data accessor methods.
|
||||
* </ul>
|
||||
*
|
||||
* <p><b>NOTE: <code>@RequestMapping</code> will only be processed if a
|
||||
* corresponding <code>HandlerMapping</code> (for type level annotations)
|
||||
* and/or <code>HandlerAdapter</code> (for method level annotations) is
|
||||
* present in the dispatcher.</b> This is the case by default in both
|
||||
* <code>DispatcherServlet</code> and <code>DispatcherPortlet</code>.
|
||||
* <p><b>NOTE:</b> <code>@RequestMapping</code> will only be processed if an
|
||||
* an appropriate <code>HandlerMapping</code>-<code>HandlerAdapter</code> pair
|
||||
* is configured. This is the case by default in both the
|
||||
* <code>DispatcherServlet</code> and the <code>DispatcherPortlet</code>.
|
||||
* However, if you are defining custom <code>HandlerMappings</code> or
|
||||
* <code>HandlerAdapters</code>, then you need to make sure that a
|
||||
* corresponding custom <code>RequestMappingHandlerMethodMapping</code>
|
||||
* and/or <code>RequestMappingHandlerMethodAdapter</code> is defined as well
|
||||
* - provided that you intend to use <code>@RequestMapping</code>.
|
||||
* <code>HandlerAdapters</code>, then you need to add
|
||||
* <code>DefaultAnnotationHandlerMapping</code> and
|
||||
* <code>AnnotationMethodHandlerAdapter</code> to your configuration.</code>.
|
||||
*
|
||||
* <p><b>NOTE:</b> Spring 3.1 introduced a new set of support classes for
|
||||
* <code>@RequestMapping</code> methods in Servlet environments called
|
||||
* <code>RequestMappingHandlerMapping</code> and
|
||||
* <code>RequestMappingHandlerAdapter</code>. They are recommended for use and
|
||||
* even required to take advantage of new features in Spring MVC 3.1 (search
|
||||
* {@literal "@MVC 3.1-only"} in this source file) and going forward.
|
||||
* The new support classes are enabled by default from the MVC namespace and
|
||||
* with use of the MVC Java config (<code>@EnableWebMvc</code>) but must be
|
||||
* configured explicitly if using neither.
|
||||
*
|
||||
* <p><b>NOTE:</b> When using controller interfaces (e.g. for AOP proxying),
|
||||
* make sure to consistently put <i>all</i> your mapping annotations - such as
|
||||
@@ -234,20 +257,6 @@ public @interface RequestMapping {
|
||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||
* When used at the type level, all method-level mappings inherit
|
||||
* this primary mapping, narrowing it for a specific handler method.
|
||||
* <p>In case of Servlet-based handler methods, the method names are
|
||||
* taken into account for narrowing if no path was specified explicitly,
|
||||
* according to the specified
|
||||
* {@link org.springframework.web.servlet.mvc.multiaction.MethodNameResolver}
|
||||
* (by default an
|
||||
* {@link org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver}).
|
||||
* Note that this only applies in case of ambiguous annotation mappings
|
||||
* that do not specify a path mapping explicitly. In other words,
|
||||
* the method name is only used for narrowing among a set of matching
|
||||
* methods; it does not constitute a primary path mapping itself.
|
||||
* <p>If you have a single default method (without explicit path mapping),
|
||||
* then all requests without a more specific mapped method found will
|
||||
* be dispatched to it. If you have multiple such default methods, then
|
||||
* the method name will be taken into account for choosing between them.
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
@@ -309,7 +318,7 @@ public @interface RequestMapping {
|
||||
* and against PortletRequest properties in a Portlet 2.0 environment.
|
||||
* @see org.springframework.http.MediaType
|
||||
*/
|
||||
String[] headers() default {};
|
||||
String[] headers() default {};
|
||||
|
||||
/**
|
||||
* The consumable media types of the mapped request, narrowing the primary mapping.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -23,11 +23,11 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
@@ -44,6 +44,7 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
/**
|
||||
* Performs the actual initialization work for the root application context.
|
||||
@@ -463,11 +464,18 @@ public class ContextLoader {
|
||||
* @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
|
||||
*/
|
||||
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
|
||||
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
|
||||
determineContextInitializerClasses(servletContext);
|
||||
|
||||
if (initializerClasses.size() == 0) {
|
||||
// no ApplicationContextInitializers have been declared -> nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
|
||||
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
|
||||
|
||||
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass :
|
||||
determineContextInitializerClasses(servletContext)) {
|
||||
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
|
||||
Class<?> contextClass = applicationContext.getClass();
|
||||
Class<?> initializerContextClass =
|
||||
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
|
||||
@@ -480,6 +488,13 @@ public class ContextLoader {
|
||||
|
||||
Collections.sort(initializerInstances, new AnnotationAwareOrderComparator());
|
||||
|
||||
// eagerly attempt to initialize servlet property sources in case initializers
|
||||
// below depend on accessing context-params via the Environment API. Note that
|
||||
// depending on application context implementation, this initialization will be
|
||||
// attempted again during context refresh.
|
||||
WebApplicationContextUtils.initServletPropertySources(
|
||||
applicationContext.getEnvironment().getPropertySources(), servletContext);
|
||||
|
||||
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
|
||||
initializer.initialize(applicationContext);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -107,6 +107,28 @@ public class ServletContextResource extends AbstractFileResolvingResource implem
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation delegates to <code>ServletContext.getResourceAsStream</code>,
|
||||
* which returns <code>null</code> in case of a non-readable resource (e.g. a directory).
|
||||
* @see javax.servlet.ServletContext#getResourceAsStream(String)
|
||||
*/
|
||||
@Override
|
||||
public boolean isReadable() {
|
||||
InputStream is = this.servletContext.getResourceAsStream(this.path);
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation delegates to <code>ServletContext.getResourceAsStream</code>,
|
||||
* but throws a FileNotFoundException if no resource found.
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.web.context.support;
|
||||
|
||||
import static org.springframework.web.context.support.StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
|
||||
import static org.springframework.web.context.support.StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
@@ -32,6 +35,7 @@ import javax.servlet.http.HttpSession;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource.StubPropertySource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
@@ -247,13 +251,15 @@ public abstract class WebApplicationContextUtils {
|
||||
public static void initServletPropertySources(
|
||||
MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) {
|
||||
Assert.notNull(propertySources, "propertySources must not be null");
|
||||
if(servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)) {
|
||||
propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
|
||||
new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
|
||||
if(servletContext != null &&
|
||||
propertySources.contains(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
|
||||
propertySources.get(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
|
||||
propertySources.replace(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, new ServletContextPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
|
||||
}
|
||||
if(servletConfig != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME)) {
|
||||
propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
|
||||
new ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
|
||||
if(servletConfig != null &&
|
||||
propertySources.contains(SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
|
||||
propertySources.get(SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
|
||||
propertySources.replace(SERVLET_CONFIG_PROPERTY_SOURCE_NAME, new ServletConfigPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -179,17 +179,10 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
|
||||
}
|
||||
|
||||
private void assertIsMultipartRequest(HttpServletRequest request) {
|
||||
if (!isMultipartRequest(request)) {
|
||||
throw new MultipartException("The current request is not a multipart request.");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMultipartRequest(HttpServletRequest request) {
|
||||
if (!"post".equals(request.getMethod().toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
String contentType = request.getContentType();
|
||||
return (contentType != null && contentType.toLowerCase().startsWith("multipart/"));
|
||||
if (contentType == null || !contentType.toLowerCase().startsWith("multipart/")) {
|
||||
throw new MultipartException("The current request is not a multipart request");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMultipartFileCollection(MethodParameter parameter) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -31,16 +31,16 @@ import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
/**
|
||||
* Manages controller-specific session attributes declared via
|
||||
* {@link SessionAttributes @SessionAttributes}. Actual storage is
|
||||
* performed via {@link SessionAttributeStore}.
|
||||
*
|
||||
* <p>When a controller annotated with {@code @SessionAttributes} adds
|
||||
* attributes to its model, those attributes are checked against names and
|
||||
* types specified via {@code @SessionAttributes}. Matching model attributes
|
||||
* are saved in the HTTP session and remain there until the controller calls
|
||||
* Manages controller-specific session attributes declared via
|
||||
* {@link SessionAttributes @SessionAttributes}. Actual storage is
|
||||
* delegated to a {@link SessionAttributeStore} instance.
|
||||
*
|
||||
* <p>When a controller annotated with {@code @SessionAttributes} adds
|
||||
* attributes to its model, those attributes are checked against names and
|
||||
* types specified via {@code @SessionAttributes}. Matching model attributes
|
||||
* are saved in the HTTP session and remain there until the controller calls
|
||||
* {@link SessionStatus#setComplete()}.
|
||||
*
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
@@ -50,51 +50,53 @@ public class SessionAttributesHandler {
|
||||
|
||||
private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>();
|
||||
|
||||
private final Set<String> resolvedAttributeNames = Collections.synchronizedSet(new HashSet<String>(4));
|
||||
private final Set<String> knownAttributeNames = Collections.synchronizedSet(new HashSet<String>(4));
|
||||
|
||||
private final SessionAttributeStore sessionAttributeStore;
|
||||
|
||||
/**
|
||||
* Creates a new instance for a controller type. Session attribute names/types
|
||||
* are extracted from a type-level {@code @SessionAttributes} if found.
|
||||
* Create a new instance for a controller type. Session attribute names and
|
||||
* types are extracted from the {@code @SessionAttributes} annotation, if
|
||||
* present, on the given type.
|
||||
* @param handlerType the controller type
|
||||
* @param sessionAttributeStore used for session access
|
||||
*/
|
||||
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
|
||||
Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null.");
|
||||
this.sessionAttributeStore = sessionAttributeStore;
|
||||
|
||||
|
||||
SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
|
||||
if (annotation != null) {
|
||||
this.attributeNames.addAll(Arrays.asList(annotation.value()));
|
||||
this.attributeNames.addAll(Arrays.asList(annotation.value()));
|
||||
this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types()));
|
||||
}
|
||||
}
|
||||
|
||||
this.knownAttributeNames.addAll(this.attributeNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the controller represented by this instance has declared session
|
||||
* attribute names or types of interest via {@link SessionAttributes}.
|
||||
* Whether the controller represented by this instance has declared any
|
||||
* session attributes through an {@link SessionAttributes} annotation.
|
||||
*/
|
||||
public boolean hasSessionAttributes() {
|
||||
return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0));
|
||||
return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether the attribute name and/or type match those specified in the
|
||||
* controller's {@code @SessionAttributes} annotation.
|
||||
*
|
||||
* Whether the attribute name or type match the names and types specified
|
||||
* via {@code @SessionAttributes} in underlying controller.
|
||||
*
|
||||
* <p>Attributes successfully resolved through this method are "remembered"
|
||||
* and used in {@link #retrieveAttributes(WebRequest)} and
|
||||
* {@link #cleanupAttributes(WebRequest)}. In other words, retrieval and
|
||||
* cleanup only affect attributes previously resolved through here.
|
||||
*
|
||||
* @param attributeName the attribute name to check; must not be null
|
||||
* @param attributeType the type for the attribute; or {@code null}
|
||||
* and subsequently used in {@link #retrieveAttributes(WebRequest)} and
|
||||
* {@link #cleanupAttributes(WebRequest)}.
|
||||
*
|
||||
* @param attributeName the attribute name to check, never {@code null}
|
||||
* @param attributeType the type for the attribute, possibly {@code null}
|
||||
*/
|
||||
public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
|
||||
Assert.notNull(attributeName, "Attribute name must not be null");
|
||||
if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
|
||||
this.resolvedAttributeNames.add(attributeName);
|
||||
this.knownAttributeNames.add(attributeName);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
@@ -103,8 +105,8 @@ public class SessionAttributesHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a subset of the given attributes in the session. Attributes not
|
||||
* declared as session attributes via {@code @SessionAttributes} are ignored.
|
||||
* Store a subset of the given attributes in the session. Attributes not
|
||||
* declared as session attributes via {@code @SessionAttributes} are ignored.
|
||||
* @param request the current request
|
||||
* @param attributes candidate attributes for session storage
|
||||
*/
|
||||
@@ -112,23 +114,23 @@ public class SessionAttributesHandler {
|
||||
for (String name : attributes.keySet()) {
|
||||
Object value = attributes.get(name);
|
||||
Class<?> attrType = (value != null) ? value.getClass() : null;
|
||||
|
||||
|
||||
if (isHandlerSessionAttribute(name, attrType)) {
|
||||
this.sessionAttributeStore.storeAttribute(request, name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve "known" attributes from the session -- i.e. attributes listed
|
||||
* in {@code @SessionAttributes} and previously stored in the in the model
|
||||
* at least once.
|
||||
* Retrieve "known" attributes from the session, i.e. attributes listed
|
||||
* by name in {@code @SessionAttributes} or attributes previously stored
|
||||
* in the model that matched by type.
|
||||
* @param request the current request
|
||||
* @return a map with handler session attributes; possibly empty.
|
||||
* @return a map with handler session attributes, possibly empty
|
||||
*/
|
||||
public Map<String, Object> retrieveAttributes(WebRequest request) {
|
||||
Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
for (String name : this.resolvedAttributeNames) {
|
||||
for (String name : this.knownAttributeNames) {
|
||||
Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
|
||||
if (value != null) {
|
||||
attributes.put(name, value);
|
||||
@@ -138,13 +140,13 @@ public class SessionAttributesHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans "known" attributes from the session - i.e. attributes listed
|
||||
* in {@code @SessionAttributes} and previously stored in the in the model
|
||||
* at least once.
|
||||
* Remove "known" attributes from the session, i.e. attributes listed
|
||||
* by name in {@code @SessionAttributes} or attributes previously stored
|
||||
* in the model that matched by type.
|
||||
* @param request the current request
|
||||
*/
|
||||
public void cleanupAttributes(WebRequest request) {
|
||||
for (String attributeName : this.resolvedAttributeNames) {
|
||||
for (String attributeName : this.knownAttributeNames) {
|
||||
this.sessionAttributeStore.cleanupAttribute(request, attributeName);
|
||||
}
|
||||
}
|
||||
@@ -158,5 +160,5 @@ public class SessionAttributesHandler {
|
||||
Object retrieveAttribute(WebRequest request, String attributeName) {
|
||||
return this.sessionAttributeStore.retrieveAttribute(request, attributeName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -286,7 +286,7 @@ public final class UriComponents {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Assert.hasLength(encoding, "'encoding' must not be empty");
|
||||
|
||||
byte[] bytes = encodeBytes(source.getBytes(encoding), type);
|
||||
@@ -406,7 +406,7 @@ public final class UriComponents {
|
||||
|
||||
private UriComponents expandInternal(UriTemplateVariables uriVariables) {
|
||||
Assert.state(!encoded, "Cannot expand an already encoded UriComponents object");
|
||||
|
||||
|
||||
String expandedScheme = expandUriComponent(this.scheme, uriVariables);
|
||||
String expandedUserInfo = expandUriComponent(this.userInfo, uriVariables);
|
||||
String expandedHost = expandUriComponent(this.host, uriVariables);
|
||||
@@ -458,6 +458,16 @@ public final class UriComponents {
|
||||
return variableValue != null ? variableValue.toString() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the path removing sequences like "path/..".
|
||||
* @see StringUtils#cleanPath(String)
|
||||
*/
|
||||
public UriComponents normalize() {
|
||||
String normalizedPath = StringUtils.cleanPath(getPath());
|
||||
return new UriComponents(scheme, userInfo, host, this.port, new FullPathComponent(normalizedPath),
|
||||
queryParams, fragment, encoded, false);
|
||||
}
|
||||
|
||||
// other functionality
|
||||
|
||||
/**
|
||||
@@ -930,7 +940,7 @@ public final class UriComponents {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Represents an empty path.
|
||||
@@ -992,6 +1002,9 @@ public final class UriComponents {
|
||||
}
|
||||
|
||||
public Object getValue(String name) {
|
||||
if (!this.uriVariables.containsKey(name)) {
|
||||
throw new IllegalArgumentException("Map has no value for '" + name + "'");
|
||||
}
|
||||
return this.uriVariables.get(name);
|
||||
}
|
||||
}
|
||||
@@ -1000,6 +1013,7 @@ public final class UriComponents {
|
||||
* URI template variables backed by a variable argument array.
|
||||
*/
|
||||
private static class VarArgsTemplateVariables implements UriTemplateVariables {
|
||||
|
||||
private final Iterator<Object> valueIterator;
|
||||
|
||||
public VarArgsTemplateVariables(Object... uriVariableValues) {
|
||||
@@ -1008,7 +1022,7 @@ public final class UriComponents {
|
||||
|
||||
public Object getValue(String name) {
|
||||
if (!valueIterator.hasNext()) {
|
||||
throw new IllegalArgumentException("Not enough variable values available to expand [" + name + "]");
|
||||
throw new IllegalArgumentException("Not enough variable values available to expand '" + name + "'");
|
||||
}
|
||||
return valueIterator.next();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -18,7 +18,7 @@ package org.springframework.web.util;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -80,7 +80,7 @@ public class UriComponentsBuilder {
|
||||
"^" + HTTP_PATTERN + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN + ")?" + ")?" +
|
||||
PATH_PATTERN + "(\\?" + LAST_PATTERN + ")?");
|
||||
|
||||
|
||||
|
||||
private String scheme;
|
||||
|
||||
private String userInfo;
|
||||
@@ -223,10 +223,10 @@ public class UriComponentsBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@code UriComponents} instance and replaces URI template variables
|
||||
* with the values from a map. This is a shortcut method, which combines
|
||||
* calls to {@link #build()} and then {@link UriComponents#expand(Map)}.
|
||||
*
|
||||
* Builds a {@code UriComponents} instance and replaces URI template variables
|
||||
* with the values from a map. This is a shortcut method, which combines
|
||||
* calls to {@link #build()} and then {@link UriComponents#expand(Map)}.
|
||||
*
|
||||
* @param uriVariables the map of URI variables
|
||||
* @return the URI components with expanded values
|
||||
*/
|
||||
@@ -235,17 +235,17 @@ public class UriComponentsBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@code UriComponents} instance and replaces URI template variables
|
||||
* with the values from an array. This is a shortcut method, which combines
|
||||
* calls to {@link #build()} and then {@link UriComponents#expand(Object...)}.
|
||||
*
|
||||
* Builds a {@code UriComponents} instance and replaces URI template variables
|
||||
* with the values from an array. This is a shortcut method, which combines
|
||||
* calls to {@link #build()} and then {@link UriComponents#expand(Object...)}.
|
||||
*
|
||||
* @param uriVariableValues URI variable values
|
||||
* @return the URI components with expanded values
|
||||
*/
|
||||
public UriComponents buildAndExpand(Object... uriVariableValues) {
|
||||
return build(false).expand(uriVariableValues);
|
||||
}
|
||||
|
||||
|
||||
// URI components methods
|
||||
|
||||
/**
|
||||
@@ -347,8 +347,8 @@ public class UriComponentsBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path of this builder overriding all existing path and path segment values.
|
||||
*
|
||||
* Sets the path of this builder overriding all existing path and path segment values.
|
||||
*
|
||||
* @param path the URI path; a {@code null} value results in an empty path.
|
||||
* @return this UriComponentsBuilder
|
||||
*/
|
||||
@@ -394,7 +394,7 @@ public class UriComponentsBuilder {
|
||||
|
||||
/**
|
||||
* Sets the query of this builder overriding all existing query parameters.
|
||||
*
|
||||
*
|
||||
* @param query the query string; a {@code null} value removes all query parameters.
|
||||
* @return this UriComponentsBuilder
|
||||
*/
|
||||
@@ -430,7 +430,7 @@ public class UriComponentsBuilder {
|
||||
/**
|
||||
* Sets the query parameter values overriding all existing query values for the same parameter.
|
||||
* If no values are given, the query parameter is removed.
|
||||
*
|
||||
*
|
||||
* @param name the query parameter name
|
||||
* @param values the query parameter values
|
||||
* @return this UriComponentsBuilder
|
||||
@@ -443,7 +443,7 @@ public class UriComponentsBuilder {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the URI fragment. The given fragment may contain URI template variables, and may also be {@code null} to clear
|
||||
* the fragment of this builder.
|
||||
@@ -509,7 +509,17 @@ public class UriComponentsBuilder {
|
||||
private final List<String> pathSegments = new ArrayList<String>();
|
||||
|
||||
private PathSegmentComponentBuilder(String... pathSegments) {
|
||||
Collections.addAll(this.pathSegments, pathSegments);
|
||||
this.pathSegments.addAll(removeEmptyPathSegments(pathSegments));
|
||||
}
|
||||
|
||||
private Collection<String> removeEmptyPathSegments(String... pathSegments) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
for (String segment : pathSegments) {
|
||||
if (StringUtils.hasText(segment)) {
|
||||
result.add(segment);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public UriComponents.PathComponent build() {
|
||||
@@ -523,7 +533,7 @@ public class UriComponentsBuilder {
|
||||
}
|
||||
|
||||
public PathComponentBuilder appendPathSegments(String... pathSegments) {
|
||||
Collections.addAll(this.pathSegments, pathSegments);
|
||||
this.pathSegments.addAll(removeEmptyPathSegments(pathSegments));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user