diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java index 3e46e0e1103..4eeddec69f0 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java @@ -17,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> endpointTypes = new HashMap>, 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(); diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/BeansEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/BeansEndpoint.java index b07f3c9408a..03839574e77 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/BeansEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/BeansEndpoint.java @@ -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 implements +public class BeansEndpoint extends AbstractEndpoint> 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 implements } @Override - @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) - @ResponseBody - public String invoke() { - return this.liveBeansView.getSnapshotAsJson(); + public List invoke() { + return this.parser.parseList(this.liveBeansView.getSnapshotAsJson()); } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java index 95fa306e7c6..e6c1441e810 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java @@ -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> implements ApplicationContextAware { @@ -67,8 +63,7 @@ public class ConfigurationPropertiesReportEndpoint extends this.keysToSanitize = keysToSanitize; } - @RequestMapping - @ResponseBody + @Override public Map invoke() { Map beans = extract(this.context); return beans; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DumpEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DumpEndpoint.java index 33d7f72a83b..7ac1744b9f3 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DumpEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/DumpEndpoint.java @@ -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> { /** @@ -43,8 +39,6 @@ public class DumpEndpoint extends AbstractEndpoint> { } @Override - @RequestMapping - @ResponseBody public List invoke() { return Arrays.asList(ManagementFactory.getThreadMXBean().dumpAllThreads(true, true)); diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EnvironmentEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EnvironmentEndpoint.java index a176d150d67..ea1b7671fff 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EnvironmentEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EnvironmentEndpoint.java @@ -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> implements EnvironmentAware { @@ -54,8 +47,6 @@ public class EnvironmentEndpoint extends AbstractEndpoint> i } @Override - @RequestMapping - @ResponseBody public Map invoke() { Map result = new LinkedHashMap(); result.put("profiles", this.environment.getActiveProfiles()); @@ -72,16 +63,6 @@ public class EnvironmentEndpoint extends AbstractEndpoint> 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> getPropertySources() { if (this.environment != null && this.environment instanceof ConfigurableEnvironment) { @@ -90,7 +71,7 @@ public class EnvironmentEndpoint extends AbstractEndpoint> 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> 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); - } - - } - } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java index 4ee546a542f..cc0ec319607 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java @@ -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 extends AbstractEndpoint { private HealthIndicator indicator; @@ -50,8 +46,6 @@ public class HealthEndpoint extends AbstractEndpoint { } @Override - @RequestMapping - @ResponseBody public T invoke() { return this.indicator.health(); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/InfoEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/InfoEndpoint.java index 27fc113ece7..4f996c6c90e 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/InfoEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/InfoEndpoint.java @@ -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> { private Map info; @@ -49,8 +45,6 @@ public class InfoEndpoint extends AbstractEndpoint> { } @Override - @RequestMapping - @ResponseBody public Map invoke() { Map info = new LinkedHashMap(this.info); info.putAll(getAdditionalInfo()); diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ManagementErrorEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ManagementErrorEndpoint.java index c7e57455246..441c59d147f 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ManagementErrorEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ManagementErrorEndpoint.java @@ -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> { +@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 invoke() { RequestAttributes attributes = RequestContextHolder.currentRequestAttributes(); return this.controller.extract(attributes, false); } + + @Override + public String getPath() { + return this.path; + } } \ No newline at end of file diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/MetricsEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/MetricsEndpoint.java index 3e0b30331e7..fff41f266ce 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/MetricsEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/MetricsEndpoint.java @@ -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> { private PublicMetrics metrics; @@ -52,8 +45,6 @@ public class MetricsEndpoint extends AbstractEndpoint> { } @Override - @RequestMapping - @ResponseBody public Map invoke() { Map result = new LinkedHashMap(); for (Metric metric : this.metrics.metrics()) { @@ -62,22 +53,4 @@ public class MetricsEndpoint extends AbstractEndpoint> { 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); - } - - } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ShutdownEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ShutdownEndpoint.java index 9585a799efb..78ceec3bbf4 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ShutdownEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ShutdownEndpoint.java @@ -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> implements ApplicationContextAware { @@ -50,8 +45,6 @@ public class ShutdownEndpoint extends AbstractEndpoint> impl } @Override - @RequestMapping(method = RequestMethod.POST) - @ResponseBody public Map invoke() { if (this.context == null) { @@ -59,21 +52,26 @@ public class ShutdownEndpoint extends AbstractEndpoint> impl "No context to shutdown."); } - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(500L); - } - catch (InterruptedException ex) { - // Swallow exception and continue - } - ShutdownEndpoint.this.context.close(); - } - }).start(); + try { + return Collections. singletonMap("message", + "Shutting down, bye..."); + } + finally { - return Collections. singletonMap("message", - "Shutting down, bye..."); + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(500L); + } + catch (InterruptedException ex) { + // Swallow exception and continue + } + ShutdownEndpoint.this.context.close(); + } + }).start(); + + } } @Override diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/TraceEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/TraceEndpoint.java index a003170af8f..02e51567bb2 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/TraceEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/TraceEndpoint.java @@ -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> { private TraceRepository repository; @@ -49,8 +45,6 @@ public class TraceEndpoint extends AbstractEndpoint> { } @Override - @RequestMapping - @ResponseBody public List invoke() { return this.repository.findAll(); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java index 5263e36a3c6..74b810dafcb 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java @@ -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(); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpoint.java new file mode 100644 index 00000000000..09207d45764 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpoint.java @@ -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); + } + + } +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/GenericMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/GenericMvcEndpoint.java new file mode 100644 index 00000000000..c8fa2e436bc --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/GenericMvcEndpoint.java @@ -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(); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MetricsMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MetricsMvcEndpoint.java new file mode 100644 index 00000000000..14f72486f9a --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MetricsMvcEndpoint.java @@ -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); + } + + } +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoint.java new file mode 100644 index 00000000000..8ea12bbea11 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoint.java @@ -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(); + +} \ No newline at end of file diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/ShutdownMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/ShutdownMvcEndpoint.java new file mode 100644 index 00000000000..d1dc2f31cb7 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/ShutdownMvcEndpoint.java @@ -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(); + } +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/BeansEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/BeansEndpointTests.java index ac35cbd3cff..ab898fd64ee 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/BeansEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/BeansEndpointTests.java @@ -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 { @Test public void invoke() throws Exception { - assertThat(getEndpointBean().invoke(), containsString("\"bean\": \"endpoint\"")); + List result = getEndpointBean().invoke(); + assertEquals(1, result.size()); + assertTrue(result.get(0) instanceof Map); } @Configuration diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMappingTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMappingTests.java index f18afa22281..741fea047c7 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMappingTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMappingTests.java @@ -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 { 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