Commit 87e00cfa authored by Dave Syer's avatar Dave Syer Committed by Phillip Webb

Extract MVC concerns completely from Endpoint implementations

parent 71ebcbff
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter; import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
...@@ -28,8 +30,19 @@ import javax.servlet.http.HttpServletResponse; ...@@ -28,8 +30,19 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; 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.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.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.actuate.properties.ManagementServerProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
...@@ -50,6 +63,7 @@ import org.springframework.context.annotation.Bean; ...@@ -50,6 +63,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
...@@ -119,6 +133,50 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, ...@@ -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() { private void createChildManagementContext() {
final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext(); final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext();
......
...@@ -16,16 +16,16 @@ ...@@ -16,16 +16,16 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.util.List;
import org.springframework.beans.BeansException; 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.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.LiveBeansView; import org.springframework.context.support.LiveBeansView;
import org.springframework.core.env.Environment; 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 * 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; ...@@ -36,12 +36,13 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Dave Syer * @author Dave Syer
*/ */
@ConfigurationProperties(name = "endpoints.beans", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.beans", ignoreUnknownFields = false)
@FrameworkEndpoint public class BeansEndpoint extends AbstractEndpoint<List<Object>> implements
public class BeansEndpoint extends AbstractEndpoint<String> implements
ApplicationContextAware { ApplicationContextAware {
private LiveBeansView liveBeansView = new LiveBeansView(); private LiveBeansView liveBeansView = new LiveBeansView();
private JsonParser parser = JsonParserFactory.getJsonParser();
public BeansEndpoint() { public BeansEndpoint() {
super("/beans"); super("/beans");
} }
...@@ -55,9 +56,7 @@ public class BeansEndpoint extends AbstractEndpoint<String> implements ...@@ -55,9 +56,7 @@ public class BeansEndpoint extends AbstractEndpoint<String> implements
} }
@Override @Override
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) public List<Object> invoke() {
@ResponseBody return this.parser.parseList(this.liveBeansView.getSnapshotAsJson());
public String invoke() {
return this.liveBeansView.getSnapshotAsJson();
} }
} }
...@@ -19,13 +19,10 @@ package org.springframework.boot.actuate.endpoint; ...@@ -19,13 +19,10 @@ package org.springframework.boot.actuate.endpoint;
import java.util.Map; import java.util.Map;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert; 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; import com.fasterxml.jackson.databind.ObjectMapper;
...@@ -41,7 +38,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; ...@@ -41,7 +38,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author Christian Dupuis * @author Christian Dupuis
*/ */
@ConfigurationProperties(name = "endpoints.configprops", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.configprops", ignoreUnknownFields = false)
@FrameworkEndpoint
public class ConfigurationPropertiesReportEndpoint extends public class ConfigurationPropertiesReportEndpoint extends
AbstractEndpoint<Map<String, Object>> implements ApplicationContextAware { AbstractEndpoint<Map<String, Object>> implements ApplicationContextAware {
...@@ -67,8 +63,7 @@ public class ConfigurationPropertiesReportEndpoint extends ...@@ -67,8 +63,7 @@ public class ConfigurationPropertiesReportEndpoint extends
this.keysToSanitize = keysToSanitize; this.keysToSanitize = keysToSanitize;
} }
@RequestMapping @Override
@ResponseBody
public Map<String, Object> invoke() { public Map<String, Object> invoke() {
Map<String, Object> beans = extract(this.context); Map<String, Object> beans = extract(this.context);
return beans; return beans;
......
...@@ -21,10 +21,7 @@ import java.lang.management.ThreadInfo; ...@@ -21,10 +21,7 @@ import java.lang.management.ThreadInfo;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties; 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. * {@link Endpoint} to expose thread info.
...@@ -32,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -32,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Dave Syer * @author Dave Syer
*/ */
@ConfigurationProperties(name = "endpoints.dump", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.dump", ignoreUnknownFields = false)
@FrameworkEndpoint
public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> { public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> {
/** /**
...@@ -43,8 +39,6 @@ public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> { ...@@ -43,8 +39,6 @@ public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> {
} }
@Override @Override
@RequestMapping
@ResponseBody
public List<ThreadInfo> invoke() { public List<ThreadInfo> invoke() {
return Arrays.asList(ManagementFactory.getThreadMXBean().dumpAllThreads(true, return Arrays.asList(ManagementFactory.getThreadMXBean().dumpAllThreads(true,
true)); true));
......
...@@ -19,7 +19,6 @@ package org.springframework.boot.actuate.endpoint; ...@@ -19,7 +19,6 @@ package org.springframework.boot.actuate.endpoint;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.EnvironmentAware; import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
...@@ -27,11 +26,6 @@ import org.springframework.core.env.EnumerablePropertySource; ...@@ -27,11 +26,6 @@ import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment; 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. * {@link Endpoint} to expose {@link ConfigurableEnvironment environment} information.
...@@ -40,7 +34,6 @@ import org.springframework.web.bind.annotation.ResponseStatus; ...@@ -40,7 +34,6 @@ import org.springframework.web.bind.annotation.ResponseStatus;
* @author Phillip Webb * @author Phillip Webb
*/ */
@ConfigurationProperties(name = "endpoints.env", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.env", ignoreUnknownFields = false)
@FrameworkEndpoint
public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> implements public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> implements
EnvironmentAware { EnvironmentAware {
...@@ -54,8 +47,6 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i ...@@ -54,8 +47,6 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
} }
@Override @Override
@RequestMapping
@ResponseBody
public Map<String, Object> invoke() { public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>(); Map<String, Object> result = new LinkedHashMap<String, Object>();
result.put("profiles", this.environment.getActiveProfiles()); result.put("profiles", this.environment.getActiveProfiles());
...@@ -72,16 +63,6 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i ...@@ -72,16 +63,6 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
return result; 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() { private Iterable<PropertySource<?>> getPropertySources() {
if (this.environment != null if (this.environment != null
&& this.environment instanceof ConfigurableEnvironment) { && this.environment instanceof ConfigurableEnvironment) {
...@@ -90,7 +71,7 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i ...@@ -90,7 +71,7 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
return new StandardEnvironment().getPropertySources(); return new StandardEnvironment().getPropertySources();
} }
private Object sanitize(String name, Object object) { public static Object sanitize(String name, Object object) {
if (name.toLowerCase().endsWith("password") if (name.toLowerCase().endsWith("password")
|| name.toLowerCase().endsWith("secret")) { || name.toLowerCase().endsWith("secret")) {
return object == null ? null : "******"; return object == null ? null : "******";
...@@ -103,13 +84,4 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i ...@@ -103,13 +84,4 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
this.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);
}
}
} }
...@@ -16,12 +16,9 @@ ...@@ -16,12 +16,9 @@
package org.springframework.boot.actuate.endpoint; 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.actuate.health.HealthIndicator;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert; 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. * {@link Endpoint} to expose application health.
...@@ -29,7 +26,6 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -29,7 +26,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Dave Syer * @author Dave Syer
*/ */
@ConfigurationProperties(name = "endpoints.health", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.health", ignoreUnknownFields = false)
@FrameworkEndpoint
public class HealthEndpoint<T> extends AbstractEndpoint<T> { public class HealthEndpoint<T> extends AbstractEndpoint<T> {
private HealthIndicator<? extends T> indicator; private HealthIndicator<? extends T> indicator;
...@@ -50,8 +46,6 @@ public class HealthEndpoint<T> extends AbstractEndpoint<T> { ...@@ -50,8 +46,6 @@ public class HealthEndpoint<T> extends AbstractEndpoint<T> {
} }
@Override @Override
@RequestMapping
@ResponseBody
public T invoke() { public T invoke() {
return this.indicator.health(); return this.indicator.health();
} }
......
...@@ -20,11 +20,8 @@ import java.util.Collections; ...@@ -20,11 +20,8 @@ import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert; 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. * {@link Endpoint} to expose arbitrary application information.
...@@ -32,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -32,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Dave Syer * @author Dave Syer
*/ */
@ConfigurationProperties(name = "endpoints.info", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.info", ignoreUnknownFields = false)
@FrameworkEndpoint
public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> { public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
private Map<String, ? extends Object> info; private Map<String, ? extends Object> info;
...@@ -49,8 +45,6 @@ public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> { ...@@ -49,8 +45,6 @@ public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
} }
@Override @Override
@RequestMapping
@ResponseBody
public Map<String, Object> invoke() { public Map<String, Object> invoke() {
Map<String, Object> info = new LinkedHashMap<String, Object>(this.info); Map<String, Object> info = new LinkedHashMap<String, Object>(this.info);
info.putAll(getAdditionalInfo()); info.putAll(getAdditionalInfo());
......
...@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.endpoint; ...@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.endpoint;
import java.util.Map; import java.util.Map;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint; 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.actuate.web.ErrorController;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -33,22 +34,27 @@ import org.springframework.web.context.request.RequestContextHolder; ...@@ -33,22 +34,27 @@ import org.springframework.web.context.request.RequestContextHolder;
* *
* @author Dave Syer * @author Dave Syer
*/ */
@FrameworkEndpoint
@ConfigurationProperties(name = "error") @ConfigurationProperties(name = "error")
public class ManagementErrorEndpoint extends AbstractEndpoint<Map<String, Object>> { @FrameworkEndpoint
public class ManagementErrorEndpoint implements MvcEndpoint {
private final ErrorController controller; private final ErrorController controller;
private String path;
public ManagementErrorEndpoint(String path, ErrorController controller) { public ManagementErrorEndpoint(String path, ErrorController controller) {
super(path, false, true); this.path = path;
this.controller = controller; this.controller = controller;
} }
@Override
@RequestMapping @RequestMapping
@ResponseBody @ResponseBody
public Map<String, Object> invoke() { public Map<String, Object> invoke() {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes(); RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
return this.controller.extract(attributes, false); return this.controller.extract(attributes, false);
} }
@Override
public String getPath() {
return this.path;
}
} }
\ No newline at end of file
...@@ -19,15 +19,9 @@ package org.springframework.boot.actuate.endpoint; ...@@ -19,15 +19,9 @@ package org.springframework.boot.actuate.endpoint;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.util.Assert; 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}. * {@link Endpoint} to expose {@link PublicMetrics}.
...@@ -35,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseStatus; ...@@ -35,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseStatus;
* @author Dave Syer * @author Dave Syer
*/ */
@ConfigurationProperties(name = "endpoints.metrics", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.metrics", ignoreUnknownFields = false)
@FrameworkEndpoint
public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> { public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
private PublicMetrics metrics; private PublicMetrics metrics;
...@@ -52,8 +45,6 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> { ...@@ -52,8 +45,6 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
} }
@Override @Override
@RequestMapping
@ResponseBody
public Map<String, Object> invoke() { public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>(); Map<String, Object> result = new LinkedHashMap<String, Object>();
for (Metric metric : this.metrics.metrics()) { for (Metric metric : this.metrics.metrics()) {
...@@ -62,22 +53,4 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> { ...@@ -62,22 +53,4 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
return result; 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; ...@@ -20,14 +20,10 @@ import java.util.Collections;
import java.util.Map; import java.util.Map;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext; 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}. * {@link Endpoint} to shutdown the {@link ApplicationContext}.
...@@ -36,7 +32,6 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -36,7 +32,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Christian Dupuis * @author Christian Dupuis
*/ */
@ConfigurationProperties(name = "endpoints.shutdown", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.shutdown", ignoreUnknownFields = false)
@FrameworkEndpoint
public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> implements public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> implements
ApplicationContextAware { ApplicationContextAware {
...@@ -50,8 +45,6 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl ...@@ -50,8 +45,6 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl
} }
@Override @Override
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> invoke() { public Map<String, Object> invoke() {
if (this.context == null) { if (this.context == null) {
...@@ -59,21 +52,26 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl ...@@ -59,21 +52,26 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl
"No context to shutdown."); "No context to shutdown.");
} }
new Thread(new Runnable() { try {
@Override return Collections.<String, Object> singletonMap("message",
public void run() { "Shutting down, bye...");
try { }
Thread.sleep(500L); finally {
}
catch (InterruptedException ex) { new Thread(new Runnable() {
// Swallow exception and continue @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 @Override
......
...@@ -18,13 +18,10 @@ package org.springframework.boot.actuate.endpoint; ...@@ -18,13 +18,10 @@ package org.springframework.boot.actuate.endpoint;
import java.util.List; 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.Trace;
import org.springframework.boot.actuate.trace.TraceRepository; import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert; 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. * {@link Endpoint} to expose {@link Trace} information.
...@@ -32,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -32,7 +29,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Dave Syer * @author Dave Syer
*/ */
@ConfigurationProperties(name = "endpoints.trace", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.trace", ignoreUnknownFields = false)
@FrameworkEndpoint
public class TraceEndpoint extends AbstractEndpoint<List<Trace>> { public class TraceEndpoint extends AbstractEndpoint<List<Trace>> {
private TraceRepository repository; private TraceRepository repository;
...@@ -49,8 +45,6 @@ public class TraceEndpoint extends AbstractEndpoint<List<Trace>> { ...@@ -49,8 +45,6 @@ public class TraceEndpoint extends AbstractEndpoint<List<Trace>> {
} }
@Override @Override
@RequestMapping
@ResponseBody
public List<Trace> invoke() { public List<Trace> invoke() {
return this.repository.findAll(); return this.repository.findAll();
} }
......
...@@ -117,8 +117,8 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme ...@@ -117,8 +117,8 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme
if (bean instanceof String) { if (bean instanceof String) {
bean = getApplicationContext().getBean((String) handler); bean = getApplicationContext().getBean((String) handler);
} }
if (bean instanceof Endpoint) { if (bean instanceof MvcEndpoint) {
Endpoint<?> endpoint = (Endpoint<?>) bean; MvcEndpoint endpoint = (MvcEndpoint) bean;
path = endpoint.getPath(); path = endpoint.getPath();
} }
......
/*
* 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);
}
}
}
/*
* 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();
}
}
/*
* 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);
}
}
}
/*
* 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();
}
\ No newline at end of file
/*
* 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 @@ ...@@ -16,13 +16,16 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.util.List;
import java.util.Map;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue;
/** /**
* Tests for {@link BeansEndpoint}. * Tests for {@link BeansEndpoint}.
...@@ -37,7 +40,9 @@ public class BeansEndpointTests extends AbstractEndpointTests<BeansEndpoint> { ...@@ -37,7 +40,9 @@ public class BeansEndpointTests extends AbstractEndpointTests<BeansEndpoint> {
@Test @Test
public void invoke() throws Exception { 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 @Configuration
......
...@@ -52,13 +52,13 @@ public class EndpointHandlerMappingTests { ...@@ -52,13 +52,13 @@ public class EndpointHandlerMappingTests {
this.context.getDefaultListableBeanFactory().registerSingleton("mapping", this.context.getDefaultListableBeanFactory().registerSingleton("mapping",
this.mapping); this.mapping);
this.mapping.setApplicationContext(this.context); this.mapping.setApplicationContext(this.context);
this.method = ReflectionUtils.findMethod(TestEndpoint.class, "invoke"); this.method = ReflectionUtils.findMethod(TestMvcEndpoint.class, "invoke");
} }
@Test @Test
public void withoutPrefix() throws Exception { public void withoutPrefix() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a"); TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a"));
TestEndpoint endpointB = new TestEndpoint("/b"); TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b"));
this.context.getDefaultListableBeanFactory().registerSingleton( this.context.getDefaultListableBeanFactory().registerSingleton(
endpointA.getPath(), endpointA); endpointA.getPath(), endpointA);
this.context.getDefaultListableBeanFactory().registerSingleton( this.context.getDefaultListableBeanFactory().registerSingleton(
...@@ -76,8 +76,8 @@ public class EndpointHandlerMappingTests { ...@@ -76,8 +76,8 @@ public class EndpointHandlerMappingTests {
@Test @Test
public void withPrefix() throws Exception { public void withPrefix() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a"); TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a"));
TestEndpoint endpointB = new TestEndpoint("/b"); TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b"));
this.context.getDefaultListableBeanFactory().registerSingleton( this.context.getDefaultListableBeanFactory().registerSingleton(
endpointA.getPath(), endpointA); endpointA.getPath(), endpointA);
this.context.getDefaultListableBeanFactory().registerSingleton( this.context.getDefaultListableBeanFactory().registerSingleton(
...@@ -96,7 +96,7 @@ public class EndpointHandlerMappingTests { ...@@ -96,7 +96,7 @@ public class EndpointHandlerMappingTests {
@Test(expected = HttpRequestMethodNotSupportedException.class) @Test(expected = HttpRequestMethodNotSupportedException.class)
public void onlyGetHttpMethodForNonActionEndpoints() throws Exception { public void onlyGetHttpMethodForNonActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestEndpoint("/a"); TestMvcEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton( this.context.getDefaultListableBeanFactory().registerSingleton(
endpoint.getPath(), endpoint); endpoint.getPath(), endpoint);
this.mapping.afterPropertiesSet(); this.mapping.afterPropertiesSet();
...@@ -106,7 +106,7 @@ public class EndpointHandlerMappingTests { ...@@ -106,7 +106,7 @@ public class EndpointHandlerMappingTests {
@Test @Test
public void postHttpMethodForActionEndpoints() throws Exception { public void postHttpMethodForActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestActionEndpoint("/a"); TestMvcEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton( this.context.getDefaultListableBeanFactory().registerSingleton(
endpoint.getPath(), endpoint); endpoint.getPath(), endpoint);
this.mapping.afterPropertiesSet(); this.mapping.afterPropertiesSet();
...@@ -115,7 +115,7 @@ public class EndpointHandlerMappingTests { ...@@ -115,7 +115,7 @@ public class EndpointHandlerMappingTests {
@Test(expected = HttpRequestMethodNotSupportedException.class) @Test(expected = HttpRequestMethodNotSupportedException.class)
public void onlyPostHttpMethodForActionEndpoints() throws Exception { public void onlyPostHttpMethodForActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestActionEndpoint("/a"); TestMvcEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton( this.context.getDefaultListableBeanFactory().registerSingleton(
endpoint.getPath(), endpoint); endpoint.getPath(), endpoint);
this.mapping.afterPropertiesSet(); this.mapping.afterPropertiesSet();
...@@ -125,7 +125,7 @@ public class EndpointHandlerMappingTests { ...@@ -125,7 +125,7 @@ public class EndpointHandlerMappingTests {
@Test @Test
public void disabled() throws Exception { public void disabled() throws Exception {
TestEndpoint endpoint = new TestEndpoint("/a"); TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton( this.context.getDefaultListableBeanFactory().registerSingleton(
endpoint.getPath(), endpoint); endpoint.getPath(), endpoint);
this.mapping.setDisabled(true); this.mapping.setDisabled(true);
...@@ -134,7 +134,6 @@ public class EndpointHandlerMappingTests { ...@@ -134,7 +134,6 @@ public class EndpointHandlerMappingTests {
nullValue()); nullValue());
} }
@FrameworkEndpoint
private static class TestEndpoint extends AbstractEndpoint<Object> { private static class TestEndpoint extends AbstractEndpoint<Object> {
public TestEndpoint(String path) { public TestEndpoint(String path) {
...@@ -150,10 +149,19 @@ public class EndpointHandlerMappingTests { ...@@ -150,10 +149,19 @@ public class EndpointHandlerMappingTests {
} }
@FrameworkEndpoint @FrameworkEndpoint
private static class TestActionEndpoint extends TestEndpoint { private static class TestMvcEndpoint extends GenericMvcEndpoint {
public TestActionEndpoint(String path) { public TestMvcEndpoint(TestEndpoint delegate) {
super(path); super(delegate);
}
}
@FrameworkEndpoint
private static class TestActionEndpoint extends TestMvcEndpoint {
public TestActionEndpoint(TestEndpoint delegate) {
super(delegate);
} }
@Override @Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment