Skip to content

Commit

Permalink
Merge branch '2.2.x' into 2.3.x
Browse files Browse the repository at this point in the history
Closes gh-24747
  • Loading branch information
philwebb committed Jan 11, 2021
2 parents 7625a97 + f9e3e0d commit 72dd3b5
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 71 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
Expand All @@ -17,13 +17,14 @@
package org.springframework.boot.autoconfigure.web.servlet;

import java.time.Duration;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
Expand Down Expand Up @@ -73,10 +74,10 @@
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.ClassUtils;
import org.springframework.util.PathMatcher;
import org.springframework.validation.DefaultMessageCodesResolver;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
Expand All @@ -88,11 +89,13 @@
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.support.ServletContextResource;
import org.springframework.web.filter.FormContentFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.filter.RequestContextFilter;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
Expand All @@ -107,13 +110,15 @@
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.servlet.i18n.FixedLocaleResolver;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.resource.AppCacheManifestTransformer;
import org.springframework.web.servlet.resource.EncodedResourceResolver;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import org.springframework.web.servlet.resource.ResourceResolver;
import org.springframework.web.servlet.resource.ResourceUrlProvider;
import org.springframework.web.servlet.resource.VersionResourceResolver;
Expand Down Expand Up @@ -155,7 +160,7 @@ public class WebMvcAutoConfiguration {
*/
public static final String DEFAULT_SUFFIX = "";

private static final String[] SERVLET_LOCATIONS = { "/" };
private static final String SERVLET_LOCATION = "/";

@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
Expand All @@ -171,13 +176,6 @@ public OrderedFormContentFilter formContentFilter() {
return new OrderedFormContentFilter();
}

static String[] getResourceLocations(String[] staticLocations) {
String[] locations = new String[staticLocations.length + SERVLET_LOCATIONS.length];
System.arraycopy(staticLocations, 0, locations, 0, staticLocations.length);
System.arraycopy(SERVLET_LOCATIONS, 0, locations, staticLocations.length, SERVLET_LOCATIONS.length);
return locations;
}

// Defined as a nested config to ensure WebMvcConfigurer is not read when not
// on the classpath
@Configuration(proxyBeanMethods = false)
Expand All @@ -186,10 +184,6 @@ static String[] getResourceLocations(String[] staticLocations) {
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);

private final ResourceProperties resourceProperties;

private final WebMvcProperties mvcProperties;

private final ListableBeanFactory beanFactory;
Expand All @@ -202,12 +196,11 @@ public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;

public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
public WebMvcAutoConfigurationAdapter(WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
ObjectProvider<HttpMessageConverters> messageConvertersProvider,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
ObjectProvider<DispatcherServletPath> dispatcherServletPath,
ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
Expand Down Expand Up @@ -328,37 +321,6 @@ public void addFormatters(FormatterRegistry registry) {
ApplicationConversionService.addBeans(registry, this.beanFactory);
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}

private Integer getSeconds(Duration cachePeriod) {
return (cachePeriod != null) ? (int) cachePeriod.getSeconds() : null;
}

private void customizeResourceHandlerRegistration(ResourceHandlerRegistration registration) {
if (this.resourceHandlerRegistrationCustomizer != null) {
this.resourceHandlerRegistrationCustomizer.customize(registration);
}
}

@Bean
@ConditionalOnMissingBean({ RequestContextListener.class, RequestContextFilter.class })
@ConditionalOnMissingFilterBean(RequestContextFilter.class)
Expand All @@ -374,22 +336,31 @@ public static RequestContextFilter requestContextFilter() {
@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {

private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);

private final ResourceProperties resourceProperties;

private final WebMvcProperties mvcProperties;

private final ListableBeanFactory beanFactory;

private final WebMvcRegistrations mvcRegistrations;

private final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;

private ResourceLoader resourceLoader;

private final ListableBeanFactory beanFactory;

private final Set<String> autoConfiguredResourceHandlers = new HashSet<>();

public EnableWebMvcConfiguration(ResourceProperties resourceProperties,
ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider, ListableBeanFactory beanFactory) {
ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
ListableBeanFactory beanFactory) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.beanFactory = beanFactory;
}

Expand Down Expand Up @@ -429,6 +400,72 @@ public RequestMappingHandlerMapping requestMappingHandlerMapping(
resourceUrlProvider);
}

@Bean
@Override
public HandlerMapping resourceHandlerMapping(UrlPathHelper urlPathHelper, PathMatcher pathMatcher,
ContentNegotiationManager contentNegotiationManager, FormattingConversionService conversionService,
ResourceUrlProvider resourceUrlProvider) {
HandlerMapping mapping = super.resourceHandlerMapping(urlPathHelper, pathMatcher, contentNegotiationManager,
conversionService, resourceUrlProvider);
if (mapping instanceof SimpleUrlHandlerMapping) {
addServletContextResourceHandlerMapping((SimpleUrlHandlerMapping) mapping);
}
return mapping;
}

private void addServletContextResourceHandlerMapping(SimpleUrlHandlerMapping mapping) {
Map<String, ?> urlMap = mapping.getUrlMap();
String pattern = this.mvcProperties.getStaticPathPattern();
Object handler = urlMap.get(pattern);
if (handler instanceof ResourceHttpRequestHandler
&& this.autoConfiguredResourceHandlers.contains(pattern)) {
addServletContextResourceHandlerMapping((ResourceHttpRequestHandler) handler);
}
}

private void addServletContextResourceHandlerMapping(ResourceHttpRequestHandler handler) {
ServletContext servletContext = getServletContext();
if (servletContext != null) {
List<Resource> locations = handler.getLocations();
locations.add(new ServletContextResource(servletContext, SERVLET_LOCATION));
}
}

@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(),
this.resourceProperties.getStaticLocations());

}

