From 08ef5f4b1feb1033a5053a8d5bf6c439f2cf2db0 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 26 Apr 2016 20:39:35 -0700 Subject: [PATCH] Make ErrorPageRegistry first class concern Create ErrorPageRegistry and ErrorPageRegistrar interfaces that allow error page registration to be a first class concern. Prior to this commit ErrorPageFilter needed to implement ConfigurableEmbeddedServletContainer in order to receive ErrorPage registrations. Closes gh-5789 --- ...ddedServletContainerAutoConfiguration.java | 16 +++- .../web/ErrorMvcAutoConfiguration.java | 13 +-- .../WebSocketContainerCustomizer.java | 14 +--- .../RemappedErrorViewIntegrationTests.java | 10 +-- .../ConfigurableEmbeddedServletContainer.java | 9 +- .../boot/context/web/ErrorPageFilter.java | 4 +- .../NonEmbeddedServletContainerFactory.java | 5 +- .../boot/web/servlet/ErrorPageRegistrar.java | 33 ++++++++ .../ErrorPageRegistrarBeanPostProcessor.java | 82 +++++++++++++++++++ .../boot/web/servlet/ErrorPageRegistry.java | 33 ++++++++ .../boot/web/support/ErrorPageFilter.java | 15 ++-- 11 files changed, 187 insertions(+), 47 deletions(-) create mode 100644 spring-boot/src/main/java/org/springframework/boot/web/servlet/ErrorPageRegistrar.java create mode 100644 spring-boot/src/main/java/org/springframework/boot/web/servlet/ErrorPageRegistrarBeanPostProcessor.java create mode 100644 spring-boot/src/main/java/org/springframework/boot/web/servlet/ErrorPageRegistry.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java index 76e7d13f6c..4c2ab7cfd9 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -37,12 +37,13 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.SearchStrategy; -import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration.EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar; +import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration.BeanPostProcessorsRegistrar; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory; +import org.springframework.boot.web.servlet.ErrorPageRegistrarBeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -61,7 +62,7 @@ import org.springframework.util.ObjectUtils; @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication -@Import(EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar.class) +@Import(BeanPostProcessorsRegistrar.class) public class EmbeddedServletContainerAutoConfiguration { /** @@ -114,7 +115,7 @@ public class EmbeddedServletContainerAutoConfiguration { * Registers a {@link EmbeddedServletContainerCustomizerBeanPostProcessor}. Registered * via {@link ImportBeanDefinitionRegistrar} for early registration. */ - public static class EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar + public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; @@ -141,6 +142,13 @@ public class EmbeddedServletContainerAutoConfiguration { EmbeddedServletContainerCustomizerBeanPostProcessor.class)); } + if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType( + ErrorPageRegistrarBeanPostProcessor.class, true, false))) { + registry.registerBeanDefinition("errorPageRegistrarBeanPostProcessor", + new RootBeanDefinition( + ErrorPageRegistrarBeanPostProcessor.class)); + + } } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration.java index a4f6dfecd8..821ab0c7ac 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration.java @@ -39,9 +39,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider; -import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.web.servlet.ErrorPage; +import org.springframework.boot.web.servlet.ErrorPageRegistrar; +import org.springframework.boot.web.servlet.ErrorPageRegistry; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; @@ -257,8 +258,7 @@ public class ErrorMvcAutoConfiguration { * {@link EmbeddedServletContainerCustomizer} that configures the container's error * pages. */ - private static class ErrorPageCustomizer - implements EmbeddedServletContainerCustomizer, Ordered { + private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered { private final ServerProperties properties; @@ -267,9 +267,10 @@ public class ErrorMvcAutoConfiguration { } @Override - public void customize(ConfigurableEmbeddedServletContainer container) { - container.addErrorPages(new ErrorPage(this.properties.getServletPrefix() - + this.properties.getError().getPath())); + public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { + ErrorPage errorPage = new ErrorPage(this.properties.getServletPrefix() + + this.properties.getError().getPath()); + errorPageRegistry.addErrorPages(errorPage); } @Override diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketContainerCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketContainerCustomizer.java index 9611467122..3440e0167d 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketContainerCustomizer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketContainerCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -16,13 +16,9 @@ package org.springframework.boot.autoconfigure.websocket; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; -import org.springframework.boot.context.web.NonEmbeddedServletContainerFactory; import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; @@ -39,8 +35,6 @@ import org.springframework.core.ResolvableType; public abstract class WebSocketContainerCustomizer implements EmbeddedServletContainerCustomizer, Ordered { - private Log logger = LogFactory.getLog(getClass()); - @Override public int getOrder() { return 0; @@ -49,12 +43,6 @@ public abstract class WebSocketContainerCustomizer registrars; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + if (bean instanceof ErrorPageRegistry) { + postProcessBeforeInitialization((ErrorPageRegistry) bean); + } + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { + return bean; + } + + private void postProcessBeforeInitialization(ErrorPageRegistry registry) { + for (ErrorPageRegistrar registrar : getRegistrars()) { + registrar.registerErrorPages(registry); + } + } + + private Collection getRegistrars() { + if (this.registrars == null) { + // Look up does not include the parent context + this.registrars = new ArrayList(this.applicationContext + .getBeansOfType(ErrorPageRegistrar.class, false, false).values()); + Collections.sort(this.registrars, AnnotationAwareOrderComparator.INSTANCE); + this.registrars = Collections.unmodifiableList(this.registrars); + } + return this.registrars; + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/web/servlet/ErrorPageRegistry.java b/spring-boot/src/main/java/org/springframework/boot/web/servlet/ErrorPageRegistry.java new file mode 100644 index 0000000000..a4eaccde52 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/web/servlet/ErrorPageRegistry.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.web.servlet; + +/** + * Interface for a registry that holds {@link ErrorPage ErrorPages}. + * + * @author Phillip Webb + * @since 1.4.0 + */ +public interface ErrorPageRegistry { + + /** + * Adds error pages that will be used when handling exceptions. + * @param errorPages the error pages + */ + void addErrorPages(ErrorPage... errorPages); + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/web/support/ErrorPageFilter.java b/spring-boot/src/main/java/org/springframework/boot/web/support/ErrorPageFilter.java index 28d8ed8627..877673b493 100644 --- a/spring-boot/src/main/java/org/springframework/boot/web/support/ErrorPageFilter.java +++ b/spring-boot/src/main/java/org/springframework/boot/web/support/ErrorPageFilter.java @@ -33,10 +33,9 @@ import javax.servlet.http.HttpServletResponseWrapper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer; -import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; -import org.springframework.boot.context.web.NonEmbeddedServletContainerFactory; import org.springframework.boot.web.servlet.ErrorPage; +import org.springframework.boot.web.servlet.ErrorPageRegistrar; +import org.springframework.boot.web.servlet.ErrorPageRegistry; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @@ -44,14 +43,13 @@ import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.NestedServletException; /** - * A special {@link AbstractConfigurableEmbeddedServletContainer} for non-embedded + * A Servlet {@link Filter} that provides an {@link ErrorPageRegistry} for non-embedded * applications (i.e. deployed WAR files). It registers error pages and handles * application errors by filtering requests and forwarding to the error pages instead of * letting the container handle them. Error pages are a feature of the servlet spec but * there is no Java API for registering them in the spec. This filter works around that by - * accepting error page registrations from Spring Boot's - * {@link EmbeddedServletContainerCustomizer} (any beans of that type in the context will - * be applied to this container). + * accepting error page registrations from Spring Boot's {@link ErrorPageRegistrar} (any + * beans of that type in the context will be applied to this container). * * @author Dave Syer * @author Phillip Webb @@ -60,8 +58,7 @@ import org.springframework.web.util.NestedServletException; */ @Component @Order(Ordered.HIGHEST_PRECEDENCE) -public class ErrorPageFilter extends AbstractConfigurableEmbeddedServletContainer - implements Filter, NonEmbeddedServletContainerFactory { +public class ErrorPageFilter implements Filter, ErrorPageRegistry { private static final Log logger = LogFactory.getLog(ErrorPageFilter.class);