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
pull/5816/merge
Phillip Webb 9 years ago
parent 609cb52cd4
commit 08ef5f4b1f

@ -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));
}
}
}

@ -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

@ -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<T extends EmbeddedServletContainerFactory>
implements EmbeddedServletContainerCustomizer, Ordered {
private Log logger = LogFactory.getLog(getClass());
@Override
public int getOrder() {
return 0;
@ -49,12 +43,6 @@ public abstract class WebSocketContainerCustomizer<T extends EmbeddedServletCont
@SuppressWarnings("unchecked")
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof NonEmbeddedServletContainerFactory) {
this.logger.info("NonEmbeddedServletContainerFactory "
+ "detected. Websockets support should be native so this "
+ "normally is not a problem.");
return;
}
if (getContainerType().isAssignableFrom(container.getClass())) {
doCustomize((T) container);
}

@ -21,13 +21,13 @@ import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
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.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Controller;
@ -76,7 +76,7 @@ public class RemappedErrorViewIntegrationTests {
EmbeddedServletContainerAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, ErrorMvcAutoConfiguration.class })
@Controller
public static class TestConfiguration implements EmbeddedServletContainerCustomizer {
public static class TestConfiguration implements ErrorPageRegistrar {
@RequestMapping("/")
public String home() {
@ -84,8 +84,8 @@ public class RemappedErrorViewIntegrationTests {
}
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage("/spring/error"));
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
errorPageRegistry.addErrorPages(new ErrorPage("/spring/error"));
}
// For manual testing

@ -23,6 +23,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.boot.web.servlet.ErrorPageRegistry;
import org.springframework.boot.web.servlet.ServletContextInitializer;
/**
@ -36,7 +37,7 @@ import org.springframework.boot.web.servlet.ServletContextInitializer;
* @see EmbeddedServletContainerFactory
* @see EmbeddedServletContainerCustomizer
*/
public interface ConfigurableEmbeddedServletContainer {
public interface ConfigurableEmbeddedServletContainer extends ErrorPageRegistry {
/**
* Sets the context path for the embedded servlet container. The context should start
@ -102,12 +103,6 @@ public interface ConfigurableEmbeddedServletContainer {
*/
void setRegisterDefaultServlet(boolean registerDefaultServlet);
/**
* Adds error pages that will be used when handling exceptions.
* @param errorPages the error pages
*/
void addErrorPages(ErrorPage... errorPages);
/**
* Sets the error pages that will be used when handling exceptions.
* @param errorPages the error pages

@ -18,8 +18,8 @@ package org.springframework.boot.context.web;
import javax.servlet.Filter;
import org.springframework.boot.context.embedded.ErrorPageRegistrar;
import org.springframework.boot.context.embedded.ErrorPageRegistry;
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;

@ -1,5 +1,5 @@
/*
* Copyright 2012-2013 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.
@ -17,13 +17,16 @@
package org.springframework.boot.context.web;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.web.servlet.ErrorPageRegistry;
/**
* Marker interface for {@link EmbeddedServletContainerFactory} types that are actually
* safe to run in a non-embedded container.
*
* @author Dave Syer
* @deprecated as of 1.4 in favor of {@link ErrorPageRegistry}
*/
@Deprecated
public interface NonEmbeddedServletContainerFactory {
}

@ -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 to be implemented by types that register {@link ErrorPage ErrorPages}.
*
* @author Phillip Webb
* @since 1.4.0
*/
public interface ErrorPageRegistrar {
/**
* Register pages as required with the given registry.
* @param registry the error page registry
*/
void registerErrorPages(ErrorPageRegistry registry);
}

@ -0,0 +1,82 @@
/*
* 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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
/**
* {@link BeanPostProcessor} that apply all {@link ErrorPageRegistrar}s from the bean
* factory to {@link ErrorPageRegistry} beans.
*
* @author Phillip Webb
* @since 1.4.0
*/
public class ErrorPageRegistrarBeanPostProcessor
implements BeanPostProcessor, ApplicationContextAware {
private ApplicationContext applicationContext;
private List<ErrorPageRegistrar> 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<ErrorPageRegistrar> getRegistrars() {
if (this.registrars == null) {
// Look up does not include the parent context
this.registrars = new ArrayList<ErrorPageRegistrar>(this.applicationContext
.getBeansOfType(ErrorPageRegistrar.class, false, false).values());
Collections.sort(this.registrars, AnnotationAwareOrderComparator.INSTANCE);
this.registrars = Collections.unmodifiableList(this.registrars);
}
return this.registrars;
}
}

@ -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);
}

@ -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);

Loading…
Cancel
Save