Merge branch '3.2.x' into master
Conflicts: gradle.properties spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java spring-core/src/main/java/org/springframework/core/convert/support/StringToEnumConverterFactory.java spring-core/src/main/java/org/springframework/core/env/ReadOnlySystemAttributesMap.java spring-jdbc/src/main/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxy.java spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/AbstractLobHandler.java spring-web/src/main/java/org/springframework/http/client/BufferingClientHttpRequestWrapper.java spring-web/src/main/java/org/springframework/http/client/SimpleBufferingClientHttpRequest.java spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -26,6 +26,7 @@ import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -39,11 +40,13 @@ 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.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
import org.springframework.web.context.ConfigurableWebEnvironment;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
@@ -638,7 +641,10 @@ public abstract class FrameworkServlet extends HttpServletBean {
|
||||
// the context is refreshed; do it eagerly here to ensure servlet property sources
|
||||
// are in place for use in any post-processing or initialization that occurs
|
||||
// below prior to #refresh
|
||||
wac.getEnvironment().initPropertySources(getServletContext(), getServletConfig());
|
||||
ConfigurableEnvironment env = wac.getEnvironment();
|
||||
if (env instanceof ConfigurableWebEnvironment) {
|
||||
((ConfigurableWebEnvironment)env).initPropertySources(getServletContext(), getServletConfig());
|
||||
}
|
||||
|
||||
postProcessWebApplicationContext(wac);
|
||||
|
||||
@@ -861,10 +867,18 @@ public abstract class FrameworkServlet extends HttpServletBean {
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.doOptions(request, response);
|
||||
String allowedMethods = response.getHeader("Allow");
|
||||
allowedMethods += ", " + RequestMethod.PATCH.name();
|
||||
response.setHeader("Allow", allowedMethods);
|
||||
|
||||
// Use response wrapper for Servlet 2.5 compatibility where
|
||||
// the getHeader() method does not exist
|
||||
super.doOptions(request, new HttpServletResponseWrapper(response) {
|
||||
@Override
|
||||
public void setHeader(String name, String value) {
|
||||
if("Allow".equals(name)) {
|
||||
value = (StringUtils.hasLength(value) ? value + ", " : "") + RequestMethod.PATCH.name();
|
||||
}
|
||||
super.setHeader(name, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -27,7 +27,6 @@ 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;
|
||||
@@ -35,6 +34,7 @@ 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.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.EnvironmentCapable;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -42,9 +42,8 @@ import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.ConfigurableWebEnvironment;
|
||||
import org.springframework.web.context.support.StandardServletEnvironment;
|
||||
import org.springframework.web.context.support.ServletContextResourceLoader;
|
||||
import org.springframework.web.context.support.StandardServletEnvironment;
|
||||
|
||||
/**
|
||||
* Simple extension of {@link javax.servlet.http.HttpServlet} which treats
|
||||
@@ -91,7 +90,7 @@ public abstract class HttpServletBean extends HttpServlet
|
||||
*/
|
||||
private final Set<String> requiredProperties = new HashSet<String>();
|
||||
|
||||
private ConfigurableWebEnvironment environment;
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
|
||||
/**
|
||||
@@ -187,11 +186,11 @@ public abstract class HttpServletBean extends HttpServlet
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException if environment is not assignable to
|
||||
* {@code ConfigurableWebEnvironment}.
|
||||
* {@code ConfigurableEnvironment}.
|
||||
*/
|
||||
public void setEnvironment(Environment environment) {
|
||||
Assert.isInstanceOf(ConfigurableWebEnvironment.class, environment);
|
||||
this.environment = (ConfigurableWebEnvironment)environment;
|
||||
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
|
||||
this.environment = (ConfigurableEnvironment) environment;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,7 +198,7 @@ public abstract class HttpServletBean extends HttpServlet
|
||||
* <p>If {@code null}, a new environment will be initialized via
|
||||
* {@link #createEnvironment()}.
|
||||
*/
|
||||
public ConfigurableWebEnvironment getEnvironment() {
|
||||
public ConfigurableEnvironment getEnvironment() {
|
||||
if (this.environment == null) {
|
||||
this.environment = this.createEnvironment();
|
||||
}
|
||||
@@ -210,7 +209,7 @@ public abstract class HttpServletBean extends HttpServlet
|
||||
* Create and return a new {@link StandardServletEnvironment}. Subclasses may override
|
||||
* in order to configure the environment or specialize the environment type returned.
|
||||
*/
|
||||
protected ConfigurableWebEnvironment createEnvironment() {
|
||||
protected ConfigurableEnvironment createEnvironment() {
|
||||
return new StandardServletEnvironment();
|
||||
}
|
||||
|
||||
|
||||
@@ -154,7 +154,6 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
handlerMappingDef.setSource(source);
|
||||
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
handlerMappingDef.getPropertyValues().add("order", 0);
|
||||
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", false);
|
||||
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
|
||||
String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -29,7 +29,7 @@ import org.springframework.web.context.request.async.DeferredResult;
|
||||
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor;
|
||||
|
||||
/**
|
||||
* Helps with configuring a options for asynchronous request processing.
|
||||
* Helps with configuring options for asynchronous request processing.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.2
|
||||
@@ -50,9 +50,9 @@ public class AsyncSupportConfigurer {
|
||||
/**
|
||||
* Set the default {@link AsyncTaskExecutor} to use when a controller method
|
||||
* returns a {@link Callable}. Controller methods can override this default on
|
||||
* a per-request basis by returning an {@link WebAsyncTask}.
|
||||
* a per-request basis by returning a {@link WebAsyncTask}.
|
||||
*
|
||||
* <p>By default a {@link SimpleAsyncTaskExecutor} instance is used and it's
|
||||
* <p>By default a {@link SimpleAsyncTaskExecutor} instance is used, and it's
|
||||
* highly recommended to change that default in production since the simple
|
||||
* executor does not re-use threads.
|
||||
*
|
||||
@@ -79,7 +79,7 @@ public class AsyncSupportConfigurer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure lifecycle intercepters with callbacks around concurrent request
|
||||
* Configure lifecycle interceptors with callbacks around concurrent request
|
||||
* execution that starts when a controller returns a
|
||||
* {@link java.util.concurrent.Callable}.
|
||||
*
|
||||
@@ -92,7 +92,7 @@ public class AsyncSupportConfigurer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure lifecycle intercepters with callbacks around concurrent request
|
||||
* Configure lifecycle interceptors with callbacks around concurrent request
|
||||
* execution that starts when a controller returns a {@link DeferredResult}.
|
||||
*
|
||||
* @param interceptors the interceptors to register
|
||||
|
||||
@@ -95,4 +95,4 @@ public class DefaultServletHandlerConfigurer {
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,4 +108,4 @@ public class ResourceHandlerRegistry {
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,4 +74,4 @@ public class ViewControllerRegistry {
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,6 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
|
||||
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
|
||||
handlerMapping.setOrder(0);
|
||||
handlerMapping.setRemoveSemicolonContent(false);
|
||||
handlerMapping.setInterceptors(getInterceptors());
|
||||
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
|
||||
return handlerMapping;
|
||||
|
||||
@@ -128,4 +128,4 @@ public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
|
||||
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
|
||||
/**
|
||||
* Set if ";" (semicolon) content should be stripped from the request URI.
|
||||
* <p>The default value is {@code false}.
|
||||
* @see org.springframework.web.util.UrlPathHelper#setRemoveSemicolonContent(boolean)
|
||||
*/
|
||||
public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link HandlerMapping} implementations that define a
|
||||
@@ -61,6 +62,12 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();
|
||||
|
||||
|
||||
public AbstractHandlerMethodMapping() {
|
||||
UrlPathHelper pathHelper = new UrlPathHelper();
|
||||
pathHelper.setRemoveSemicolonContent(false);
|
||||
setUrlPathHelper(pathHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to detect handler methods in beans in ancestor ApplicationContexts.
|
||||
* <p>Default is "false": Only beans in the current ApplicationContext are
|
||||
@@ -165,25 +172,17 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
* 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 newHandlerMethod = createHandlerMethod(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 '"
|
||||
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
|
||||
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean()
|
||||
+ "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '"
|
||||
+ oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
|
||||
}
|
||||
|
||||
this.handlerMethods.put(mapping, handlerMethod);
|
||||
this.handlerMethods.put(mapping, newHandlerMethod);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
|
||||
logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
|
||||
}
|
||||
|
||||
Set<String> patterns = getMappingPathPatterns(mapping);
|
||||
@@ -194,6 +193,24 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the HandlerMethod instance.
|
||||
* @param handler either a bean name or an actual handler instance
|
||||
* @param method the target method
|
||||
* @return the created HandlerMethod
|
||||
*/
|
||||
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
|
||||
HandlerMethod handlerMethod;
|
||||
if (handler instanceof String) {
|
||||
String beanName = (String) handler;
|
||||
handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method);
|
||||
}
|
||||
else {
|
||||
handlerMethod = new HandlerMethod(handler, method);
|
||||
}
|
||||
return handlerMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and return the URL paths contained in a mapping.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -19,6 +19,9 @@ package org.springframework.web.servlet.mvc.annotation;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
@@ -32,9 +35,17 @@ import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
|
||||
* <p>This exception resolver is enabled by default in the {@link org.springframework.web.servlet.DispatcherServlet}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver {
|
||||
public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {
|
||||
|
||||
private MessageSource messageSource;
|
||||
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
|
||||
@@ -69,6 +80,9 @@ public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionRes
|
||||
|
||||
int statusCode = responseStatus.value().value();
|
||||
String reason = responseStatus.reason();
|
||||
if (this.messageSource != null) {
|
||||
reason = this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale());
|
||||
}
|
||||
if (!StringUtils.hasLength(reason)) {
|
||||
response.sendError(statusCode);
|
||||
}
|
||||
|
||||
@@ -107,4 +107,4 @@ abstract class AbstractMediaTypeExpression implements Comparable<AbstractMediaTy
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +36,4 @@ public interface MediaTypeExpression {
|
||||
|
||||
boolean isNegated();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,4 +37,4 @@ public interface NameValueExpression<T> {
|
||||
|
||||
boolean isNegated();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,16 @@ import org.springframework.web.util.UrlPathHelper;
|
||||
*/
|
||||
public final class PatternsRequestCondition extends AbstractRequestCondition<PatternsRequestCondition> {
|
||||
|
||||
private static UrlPathHelper pathHelperNoSemicolonContent;
|
||||
|
||||
static {
|
||||
pathHelperNoSemicolonContent = new UrlPathHelper();
|
||||
pathHelperNoSemicolonContent.setRemoveSemicolonContent(true);
|
||||
}
|
||||
|
||||
private final Set<String> patterns;
|
||||
|
||||
private final UrlPathHelper urlPathHelper;
|
||||
private final UrlPathHelper pathHelper;
|
||||
|
||||
private final PathMatcher pathMatcher;
|
||||
|
||||
@@ -105,7 +112,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
List<String> fileExtensions) {
|
||||
|
||||
this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
|
||||
this.urlPathHelper = urlPathHelper != null ? urlPathHelper : new UrlPathHelper();
|
||||
this.pathHelper = urlPathHelper != null ? urlPathHelper : new UrlPathHelper();
|
||||
this.pathMatcher = pathMatcher != null ? pathMatcher : new AntPathMatcher();
|
||||
this.useSuffixPatternMatch = useSuffixPatternMatch;
|
||||
this.useTrailingSlashMatch = useTrailingSlashMatch;
|
||||
@@ -179,7 +186,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
else {
|
||||
result.add("");
|
||||
}
|
||||
return new PatternsRequestCondition(result, this.urlPathHelper, this.pathMatcher, this.useSuffixPatternMatch,
|
||||
return new PatternsRequestCondition(result, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
|
||||
this.useTrailingSlashMatch, this.fileExtensions);
|
||||
}
|
||||
|
||||
@@ -206,17 +213,24 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
if (this.patterns.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
|
||||
|
||||
String lookupPath = this.pathHelper.getLookupPathForRequest(request);
|
||||
String lookupPathNoSemicolonContent = (lookupPath.indexOf(';') != -1) ?
|
||||
pathHelperNoSemicolonContent.getLookupPathForRequest(request) : null;
|
||||
|
||||
List<String> matches = new ArrayList<String>();
|
||||
for (String pattern : patterns) {
|
||||
String match = getMatchingPattern(pattern, lookupPath);
|
||||
if (match == null && lookupPathNoSemicolonContent != null) {
|
||||
match = getMatchingPattern(pattern, lookupPathNoSemicolonContent);
|
||||
}
|
||||
if (match != null) {
|
||||
matches.add(match);
|
||||
}
|
||||
}
|
||||
Collections.sort(matches, this.pathMatcher.getPatternComparator(lookupPath));
|
||||
return matches.isEmpty() ? null :
|
||||
new PatternsRequestCondition(matches, this.urlPathHelper, this.pathMatcher, this.useSuffixPatternMatch,
|
||||
new PatternsRequestCondition(matches, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch,
|
||||
this.useTrailingSlashMatch, this.fileExtensions);
|
||||
}
|
||||
|
||||
@@ -225,7 +239,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
return pattern;
|
||||
}
|
||||
if (this.useSuffixPatternMatch) {
|
||||
if (useSmartSuffixPatternMatch(pattern, lookupPath)) {
|
||||
if (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) {
|
||||
for (String extension : this.fileExtensions) {
|
||||
if (this.pathMatcher.match(pattern + extension, lookupPath)) {
|
||||
return pattern + extension;
|
||||
@@ -251,14 +265,6 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to match by known file extensions. Return "true" if file extensions
|
||||
* are configured, and the lookup path has a suffix.
|
||||
*/
|
||||
private boolean useSmartSuffixPatternMatch(String pattern, String lookupPath) {
|
||||
return (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the two conditions based on the URL patterns they contain.
|
||||
* Patterns are compared one at a time, from top to bottom via
|
||||
@@ -272,7 +278,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
* the best matches on top.
|
||||
*/
|
||||
public int compareTo(PatternsRequestCondition other, HttpServletRequest request) {
|
||||
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
|
||||
String lookupPath = this.pathHelper.getLookupPathForRequest(request);
|
||||
Comparator<String> patternComparator = this.pathMatcher.getPatternComparator(lookupPath);
|
||||
|
||||
Iterator<String> iterator = patterns.iterator();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -28,15 +28,19 @@ import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.UnsatisfiedServletRequestParameterException;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
|
||||
import org.springframework.web.servlet.mvc.condition.NameValueExpression;
|
||||
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
@@ -100,7 +104,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
|
||||
|
||||
if (isMatrixVariableContentAvailable()) {
|
||||
request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, extractMatrixVariables(uriVariables));
|
||||
request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, extractMatrixVariables(request, uriVariables));
|
||||
}
|
||||
|
||||
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
|
||||
@@ -113,7 +117,9 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
return !getUrlPathHelper().shouldRemoveSemicolonContent();
|
||||
}
|
||||
|
||||
private Map<String, MultiValueMap<String, String>> extractMatrixVariables(Map<String, String> uriVariables) {
|
||||
private Map<String, MultiValueMap<String, String>> extractMatrixVariables(
|
||||
HttpServletRequest request, Map<String, String> uriVariables) {
|
||||
|
||||
Map<String, MultiValueMap<String, String>> result = new LinkedHashMap<String, MultiValueMap<String, String>>();
|
||||
for (Entry<String, String> uriVar : uriVariables.entrySet()) {
|
||||
String uriVarValue = uriVar.getValue();
|
||||
@@ -134,7 +140,8 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
uriVariables.put(uriVar.getKey(), uriVarValue.substring(0, semicolonIndex));
|
||||
}
|
||||
|
||||
result.put(uriVar.getKey(), WebUtils.parseMatrixVariables(matrixVariables));
|
||||
MultiValueMap<String, String> vars = WebUtils.parseMatrixVariables(matrixVariables);
|
||||
result.put(uriVar.getKey(), getUrlPathHelper().decodeMatrixVariables(request, vars));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -182,26 +189,38 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
|
||||
Set<MediaType> consumableMediaTypes;
|
||||
Set<MediaType> producibleMediaTypes;
|
||||
Set<String> paramConditions;
|
||||
|
||||
if (patternAndMethodMatches.isEmpty()) {
|
||||
consumableMediaTypes = getConsumableMediaTypes(request, patternMatches);
|
||||
producibleMediaTypes = getProdicubleMediaTypes(request, patternMatches);
|
||||
paramConditions = getRequestParams(request, patternMatches);
|
||||
}
|
||||
else {
|
||||
consumableMediaTypes = getConsumableMediaTypes(request, patternAndMethodMatches);
|
||||
producibleMediaTypes = getProdicubleMediaTypes(request, patternAndMethodMatches);
|
||||
paramConditions = getRequestParams(request, patternAndMethodMatches);
|
||||
}
|
||||
|
||||
if (!consumableMediaTypes.isEmpty()) {
|
||||
MediaType contentType = null;
|
||||
if (StringUtils.hasLength(request.getContentType())) {
|
||||
contentType = MediaType.parseMediaType(request.getContentType());
|
||||
try {
|
||||
contentType = MediaType.parseMediaType(request.getContentType());
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
|
||||
}
|
||||
}
|
||||
throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<MediaType>(consumableMediaTypes));
|
||||
}
|
||||
else if (!producibleMediaTypes.isEmpty()) {
|
||||
throw new HttpMediaTypeNotAcceptableException(new ArrayList<MediaType>(producibleMediaTypes));
|
||||
}
|
||||
else if (!CollectionUtils.isEmpty(paramConditions)) {
|
||||
String[] params = paramConditions.toArray(new String[paramConditions.size()]);
|
||||
throw new UnsatisfiedServletRequestParameterException(params, request.getParameterMap());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
@@ -227,4 +246,18 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
return result;
|
||||
}
|
||||
|
||||
private Set<String> getRequestParams(HttpServletRequest request, Set<RequestMappingInfo> partialMatches) {
|
||||
for (RequestMappingInfo partialMatch : partialMatches) {
|
||||
ParamsRequestCondition condition = partialMatch.getParamsCondition();
|
||||
if (!CollectionUtils.isEmpty(condition.getExpressions()) && (condition.getMatchingCondition(request) == null)) {
|
||||
Set<String> expressions = new HashSet<String>();
|
||||
for (NameValueExpression expr : condition.getExpressions()) {
|
||||
expressions.add(expr.toString());
|
||||
}
|
||||
return expressions;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
||||
}
|
||||
}
|
||||
if (compatibleMediaTypes.isEmpty()) {
|
||||
throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
|
||||
throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
|
||||
}
|
||||
|
||||
List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -68,7 +68,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
||||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
Class<?> parameterType = returnType.getParameterType();
|
||||
return HttpEntity.class.equals(parameterType) || ResponseEntity.class.equals(parameterType);
|
||||
return HttpEntity.class.isAssignableFrom(parameterType) || ResponseEntity.class.isAssignableFrom(parameterType);
|
||||
}
|
||||
|
||||
public Object resolveArgument(
|
||||
|
||||
@@ -67,4 +67,4 @@ public class PathVariableMapMethodArgumentResolver implements HandlerMethodArgum
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,4 +119,4 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethod
|
||||
super(annotation.value(), true, ValueConstants.DEFAULT_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -879,4 +879,4 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -20,9 +20,11 @@ import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.mvc.condition.AbstractRequestCondition;
|
||||
@@ -46,7 +48,8 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMappi
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping {
|
||||
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
|
||||
implements EmbeddedValueResolverAware {
|
||||
|
||||
private boolean useSuffixPatternMatch = true;
|
||||
|
||||
@@ -58,6 +61,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
|
||||
private final List<String> fileExtensions = new ArrayList<String>();
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
|
||||
/**
|
||||
* Whether to use suffix pattern match (".*") when matching patterns to
|
||||
@@ -101,6 +106,11 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
this.useTrailingSlashMatch = useTrailingSlashMatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||
this.embeddedValueResolver = resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ContentNegotiationManager} to use to determine requested media types.
|
||||
* If not set, the default constructor is used.
|
||||
@@ -142,15 +152,15 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
* Return the file extensions to use for suffix pattern matching.
|
||||
*/
|
||||
public List<String> getFileExtensions() {
|
||||
return fileExtensions;
|
||||
return this.fileExtensions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
if (this.useRegisteredSuffixPatternMatch) {
|
||||
this.fileExtensions.addAll(contentNegotiationManager.getAllFileExtensions());
|
||||
}
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,8 +237,9 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
* Created a RequestMappingInfo from a RequestMapping annotation.
|
||||
*/
|
||||
private RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {
|
||||
String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());
|
||||
return new RequestMappingInfo(
|
||||
new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher(),
|
||||
new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),
|
||||
this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),
|
||||
new RequestMethodsRequestCondition(annotation.method()),
|
||||
new ParamsRequestCondition(annotation.params()),
|
||||
@@ -238,4 +249,21 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
customCondition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve placeholder values in the given array of patterns.
|
||||
* @return a new array with updated patterns
|
||||
*/
|
||||
protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) {
|
||||
if (this.embeddedValueResolver == null) {
|
||||
return patterns;
|
||||
}
|
||||
else {
|
||||
String[] resolvedPatterns = new String[patterns.length];
|
||||
for (int i=0; i < patterns.length; i++) {
|
||||
resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]);
|
||||
}
|
||||
return resolvedPatterns;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -190,4 +190,4 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,4 +60,4 @@ public class ServletCookieValueMethodArgumentResolver extends AbstractCookieValu
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,4 +153,4 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
|
||||
servletBinder.bind(servletRequest);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,4 +51,4 @@ public class ServletWebArgumentResolverAdapter extends AbstractWebArgumentResolv
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -50,14 +50,14 @@ import org.springframework.web.util.WebUtils;
|
||||
*
|
||||
* <p>Note that the second strategy also supports the use of submit buttons of
|
||||
* type 'image'. That is, an image submit button named 'reset' will normally be
|
||||
* submitted by the browser as two request paramters called 'reset.x', and
|
||||
* 'reset.y'. When checking for the existence of a paramter from the
|
||||
* submitted by the browser as two request parameters called 'reset.x', and
|
||||
* 'reset.y'. When checking for the existence of a parameter from the
|
||||
* {@code methodParamNames} list, to indicate that a specific method should
|
||||
* be called, the code will look for request parameter in the "reset" form
|
||||
* (exactly as spcified in the list), and in the "reset.x" form ('.x' appended to
|
||||
* the name in the list). In this way it can handle both normal and image submit
|
||||
* buttons. The actual method name resolved if there is a match will always be
|
||||
* the bare form without the ".x".
|
||||
* be called, the code will look for a request parameter in the "reset" form
|
||||
* (exactly as specified in the list), and in the "reset.x" form ('.x' appended
|
||||
* to the name in the list). In this way it can handle both normal and image
|
||||
* submit buttons. The actual method name resolved, if there is a match, will
|
||||
* always be the bare form without the ".x".
|
||||
*
|
||||
* <p><b>Note:</b> If both strategies are configured, i.e. both "paramName"
|
||||
* and "methodParamNames" are specified, then both will be checked for any given
|
||||
@@ -69,7 +69,7 @@ import org.springframework.web.util.WebUtils;
|
||||
*
|
||||
* <p>For both resolution strategies, the method name is of course coming from
|
||||
* some sort of view code, (such as a JSP page). While this may be acceptable,
|
||||
* it is sometimes desireable to treat this only as a 'logical' method name,
|
||||
* it is sometimes desirable to treat this only as a 'logical' method name,
|
||||
* with a further mapping to a 'real' method name. As such, an optional
|
||||
* 'logical' mapping may be specified for this purpose.
|
||||
*
|
||||
|
||||
@@ -94,6 +94,11 @@ public abstract class AbstractDispatcherServletInitializer
|
||||
|
||||
ServletRegistration.Dynamic registration =
|
||||
servletContext.addServlet(servletName, dispatcherServlet);
|
||||
|
||||
Assert.notNull(registration,
|
||||
"Failed to register servlet with name '" + servletName + "'." +
|
||||
"Check if there is another servlet registered under the same name.");
|
||||
|
||||
registration.setLoadOnStartup(1);
|
||||
registration.addMapping(getServletMappings());
|
||||
registration.setAsyncSupported(isAsyncSupported());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -36,6 +36,7 @@ import org.springframework.ui.context.ThemeSource;
|
||||
import org.springframework.ui.context.support.ResourceBundleThemeSource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
@@ -61,6 +62,7 @@ import org.springframework.web.util.WebUtils;
|
||||
* appropriate fallback for the locale (the HttpServletRequest's primary locale).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 03.03.2003
|
||||
* @see org.springframework.web.servlet.DispatcherServlet
|
||||
* @see org.springframework.web.servlet.view.AbstractView#setRequestContextAttribute
|
||||
@@ -233,8 +235,10 @@ public class RequestContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the fallback locale for this context. <p>The default implementation checks for a JSTL locale attribute
|
||||
* in request, session or application scope; if not found, returns the {@code HttpServletRequest.getLocale()}.
|
||||
* Determine the fallback locale for this context.
|
||||
* <p>The default implementation checks for a JSTL locale attribute in request,
|
||||
* session or application scope; if not found, returns the
|
||||
* {@code HttpServletRequest.getLocale()}.
|
||||
* @return the fallback locale (never {@code null})
|
||||
* @see javax.servlet.http.HttpServletRequest#getLocale()
|
||||
*/
|
||||
@@ -249,8 +253,8 @@ public class RequestContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the fallback theme for this context. <p>The default implementation returns the default theme (with name
|
||||
* "theme").
|
||||
* Determine the fallback theme for this context.
|
||||
* <p>The default implementation returns the default theme (with name "theme").
|
||||
* @return the fallback theme (never {@code null})
|
||||
*/
|
||||
protected Theme getFallbackTheme() {
|
||||
@@ -309,8 +313,8 @@ public class RequestContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current theme (never {@code null}). <p>Resolved lazily for more efficiency when theme support is
|
||||
* not being used.
|
||||
* Return the current theme (never {@code null}).
|
||||
* <p>Resolved lazily for more efficiency when theme support is not being used.
|
||||
*/
|
||||
public final Theme getTheme() {
|
||||
if (this.theme == null) {
|
||||
@@ -350,7 +354,8 @@ public class RequestContext {
|
||||
|
||||
/**
|
||||
* Set the UrlPathHelper to use for context path and request URI decoding. Can be used to pass a shared
|
||||
* UrlPathHelper instance in. <p>A default UrlPathHelper is always available.
|
||||
* UrlPathHelper instance in.
|
||||
* <p>A default UrlPathHelper is always available.
|
||||
*/
|
||||
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
|
||||
@@ -359,7 +364,8 @@ public class RequestContext {
|
||||
|
||||
/**
|
||||
* Return the UrlPathHelper used for context path and request URI decoding. Can be used to configure the current
|
||||
* UrlPathHelper. <p>A default UrlPathHelper is always available.
|
||||
* UrlPathHelper.
|
||||
* <p>A default UrlPathHelper is always available.
|
||||
*/
|
||||
public UrlPathHelper getUrlPathHelper() {
|
||||
return this.urlPathHelper;
|
||||
@@ -376,8 +382,8 @@ public class RequestContext {
|
||||
|
||||
/**
|
||||
* Return the context path of the original request, that is, the path that indicates the current web application.
|
||||
* This is useful for building links to other resources within the application. <p>Delegates to the UrlPathHelper
|
||||
* for decoding.
|
||||
* This is useful for building links to other resources within the application.
|
||||
* <p>Delegates to the UrlPathHelper for decoding.
|
||||
* @see javax.servlet.http.HttpServletRequest#getContextPath
|
||||
* @see #getUrlPathHelper
|
||||
*/
|
||||
@@ -422,22 +428,22 @@ public class RequestContext {
|
||||
* context path and the servlet path of the original request. This is useful
|
||||
* for building links to other resources within the application where a
|
||||
* servlet mapping of the style {@code "/main/*"} is used.
|
||||
* <p>Delegates to the UrlPathHelper for decoding the context path.
|
||||
* @see javax.servlet.http.HttpServletRequest#getContextPath
|
||||
* @see javax.servlet.http.HttpServletRequest#getServletPath()
|
||||
* @see #getUrlPathHelper
|
||||
* <p>Delegates to the UrlPathHelper to determine the context and servlet path.
|
||||
*/
|
||||
public String getPathToServlet() {
|
||||
return this.urlPathHelper.getOriginatingContextPath(this.request)
|
||||
+ this.urlPathHelper.getOriginatingServletPath(this.request);
|
||||
String path = this.urlPathHelper.getOriginatingContextPath(this.request);
|
||||
if (StringUtils.hasText(this.urlPathHelper.getPathWithinServletMapping(this.request))) {
|
||||
path += this.urlPathHelper.getOriginatingServletPath(this.request);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the request URI of the original request, that is, the invoked URL without parameters. This is particularly
|
||||
* useful as HTML form action target, possibly in combination with the original query string. <p><b>Note this
|
||||
* implementation will correctly resolve to the URI of any originating root request in the presence of a forwarded
|
||||
* request. However, this can only work when the Servlet 2.4 'forward' request attributes are present. <p>Delegates
|
||||
* to the UrlPathHelper for decoding.
|
||||
* request. However, this can only work when the Servlet 2.4 'forward' request attributes are present.
|
||||
* <p>Delegates to the UrlPathHelper for decoding.
|
||||
* @see #getQueryString
|
||||
* @see org.springframework.web.util.UrlPathHelper#getOriginatingRequestUri
|
||||
* @see #getUrlPathHelper
|
||||
@@ -573,8 +579,9 @@ public class RequestContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the theme message for the given code. <p>Note that theme messages are never HTML-escaped, as they
|
||||
* typically denote theme-specific resource paths and not client-visible messages.
|
||||
* Retrieve the theme message for the given code.
|
||||
* <p>Note that theme messages are never HTML-escaped, as they typically denote
|
||||
* theme-specific resource paths and not client-visible messages.
|
||||
* @param code code of the message
|
||||
* @param defaultMessage String to return if the lookup fails
|
||||
* @return the message
|
||||
@@ -584,8 +591,9 @@ public class RequestContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the theme message for the given code. <p>Note that theme messages are never HTML-escaped, as they
|
||||
* typically denote theme-specific resource paths and not client-visible messages.
|
||||
* Retrieve the theme message for the given code.
|
||||
* <p>Note that theme messages are never HTML-escaped, as they typically denote
|
||||
* theme-specific resource paths and not client-visible messages.
|
||||
* @param code code of the message
|
||||
* @param args arguments for the message, or {@code null} if none
|
||||
* @param defaultMessage String to return if the lookup fails
|
||||
@@ -596,8 +604,9 @@ public class RequestContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the theme message for the given code. <p>Note that theme messages are never HTML-escaped, as they
|
||||
* typically denote theme-specific resource paths and not client-visible messages.
|
||||
* Retrieve the theme message for the given code.
|
||||
* <p>Note that theme messages are never HTML-escaped, as they typically denote
|
||||
* theme-specific resource paths and not client-visible messages.
|
||||
* @param code code of the message
|
||||
* @param args arguments for the message as a List, or {@code null} if none
|
||||
* @param defaultMessage String to return if the lookup fails
|
||||
@@ -609,8 +618,9 @@ public class RequestContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the theme message for the given code. <p>Note that theme messages are never HTML-escaped, as they
|
||||
* typically denote theme-specific resource paths and not client-visible messages.
|
||||
* Retrieve the theme message for the given code.
|
||||
* <p>Note that theme messages are never HTML-escaped, as they typically denote
|
||||
* theme-specific resource paths and not client-visible messages.
|
||||
* @param code code of the message
|
||||
* @return the message
|
||||
* @throws org.springframework.context.NoSuchMessageException if not found
|
||||
@@ -620,8 +630,9 @@ public class RequestContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the theme message for the given code. <p>Note that theme messages are never HTML-escaped, as they
|
||||
* typically denote theme-specific resource paths and not client-visible messages.
|
||||
* Retrieve the theme message for the given code.
|
||||
* <p>Note that theme messages are never HTML-escaped, as they typically denote
|
||||
* theme-specific resource paths and not client-visible messages.
|
||||
* @param code code of the message
|
||||
* @param args arguments for the message, or {@code null} if none
|
||||
* @return the message
|
||||
@@ -632,8 +643,9 @@ public class RequestContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the theme message for the given code. <p>Note that theme messages are never HTML-escaped, as they
|
||||
* typically denote theme-specific resource paths and not client-visible messages.
|
||||
* Retrieve the theme message for the given code.
|
||||
* <p>Note that theme messages are never HTML-escaped, as they typically denote
|
||||
* theme-specific resource paths and not client-visible messages.
|
||||
* @param code code of the message
|
||||
* @param args arguments for the message as a List, or {@code null} if none
|
||||
* @return the message
|
||||
@@ -644,8 +656,9 @@ public class RequestContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the given MessageSourceResolvable in the current theme. <p>Note that theme messages are never
|
||||
* HTML-escaped, as they typically denote theme-specific resource paths and not client-visible messages.
|
||||
* Retrieve the given MessageSourceResolvable in the current theme.
|
||||
* <p>Note that theme messages are never HTML-escaped, as they typically denote
|
||||
* theme-specific resource paths and not client-visible messages.
|
||||
* @param resolvable the MessageSourceResolvable
|
||||
* @return the message
|
||||
* @throws org.springframework.context.NoSuchMessageException if not found
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -95,9 +95,12 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder {
|
||||
String scheme = request.getScheme();
|
||||
int port = request.getServerPort();
|
||||
|
||||
String header = request.getHeader("X-Forwarded-Host");
|
||||
String host = StringUtils.hasText(header) ? header: request.getServerName();
|
||||
|
||||
ServletUriComponentsBuilder builder = new ServletUriComponentsBuilder();
|
||||
builder.scheme(scheme);
|
||||
builder.host(request.getServerName());
|
||||
builder.host(host);
|
||||
if ((scheme.equals("http") && port != 80) || (scheme.equals("https") && port != 443)) {
|
||||
builder.port(port);
|
||||
}
|
||||
@@ -138,7 +141,10 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder {
|
||||
return fromRequest(getCurrentRequest());
|
||||
}
|
||||
|
||||
private static HttpServletRequest getCurrentRequest() {
|
||||
/**
|
||||
* Obtain the request through {@link RequestContextHolder}.
|
||||
*/
|
||||
protected static HttpServletRequest getCurrentRequest() {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
Assert.state(requestAttributes != null, "Could not find current request via RequestContextHolder");
|
||||
Assert.isInstanceOf(ServletRequestAttributes.class, requestAttributes);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -26,7 +26,9 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.HttpSessionRequiredException;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.context.support.WebApplicationObjectSupport;
|
||||
import org.springframework.web.servlet.mvc.LastModified;
|
||||
|
||||
/**
|
||||
* Convenient superclass for any kind of web content generator,
|
||||
@@ -79,6 +81,8 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
|
||||
|
||||
private int cacheSeconds = -1;
|
||||
|
||||
private boolean alwaysMustRevalidate = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new WebContentGenerator which supports
|
||||
@@ -194,6 +198,25 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
|
||||
return this.useCacheControlNoStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* An option to add 'must-revalidate' to every Cache-Control header. This
|
||||
* may be useful with annotated controller methods, which can
|
||||
* programmatically do a lastModified calculation as described in
|
||||
* {@link WebRequest#checkNotModified(long)}. Default is "false",
|
||||
* effectively relying on whether the handler implements
|
||||
* {@link LastModified} or not.
|
||||
*/
|
||||
public void setAlwaysMustRevalidate(boolean mustRevalidate) {
|
||||
this.alwaysMustRevalidate = mustRevalidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether 'must-revaliate' is added to every Cache-Control header.
|
||||
*/
|
||||
public boolean isAlwaysMustRevalidate() {
|
||||
return alwaysMustRevalidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache content for the given number of seconds. Default is -1,
|
||||
* indicating no generation of cache-related headers.
|
||||
@@ -313,7 +336,7 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport {
|
||||
if (this.useCacheControlHeader) {
|
||||
// HTTP 1.1 header
|
||||
String headerValue = "max-age=" + seconds;
|
||||
if (mustRevalidate) {
|
||||
if (mustRevalidate || this.alwaysMustRevalidate) {
|
||||
headerValue += ", must-revalidate";
|
||||
}
|
||||
response.setHeader(HEADER_CACHE_CONTROL, headerValue);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -50,6 +50,7 @@ import org.springframework.web.util.HtmlUtils;
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Scott Andrews
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 2.0
|
||||
* @see org.springframework.web.servlet.mvc.SimpleFormController
|
||||
*/
|
||||
@@ -411,6 +412,10 @@ public class FormTag extends AbstractHtmlElementTag {
|
||||
protected String resolveAction() throws JspException {
|
||||
String action = getAction();
|
||||
if (StringUtils.hasText(action)) {
|
||||
String pathToServlet = getRequestContext().getPathToServlet();
|
||||
if (action.startsWith("/") && !action.startsWith(getRequestContext().getContextPath())) {
|
||||
action = pathToServlet + action;
|
||||
}
|
||||
action = getDisplayString(evaluate(ACTION_ATTRIBUTE, action));
|
||||
return processAction(action);
|
||||
}
|
||||
@@ -469,8 +474,8 @@ public class FormTag extends AbstractHtmlElementTag {
|
||||
if (hiddenFields != null) {
|
||||
for (String name : hiddenFields.keySet()) {
|
||||
this.tagWriter.appendValue("<input type=\"hidden\" ");
|
||||
this.tagWriter.appendValue("name=\"" + name + "\" value=\"" + hiddenFields.get(name) + "\">");
|
||||
this.tagWriter.appendValue("</input>\n");
|
||||
this.tagWriter.appendValue("name=\"" + name + "\" value=\"" + hiddenFields.get(name) + "\" ");
|
||||
this.tagWriter.appendValue("/>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -19,6 +19,9 @@ package org.springframework.web.servlet.view;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.context.support.WebApplicationObjectSupport;
|
||||
import org.springframework.web.servlet.View;
|
||||
@@ -42,19 +45,38 @@ public abstract class AbstractCachingViewResolver extends WebApplicationObjectSu
|
||||
/** Default maximum number of entries for the view cache: 1024 */
|
||||
public static final int DEFAULT_CACHE_LIMIT = 1024;
|
||||
|
||||
/** Dummy marker object for unresolved views in the cache Maps */
|
||||
private static final View UNRESOLVED_VIEW = new View() {
|
||||
public String getContentType() {
|
||||
return null;
|
||||
}
|
||||
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** The maximum number of entries in the cache */
|
||||
private volatile int cacheLimit = DEFAULT_CACHE_LIMIT;
|
||||
|
||||
/** Whether we should refrain from resolving views again if unresolved once */
|
||||
private boolean cacheUnresolved = true;
|
||||
|
||||
/** Map from view key to View instance */
|
||||
/** Fast access cache for Views, returning already cached instances without a global lock */
|
||||
private final Map<Object, View> viewAccessCache = new ConcurrentHashMap<Object, View>(DEFAULT_CACHE_LIMIT);
|
||||
|
||||
/** Map from view key to View instance, synchronized for View creation */
|
||||
@SuppressWarnings("serial")
|
||||
private final Map<Object, View> viewCache =
|
||||
private final Map<Object, View> viewCreationCache =
|
||||
new LinkedHashMap<Object, View>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Object, View> eldest) {
|
||||
return size() > getCacheLimit();
|
||||
if (size() > getCacheLimit()) {
|
||||
viewAccessCache.remove(eldest.getKey());
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -122,20 +144,27 @@ public abstract class AbstractCachingViewResolver extends WebApplicationObjectSu
|
||||
}
|
||||
else {
|
||||
Object cacheKey = getCacheKey(viewName, locale);
|
||||
synchronized (this.viewCache) {
|
||||
View view = this.viewCache.get(cacheKey);
|
||||
if (view == null && (!this.cacheUnresolved || !this.viewCache.containsKey(cacheKey))) {
|
||||
// Ask the subclass to create the View object.
|
||||
view = createView(viewName, locale);
|
||||
if (view != null || this.cacheUnresolved) {
|
||||
this.viewCache.put(cacheKey, view);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Cached view [" + cacheKey + "]");
|
||||
View view = this.viewAccessCache.get(cacheKey);
|
||||
if (view == null) {
|
||||
synchronized (this.viewCreationCache) {
|
||||
view = this.viewCreationCache.get(cacheKey);
|
||||
if (view == null) {
|
||||
// Ask the subclass to create the View object.
|
||||
view = createView(viewName, locale);
|
||||
if (view == null && this.cacheUnresolved) {
|
||||
view = UNRESOLVED_VIEW;
|
||||
}
|
||||
if (view != null) {
|
||||
this.viewAccessCache.put(cacheKey, view);
|
||||
this.viewCreationCache.put(cacheKey, view);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Cached view [" + cacheKey + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return view;
|
||||
}
|
||||
return (view != UNRESOLVED_VIEW ? view : null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,17 +195,16 @@ public abstract class AbstractCachingViewResolver extends WebApplicationObjectSu
|
||||
else {
|
||||
Object cacheKey = getCacheKey(viewName, locale);
|
||||
Object cachedView;
|
||||
synchronized (this.viewCache) {
|
||||
cachedView = this.viewCache.remove(cacheKey);
|
||||
synchronized (this.viewCreationCache) {
|
||||
this.viewAccessCache.remove(cacheKey);
|
||||
cachedView = this.viewCreationCache.remove(cacheKey);
|
||||
}
|
||||
if (cachedView == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Some debug output might be useful...
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (cachedView == null) {
|
||||
logger.debug("No cached instance for view '" + cacheKey + "' was found");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
else {
|
||||
logger.debug("Cache for view " + cacheKey + " has been cleared");
|
||||
}
|
||||
}
|
||||
@@ -189,8 +217,9 @@ public abstract class AbstractCachingViewResolver extends WebApplicationObjectSu
|
||||
*/
|
||||
public void clearCache() {
|
||||
logger.debug("Clearing entire view cache");
|
||||
synchronized (this.viewCache) {
|
||||
this.viewCache.clear();
|
||||
synchronized (this.viewCreationCache) {
|
||||
this.viewAccessCache.clear();
|
||||
this.viewCreationCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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,7 +25,6 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -33,6 +32,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.OrderComparator;
|
||||
@@ -95,7 +95,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
|
||||
private ContentNegotiationManager contentNegotiationManager;
|
||||
|
||||
private ContentNegotiationManagerFactoryBean cnManagerFactoryBean = new ContentNegotiationManagerFactoryBean();
|
||||
private final ContentNegotiationManagerFactoryBean cnManagerFactoryBean = new ContentNegotiationManagerFactoryBean();
|
||||
|
||||
private boolean useNotAcceptableStatusCode = false;
|
||||
|
||||
@@ -104,10 +104,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
private List<ViewResolver> viewResolvers;
|
||||
|
||||
|
||||
public ContentNegotiatingViewResolver() {
|
||||
super();
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
@@ -118,7 +114,9 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
|
||||
/**
|
||||
* Set the {@link ContentNegotiationManager} to use to determine requested media types.
|
||||
* If not set, the default constructor is used.
|
||||
* <p>If not set, ContentNegotiationManager's default constructor will be used,
|
||||
* applying a {@link org.springframework.web.accept.HeaderContentNegotiationStrategy}.
|
||||
* @see ContentNegotiationManager#ContentNegotiationManager()
|
||||
*/
|
||||
public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
|
||||
this.contentNegotiationManager = contentNegotiationManager;
|
||||
@@ -130,18 +128,16 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
* <p>For instance, when this flag is {@code true} (the default), a request for {@code /hotels.pdf}
|
||||
* will result in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the
|
||||
* browser-defined {@code text/html,application/xhtml+xml}.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setFavorPathExtension(boolean favorPathExtension) {
|
||||
this.cnManagerFactoryBean.setFavorParameter(favorPathExtension);
|
||||
this.cnManagerFactoryBean.setFavorPathExtension(favorPathExtension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether to use the Java Activation Framework to map from file extensions to media types.
|
||||
* <p>Default is {@code true}, i.e. the Java Activation Framework is used (if available).
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -155,7 +151,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
* <p>For instance, when this flag is {@code true}, a request for {@code /hotels?format=pdf} will result
|
||||
* in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the browser-defined
|
||||
* {@code text/html,application/xhtml+xml}.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -166,7 +161,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
/**
|
||||
* Set the parameter name that can be used to determine the requested media type if the {@link
|
||||
* #setFavorParameter} property is {@code true}. The default parameter name is {@code format}.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -179,7 +173,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
* <p>If set to {@code true}, this view resolver will only refer to the file extension and/or
|
||||
* parameter, as indicated by the {@link #setFavorPathExtension favorPathExtension} and
|
||||
* {@link #setFavorParameter favorParameter} properties.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -191,7 +184,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
* Set the mapping from file extensions to media types.
|
||||
* <p>When this mapping is not set or when an extension is not present, this view resolver
|
||||
* will fall back to using a {@link FileTypeMap} when the Java Action Framework is available.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -207,7 +199,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
* Set the default content type.
|
||||
* <p>This content type will be used when file extension, parameter, nor {@code Accept}
|
||||
* header define a content-type, either through being disabled or empty.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -275,7 +266,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
||||
this.cnManagerFactoryBean.setServletContext(servletContext);
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
public void afterPropertiesSet() {
|
||||
if (this.contentNegotiationManager == null) {
|
||||
this.cnManagerFactoryBean.afterPropertiesSet();
|
||||
this.contentNegotiationManager = this.cnManagerFactoryBean.getObject();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -160,7 +160,7 @@ public class ResourceBundleViewResolver extends AbstractCachingViewResolver
|
||||
* <p>View definitions that define their own parent or carry their own
|
||||
* class can still override this. Strictly speaking, the rule that a
|
||||
* default parent setting does not apply to a bean definition that
|
||||
* carries a class is there for backwards compatiblity reasons.
|
||||
* carries a class is there for backwards compatibility reasons.
|
||||
* It still matches the typical use case.
|
||||
*/
|
||||
public void setDefaultParentView(String defaultParentView) {
|
||||
|
||||
@@ -309,8 +309,8 @@
|
||||
<@bind path />
|
||||
<#assign id="${status.expression?replace('[','')?replace(']','')}">
|
||||
<#assign isSelected = status.value?? && status.value?string=="true">
|
||||
<input type="hidden" name="_${id}" value="on"/>
|
||||
<input type="checkbox" id="${id}" name="${id}"<#if isSelected> checked="checked"</#if> ${attributes}/>
|
||||
<input type="hidden" name="_${status.expression}" value="on"/>
|
||||
<input type="checkbox" id="${id}" name="${status.expression}"<#if isSelected> checked="checked"</#if> ${attributes}/>
|
||||
</#macro>
|
||||
|
||||
<#--
|
||||
|
||||
@@ -43,4 +43,4 @@ public class JasperReportsCsvView extends AbstractJasperReportsSingleFormatView
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.web.servlet.view.json;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -27,7 +28,6 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonEncoding;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
||||
@@ -48,13 +48,15 @@ import org.springframework.web.servlet.view.AbstractView;
|
||||
* @author Jeremy Grelle
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1.2
|
||||
* @see org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
|
||||
*/
|
||||
public class MappingJackson2JsonView extends AbstractView {
|
||||
|
||||
/**
|
||||
* Default content type. Overridable as bean property.
|
||||
* Default content type: "application/json".
|
||||
* Overridable through {@link #setContentType}.
|
||||
*/
|
||||
public static final String DEFAULT_CONTENT_TYPE = "application/json";
|
||||
|
||||
@@ -75,8 +77,9 @@ public class MappingJackson2JsonView extends AbstractView {
|
||||
|
||||
private boolean updateContentLength = false;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new {@code JacksonJsonView}, setting the content type to {@code application/json}.
|
||||
* Construct a new {@code MappingJackson2JsonView}, setting the content type to {@code application/json}.
|
||||
*/
|
||||
public MappingJackson2JsonView() {
|
||||
setContentType(DEFAULT_CONTENT_TYPE);
|
||||
@@ -85,13 +88,11 @@ public class MappingJackson2JsonView extends AbstractView {
|
||||
|
||||
|
||||
/**
|
||||
* Sets the {@code ObjectMapper} for this view.
|
||||
* If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper} is used.
|
||||
* <p>Setting a custom-configured {@code ObjectMapper} is one way to take further control
|
||||
* of the JSON serialization process. For example, an extended {@code SerializerFactory}
|
||||
* can be configured that provides custom serializers for specific types. The other option
|
||||
* for refining the serialization process is to use Jackson's provided annotations on the
|
||||
* types to be serialized, in which case a custom-configured ObjectMapper is unnecessary.
|
||||
* Set the {@code ObjectMapper} for this view.
|
||||
* If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper} will be used.
|
||||
* <p>Setting a custom-configured {@code ObjectMapper} is one way to take further control of
|
||||
* the JSON serialization process. The other option is to use Jackson's provided annotations
|
||||
* on the types to be serialized, in which case a custom-configured ObjectMapper is unnecessary.
|
||||
*/
|
||||
public void setObjectMapper(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "'objectMapper' must not be null");
|
||||
@@ -99,14 +100,15 @@ public class MappingJackson2JsonView extends AbstractView {
|
||||
configurePrettyPrint();
|
||||
}
|
||||
|
||||
private void configurePrettyPrint() {
|
||||
if (this.prettyPrint != null) {
|
||||
this.objectMapper.configure(SerializationFeature.INDENT_OUTPUT, this.prettyPrint);
|
||||
}
|
||||
/**
|
||||
* Return the {@code ObjectMapper} for this view.
|
||||
*/
|
||||
public final ObjectMapper getObjectMapper() {
|
||||
return this.objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@code JsonEncoding} for this converter.
|
||||
* Set the {@code JsonEncoding} for this view.
|
||||
* By default, {@linkplain JsonEncoding#UTF8 UTF-8} is used.
|
||||
*/
|
||||
public void setEncoding(JsonEncoding encoding) {
|
||||
@@ -114,9 +116,16 @@ public class MappingJackson2JsonView extends AbstractView {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@code JsonEncoding} for this view.
|
||||
*/
|
||||
public final JsonEncoding getEncoding() {
|
||||
return this.encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the JSON output by this view should be prefixed with <tt>"{} && "</tt>.
|
||||
* Default is false.
|
||||
* Default is {@code false}.
|
||||
* <p>Prefixing the JSON string in this manner is used to help prevent JSON Hijacking.
|
||||
* The prefix renders the string syntactically invalid as a script so that it cannot be hijacked.
|
||||
* This prefix does not affect the evaluation of JSON, but if JSON validation is performed
|
||||
@@ -127,12 +136,11 @@ public class MappingJackson2JsonView extends AbstractView {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use the {@link DefaultPrettyPrinter} when writing JSON.
|
||||
* Whether to use the default pretty printer when writing JSON.
|
||||
* This is a shortcut for setting up an {@code ObjectMapper} as follows:
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
|
||||
* converter.setObjectMapper(mapper);
|
||||
* </pre>
|
||||
* <p>The default value is {@code false}.
|
||||
*/
|
||||
@@ -141,6 +149,12 @@ public class MappingJackson2JsonView extends AbstractView {
|
||||
configurePrettyPrint();
|
||||
}
|
||||
|
||||
private void configurePrettyPrint() {
|
||||
if (this.prettyPrint != null) {
|
||||
this.objectMapper.configure(SerializationFeature.INDENT_OUTPUT, this.prettyPrint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the attribute in the model that should be rendered by this view.
|
||||
* When set, all other model attributes will be ignored.
|
||||
@@ -160,7 +174,7 @@ public class MappingJackson2JsonView extends AbstractView {
|
||||
/**
|
||||
* Return the attributes in the model that should be rendered by this view.
|
||||
*/
|
||||
public Set<String> getModelKeys() {
|
||||
public final Set<String> getModelKeys() {
|
||||
return this.modelKeys;
|
||||
}
|
||||
|
||||
@@ -179,7 +193,7 @@ public class MappingJackson2JsonView extends AbstractView {
|
||||
* @deprecated use {@link #getModelKeys()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<String> getRenderedAttributes() {
|
||||
public final Set<String> getRenderedAttributes() {
|
||||
return this.modelKeys;
|
||||
}
|
||||
|
||||
@@ -212,6 +226,7 @@ public class MappingJackson2JsonView extends AbstractView {
|
||||
this.updateContentLength = updateContentLength;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {
|
||||
setResponseContentType(request, response);
|
||||
@@ -227,34 +242,21 @@ public class MappingJackson2JsonView extends AbstractView {
|
||||
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
|
||||
HttpServletResponse response) throws Exception {
|
||||
|
||||
OutputStream stream = this.updateContentLength ? createTemporaryOutputStream() : response.getOutputStream();
|
||||
|
||||
OutputStream stream = (this.updateContentLength ? createTemporaryOutputStream() : response.getOutputStream());
|
||||
Object value = filterModel(model);
|
||||
JsonGenerator generator = this.objectMapper.getJsonFactory().createJsonGenerator(stream, this.encoding);
|
||||
|
||||
// A workaround for JsonGenerators not applying serialization features
|
||||
// https://github.com/FasterXML/jackson-databind/issues/12
|
||||
if (this.objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
|
||||
generator.useDefaultPrettyPrinter();
|
||||
}
|
||||
|
||||
if (this.prefixJson) {
|
||||
generator.writeRaw("{} && ");
|
||||
}
|
||||
this.objectMapper.writeValue(generator, value);
|
||||
|
||||
writeContent(stream, value, this.prefixJson);
|
||||
if (this.updateContentLength) {
|
||||
writeToResponse(response, (ByteArrayOutputStream) stream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out undesired attributes from the given model.
|
||||
* Filter out undesired attributes from the given model.
|
||||
* The return value can be either another {@link Map} or a single value object.
|
||||
* <p>The default implementation removes {@link BindingResult} instances and entries
|
||||
* not included in the {@link #setRenderedAttributes renderedAttributes} property.
|
||||
* @param model the model, as passed on to {@link #renderMergedOutputModel}
|
||||
* @return the object to be rendered
|
||||
* @return the value to be rendered
|
||||
*/
|
||||
protected Object filterModel(Map<String, Object> model) {
|
||||
Map<String, Object> result = new HashMap<String, Object>(model.size());
|
||||
@@ -267,4 +269,27 @@ public class MappingJackson2JsonView extends AbstractView {
|
||||
return (this.extractValueFromSingleKeyModel && result.size() == 1 ? result.values().iterator().next() : result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the actual JSON content to the stream.
|
||||
* @param stream the output stream to use
|
||||
* @param value the value to be rendered, as returned from {@link #filterModel}
|
||||
* @param prefixJson whether the JSON output by this view should be prefixed
|
||||
* with <tt>"{} && "</tt> (as indicated through {@link #setPrefixJson})
|
||||
* @throws IOException if writing failed
|
||||
*/
|
||||
protected void writeContent(OutputStream stream, Object value, boolean prefixJson) throws IOException {
|
||||
JsonGenerator generator = this.objectMapper.getJsonFactory().createJsonGenerator(stream, this.encoding);
|
||||
|
||||
// A workaround for JsonGenerators not applying serialization features
|
||||
// https://github.com/FasterXML/jackson-databind/issues/12
|
||||
if (this.objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
|
||||
generator.useDefaultPrettyPrinter();
|
||||
}
|
||||
|
||||
if (prefixJson) {
|
||||
generator.writeRaw("{} && ");
|
||||
}
|
||||
this.objectMapper.writeValue(generator, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -17,12 +17,12 @@
|
||||
package org.springframework.web.servlet.view.json;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -30,15 +30,13 @@ import org.codehaus.jackson.JsonEncoding;
|
||||
import org.codehaus.jackson.JsonGenerator;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.map.SerializationConfig;
|
||||
import org.codehaus.jackson.map.SerializerFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.view.AbstractView;
|
||||
|
||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||
|
||||
/**
|
||||
* Spring MVC {@link View} that renders JSON content by serializing the model for the current request
|
||||
* using <a href="http://jackson.codehaus.org/">Jackson's</a> {@link ObjectMapper}.
|
||||
@@ -50,13 +48,15 @@ import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||
* @author Jeremy Grelle
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see org.springframework.http.converter.json.MappingJacksonHttpMessageConverter
|
||||
*/
|
||||
public class MappingJacksonJsonView extends AbstractView {
|
||||
|
||||
/**
|
||||
* Default content type. Overridable as bean property.
|
||||
* Default content type: "application/json".
|
||||
* Overridable through {@link #setContentType}.
|
||||
*/
|
||||
public static final String DEFAULT_CONTENT_TYPE = "application/json";
|
||||
|
||||
@@ -77,8 +77,9 @@ public class MappingJacksonJsonView extends AbstractView {
|
||||
|
||||
private boolean updateContentLength = false;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new {@code JacksonJsonView}, setting the content type to {@code application/json}.
|
||||
* Construct a new {@code MappingJacksonJsonView}, setting the content type to {@code application/json}.
|
||||
*/
|
||||
public MappingJacksonJsonView() {
|
||||
setContentType(DEFAULT_CONTENT_TYPE);
|
||||
@@ -87,13 +88,11 @@ public class MappingJacksonJsonView extends AbstractView {
|
||||
|
||||
|
||||
/**
|
||||
* Sets the {@code ObjectMapper} for this view.
|
||||
* If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper} is used.
|
||||
* <p>Setting a custom-configured {@code ObjectMapper} is one way to take further control
|
||||
* of the JSON serialization process. For example, an extended {@link SerializerFactory}
|
||||
* can be configured that provides custom serializers for specific types. The other option
|
||||
* for refining the serialization process is to use Jackson's provided annotations on the
|
||||
* types to be serialized, in which case a custom-configured ObjectMapper is unnecessary.
|
||||
* Set the {@code ObjectMapper} for this view.
|
||||
* If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper} will be used.
|
||||
* <p>Setting a custom-configured {@code ObjectMapper} is one way to take further control of
|
||||
* the JSON serialization process. The other option is to use Jackson's provided annotations
|
||||
* on the types to be serialized, in which case a custom-configured ObjectMapper is unnecessary.
|
||||
*/
|
||||
public void setObjectMapper(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "'objectMapper' must not be null");
|
||||
@@ -101,14 +100,15 @@ public class MappingJacksonJsonView extends AbstractView {
|
||||
configurePrettyPrint();
|
||||
}
|
||||
|
||||
private void configurePrettyPrint() {
|
||||
if (this.prettyPrint != null) {
|
||||
this.objectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, this.prettyPrint);
|
||||
}
|
||||
/**
|
||||
* Return the {@code ObjectMapper} for this view.
|
||||
*/
|
||||
public final ObjectMapper getObjectMapper() {
|
||||
return this.objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@code JsonEncoding} for this converter.
|
||||
* Set the {@code JsonEncoding} for this view.
|
||||
* By default, {@linkplain JsonEncoding#UTF8 UTF-8} is used.
|
||||
*/
|
||||
public void setEncoding(JsonEncoding encoding) {
|
||||
@@ -116,9 +116,16 @@ public class MappingJacksonJsonView extends AbstractView {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@code JsonEncoding} for this view.
|
||||
*/
|
||||
public final JsonEncoding getEncoding() {
|
||||
return this.encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the JSON output by this view should be prefixed with <tt>"{} && "</tt>.
|
||||
* Default is false.
|
||||
* Default is {@code false}.
|
||||
* <p>Prefixing the JSON string in this manner is used to help prevent JSON Hijacking.
|
||||
* The prefix renders the string syntactically invalid as a script so that it cannot be hijacked.
|
||||
* This prefix does not affect the evaluation of JSON, but if JSON validation is performed
|
||||
@@ -129,12 +136,11 @@ public class MappingJacksonJsonView extends AbstractView {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use the {@link DefaultPrettyPrinter} when writing JSON.
|
||||
* Whether to use the default pretty printer when writing JSON.
|
||||
* This is a shortcut for setting up an {@code ObjectMapper} as follows:
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
|
||||
* converter.setObjectMapper(mapper);
|
||||
* </pre>
|
||||
* <p>The default value is {@code false}.
|
||||
*/
|
||||
@@ -143,6 +149,12 @@ public class MappingJacksonJsonView extends AbstractView {
|
||||
configurePrettyPrint();
|
||||
}
|
||||
|
||||
private void configurePrettyPrint() {
|
||||
if (this.prettyPrint != null) {
|
||||
this.objectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, this.prettyPrint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the attribute in the model that should be rendered by this view.
|
||||
* When set, all other model attributes will be ignored.
|
||||
@@ -162,7 +174,7 @@ public class MappingJacksonJsonView extends AbstractView {
|
||||
/**
|
||||
* Return the attributes in the model that should be rendered by this view.
|
||||
*/
|
||||
public Set<String> getModelKeys() {
|
||||
public final Set<String> getModelKeys() {
|
||||
return this.modelKeys;
|
||||
}
|
||||
|
||||
@@ -181,7 +193,7 @@ public class MappingJacksonJsonView extends AbstractView {
|
||||
* @deprecated use {@link #getModelKeys()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<String> getRenderedAttributes() {
|
||||
public final Set<String> getRenderedAttributes() {
|
||||
return this.modelKeys;
|
||||
}
|
||||
|
||||
@@ -230,34 +242,21 @@ public class MappingJacksonJsonView extends AbstractView {
|
||||
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
|
||||
HttpServletResponse response) throws Exception {
|
||||
|
||||
OutputStream stream = this.updateContentLength ? createTemporaryOutputStream() : response.getOutputStream();
|
||||
|
||||
OutputStream stream = (this.updateContentLength ? createTemporaryOutputStream() : response.getOutputStream());
|
||||
Object value = filterModel(model);
|
||||
JsonGenerator generator = this.objectMapper.getJsonFactory().createJsonGenerator(stream, this.encoding);
|
||||
|
||||
// A workaround for JsonGenerators not applying serialization features
|
||||
// https://github.com/FasterXML/jackson-databind/issues/12
|
||||
if (this.objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)) {
|
||||
generator.useDefaultPrettyPrinter();
|
||||
}
|
||||
|
||||
if (this.prefixJson) {
|
||||
generator.writeRaw("{} && ");
|
||||
}
|
||||
this.objectMapper.writeValue(generator, value);
|
||||
|
||||
writeContent(stream, value, this.prefixJson);
|
||||
if (this.updateContentLength) {
|
||||
writeToResponse(response, (ByteArrayOutputStream) stream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out undesired attributes from the given model.
|
||||
* Filter out undesired attributes from the given model.
|
||||
* The return value can be either another {@link Map} or a single value object.
|
||||
* <p>The default implementation removes {@link BindingResult} instances and entries
|
||||
* not included in the {@link #setRenderedAttributes renderedAttributes} property.
|
||||
* @param model the model, as passed on to {@link #renderMergedOutputModel}
|
||||
* @return the object to be rendered
|
||||
* @return the value to be rendered
|
||||
*/
|
||||
protected Object filterModel(Map<String, Object> model) {
|
||||
Map<String, Object> result = new HashMap<String, Object>(model.size());
|
||||
@@ -270,4 +269,27 @@ public class MappingJacksonJsonView extends AbstractView {
|
||||
return (this.extractValueFromSingleKeyModel && result.size() == 1 ? result.values().iterator().next() : result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the actual JSON content to the stream.
|
||||
* @param stream the output stream to use
|
||||
* @param value the value to be rendered, as returned from {@link #filterModel}
|
||||
* @param prefixJson whether the JSON output by this view should be prefixed
|
||||
* with <tt>"{} && "</tt> (as indicated through {@link #setPrefixJson})
|
||||
* @throws IOException if writing failed
|
||||
*/
|
||||
protected void writeContent(OutputStream stream, Object value, boolean prefixJson) throws IOException {
|
||||
JsonGenerator generator = this.objectMapper.getJsonFactory().createJsonGenerator(stream, this.encoding);
|
||||
|
||||
// A workaround for JsonGenerators not applying serialization features
|
||||
// https://github.com/FasterXML/jackson-databind/issues/12
|
||||
if (this.objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)) {
|
||||
generator.useDefaultPrettyPrinter();
|
||||
}
|
||||
|
||||
if (prefixJson) {
|
||||
generator.writeRaw("{} && ");
|
||||
}
|
||||
this.objectMapper.writeValue(generator, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user