Extract MVC concerns completely from Endpoint implementations

pull/176/head
Dave Syer 11 years ago committed by Phillip Webb
parent 71ebcbff3e
commit 87e00cfae9

@ -17,6 +17,8 @@
package org.springframework.boot.actuate.autoconfigure;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@ -28,8 +30,19 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
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.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.GenericMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint;
import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -50,6 +63,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.DispatcherServlet;
@ -119,6 +133,50 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
};
}
@Component
protected static class GenericEndpointPostProcessor implements
BeanDefinitionRegistryPostProcessor {
private BeanDefinitionRegistry registry;
private Map<Class<? extends Endpoint<?>>, Class<?>> endpointTypes = new HashMap<Class<? extends Endpoint<?>>, Class<?>>();
public GenericEndpointPostProcessor() {
this.endpointTypes.put(EnvironmentEndpoint.class,
EnvironmentMvcEndpoint.class);
this.endpointTypes.put(MetricsEndpoint.class, MetricsMvcEndpoint.class);
this.endpointTypes.put(ShutdownEndpoint.class, ShutdownMvcEndpoint.class);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
for (String name : beanFactory.getBeanNamesForType(Endpoint.class)) {
Class<?> type = getTypeForEndpoint(beanFactory.getType(name));
BeanDefinitionBuilder bean = BeanDefinitionBuilder
.genericBeanDefinition(type);
bean.addConstructorArgReference(name);
this.registry.registerBeanDefinition("mvc." + name,
bean.getBeanDefinition());
}
}
protected Class<?> getTypeForEndpoint(Class<?> endpoint) {
Class<?> type = GenericMvcEndpoint.class;
if (this.endpointTypes.containsKey(endpoint)) {
type = this.endpointTypes.get(endpoint);
}
return type;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
this.registry = registry;
}
}
private void createChildManagementContext() {
final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext();

@ -16,16 +16,16 @@
package org.springframework.boot.actuate.endpoint;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.config.JsonParser;
import org.springframework.boot.config.JsonParserFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.LiveBeansView;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Exposes JSON view of Spring beans. If the {@link Environment} contains a key setting
@ -36,12 +36,13 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.beans", ignoreUnknownFields = false)
@FrameworkEndpoint
public class BeansEndpoint extends AbstractEndpoint<String> implements
public class BeansEndpoint extends AbstractEndpoint<List<Object>> implements
ApplicationContextAware {
private LiveBeansView liveBeansView = new LiveBeansView();
private JsonParser parser = JsonParserFactory.getJsonParser();
public BeansEndpoint() {
super("/beans");
}
@ -55,9 +56,7 @@ public class BeansEndpoint extends AbstractEndpoint<String> implements
}
@Override
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String invoke() {
return this.liveBeansView.getSnapshotAsJson();
public List<Object> invoke() {
return this.parser.parseList(this.liveBeansView.getSnapshotAsJson());
}
}

@ -19,13 +19,10 @@ package org.springframework.boot.actuate.endpoint;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -41,7 +38,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author Christian Dupuis
*/
@ConfigurationProperties(name = "endpoints.configprops", ignoreUnknownFields = false)
@FrameworkEndpoint
public class ConfigurationPropertiesReportEndpoint extends
AbstractEndpoint<Map<String, Object>> implements ApplicationContextAware {
@ -67,8 +63,7 @@ public class ConfigurationPropertiesReportEndpoint extends
this.keysToSanitize = keysToSanitize;
}
@RequestMapping
@ResponseBody
@Override
public Map<String, Object> invoke() {
Map<String, Object> beans = extract(this.context);
return beans;

@ -21,10 +21,7 @@ import java.lang.management.ThreadInfo;
import java.util.Arrays;
import java.util.List;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* {@link Endpoint} to expose thread info.
@ -32,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.dump", ignoreUnknownFields = false)
@FrameworkEndpoint
public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> {
/**
@ -43,8 +39,6 @@ public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> {
}
@Override
@RequestMapping
@ResponseBody
public List<ThreadInfo> invoke() {
return Arrays.asList(ManagementFactory.getThreadMXBean().dumpAllThreads(true,
true));

@ -19,7 +19,6 @@ package org.springframework.boot.actuate.endpoint;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
@ -27,11 +26,6 @@ import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* {@link Endpoint} to expose {@link ConfigurableEnvironment environment} information.
@ -40,7 +34,6 @@ import org.springframework.web.bind.annotation.ResponseStatus;
* @author Phillip Webb
*/
@ConfigurationProperties(name = "endpoints.env", ignoreUnknownFields = false)
@FrameworkEndpoint
public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> implements
EnvironmentAware {
@ -54,8 +47,6 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
}
@Override
@RequestMapping
@ResponseBody
public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>();
result.put("profiles", this.environment.getActiveProfiles());
@ -72,16 +63,6 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
return result;
}
@RequestMapping("/{name:.*}")
@ResponseBody
public Object value(@PathVariable String name) {
String result = this.environment.getProperty(name);
if (result == null) {
throw new NoSuchPropertyException("No such property: " + name);
}
return sanitize(name, result);
}
private Iterable<PropertySource<?>> getPropertySources() {
if (this.environment != null
&& this.environment instanceof ConfigurableEnvironment) {
@ -90,7 +71,7 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
return new StandardEnvironment().getPropertySources();
}
private Object sanitize(String name, Object object) {
public static Object sanitize(String name, Object object) {
if (name.toLowerCase().endsWith("password")
|| name.toLowerCase().endsWith("secret")) {
return object == null ? null : "******";
@ -103,13 +84,4 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
this.environment = environment;
}
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such property")
public static class NoSuchPropertyException extends RuntimeException {
public NoSuchPropertyException(String string) {
super(string);
}
}
}

@ -16,12 +16,9 @@
package org.springframework.boot.actuate.endpoint;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* {@link Endpoint} to expose application health.
@ -29,7 +26,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.health", ignoreUnknownFields = false)
@FrameworkEndpoint
public class HealthEndpoint<T> extends AbstractEndpoint<T> {
private HealthIndicator<? extends T> indicator;
@ -50,8 +46,6 @@ public class HealthEndpoint<T> extends AbstractEndpoint<T> {
}
@Override
@RequestMapping
@ResponseBody
public T invoke() {
return this.indicator.health();
}

@ -20,11 +20,8 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* {@link Endpoint} to expose arbitrary application information.
@ -32,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.info", ignoreUnknownFields = false)
@FrameworkEndpoint
public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
private Map<String, ? extends Object> info;
@ -49,8 +45,6 @@ public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
}
@Override
@RequestMapping
@ResponseBody
public Map<String, Object> invoke() {
Map<String, Object> info = new LinkedHashMap<String, Object>(this.info);
info.putAll(getAdditionalInfo());

@ -19,6 +19,7 @@ package org.springframework.boot.actuate.endpoint;
import java.util.Map;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.web.ErrorController;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;
@ -33,22 +34,27 @@ import org.springframework.web.context.request.RequestContextHolder;
*
* @author Dave Syer
*/
@FrameworkEndpoint
@ConfigurationProperties(name = "error")
public class ManagementErrorEndpoint extends AbstractEndpoint<Map<String, Object>> {
@FrameworkEndpoint
public class ManagementErrorEndpoint implements MvcEndpoint {
private final ErrorController controller;
private String path;
public ManagementErrorEndpoint(String path, ErrorController controller) {
super(path, false, true);
this.path = path;
this.controller = controller;
}
@Override
@RequestMapping
@ResponseBody
public Map<String, Object> invoke() {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
return this.controller.extract(attributes, false);
}
@Override
public String getPath() {
return this.path;
}
}

@ -19,15 +19,9 @@ package org.springframework.boot.actuate.endpoint;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* {@link Endpoint} to expose {@link PublicMetrics}.
@ -35,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseStatus;
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.metrics", ignoreUnknownFields = false)
@FrameworkEndpoint
public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
private PublicMetrics metrics;
@ -52,8 +45,6 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
}
@Override
@RequestMapping
@ResponseBody
public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>();
for (Metric metric : this.metrics.metrics()) {
@ -62,22 +53,4 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
return result;
}
@RequestMapping("/{name:.*}")
@ResponseBody
public Object value(@PathVariable String name) {
Object value = invoke().get(name);
if (value == null) {
throw new NoSuchMetricException("No such metric: " + name);
}
return value;
}
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such metric")
public static class NoSuchMetricException extends RuntimeException {
public NoSuchMetricException(String string) {
super(string);
}
}
}

@ -20,14 +20,10 @@ import java.util.Collections;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* {@link Endpoint} to shutdown the {@link ApplicationContext}.
@ -36,7 +32,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Christian Dupuis
*/
@ConfigurationProperties(name = "endpoints.shutdown", ignoreUnknownFields = false)
@FrameworkEndpoint
public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> implements
ApplicationContextAware {
@ -50,8 +45,6 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl
}
@Override
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> invoke() {
if (this.context == null) {
@ -59,21 +52,26 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl
"No context to shutdown.");
}
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500L);
}
catch (InterruptedException ex) {
// Swallow exception and continue
try {
return Collections.<String, Object> singletonMap("message",
"Shutting down, bye...");
}
finally {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500L);
}
catch (InterruptedException ex) {
// Swallow exception and continue
}
ShutdownEndpoint.this.context.close();
}
ShutdownEndpoint.this.context.close();
}
}).start();
}).start();
return Collections.<String, Object> singletonMap("message",
"Shutting down, bye...");
}
}
@Override

