diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ErrorMvcAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ErrorMvcAutoConfiguration.java index 4b3185a6c2..cef6bb7bbe 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ErrorMvcAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ErrorMvcAutoConfiguration.java @@ -42,6 +42,7 @@ import org.springframework.boot.context.embedded.ErrorPage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; import org.springframework.context.expression.MapAccessor; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.expression.Expression; @@ -52,6 +53,7 @@ import org.springframework.util.PropertyPlaceholderHelper; import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.View; +import org.springframework.web.servlet.view.BeanNameViewResolver; /** * {@link EnableAutoConfiguration Auto-configuration} to render errors via a MVC error @@ -80,19 +82,34 @@ public class ErrorMvcAutoConfiguration implements EmbeddedServletContainerCustom factory.addErrorPages(new ErrorPage(this.errorPath)); } - private SpelView defaultErrorView = new SpelView( - "

Whitelabel Error Page

" - + "

This application has no explicit mapping for /error, so you are seeing this as a fallback.

" - + "
${timestamp}
" - + "
There was an unexpected error (type=${error}, status=${status}).
" - + "
${message}
" + ""); - - @Bean(name = "error") - @ConditionalOnMissingBean(name = "error") + @Configuration @ConditionalOnExpression("${error.whitelabel.enabled:true}") @Conditional(ErrorTemplateMissingCondition.class) - public View defaultErrorView() { - return this.defaultErrorView; + protected static class WhitelabelErrorViewConfiguration { + + private SpelView defaultErrorView = new SpelView( + "

Whitelabel Error Page

" + + "

This application has no explicit mapping for /error, so you are seeing this as a fallback.

" + + "
${timestamp}
" + + "
There was an unexpected error (type=${error}, status=${status}).
" + + "
${message}
" + ""); + + @Bean(name = "error") + @ConditionalOnMissingBean(name = "error") + public View defaultErrorView() { + return this.defaultErrorView; + } + + // If the user adds @EnableWebMvc then the bean name view resolver from + // WebMvcAutoConfiguration disappears, so add it back in to avoid disappointment. + @Bean + @ConditionalOnMissingBean(BeanNameViewResolver.class) + public BeanNameViewResolver beanNameViewResolver() { + BeanNameViewResolver resolver = new BeanNameViewResolver(); + resolver.setOrder(0); + return resolver; + } + } private static class ErrorTemplateMissingCondition extends SpringBootCondition { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java index 7d989bb29a..5f014eb5d8 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java @@ -16,6 +16,8 @@ package org.springframework.boot.actuate.autoconfigure; +import java.util.Map; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -30,11 +32,13 @@ import org.springframework.boot.actuate.endpoint.MetricsEndpoint; import org.springframework.boot.actuate.endpoint.ShutdownEndpoint; import org.springframework.boot.actuate.endpoint.TraceEndpoint; import org.springframework.boot.autoconfigure.AutoConfigurationReport; +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; /** * Tests for {@link EndpointAutoConfiguration}. @@ -73,6 +77,21 @@ public class EndpointAutoConfigurationTests { assertNotNull(this.context.getBean(TraceEndpoint.class)); } + @Test + public void healthEndpoint() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(EndpointAutoConfiguration.class, + EmbeddedDataSourceConfiguration.class); + this.context.refresh(); + HealthEndpoint bean = this.context.getBean(HealthEndpoint.class); + assertNotNull(bean); + @SuppressWarnings("unchecked") + Map result = (Map) bean.invoke(); + assertNotNull(result); + assertTrue("Wrong result: " + result, result.containsKey("status")); + assertTrue("Wrong result: " + result, result.containsKey("database")); + } + @Test public void autoconfigurationAuditEndpoints() { this.context = new AnnotationConfigApplicationContext(); diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerSpecialIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerSpecialIntegrationTests.java new file mode 100644 index 0000000000..dcdbfa5369 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerSpecialIntegrationTests.java @@ -0,0 +1,109 @@ +/* + * Copyright 2012-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. + * 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.actuate.web; + +import org.junit.After; +import org.junit.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.ConfigurableWebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Dave Syer + */ +public class BasicErrorControllerSpecialIntegrationTests { + + private ConfigurableWebApplicationContext wac; + + private MockMvc mockMvc; + + @After + public void close() { + if (this.wac != null) { + this.wac.close(); + } + } + + public void setup(ConfigurableWebApplicationContext context) { + this.wac = context; + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + } + + @Test + public void errorPageAvailableWithParentContext() throws Exception { + setup((ConfigurableWebApplicationContext) new SpringApplicationBuilder( + ParentConfiguration.class).child(ChildConfiguration.class).run()); + MvcResult response = this.mockMvc + .perform(get("/error").accept(MediaType.TEXT_HTML)) + .andExpect(status().isOk()).andReturn(); + String content = response.getResponse().getContentAsString(); + assertTrue("Wrong content: " + content, content.contains("status=999")); + } + + @Test + public void errorPageAvailableWithMvcIncluded() throws Exception { + setup((ConfigurableWebApplicationContext) SpringApplication + .run(WebMvcIncludedConfiguration.class)); + MvcResult response = this.mockMvc + .perform(get("/error").accept(MediaType.TEXT_HTML)) + .andExpect(status().isOk()).andReturn(); + String content = response.getResponse().getContentAsString(); + assertTrue("Wrong content: " + content, content.contains("status=999")); + } + + @Configuration + // TODO: fix this so it doesn't need to be excluded + @EnableAutoConfiguration(exclude = ServerPropertiesAutoConfiguration.class) + protected static class ParentConfiguration { + + } + + @Configuration + @EnableAutoConfiguration + @EnableWebMvc + protected static class WebMvcIncludedConfiguration { + // For manual testing + public static void main(String[] args) { + SpringApplication.run(WebMvcIncludedConfiguration.class, args); + } + + } + + @Configuration + @EnableAutoConfiguration + protected static class ChildConfiguration { + + // For manual testing + public static void main(String[] args) { + new SpringApplicationBuilder(ParentConfiguration.class).child( + ChildConfiguration.class).run(args); + } + } + +}