Polish Actuator MVC customization support

See gh-3345
pull/3363/head
Phillip Webb 10 years ago
parent c84658af1b
commit b5c435f8c4

@ -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<Class<?>> configurations = new ArrayList<Class<?>>();
configurations.addAll(Arrays.<Class<?>> 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<ContextClosedEvent>() {
@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=<different>,
@ -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<ContextClosedEvent> {
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;
}
}
}
}

@ -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
@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
@Import(EndpointWebMvcImportSelector.class)
protected static class EndpointWebMvcConfiguration {
@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);
}
}
}

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

@ -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 <code>/META-INF/spring.factories</code> 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<String> factories = new ArrayList<String>(new LinkedHashSet<String>(
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;
}
}

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

@ -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.<MvcEndpoint> emptySet());
}
if (delegate == null) {
if (this.delegate == null) {
List<RequestMatcher> pathMatchers = new ArrayList<RequestMatcher>();
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);
}
}

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

@ -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> T getBeanCarefully(ConditionContext context, Class<T> 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;
}
}

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

@ -52,8 +52,9 @@ public class MvcEndpoints implements ApplicationContextAware, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
Collection<MvcEndpoint> existing = BeanFactoryUtils.beansOfTypeIncludingAncestors(
this.applicationContext, MvcEndpoint.class).values();
Collection<MvcEndpoint> existing = BeanFactoryUtils
.beansOfTypeIncludingAncestors(this.applicationContext, MvcEndpoint.class)
.values();
this.endpoints.addAll(existing);
this.customTypes = findEndpointClasses(existing);
@SuppressWarnings("rawtypes")

@ -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<EmbeddedServletContainerInitializedEvent> {
ApplicationListener<EmbeddedServletContainerInitializedEvent> {
private ApplicationContext rootContext;

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

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

@ -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<Resource> getFaviconLocations() {
List<Resource> locations = new ArrayList<Resource>(
CLASSPATH_RESOURCE_LOCATIONS.length + 1);
if (resourceLoader != null) {
if (this.resourceLoader != null) {
for (String location : CLASSPATH_RESOURCE_LOCATIONS) {
locations.add(this.resourceLoader.getResource(location));
}

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

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

@ -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.
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.
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.
[[production-ready-health]]

@ -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/**+`

Loading…
Cancel
Save