@ -18,13 +18,10 @@ package org.springframework.boot.actuate.endpoint;
import java.util.List;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.actuate.trace.Trace;
import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* {@link Endpoint} to expose {@link Trace} information.
@ -32,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.trace", ignoreUnknownFields = false)
@FrameworkEndpoint
public class TraceEndpoint extends AbstractEndpoint<List<Trace>> {
private TraceRepository repository;
@ -49,8 +45,6 @@ public class TraceEndpoint extends AbstractEndpoint<List<Trace>> {
}
@Override
@RequestMapping
@ResponseBody
public List<Trace> invoke() {
return this.repository.findAll();
}

@ -117,8 +117,8 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme
if (bean instanceof String) {
bean = getApplicationContext().getBean((String) handler);
}
if (bean instanceof Endpoint) {
Endpoint<?> endpoint = (Endpoint<?>) bean;
if (bean instanceof MvcEndpoint) {
MvcEndpoint endpoint = (MvcEndpoint) bean;
path = endpoint.getPath();
}

@ -0,0 +1,64 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint.mvc;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* @author Dave Syer
*/
@FrameworkEndpoint
public class EnvironmentMvcEndpoint extends GenericMvcEndpoint implements
EnvironmentAware {
private Environment environment;
public EnvironmentMvcEndpoint(EnvironmentEndpoint delegate) {
super(delegate);
}
@RequestMapping("/{name:.*}")
@ResponseBody
public Object value(@PathVariable String name) {
String result = this.environment.getProperty(name);
if (result == null) {
throw new NoSuchPropertyException("No such property: " + name);
}
return EnvironmentEndpoint.sanitize(name, result);
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such property")
public static class NoSuchPropertyException extends RuntimeException {
public NoSuchPropertyException(String string) {
super(string);
}
}
}

@ -0,0 +1,47 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint.mvc;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Dave Syer
*/
@FrameworkEndpoint
public class GenericMvcEndpoint implements MvcEndpoint {
private Endpoint<?> delegate;
public GenericMvcEndpoint(Endpoint<?> delegate) {
this.delegate = delegate;
}
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public Object invoke() {
return this.delegate.invoke();
}
@Override
public String getPath() {
return this.delegate.getPath();
}
}

@ -0,0 +1,57 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint.mvc;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* @author Dave Syer
*/
@FrameworkEndpoint
public class MetricsMvcEndpoint extends GenericMvcEndpoint {
private MetricsEndpoint delegate;
public MetricsMvcEndpoint(MetricsEndpoint delegate) {
super(delegate);
this.delegate = delegate;
}
@RequestMapping("/{name:.*}")
@ResponseBody
public Object value(@PathVariable String name) {
Object value = this.delegate.invoke().get(name);
if (value == null) {
throw new NoSuchMetricException("No such metric: " + name);
}
return value;
}
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such metric")
public static class NoSuchMetricException extends RuntimeException {
public NoSuchMetricException(String string) {
super(string);
}
}
}

@ -0,0 +1,26 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint.mvc;
/**
* @author Dave Syer
*/
public interface MvcEndpoint {
String getPath();
}

@ -0,0 +1,40 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint.mvc;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Dave Syer
*/
@FrameworkEndpoint
public class ShutdownMvcEndpoint extends GenericMvcEndpoint {
public ShutdownMvcEndpoint(ShutdownEndpoint delegate) {
super(delegate);
}
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
@Override
public Object invoke() {
return super.invoke();
}
}

@ -16,13 +16,16 @@
package org.springframework.boot.actuate.endpoint;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link BeansEndpoint}.
@ -37,7 +40,9 @@ public class BeansEndpointTests extends AbstractEndpointTests<BeansEndpoint> {
@Test
public void invoke() throws Exception {
assertThat(getEndpointBean().invoke(), containsString("\"bean\": \"endpoint\""));
List<Object> result = getEndpointBean().invoke();
assertEquals(1, result.size());
assertTrue(result.get(0) instanceof Map);
}
@Configuration

@ -52,13 +52,13 @@ public class EndpointHandlerMappingTests {
this.context.getDefaultListableBeanFactory().registerSingleton("mapping",
this.mapping);
this.mapping.setApplicationContext(this.context);
this.method = ReflectionUtils.findMethod(TestEndpoint.class, "invoke");
this.method = ReflectionUtils.findMethod(TestMvcEndpoint.class, "invoke");
}
@Test
public void withoutPrefix() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a");
TestEndpoint endpointB = new TestEndpoint("/b");
TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a"));
TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b"));
this.context.getDefaultListableBeanFactory().registerSingleton(
endpointA.getPath(), endpointA);
this.context.getDefaultListableBeanFactory().registerSingleton(
@ -76,8 +76,8 @@ public class EndpointHandlerMappingTests {
@Test
public void withPrefix() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a");
TestEndpoint endpointB = new TestEndpoint("/b");
TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a"));
TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b"));
this.context.getDefaultListableBeanFactory().registerSingleton(
endpointA.getPath(), endpointA);
this.context.getDefaultListableBeanFactory().registerSingleton(
@ -96,7 +96,7 @@ public class EndpointHandlerMappingTests {
@Test(expected = HttpRequestMethodNotSupportedException.class)
public void onlyGetHttpMethodForNonActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestEndpoint("/a");
TestMvcEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton(
endpoint.getPath(), endpoint);
this.mapping.afterPropertiesSet();
@ -106,7 +106,7 @@ public class EndpointHandlerMappingTests {
@Test
public void postHttpMethodForActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestActionEndpoint("/a");
TestMvcEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton(
endpoint.getPath(), endpoint);
this.mapping.afterPropertiesSet();
@ -115,7 +115,7 @@ public class EndpointHandlerMappingTests {
@Test(expected = HttpRequestMethodNotSupportedException.class)
public void onlyPostHttpMethodForActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestActionEndpoint("/a");
TestMvcEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton(
endpoint.getPath(), endpoint);
this.mapping.afterPropertiesSet();
@ -125,7 +125,7 @@ public class EndpointHandlerMappingTests {
@Test
public void disabled() throws Exception {
TestEndpoint endpoint = new TestEndpoint("/a");
TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton(
endpoint.getPath(), endpoint);
this.mapping.setDisabled(true);
@ -134,7 +134,6 @@ public class EndpointHandlerMappingTests {
nullValue());
}
@FrameworkEndpoint
private static class TestEndpoint extends AbstractEndpoint<Object> {
public TestEndpoint(String path) {
@ -150,10 +149,19 @@ public class EndpointHandlerMappingTests {
}
@FrameworkEndpoint
private static class TestActionEndpoint extends TestEndpoint {
private static class TestMvcEndpoint extends GenericMvcEndpoint {
public TestActionEndpoint(String path) {
super(path);
public TestMvcEndpoint(TestEndpoint delegate) {
super(delegate);
}
}
@FrameworkEndpoint
private static class TestActionEndpoint extends TestMvcEndpoint {
public TestActionEndpoint(TestEndpoint delegate) {
super(delegate);
}
@Override

Loading…
Cancel
Save