private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, String... locations) {
if (registry.hasMappingForPattern(pattern)) {
return;
}
ResourceHandlerRegistration registration = registry.addResourceHandler(pattern);
registration.addResourceLocations(locations);
registration.setCachePeriod(getSeconds(this.resourceProperties.getCache().getPeriod()));
registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
customizeResourceHandlerRegistration(registration);
this.autoConfiguredResourceHandlers.add(pattern);
}

private Integer getSeconds(Duration cachePeriod) {
return (cachePeriod != null) ? (int) cachePeriod.getSeconds() : null;
}

private void customizeResourceHandlerRegistration(ResourceHandlerRegistration registration) {
if (this.resourceHandlerRegistrationCustomizer != null) {
this.resourceHandlerRegistrationCustomizer.customize(registration);
}
}

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
Expand All @@ -440,22 +477,34 @@ public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext ap
return welcomePageHandlerMapping;
}

private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
private Resource getWelcomePage() {
for (String location : this.resourceProperties.getStaticLocations()) {
Resource indexHtml = getIndexHtml(location);
if (indexHtml != null) {
return indexHtml;
}
}
ServletContext servletContext = getServletContext();
if (servletContext != null) {
return getIndexHtml(new ServletContextResource(servletContext, SERVLET_LOCATION));
}
return null;
}

private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
return getIndexHtml(this.resourceLoader.getResource(location));
}

private boolean isReadable(Resource resource) {
private Resource getIndexHtml(Resource location) {
try {
return resource.exists() && (resource.getURL() != null);
Resource resource = location.createRelative("index.html");
if (resource.exists() && (resource.getURL() != null)) {
return resource;
}
}
catch (Exception ex) {
return false;
}
return null;
}

@Bean
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
Expand All @@ -18,7 +18,6 @@

import java.util.Collections;
import java.util.List;
import java.util.Optional;

import javax.servlet.http.HttpServletRequest;

Expand Down Expand Up @@ -49,9 +48,9 @@ final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping {
private static final List<MediaType> MEDIA_TYPES_ALL = Collections.singletonList(MediaType.ALL);

WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {
if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
logger.info("Adding welcome page: " + welcomePage.get());
ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) {
if (welcomePage != null && "/**".equals(staticPathPattern)) {
logger.info("Adding welcome page: " + welcomePage);
setRootViewName("forward:index.html");
}
else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
Expand Down Expand Up @@ -913,7 +913,7 @@ private void assertCacheControl(AssertableWebApplicationContext context) {
protected Map<String, List<Resource>> getResourceMappingLocations(ApplicationContext context) {
Object bean = context.getBean("resourceHandlerMapping");
if (bean instanceof HandlerMapping) {
return getMappingLocations(context.getBean("resourceHandlerMapping", HandlerMapping.class));
return getMappingLocations((HandlerMapping) bean);
}
assertThat(bean.toString()).isEqualTo("null");
return Collections.emptyMap();
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
Expand All @@ -18,7 +18,6 @@

import java.util.Collections;
import java.util.Map;
import java.util.Optional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
Expand Down Expand Up @@ -162,8 +161,7 @@ WelcomePageHandlerMapping handlerMapping(ApplicationContext applicationContext,
return new WelcomePageHandlerMapping(
templateAvailabilityProviders
.getIfAvailable(() -> new TemplateAvailabilityProviders(applicationContext)),
applicationContext, Optional.ofNullable(staticIndexPage.getIfAvailable()), staticPathPattern);

applicationContext, staticIndexPage.getIfAvailable(), staticPathPattern);
}

}
Expand Down

0 comments on commit 72dd3b5

Please sign in to comment.