Add EndpointHandlerMappingCustomizer callback

Users can add @Beans of this type to customize the
EndpointHandlerMapping (e.g. add interceptors) even if
it is in a child context.

Fixes gh-1933
pull/1958/head
Dave Syer 10 years ago
parent 1254508357
commit 5d2d39e87d

@ -17,6 +17,7 @@
package org.springframework.boot.actuate.autoconfigure;
import java.io.IOException;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@ -38,6 +39,7 @@ import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCustomizer;
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
@ -102,6 +104,9 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
@Autowired
private ManagementServerProperties managementServerProperties;
@Autowired(required = false)
private List<EndpointHandlerMappingCustomizer> mappingCustomizers;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
@ -118,6 +123,11 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
if (!disabled) {
mapping.setPrefix(this.managementServerProperties.getContextPath());
}
if (this.mappingCustomizers != null) {
for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
customizer.customize(mapping);
}
}
return mapping;
}

@ -18,6 +18,7 @@ package org.springframework.boot.actuate.autoconfigure;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.Filter;
@ -29,6 +30,7 @@ import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCustomizer;
import org.springframework.boot.actuate.endpoint.mvc.ManagementErrorEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
@ -63,6 +65,9 @@ public class EndpointWebMvcChildContextConfiguration {
@Value("${error.path:/error}")
private String errorPath = "/error";
@Autowired(required = false)
private List<EndpointHandlerMappingCustomizer> mappingCustomizers;
@Configuration
protected static class ServerCustomization implements
EmbeddedServletContainerCustomizer {
@ -90,7 +95,7 @@ public class EndpointWebMvcChildContextConfiguration {
}
// Customize as per the parent context first (so e.g. the access logs go to
// the same place)
server.customize(container);
this.server.customize(container);
// Then reset the error pages
container.setErrorPages(Collections.<ErrorPage> emptySet());
// and add the management-specific bits
@ -130,6 +135,11 @@ public class EndpointWebMvcChildContextConfiguration {
EndpointHandlerMapping mapping = new EndpointHandlerMapping(set);
// In a child context we definitely want to see the parent endpoints
mapping.setDetectHandlerMethodsInAncestorContexts(true);
if (this.mappingCustomizers != null) {
for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
customizer.customize(mapping);
}
}
return mapping;
}

@ -0,0 +1,28 @@
/*
* Copyright 2012-2014 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.endpoint.mvc;
/**
* Callback for customizing the {@link EndpointHandlerMapping} at configuration time.
*
* @author Dave Syer
*/
public interface EndpointHandlerMappingCustomizer {
void customize(EndpointHandlerMapping mapping);
}

@ -24,11 +24,17 @@ import java.lang.annotation.Target;
import java.util.Collections;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.EndpointMvcIntegrationTests.Application;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCustomizer;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
@ -39,6 +45,7 @@ import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
@ -47,7 +54,10 @@ import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@ -66,12 +76,16 @@ public class EndpointMvcIntegrationTests {
@Value("${local.server.port}")
private int port;
@Autowired
private TestInterceptor interceptor;
@Test
public void envEndpointNotHidden() {
String body = new TestRestTemplate().getForObject("http://localhost:" + this.port
+ "/env/user.dir", String.class);
assertNotNull(body);
assertTrue("Wrong body: \n" + body, body.contains("spring-boot-actuator"));
assertEquals(1, this.interceptor.getCount());
}
@Target(ElementType.TYPE)
@ -104,6 +118,36 @@ public class EndpointMvcIntegrationTests {
return Collections.singletonMap("foo", (Object) "bar");
}
@Bean
public EndpointHandlerMappingCustomizer mappingCustomizer() {
return new EndpointHandlerMappingCustomizer() {
@Override
public void customize(EndpointHandlerMapping mapping) {
mapping.setInterceptors(new Object[] { interceptor() });
}
};
}
@Bean
protected TestInterceptor interceptor() {
return new TestInterceptor();
}
}
protected static class TestInterceptor extends HandlerInterceptorAdapter {
private int count = 0;
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
this.count++;
}
public int getCount() {
return this.count;
}
}
}

@ -20,11 +20,17 @@ import java.io.FileNotFoundException;
import java.net.SocketException;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCustomizer;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
@ -50,14 +56,18 @@ import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Controller;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.SocketUtils;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import static org.hamcrest.Matchers.equalTo;
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;
@ -122,6 +132,10 @@ public class EndpointWebMvcAutoConfigurationTests {
assertContent("/endpoint", ports.get().server, null);
assertContent("/controller", ports.get().management, null);
assertContent("/endpoint", ports.get().management, "endpointoutput");
List<?> interceptors = (List<?>) ReflectionTestUtils.getField(
this.applicationContext.getBean(EndpointHandlerMapping.class),
"interceptors");
assertEquals(1, interceptors.size());
this.applicationContext.close();
assertAllClosed();
}
@ -350,6 +364,38 @@ public class EndpointWebMvcAutoConfigurationTests {
return properties;
}
@Bean
public EndpointHandlerMappingCustomizer mappingCustomizer() {
return new EndpointHandlerMappingCustomizer() {
@Override
public void customize(EndpointHandlerMapping mapping) {
mapping.setInterceptors(new Object[] { interceptor() });
}
};
}
@Bean
protected TestInterceptor interceptor() {
return new TestInterceptor();
}
protected static class TestInterceptor extends HandlerInterceptorAdapter {
private int count = 0;
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
this.count++;
}
public int getCount() {
return this.count;
}
}
}
@Configuration

Loading…
Cancel
Save