From b5c435f8c450cd0a26b86826e802d98c73fd9b42 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 30 Jun 2015 18:46:37 -0700 Subject: [PATCH] Polish Actuator MVC customization support See gh-3345 --- .../EndpointWebMvcAutoConfiguration.java | 102 ++++---- ...dpointWebMvcChildContextConfiguration.java | 219 +++++++++--------- .../EndpointWebMvcConfiguration.java | 12 +- .../EndpointWebMvcImportSelector.java | 11 +- .../ManagementContextResolver.java | 62 +++++ .../ManagementSecurityAutoConfiguration.java | 35 +-- .../ManagementServerProperties.java | 2 +- .../condition/OnManagementMvcCondition.java | 22 +- .../endpoint/mvc/EndpointHandlerMapping.java | 1 - .../actuate/endpoint/mvc/MvcEndpoints.java | 5 +- .../EndpointWebMvcAutoConfigurationTests.java | 43 ++-- .../mvc/EnvironmentMvcEndpointTests.java | 12 +- .../endpoint/mvc/MetricsMvcEndpointTests.java | 12 +- .../autoconfigure/web/ResourceProperties.java | 10 +- .../web/WebMvcAutoConfiguration.java | 11 +- .../web/WelcomePageMockMvcTests.java | 21 +- .../asciidoc/production-ready-features.adoc | 26 +-- .../main/asciidoc/spring-boot-features.adoc | 11 +- 18 files changed, 345 insertions(+), 272 deletions(-) create mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementContextResolver.java diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java index f11e908282..7319053005 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java @@ -17,9 +17,6 @@ package org.springframework.boot.actuate.autoconfigure; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -36,7 +33,6 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.boot.actuate.condition.ConditionalOnManagementMvcContext; import org.springframework.boot.actuate.endpoint.Endpoint; -import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; @@ -82,7 +78,8 @@ import org.springframework.web.servlet.DispatcherServlet; @AutoConfigureAfter({ PropertyPlaceholderAutoConfiguration.class, EmbeddedServletContainerAutoConfiguration.class, WebMvcAutoConfiguration.class, ManagementServerPropertiesAutoConfiguration.class }) -public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, SmartInitializingSingleton { +public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, + SmartInitializingSingleton { private static Log logger = LogFactory.getLog(EndpointWebMvcAutoConfiguration.class); @@ -123,38 +120,19 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, } private void createChildManagementContext() { - final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext(); childContext.setParent(this.applicationContext); childContext.setNamespace("management"); childContext.setId(this.applicationContext.getId() + ":management"); - - List> configurations = new ArrayList>(); - configurations.addAll(Arrays.> asList( - EndpointWebMvcChildContextConfiguration.class, + childContext.register(EndpointWebMvcChildContextConfiguration.class, PropertyPlaceholderAutoConfiguration.class, EmbeddedServletContainerAutoConfiguration.class, - DispatcherServletAutoConfiguration.class)); - // Register the ManagementServerChildContextConfiguration first followed - // by various specific AutoConfiguration classes. NOTE: The child context - // is intentionally not completely auto-configured. - childContext.register(configurations.toArray(new Class[0])); - - // Ensure close on the parent also closes the child - if (this.applicationContext instanceof ConfigurableApplicationContext) { - ((ConfigurableApplicationContext) this.applicationContext) - .addApplicationListener(new ApplicationListener() { - @Override - public void onApplicationEvent(ContextClosedEvent event) { - if (event.getApplicationContext() == EndpointWebMvcAutoConfiguration.this.applicationContext) { - childContext.close(); - } - } - }); - } - managementContextResolver().setApplicationContext(childContext); + DispatcherServletAutoConfiguration.class); + CloseEventPropagationListener + .addIfPossible(this.applicationContext, childContext); try { childContext.refresh(); + managementContextResolver().setApplicationContext(childContext); } catch (RuntimeException ex) { // No support currently for deploying a war with management.port=, @@ -231,6 +209,45 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, } + /** + * {@link ApplicationListener} to propagate the {@link ContextClosedEvent} from a + * parent to a child. + */ + private static class CloseEventPropagationListener implements + ApplicationListener { + + private final ApplicationContext parentContext; + + private final ConfigurableApplicationContext childContext; + + public CloseEventPropagationListener(ApplicationContext parentContext, + ConfigurableApplicationContext childContext) { + this.parentContext = parentContext; + this.childContext = childContext; + } + + @Override + public void onApplicationEvent(ContextClosedEvent event) { + if (event.getApplicationContext() == this.parentContext) { + this.childContext.close(); + } + } + + public static void addIfPossible(ApplicationContext parentContext, + ConfigurableApplicationContext childContext) { + if (parentContext instanceof ConfigurableApplicationContext) { + add((ConfigurableApplicationContext) parentContext, childContext); + } + } + + private static void add(ConfigurableApplicationContext parentContext, + ConfigurableApplicationContext childContext) { + parentContext.addApplicationListener(new CloseEventPropagationListener( + parentContext, childContext)); + } + + } + protected static enum ManagementServerPort { DISABLE, SAME, DIFFERENT; @@ -267,31 +284,4 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, } - public static class ManagementContextResolver { - - private ApplicationContext applicationContext; - - public ManagementContextResolver(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - public ApplicationContext getApplicationContext() { - return this.applicationContext; - } - - public void setApplicationContext(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - public MvcEndpoints getMvcEndpoints() { - try { - return applicationContext.getBean(MvcEndpoints.class); - } - catch (Exception e) { - return null; - } - } - - } - } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java index b22bb2f2f6..623e720677 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java @@ -72,6 +72,8 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc; * @see EndpointWebMvcAutoConfiguration */ @Configuration +@EnableWebMvc +@Import(EndpointWebMvcImportSelector.class) public class EndpointWebMvcChildContextConfiguration { private static Log logger = LogFactory @@ -83,14 +85,119 @@ public class EndpointWebMvcChildContextConfiguration { @Autowired private ManagementServerProperties managementServerProperties; + @Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) + public DispatcherServlet dispatcherServlet() { + DispatcherServlet dispatcherServlet = new DispatcherServlet(); + // Ensure the parent configuration does not leak down to us + dispatcherServlet.setDetectAllHandlerAdapters(false); + dispatcherServlet.setDetectAllHandlerExceptionResolvers(false); + dispatcherServlet.setDetectAllHandlerMappings(false); + dispatcherServlet.setDetectAllViewResolvers(false); + return dispatcherServlet; + } + + @Bean(name = DispatcherServlet.HANDLER_MAPPING_BEAN_NAME) + public CompositeHandlerMapping compositeHandlerMapping() { + return new CompositeHandlerMapping(); + } + + @Bean(name = DispatcherServlet.HANDLER_ADAPTER_BEAN_NAME) + public CompositeHandlerAdapter compositeHandlerAdapter() { + return new CompositeHandlerAdapter(); + } + + @Bean(name = DispatcherServlet.HANDLER_EXCEPTION_RESOLVER_BEAN_NAME) + public CompositeHandlerExceptionResolver compositeHandlerExceptionResolver() { + return new CompositeHandlerExceptionResolver(); + } + + @Bean + public ServerCustomization serverCustomization() { + return new ServerCustomization(); + } + + /* + * The error controller is present but not mapped as an endpoint in this context + * because of the DispatcherServlet having had it's HandlerMapping explicitly + * disabled. So we expose the same feature but only for machine endpoints. + */ + @Bean + public ManagementErrorEndpoint errorEndpoint(final ErrorAttributes errorAttributes) { + return new ManagementErrorEndpoint(this.errorPath, errorAttributes); + } + + /** + * Configuration to add {@link HandlerMapping} for {@link MvcEndpoint}s. See + * {@link SecureEndpointHandlerMappingConfiguration} for an extended version that also + * configures the security filter. + */ @Configuration - @Import(EndpointWebMvcImportSelector.class) - protected static class EndpointWebMvcConfiguration { + @ConditionalOnMissingClass("org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter") + protected static class EndpointHandlerMappingConfiguration { + + @Autowired + public void handlerMapping(MvcEndpoints endpoints, + ListableBeanFactory beanFactory, EndpointHandlerMapping mapping) { + // In a child context we definitely want to see the parent endpoints + mapping.setDetectHandlerMethodsInAncestorContexts(true); + postProcessMapping(beanFactory, mapping); + } + + /** + * Hook to allow additional post processing of {@link EndpointHandlerMapping}. + * @param beanFactory the source bean factory + * @param mapping the mapping to customize + */ + protected void postProcessMapping(ListableBeanFactory beanFactory, + EndpointHandlerMapping mapping) { + } + + } + + /** + * Extension of {@link EndpointHandlerMappingConfiguration} that also configures the + * security filter. + */ + @Configuration + @ConditionalOnClass(WebSecurityConfigurerAdapter.class) + protected static class SecureEndpointHandlerMappingConfiguration extends + EndpointHandlerMappingConfiguration { + + @Override + protected void postProcessMapping(ListableBeanFactory beanFactory, + EndpointHandlerMapping mapping) { + // The parent context has the security filter, so we need to get it injected + // with our EndpointHandlerMapping if we can. + if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, + ManagementWebSecurityConfigurerAdapter.class).length == 1) { + ManagementWebSecurityConfigurerAdapter bean = beanFactory + .getBean(ManagementWebSecurityConfigurerAdapter.class); + bean.setEndpointHandlerMapping(mapping); + } + else { + logger.warn("No single bean of type " + + ManagementWebSecurityConfigurerAdapter.class.getSimpleName() + + " found (this might make some endpoints inaccessible without authentication)"); + } + } + } @Configuration - protected static class ServerCustomization implements - EmbeddedServletContainerCustomizer, Ordered { + @ConditionalOnClass({ EnableWebSecurity.class, Filter.class }) + @ConditionalOnBean(name = "springSecurityFilterChain", search = SearchStrategy.PARENTS) + public static class EndpointWebMvcChildContextSecurityConfiguration { + + @Bean + public Filter springSecurityFilterChain(HierarchicalBeanFactory beanFactory) { + BeanFactory parent = beanFactory.getParentBeanFactory(); + return parent.getBean("springSecurityFilterChain", Filter.class); + } + + } + + static class ServerCustomization implements EmbeddedServletContainerCustomizer, + Ordered { @Value("${error.path:/error}") private String errorPath = "/error"; @@ -131,22 +238,7 @@ public class EndpointWebMvcChildContextConfiguration { } - @Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) - public DispatcherServlet dispatcherServlet() { - DispatcherServlet dispatcherServlet = new DispatcherServlet(); - - // Ensure the parent configuration does not leak down to us - dispatcherServlet.setDetectAllHandlerAdapters(false); - dispatcherServlet.setDetectAllHandlerExceptionResolvers(false); - dispatcherServlet.setDetectAllHandlerMappings(false); - dispatcherServlet.setDetectAllViewResolvers(false); - - return dispatcherServlet; - } - - @Configuration(DispatcherServlet.HANDLER_MAPPING_BEAN_NAME) - @EnableWebMvc - public static class CompositeHandlerMapping implements HandlerMapping { + static class CompositeHandlerMapping implements HandlerMapping { @Autowired private ListableBeanFactory beanFactory; @@ -178,8 +270,7 @@ public class EndpointWebMvcChildContextConfiguration { } - @Configuration(DispatcherServlet.HANDLER_ADAPTER_BEAN_NAME) - public static class CompositeHandlerAdapter implements HandlerAdapter { + static class CompositeHandlerAdapter implements HandlerAdapter { @Autowired private ListableBeanFactory beanFactory; @@ -236,9 +327,7 @@ public class EndpointWebMvcChildContextConfiguration { } - @Configuration(DispatcherServlet.HANDLER_EXCEPTION_RESOLVER_BEAN_NAME) - public static class CompositeHandlerExceptionResolver implements - HandlerExceptionResolver { + static class CompositeHandlerExceptionResolver implements HandlerExceptionResolver { @Autowired private ListableBeanFactory beanFactory; @@ -272,84 +361,4 @@ public class EndpointWebMvcChildContextConfiguration { } - /* - * The error controller is present but not mapped as an endpoint in this context - * because of the DispatcherServlet having had it's HandlerMapping explicitly - * disabled. So we expose the same feature but only for machine endpoints. - */ - @Bean - public ManagementErrorEndpoint errorEndpoint(final ErrorAttributes errorAttributes) { - return new ManagementErrorEndpoint(this.errorPath, errorAttributes); - } - - /** - * Configuration to add {@link HandlerMapping} for {@link MvcEndpoint}s. See - * {@link SecureEndpointHandlerMappingConfiguration} for an extended version that also - * configures the security filter. - */ - @Configuration - @ConditionalOnMissingClass("org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter") - protected static class EndpointHandlerMappingConfiguration { - - @Autowired - public void handlerMapping(MvcEndpoints endpoints, - ListableBeanFactory beanFactory, EndpointHandlerMapping mapping) { - // In a child context we definitely want to see the parent endpoints - mapping.setDetectHandlerMethodsInAncestorContexts(true); - postProcessMapping(beanFactory, mapping); - } - - /** - * Hook to allow additional post processing of {@link EndpointHandlerMapping}. - * @param beanFactory the source bean factory - * @param mapping the mapping to customize - */ - protected void postProcessMapping(ListableBeanFactory beanFactory, - EndpointHandlerMapping mapping) { - } - - } - - /** - * Extension of {@link EndpointHandlerMappingConfiguration} that also configures the - * security filter. - */ - @Configuration - @ConditionalOnClass(WebSecurityConfigurerAdapter.class) - protected static class SecureEndpointHandlerMappingConfiguration extends - EndpointHandlerMappingConfiguration { - - @Override - protected void postProcessMapping(ListableBeanFactory beanFactory, - EndpointHandlerMapping mapping) { - // The parent context has the security filter, so we need to get it injected - // with our EndpointHandlerMapping if we can. - if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, - ManagementWebSecurityConfigurerAdapter.class).length == 1) { - ManagementWebSecurityConfigurerAdapter bean = beanFactory - .getBean(ManagementWebSecurityConfigurerAdapter.class); - bean.setEndpointHandlerMapping(mapping); - } - else { - logger.warn("No single bean of type " - + ManagementWebSecurityConfigurerAdapter.class.getSimpleName() - + " found (this might make some endpoints inaccessible without authentication)"); - } - } - - } - - @Configuration - @ConditionalOnClass({ EnableWebSecurity.class, Filter.class }) - @ConditionalOnBean(name = "springSecurityFilterChain", search = SearchStrategy.PARENTS) - public static class EndpointWebMvcChildContextSecurityConfiguration { - - @Bean - public Filter springSecurityFilterChain(HierarchicalBeanFactory beanFactory) { - BeanFactory parent = beanFactory.getParentBeanFactory(); - return parent.getBean("springSecurityFilterChain", Filter.class); - } - - } - } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcConfiguration.java index d33d292d48..f205ef9ee7 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2012-2015 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. @@ -22,6 +22,7 @@ import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties.Security; import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint; +import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint; import org.springframework.boot.actuate.endpoint.HealthEndpoint; import org.springframework.boot.actuate.endpoint.MetricsEndpoint; @@ -45,12 +46,14 @@ import org.springframework.util.CollectionUtils; import org.springframework.web.cors.CorsConfiguration; /** - * @author Dave Syer + * Configuration to expose {@link Endpoint} instances over Spring MVC. * + * @author Dave Syer + * @since 1.3.0 */ @Configuration @EnableConfigurationProperties({ HealthMvcEndpointProperties.class, - EndpointCorsProperties.class }) + EndpointCorsProperties.class }) public class EndpointWebMvcConfiguration { @Autowired @@ -75,7 +78,8 @@ public class EndpointWebMvcConfiguration { CorsConfiguration corsConfiguration = getCorsConfiguration(this.corsProperties); EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints, corsConfiguration); - boolean disabled = this.managementServerProperties.getPort()!=null && this.managementServerProperties.getPort()==-1; + boolean disabled = this.managementServerProperties.getPort() != null + && this.managementServerProperties.getPort() == -1; mapping.setDisabled(disabled); if (!disabled) { mapping.setPrefix(this.managementServerProperties.getContextPath()); diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcImportSelector.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcImportSelector.java index 2019455705..6e0268dc1d 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcImportSelector.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcImportSelector.java @@ -29,27 +29,26 @@ import org.springframework.core.type.AnnotationMetadata; * Selects configuration classes for the Actuator MVC endpoints. Customize the MVC * endpoints by adding an entries to /META-INF/spring.factories under the * {@link EndpointWebMvcConfiguration} key. - * - * @author Dave Syer * + * @author Dave Syer */ -public class EndpointWebMvcImportSelector implements DeferredImportSelector, +class EndpointWebMvcImportSelector implements DeferredImportSelector, BeanClassLoaderAware { - private ClassLoader beanClassLoader; + private ClassLoader classLoader; @Override public String[] selectImports(AnnotationMetadata metadata) { // Find all possible auto configuration classes, filtering duplicates List factories = new ArrayList(new LinkedHashSet( SpringFactoriesLoader.loadFactoryNames(EndpointWebMvcConfiguration.class, - this.beanClassLoader))); + this.classLoader))); return factories.toArray(new String[0]); } @Override public void setBeanClassLoader(ClassLoader classLoader) { - beanClassLoader = classLoader; + this.classLoader = classLoader; } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementContextResolver.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementContextResolver.java new file mode 100644 index 0000000000..0273d2188f --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementContextResolver.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2015 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.autoconfigure; + +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; +import org.springframework.context.ApplicationContext; + +/** + * Provides access to the {@link ApplicationContext} being used to manage actuator + * endpoints. + * + * @author Dave Syer + * @since 1.3.0 + */ +public class ManagementContextResolver { + + private ApplicationContext applicationContext; + + ManagementContextResolver(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + /** + * Return all {@link MvcEndpoints} from the management context. + * @return {@link MvcEndpoints} from the management context + */ + public MvcEndpoints getMvcEndpoints() { + try { + return getApplicationContext().getBean(MvcEndpoints.class); + } + catch (Exception ex) { + return null; + } + } + + /** + * Return the management {@link ApplicationContext} + * @return the management {@link ApplicationContext} + */ + public ApplicationContext getApplicationContext() { + return this.applicationContext; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java index 7982b12bc0..534e834054 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java @@ -26,7 +26,6 @@ import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration.ManagementContextResolver; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; @@ -259,10 +258,10 @@ public class ManagementSecurityAutoConfiguration { if (!this.management.getSecurity().isEnabled()) { return null; } - String path = management.getContextPath(); + String path = this.management.getContextPath(); if (StringUtils.hasText(path)) { AntPathRequestMatcher matcher = new AntPathRequestMatcher( - server.getPath(path) + "/**"); + this.server.getPath(path) + "/**"); return matcher; } return new EndpointPathRequestMatcher(); @@ -297,30 +296,36 @@ public class ManagementSecurityAutoConfiguration { @Override public boolean matches(HttpServletRequest request) { - if (endpointHandlerMapping == null && contextResolver != null) { - ApplicationContext context = contextResolver.getApplicationContext(); + EndpointHandlerMapping endpointMapping = ManagementWebSecurityConfigurerAdapter.this.endpointHandlerMapping; + if (endpointMapping == null + && ManagementWebSecurityConfigurerAdapter.this.contextResolver != null) { + ApplicationContext context = ManagementWebSecurityConfigurerAdapter.this.contextResolver + .getApplicationContext(); if (context != null && context.getBeanNamesForType(EndpointHandlerMapping.class).length > 0) { - endpointHandlerMapping = context + ManagementWebSecurityConfigurerAdapter.this.endpointHandlerMapping = context .getBean(EndpointHandlerMapping.class); } } - if (endpointHandlerMapping == null) { - endpointHandlerMapping = new EndpointHandlerMapping( + if (endpointMapping == null) { + ManagementWebSecurityConfigurerAdapter.this.endpointHandlerMapping = new EndpointHandlerMapping( Collections. emptySet()); } - if (delegate == null) { + if (this.delegate == null) { List pathMatchers = new ArrayList(); - String[] paths = !sensitive ? getEndpointPaths( - endpointHandlerMapping, false) - : getEndpointPaths(endpointHandlerMapping); + String[] paths = !this.sensitive ? getEndpointPaths( + endpointMapping, + false) + : getEndpointPaths(endpointMapping); for (String path : paths) { - pathMatchers.add(new AntPathRequestMatcher(server.getPath(path))); + pathMatchers.add(new AntPathRequestMatcher( + ManagementWebSecurityConfigurerAdapter.this.server + .getPath(path))); } - delegate = pathMatchers.isEmpty() ? AnyRequestMatcher.INSTANCE + this.delegate = pathMatchers.isEmpty() ? AnyRequestMatcher.INSTANCE : new OrRequestMatcher(pathMatchers); } - return delegate.matches(request); + return this.delegate.matches(request); } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerProperties.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerProperties.java index 5653d1f96b..44694effca 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerProperties.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerProperties.java @@ -54,7 +54,7 @@ public class ManagementServerProperties implements SecurityPrerequisite { public static final int ACCESS_OVERRIDE_ORDER = ManagementServerProperties.BASIC_AUTH_ORDER - 1; /** - * Management endpoint HTTP port. Use the same port as the applicationby default. + * Management endpoint HTTP port. Use the same port as the application by default. */ private Integer port; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/condition/OnManagementMvcCondition.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/condition/OnManagementMvcCondition.java index 7617d98e2f..8fcf3cb9b1 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/condition/OnManagementMvcCondition.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/condition/OnManagementMvcCondition.java @@ -37,7 +37,7 @@ import org.springframework.web.context.WebApplicationContext; * main context. * * @author Dave Syer - * @since 1.3.0 + * @see ConditionalOnManagementMvcContext */ class OnManagementMvcCondition extends SpringBootCondition { @@ -74,18 +74,17 @@ class OnManagementMvcCondition extends SpringBootCondition { if ((managementPort == null) || (serverPort == null && managementPort.equals(8080)) || (managementPort != 0 && managementPort.equals(serverPort))) { - return new ConditionOutcome(true, - "The main context is the management context"); + return ConditionOutcome.match("The main context is the management context"); } - return new ConditionOutcome(false, - "The main context is not the management context"); + return ConditionOutcome.noMatch("The main context is not the management context"); } private T getBeanCarefully(ConditionContext context, Class type) { String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( context.getBeanFactory(), type, false, false); if (names.length == 1) { - BeanDefinition original = findBeanDefinition(context.getBeanFactory(), names[0]); + BeanDefinition original = findBeanDefinition(context.getBeanFactory(), + names[0]); if (original instanceof RootBeanDefinition) { DefaultListableBeanFactory temp = new DefaultListableBeanFactory(); temp.setParentBeanFactory(context.getBeanFactory()); @@ -100,16 +99,19 @@ class OnManagementMvcCondition extends SpringBootCondition { return null; } - private BeanDefinition findBeanDefinition(ConfigurableListableBeanFactory beanFactory, String name) { + private BeanDefinition findBeanDefinition( + ConfigurableListableBeanFactory beanFactory, String name) { BeanDefinition original = null; - while (beanFactory!=null && original==null){ + while (beanFactory != null && original == null) { if (beanFactory.containsLocalBean(name)) { original = beanFactory.getBeanDefinition(name); - } else { + } + else { BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory(); if (parentBeanFactory instanceof ConfigurableListableBeanFactory) { beanFactory = (ConfigurableListableBeanFactory) parentBeanFactory; - } else { + } + else { beanFactory = null; } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java index 5309467e08..1d1e84197b 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java @@ -26,7 +26,6 @@ import java.util.Set; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; -import org.springframework.core.Ordered; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.cors.CorsConfiguration; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoints.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoints.java index 2e801f273c..a437a6da35 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoints.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoints.java @@ -52,8 +52,9 @@ public class MvcEndpoints implements ApplicationContextAware, InitializingBean { @Override public void afterPropertiesSet() throws Exception { - Collection existing = BeanFactoryUtils.beansOfTypeIncludingAncestors( - this.applicationContext, MvcEndpoint.class).values(); + Collection existing = BeanFactoryUtils + .beansOfTypeIncludingAncestors(this.applicationContext, MvcEndpoint.class) + .values(); this.endpoints.addAll(existing); this.customTypes = findEndpointClasses(existing); @SuppressWarnings("rawtypes") diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java index 00b3becc7d..0d22296a90 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java @@ -16,18 +16,6 @@ package org.springframework.boot.actuate.autoconfigure; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - import java.io.FileNotFoundException; import java.net.SocketException; import java.net.URI; @@ -42,7 +30,6 @@ import javax.servlet.http.HttpServletResponse; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration.ManagementContextResolver; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCustomizer; @@ -84,6 +71,18 @@ import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + /** * Tests for {@link EndpointWebMvcAutoConfiguration}. * @@ -245,7 +244,7 @@ public class EndpointWebMvcAutoConfigurationTests { this.applicationContext.register(RootConfig.class, BaseConfiguration.class, ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class); new ServerPortInfoApplicationContextInitializer() - .initialize(this.applicationContext); + .initialize(this.applicationContext); this.applicationContext.refresh(); Integer localServerPort = this.applicationContext.getEnvironment().getProperty( "local.server.port", Integer.class); @@ -261,7 +260,7 @@ public class EndpointWebMvcAutoConfigurationTests { @Test public void portPropertiesOnDifferentPort() throws Exception { new ServerPortInfoApplicationContextInitializer() - .initialize(this.applicationContext); + .initialize(this.applicationContext); this.applicationContext.register(RootConfig.class, DifferentPortConfig.class, BaseConfiguration.class, EndpointWebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class); @@ -422,12 +421,12 @@ public class EndpointWebMvcAutoConfigurationTests { @Configuration @Import({ PropertyPlaceholderAutoConfiguration.class, - EmbeddedServletContainerAutoConfiguration.class, - EndpointAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, - DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, - ManagementServerPropertiesAutoConfiguration.class, - ServerPropertiesAutoConfiguration.class, WebMvcAutoConfiguration.class }) + EmbeddedServletContainerAutoConfiguration.class, + EndpointAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class, + ServerPropertiesAutoConfiguration.class, WebMvcAutoConfiguration.class }) protected static class BaseConfiguration { } @@ -585,7 +584,7 @@ public class EndpointWebMvcAutoConfigurationTests { } private static class GrabManagementPort implements - ApplicationListener { + ApplicationListener { private ApplicationContext rootContext; diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java index e72825b029..0f866a1cda 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java @@ -16,12 +16,6 @@ package org.springframework.boot.actuate.endpoint.mvc; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalToIgnoringCase; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,6 +37,12 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalToIgnoringCase; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + /** * Tests for {@link EnvironmentMvcEndpoint} * diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MetricsMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MetricsMvcEndpointTests.java index 1002cdc244..1d46913733 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MetricsMvcEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MetricsMvcEndpointTests.java @@ -16,12 +16,6 @@ package org.springframework.boot.actuate.endpoint.mvc; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -47,6 +41,12 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + /** * Tests for {@link MetricsMvcEndpoint} * diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ResourceProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ResourceProperties.java index f5abaa5ed1..34d4ad5654 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ResourceProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ResourceProperties.java @@ -75,8 +75,8 @@ public class ResourceProperties implements ResourceLoaderAware { private final Chain chain = new Chain(); /** - * Locations of static resources. Defaults to classpath:[/META-INF/resources/, /resources/, /static/, /public/] - * plus context:/ (the root of the servlet context). + * Locations of static resources. Defaults to classpath:[/META-INF/resources/, + * /resources/, /static/, /public/] plus context:/ (the root of the servlet context). */ private String[] staticLocations = RESOURCE_LOCATIONS; @@ -267,9 +267,9 @@ public class ResourceProperties implements ResourceLoaderAware { } private String[] getStaticWelcomePageLocations() { - String[] result = new String[staticLocations.length]; + String[] result = new String[this.staticLocations.length]; for (int i = 0; i < result.length; i++) { - String location = staticLocations[i]; + String location = this.staticLocations[i]; if (!location.endsWith("/")) { location = location + "/"; } @@ -281,7 +281,7 @@ public class ResourceProperties implements ResourceLoaderAware { public List getFaviconLocations() { List locations = new ArrayList( CLASSPATH_RESOURCE_LOCATIONS.length + 1); - if (resourceLoader != null) { + if (this.resourceLoader != null) { for (String location : CLASSPATH_RESOURCE_LOCATIONS) { locations.add(this.resourceLoader.getResource(location)); } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java index b6b7284bee..5aca328b5a 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java @@ -238,8 +238,10 @@ public class WebMvcAutoConfiguration { .setCachePeriod(cachePeriod)); } if (!registry.hasMappingForPattern("/**")) { - registerResourceChain(registry.addResourceHandler("/**") - .addResourceLocations(resourceProperties.getStaticLocations()) + registerResourceChain(registry + .addResourceHandler("/**") + .addResourceLocations( + this.resourceProperties.getStaticLocations()) .setCachePeriod(cachePeriod)); } } @@ -282,7 +284,7 @@ public class WebMvcAutoConfiguration { @Override public void addViewControllers(ViewControllerRegistry registry) { - Resource page = resourceProperties.getWelcomePage(); + Resource page = this.resourceProperties.getWelcomePage(); if (page != null) { logger.info("Adding welcome page: " + page); registry.addViewController("/").setViewName("forward:index.html"); @@ -308,7 +310,8 @@ public class WebMvcAutoConfiguration { @Bean public ResourceHttpRequestHandler faviconRequestHandler() { ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler(); - requestHandler.setLocations(resourceProperties.getFaviconLocations()); + requestHandler + .setLocations(this.resourceProperties.getFaviconLocations()); return requestHandler; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WelcomePageMockMvcTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WelcomePageMockMvcTests.java index e38f0bb1f6..8b72242a5c 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WelcomePageMockMvcTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WelcomePageMockMvcTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2015 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,9 +16,6 @@ package org.springframework.boot.autoconfigure.web; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -37,6 +34,9 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.ConfigurableWebApplicationContext; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + /** * Tests for welcome page using {@link MockMvc} and {@link SpringJUnit4ClassRunner}. * @@ -50,14 +50,14 @@ public class WelcomePageMockMvcTests { @After public void close() { - if (wac != null) { - wac.close(); + if (this.wac != null) { + this.wac.close(); } } @Test public void homePageNotFound() throws Exception { - wac = (ConfigurableWebApplicationContext) new SpringApplicationBuilder( + this.wac = (ConfigurableWebApplicationContext) new SpringApplicationBuilder( TestConfiguration.class).run(); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); this.mockMvc.perform(get("/")).andExpect(status().isNotFound()).andReturn(); @@ -65,7 +65,7 @@ public class WelcomePageMockMvcTests { @Test public void homePageCustomLocation() throws Exception { - wac = (ConfigurableWebApplicationContext) new SpringApplicationBuilder( + this.wac = (ConfigurableWebApplicationContext) new SpringApplicationBuilder( TestConfiguration.class).properties( "spring.resources.staticLocations:classpath:/custom/").run(); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); @@ -74,8 +74,9 @@ public class WelcomePageMockMvcTests { @Test public void homePageCustomLocationNoTrailingSlash() throws Exception { - wac = (ConfigurableWebApplicationContext) new SpringApplicationBuilder( - TestConfiguration.class).properties("spring.resources.staticLocations:classpath:/custom").run(); + this.wac = (ConfigurableWebApplicationContext) new SpringApplicationBuilder( + TestConfiguration.class).properties( + "spring.resources.staticLocations:classpath:/custom").run(); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); this.mockMvc.perform(get("/")).andExpect(status().isOk()).andReturn(); } diff --git a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 98a88f6904..43e8754c3a 100644 --- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -150,23 +150,23 @@ For example, the following will disable _all_ endpoints except for `info`: endpoints.info.enabled=true ---- + + [[production-ready-customizing-endpoints-programmatically]] === Adding Custom Endpoints +If you add a `@Bean` of type `Endpoint` then it will automatically be exposed over JMX and +HTTP (if there is an server available). An HTTP endpoints can be customized further by +creating a bean of type `MvcEndpoint`. Your `MvcEndpoint` is not a `@Controller` but it +can use `@RequestMapping` (and `@Managed*`) to expose resources. -If you add a `@Bean` of type `Endpoint` then it will automatically be -exposed over JMX and HTTP (if there is an server available). An HTTP -endpoints can be customized further by creating a bean of type -`MvcEndpoint`. Your `MvcEndpoint` is not a `@Controller` but it can use -`@RequestMapping` (and `@Managed*`) to expose resources. +TIP: If you are doing this as a library feature consider adding a configuration class to +`/META-INF/spring.factories` under the key +`org.springframework.boot.actuate.autoconfigure.EndpointWebMvcConfiguration`. If you do +that then the endpoint will move to a child context with all the other MVC endpoints if +your users ask for a separate management port or address. A configuration declared this +way can be a `WebConfigurerAdapter` if it wants to add static resources (for instance) to +the management endpoints. -TIP: If you are doing this as a library feature consider adding a -configuration class to `/META-INF/spring.factories` under the key -`org.springframework.boot.actuate.autoconfigure.EndpointWebMvcConfiguration`. -If you do that then the endpoint will move to a child context with all -the other MVC endpoints if your users ask for a separate management port -or address. A configuration declared this way can be a `WebConfigurerAdapter` -if it wants to add static resources (for instance) to the management -endpoints. [[production-ready-health]] diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 05cbd99fdf..1a17c89818 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -1175,12 +1175,11 @@ Spring decides not to handle it. Most of the time this will not happen (unless y the default MVC configuration) because Spring will always be able to handle requests through the `DispatcherServlet`. -You can customize the static resource locations using -`spring.resources.staticLocations` (replacing the default values with -a list of directory locations). If you do this the default welcome -page detection will switch to your custom locations, so if there is an -`index.html` in any of your locations on startup, it will be the home -page of the application. +You can customize the static resource locations using `spring.resources.staticLocations` +(replacing the default values with a list of directory locations). If you do this the +default welcome page detection will switch to your custom locations, so if there is an +`index.html` in any of your locations on startup, it will be the home page of the +application. In addition to the '`standard`' static resource locations above, a special case is made for http://www.webjars.org/[Webjars content]. Any resources with a path in `+/webjars/**+`