diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java index 5cf1e88c7ef..3033494544d 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java @@ -47,7 +47,6 @@ import org.springframework.boot.actuate.trace.TraceRepository; import org.springframework.boot.autoconfigure.AutoConfigurationReport; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.bind.PropertiesConfigurationFactory; @@ -57,7 +56,6 @@ import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PropertiesLoaderUtils; -import org.springframework.http.MediaType; /** * {@link EnableAutoConfiguration Auto-configuration} for common management @@ -68,7 +66,6 @@ import org.springframework.http.MediaType; * @author Greg Turnquist */ @Configuration -@ConditionalOnClass(MediaType.class) public class EndpointAutoConfiguration { @Autowired(required = false) 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 895f8f2a515..fb381ea0064 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 @@ -29,18 +29,24 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.Endpoint; -import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerAdapter; +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.MetricsMvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; +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; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; -import org.springframework.boot.autoconfigure.web.HttpMessageConverters; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; @@ -85,21 +91,13 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, @Bean @ConditionalOnMissingBean public EndpointHandlerMapping endpointHandlerMapping() { - EndpointHandlerMapping mapping = new EndpointHandlerMapping(); + EndpointHandlerMapping mapping = new EndpointHandlerMapping(mvcEndpoints() + .getEndpoints()); mapping.setDisabled(ManagementServerPort.get(this.applicationContext) != ManagementServerPort.SAME); mapping.setPrefix(this.managementServerProperties.getContextPath()); return mapping; } - @Bean - @ConditionalOnMissingBean - public EndpointHandlerAdapter endpointHandlerAdapter( - final HttpMessageConverters messageConverters) { - EndpointHandlerAdapter adapter = new EndpointHandlerAdapter(); - adapter.setMessageConverters(messageConverters.getConverters()); - return adapter; - } - @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { @@ -130,6 +128,30 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, }; } + @Bean + @ConditionalOnMissingBean + public MvcEndpoints mvcEndpoints() { + return new MvcEndpoints(); + } + + @Bean + @ConditionalOnBean(EnvironmentEndpoint.class) + public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) { + return new EnvironmentMvcEndpoint(delegate); + } + + @Bean + @ConditionalOnBean(MetricsEndpoint.class) + public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) { + return new MetricsMvcEndpoint(delegate); + } + + @Bean + @ConditionalOnBean(ShutdownEndpoint.class) + public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) { + return new ShutdownMvcEndpoint(delegate); + } + private void createChildManagementContext() { final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext(); diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java index cb37692695e..4fa3dee7f73 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java @@ -16,7 +16,8 @@ package org.springframework.boot.actuate.autoconfigure; -import java.util.Map; +import java.util.HashSet; +import java.util.Set; import javax.servlet.Filter; @@ -26,15 +27,16 @@ import org.springframework.beans.factory.HierarchicalBeanFactory; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.actuate.endpoint.AbstractEndpoint; -import org.springframework.boot.actuate.endpoint.Endpoint; -import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerAdapter; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; +import org.springframework.boot.actuate.endpoint.mvc.ManagementErrorEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.boot.actuate.web.ErrorController; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.SearchStrategy; +import org.springframework.boot.autoconfigure.web.HttpMessageConverters; import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; @@ -42,11 +44,10 @@ import org.springframework.boot.context.embedded.ErrorPage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; /** * Configuration triggered from {@link EndpointWebMvcAutoConfiguration} when a new @@ -57,6 +58,9 @@ import org.springframework.web.servlet.HandlerMapping; @Configuration public class EndpointWebMvcChildContextConfiguration { + @Value("${error.path:/error}") + private String errorPath = "/error"; + @Configuration protected static class ServerCustomization implements EmbeddedServletContainerCustomizer { @@ -100,13 +104,22 @@ public class EndpointWebMvcChildContextConfiguration { } @Bean - public HandlerMapping handlerMapping() { - return new EndpointHandlerMapping(); + public HandlerAdapter handlerAdapter(HttpMessageConverters converters) { + // TODO: maybe this needs more configuration for non-basic response use cases + RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter(); + adapter.setMessageConverters(converters.getConverters()); + return adapter; } @Bean - public HandlerAdapter handlerAdapter() { - return new EndpointHandlerAdapter(); + public HandlerMapping handlerMapping(MvcEndpoints endpoints, + ListableBeanFactory beanFactory) { + Set set = new HashSet(endpoints.getEndpoints()); + set.addAll(beanFactory.getBeansOfType(MvcEndpoint.class).values()); + EndpointHandlerMapping mapping = new EndpointHandlerMapping(set); + // In a child context we definitely want to see the parent endpoints + mapping.setDetectHandlerMethodsInAncestorContexts(true); + return mapping; } /* @@ -116,15 +129,8 @@ public class EndpointWebMvcChildContextConfiguration { * endpoints. */ @Bean - public Endpoint> errorEndpoint(final ErrorController controller) { - return new AbstractEndpoint>("/error", false, true) { - @Override - protected Map doInvoke() { - RequestAttributes attributes = RequestContextHolder - .currentRequestAttributes(); - return controller.extract(attributes, false); - } - }; + public ManagementErrorEndpoint errorEndpoint(final ErrorController controller) { + return new ManagementErrorEndpoint(this.errorPath, controller); } @Configuration diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java index 030e04f235e..9e4e5eb6cc4 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java @@ -20,7 +20,8 @@ import java.util.Map; import org.jolokia.http.AgentServlet; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.actuate.endpoint.JolokiaEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint; +import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -65,6 +66,9 @@ public class JolokiaAutoConfiguration { private RelaxedPropertyResolver environment; + @Autowired + private ManagementServerProperties management; + @Autowired public void setEnvironment(Environment environment) { this.environment = new RelaxedPropertyResolver(environment); @@ -77,19 +81,17 @@ public class JolokiaAutoConfiguration { } @Bean - @ConditionalOnMissingBean() - public ServletRegistrationBean jolokiaServletRegistration() { - ServletRegistrationBean registrationBean = new ServletRegistrationBean( - jolokiaServlet(), this.environment.getProperty("endpoints.jolokia.path", - "/jolokia") + "/*"); + public ServletRegistrationBean jolokiaServletRegistration(AgentServlet servlet) { + ServletRegistrationBean registrationBean = new ServletRegistrationBean(servlet, + this.management.getContextPath() + jolokiaEndpoint().getPath() + "/*"); addInitParameters(registrationBean); return registrationBean; } @Bean @ConditionalOnMissingBean - public JolokiaEndpoint jolokiaEndpoint() { - return new JolokiaEndpoint(); + public JolokiaMvcEndpoint jolokiaEndpoint() { + return new JolokiaMvcEndpoint(); } protected void addInitParameters(ServletRegistrationBean registrationBean) { diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java index 7de8a357d9a..c8faa74e3d1 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; import javax.annotation.PostConstruct; import javax.servlet.Filter; @@ -26,6 +27,7 @@ import javax.servlet.Filter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.boot.actuate.web.ErrorController; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -210,9 +212,9 @@ public class ManagementSecurityAutoConfiguration { return NO_PATHS; } - List> endpoints = endpointHandlerMapping.getEndpoints(); + Set endpoints = endpointHandlerMapping.getEndpoints(); List paths = new ArrayList(endpoints.size()); - for (Endpoint endpoint : endpoints) { + for (MvcEndpoint endpoint : endpoints) { if (endpoint.isSensitive() == secure) { paths.add(endpoint.getPath()); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AbstractEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AbstractEndpoint.java index dff785b980d..717f5a905a7 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AbstractEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AbstractEndpoint.java @@ -19,61 +19,48 @@ package org.springframework.boot.actuate.endpoint; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; - /** * Abstract base for {@link Endpoint} implementations. *

- * {@link Endpoint}s that support other {@link HttpMethod}s than {@link HttpMethod#GET} - * should override {@link #methods()} and provide a list of supported methods. * * @author Phillip Webb * @author Christian Dupuis */ public abstract class AbstractEndpoint implements Endpoint { - private static final MediaType[] NO_MEDIA_TYPES = new MediaType[0]; - - protected static final HttpMethod[] NO_HTTP_METHOD = new HttpMethod[0]; - - protected static final HttpMethod[] GET_HTTP_METHOD = new HttpMethod[] { HttpMethod.GET }; - - protected static final HttpMethod[] POST_HTTP_METHOD = new HttpMethod[] { HttpMethod.POST }; - @NotNull - @Pattern(regexp = "/[^/]*", message = "Path must start with /") - private String path; + @Pattern(regexp = "\\w+", message = "ID must only contains letters, numbers and '_'") + private String id; private boolean sensitive; private boolean enabled = true; - public AbstractEndpoint(String path) { - this(path, true, true); + public AbstractEndpoint(String id) { + this(id, true, true); } - public AbstractEndpoint(String path, boolean sensitive, boolean enabled) { - this.path = path; + public AbstractEndpoint(String id, boolean sensitive, boolean enabled) { + this.id = id; this.sensitive = sensitive; this.enabled = enabled; } - public boolean isEnabled() { - return this.enabled; + @Override + public String getId() { + return this.id; } - public void setEnabled(boolean enabled) { - this.enabled = enabled; + public void setId(String id) { + this.id = id; } - @Override - public String getPath() { - return this.path; + public boolean isEnabled() { + return this.enabled; } - public void setPath(String path) { - this.path = path; + public void setEnabled(boolean enabled) { + this.enabled = enabled; } @Override @@ -85,23 +72,4 @@ public abstract class AbstractEndpoint implements Endpoint { this.sensitive = sensitive; } - @Override - public MediaType[] produces() { - return NO_MEDIA_TYPES; - } - - @Override - public HttpMethod[] methods() { - return GET_HTTP_METHOD; - } - - @Override - public final T invoke() { - if (this.enabled) { - return doInvoke(); - } - throw new EndpointDisabledException(); - } - - protected abstract T doInvoke(); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AutoConfigurationReportEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AutoConfigurationReportEndpoint.java index a6445182ee3..7afbce612ee 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AutoConfigurationReportEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AutoConfigurationReportEndpoint.java @@ -48,11 +48,11 @@ public class AutoConfigurationReportEndpoint extends AbstractEndpoint { private AutoConfigurationReport autoConfigurationReport; public AutoConfigurationReportEndpoint() { - super("/autoconfig"); + super("autoconfig"); } @Override - protected Report doInvoke() { + public Report invoke() { return new Report(this.autoConfigurationReport); } 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 6ee2000f30c..83fed278c85 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,13 +16,16 @@ package org.springframework.boot.actuate.endpoint; +import java.util.List; + import org.springframework.beans.BeansException; +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; /** * Exposes JSON view of Spring beans. If the {@link Environment} contains a key setting @@ -33,13 +36,15 @@ import org.springframework.http.MediaType; * @author Dave Syer */ @ConfigurationProperties(name = "endpoints.beans", ignoreUnknownFields = false) -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"); + super("beans"); } @Override @@ -51,12 +56,7 @@ public class BeansEndpoint extends AbstractEndpoint implements } @Override - public MediaType[] produces() { - return new MediaType[] { MediaType.APPLICATION_JSON }; - } - - @Override - protected String doInvoke() { - 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 d2f4c1b8c89..00fae013a11 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 @@ -31,8 +31,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; * annotated classes. * *

- * To protect sensitive information from being exposed, configure property names by using - * endpoints.configprops.keys_to_sanitize. + * To protect sensitive information from being exposed, certain property values are masked + * if their names end with a set of configurable values (default "password" and "secret"). + * Configure property names by using {@link #setKeysToSanitize(String[])}. * * @author Christian Dupuis */ @@ -45,7 +46,7 @@ public class ConfigurationPropertiesReportEndpoint extends private ApplicationContext context; public ConfigurationPropertiesReportEndpoint() { - super("/configprops"); + super("configprops"); } @Override @@ -63,9 +64,15 @@ public class ConfigurationPropertiesReportEndpoint extends } @Override + public Map invoke() { + Map beans = extract(this.context); + return beans; + } + @SuppressWarnings("unchecked") - protected Map doInvoke() { - Map beans = this.context + private Map extract(ApplicationContext context) { + + Map beans = context .getBeansWithAnnotation(ConfigurationProperties.class); // Serialize beans into map structure and sanitize values @@ -75,6 +82,10 @@ public class ConfigurationPropertiesReportEndpoint extends beans.put(entry.getKey(), sanitize(value)); } + if (context.getParent() != null) { + beans.put("parent", extract(context.getParent())); + } + 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 15fbc55e27f..26ac45e35ea 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 @@ -35,11 +35,11 @@ public class DumpEndpoint extends AbstractEndpoint> { * Create a new {@link DumpEndpoint} instance. */ public DumpEndpoint() { - super("/dump"); + super("dump"); } @Override - protected List doInvoke() { + 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/Endpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Endpoint.java index 7fe94e4ecdd..591ed9bd905 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Endpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Endpoint.java @@ -16,9 +16,6 @@ package org.springframework.boot.actuate.endpoint; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; - /** * An endpoint that can be used to expose useful information to operations. Usually * exposed via Spring MVC but could also be exposed using some other technique. @@ -30,26 +27,21 @@ import org.springframework.http.MediaType; public interface Endpoint { /** - * Returns the path of the endpoint. Must start with '/' and should not include - * wildcards. - */ - String getPath(); - - /** - * Returns if the endpoint is sensitive, i.e. may return data that the average user - * should not see. Mappings can use this as a security hint. + * The logical ID of the endpoint. Must only contain simple letters, numbers and '_' + * characters (ie a {@literal "\w"} regex). */ - boolean isSensitive(); + String getId(); /** - * Returns the {@link MediaType}s that this endpoint produces or {@code null}. + * Return if the endpoint is enabled. */ - MediaType[] produces(); + boolean isEnabled(); /** - * Returns the {@link HttpMethod}s that this endpoint supports. + * Return if the endpoint is sensitive, i.e. may return data that the average user + * should not see. Mappings can use this as a security hint. */ - HttpMethod[] methods(); + boolean isSensitive(); /** * Called to invoke the endpoint. 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 1e0e8d600b8..649d9d0a861 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 @@ -43,11 +43,11 @@ public class EnvironmentEndpoint extends AbstractEndpoint> i * Create a new {@link EnvironmentEndpoint} instance. */ public EnvironmentEndpoint() { - super("/env"); + super("env"); } @Override - protected Map doInvoke() { + public Map invoke() { Map result = new LinkedHashMap(); result.put("profiles", this.environment.getActiveProfiles()); for (PropertySource source : getPropertySources()) { @@ -71,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 : "******"; 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 9863bc6243d..6821bb24146 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 @@ -36,13 +36,13 @@ public class HealthEndpoint extends AbstractEndpoint { * @param indicator the health indicator */ public HealthEndpoint(HealthIndicator indicator) { - super("/health", false, true); + super("health", false, true); Assert.notNull(indicator, "Indicator must not be null"); this.indicator = indicator; } @Override - protected T doInvoke() { + 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 06ae714086f..a9ce11b6c69 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 @@ -39,13 +39,13 @@ public class InfoEndpoint extends AbstractEndpoint> { * @param info the info to expose */ public InfoEndpoint(Map info) { - super("/info", false, true); + super("info", false, true); Assert.notNull(info, "Info must not be null"); this.info = info; } @Override - protected Map doInvoke() { + public Map invoke() { Map info = new LinkedHashMap(this.info); info.putAll(getAdditionalInfo()); return info; 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 47f82346550..bd41c270ffa 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 @@ -39,13 +39,13 @@ public class MetricsEndpoint extends AbstractEndpoint> { * @param metrics the metrics to expose */ public MetricsEndpoint(PublicMetrics metrics) { - super("/metrics"); + super("metrics"); Assert.notNull(metrics, "Metrics must not be null"); this.metrics = metrics; } @Override - protected Map doInvoke() { + public Map invoke() { Map result = new LinkedHashMap(); for (Metric metric : this.metrics.metrics()) { result.put(metric.getName(), metric.getValue()); 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 fc7be0a833e..ad814e70c16 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 @@ -24,7 +24,6 @@ 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.http.HttpMethod; /** * {@link Endpoint} to shutdown the {@link ApplicationContext}. @@ -42,32 +41,37 @@ public class ShutdownEndpoint extends AbstractEndpoint> impl * Create a new {@link ShutdownEndpoint} instance. */ public ShutdownEndpoint() { - super("/shutdown", true, false); + super("shutdown", true, false); } @Override - protected Map doInvoke() { + public Map invoke() { if (this.context == null) { return Collections. singletonMap("message", "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. 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. singletonMap("message", - "Shutting down, bye..."); + } } @Override @@ -77,9 +81,4 @@ public class ShutdownEndpoint extends AbstractEndpoint> impl } } - @Override - public HttpMethod[] methods() { - return POST_HTTP_METHOD; - } - } 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 d21f4acd0b2..ef687ec3b26 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 @@ -39,13 +39,13 @@ public class TraceEndpoint extends AbstractEndpoint> { * @param repository the trace repository */ public TraceEndpoint(TraceRepository repository) { - super("/trace"); + super("trace"); Assert.notNull(repository, "Repository must not be null"); this.repository = repository; } @Override - protected List doInvoke() { + public List invoke() { return this.repository.findAll(); } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerAdapter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerAdapter.java deleted file mode 100644 index debd78158b6..00000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerAdapter.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * 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 java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.boot.actuate.endpoint.Endpoint; -import org.springframework.boot.actuate.endpoint.EndpointDisabledException; -import org.springframework.http.MediaType; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.http.server.ServletServerHttpResponse; -import org.springframework.web.HttpMediaTypeNotAcceptableException; -import org.springframework.web.accept.ContentNegotiationManager; -import org.springframework.web.context.request.ServletWebRequest; -import org.springframework.web.servlet.HandlerAdapter; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; -import org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor; -import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; - -import com.fasterxml.jackson.databind.SerializationFeature; - -/** - * MVC {@link HandlerAdapter} for {@link Endpoint}s. Similar in may respects to - * {@link AbstractMessageConverterMethodProcessor} but not tied to annotated methods. - * - * @author Phillip Webb - * - * @see EndpointHandlerMapping - */ -public final class EndpointHandlerAdapter implements HandlerAdapter { - - private final Log logger = LogFactory.getLog(getClass()); - - private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application"); - - private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(); - - private List> messageConverters; - - private List allSupportedMediaTypes; - - public EndpointHandlerAdapter() { - WebMvcConfigurationSupportConventions conventions = new WebMvcConfigurationSupportConventions(); - setMessageConverters(conventions.getDefaultHttpMessageConverters()); - } - - @Override - public boolean supports(Object handler) { - return handler instanceof Endpoint; - } - - @Override - public long getLastModified(HttpServletRequest request, Object handler) { - return -1; - } - - @Override - public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, - Object handler) throws Exception { - handle(request, response, (Endpoint) handler); - return null; - } - - @SuppressWarnings("unchecked") - private void handle(HttpServletRequest request, HttpServletResponse response, - Endpoint endpoint) throws Exception { - - Object result = null; - try { - result = endpoint.invoke(); - } - catch (EndpointDisabledException e) { - // Disabled endpoints should get mapped to a HTTP 404 - throw new NoSuchRequestHandlingMethodException(request); - } - - Class resultClass = result.getClass(); - - List mediaTypes = getMediaTypes(request, endpoint, resultClass); - MediaType selectedMediaType = selectMediaType(mediaTypes); - - ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response); - try { - if (selectedMediaType != null) { - selectedMediaType = selectedMediaType.removeQualityValue(); - for (HttpMessageConverter messageConverter : this.messageConverters) { - if (messageConverter.canWrite(resultClass, selectedMediaType)) { - ((HttpMessageConverter) messageConverter).write(result, - selectedMediaType, outputMessage); - if (this.logger.isDebugEnabled()) { - this.logger.debug("Written [" + result + "] as \"" - + selectedMediaType + "\" using [" + messageConverter - + "]"); - } - return; - } - } - } - throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); - } - finally { - outputMessage.close(); - } - } - - private List getMediaTypes(HttpServletRequest request, - Endpoint endpoint, Class resultClass) - throws HttpMediaTypeNotAcceptableException { - List requested = getAcceptableMediaTypes(request); - List producible = getProducibleMediaTypes(endpoint, resultClass); - - Set compatible = new LinkedHashSet(); - for (MediaType r : requested) { - for (MediaType p : producible) { - if (r.isCompatibleWith(p)) { - compatible.add(getMostSpecificMediaType(r, p)); - } - } - } - if (compatible.isEmpty()) { - throw new HttpMediaTypeNotAcceptableException(producible); - } - List mediaTypes = new ArrayList(compatible); - MediaType.sortBySpecificityAndQuality(mediaTypes); - return mediaTypes; - } - - private List getAcceptableMediaTypes(HttpServletRequest request) - throws HttpMediaTypeNotAcceptableException { - List mediaTypes = this.contentNegotiationManager - .resolveMediaTypes(new ServletWebRequest(request)); - return mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) - : mediaTypes; - } - - private List getProducibleMediaTypes(Endpoint endpoint, - Class returnValueClass) { - MediaType[] mediaTypes = endpoint.produces(); - if (mediaTypes != null && mediaTypes.length != 0) { - return Arrays.asList(mediaTypes); - } - - if (this.allSupportedMediaTypes.isEmpty()) { - return Collections.singletonList(MediaType.ALL); - } - - List result = new ArrayList(); - for (HttpMessageConverter converter : this.messageConverters) { - if (converter.canWrite(returnValueClass, null)) { - result.addAll(converter.getSupportedMediaTypes()); - } - } - return result; - } - - private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) { - produceType = produceType.copyQualityValue(acceptType); - return MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceType) <= 0 ? acceptType - : produceType; - } - - private MediaType selectMediaType(List mediaTypes) { - MediaType selectedMediaType = null; - for (MediaType mediaType : mediaTypes) { - if (mediaType.isConcrete()) { - selectedMediaType = mediaType; - break; - } - else if (mediaType.equals(MediaType.ALL) - || mediaType.equals(MEDIA_TYPE_APPLICATION)) { - selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; - break; - } - } - return selectedMediaType; - } - - public void setContentNegotiationManager( - ContentNegotiationManager contentNegotiationManager) { - this.contentNegotiationManager = contentNegotiationManager; - } - - public void setMessageConverters(List> messageConverters) { - this.messageConverters = messageConverters; - Set allSupportedMediaTypes = new LinkedHashSet(); - for (HttpMessageConverter messageConverter : messageConverters) { - allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); - } - this.allSupportedMediaTypes = new ArrayList(allSupportedMediaTypes); - MediaType.sortBySpecificity(this.allSupportedMediaTypes); - } - - /** - * Default conventions, taken from {@link WebMvcConfigurationSupport} with a few minor - * tweaks. - */ - private static class WebMvcConfigurationSupportConventions extends - WebMvcConfigurationSupport { - public List> getDefaultHttpMessageConverters() { - List> converters = new ArrayList>(); - addDefaultHttpMessageConverters(converters); - for (HttpMessageConverter converter : converters) { - if (converter instanceof MappingJackson2HttpMessageConverter) { - MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter; - jacksonConverter.getObjectMapper().disable( - SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - } - } - return converters; - } - } -} 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 9e65bfa9662..ed98da9aa09 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 @@ -16,36 +16,42 @@ package org.springframework.boot.actuate.endpoint.mvc; -import java.util.ArrayList; +import java.lang.reflect.Method; import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.HashSet; +import java.util.Set; -import javax.servlet.http.HttpServletRequest; - -import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; -import org.springframework.http.HttpMethod; import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping; +import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; /** - * {@link HandlerMapping} to map {@link Endpoint}s to URLs via {@link Endpoint#getPath()}. + * {@link HandlerMapping} to map {@link Endpoint}s to URLs via {@link Endpoint#getId()}. + * The semantics of {@code @RequestMapping} should be identical to a normal + * {@code @Controller}, but the endpoints should not be annotated as {@code @Controller} + * (otherwise they will be mapped by the normal MVC mechanisms). + * + *

+ * One of the aims of the mapping is to support endpoints that work as HTTP endpoints but + * can still provide useful service interfaces when there is no HTTP server (and no Spring + * MVC on the classpath). Note that any endpoints having method signaturess will break in + * a non-servlet environment. * * @author Phillip Webb * @author Christian Dupuis - * @see EndpointHandlerAdapter + * @author Dave Syer + * */ -public class EndpointHandlerMapping extends AbstractUrlHandlerMapping implements - InitializingBean, ApplicationContextAware { +public class EndpointHandlerMapping extends RequestMappingHandlerMapping implements + ApplicationContextAware { - private List> endpoints; + private Set endpoints; private String prefix = ""; @@ -54,52 +60,74 @@ public class EndpointHandlerMapping extends AbstractUrlHandlerMapping implements /** * Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be * detected from the {@link ApplicationContext}. + * @param endpoints */ - public EndpointHandlerMapping() { - setOrder(HIGHEST_PRECEDENCE); - } - - /** - * Create a new {@link EndpointHandlerMapping} with the specified endpoints. - * @param endpoints the endpoints - */ - public EndpointHandlerMapping(Collection> endpoints) { - Assert.notNull(endpoints, "Endpoints must not be null"); - this.endpoints = new ArrayList>(endpoints); + public EndpointHandlerMapping(Collection endpoints) { + this.endpoints = new HashSet(endpoints); + // By default the static resource handler mapping is LOWEST_PRECEDENCE - 1 + setOrder(LOWEST_PRECEDENCE - 2); } @Override - public void afterPropertiesSet() throws Exception { - if (this.endpoints == null) { - this.endpoints = findEndpointBeans(); - } + public void afterPropertiesSet() { + super.afterPropertiesSet(); if (!this.disabled) { - for (Endpoint endpoint : this.endpoints) { - registerHandler(this.prefix + endpoint.getPath(), endpoint); + for (MvcEndpoint endpoint : this.endpoints) { + detectHandlerMethods(endpoint); } } } - @SuppressWarnings({ "rawtypes", "unchecked" }) - private List> findEndpointBeans() { - return new ArrayList(BeanFactoryUtils.beansOfTypeIncludingAncestors( - getApplicationContext(), Endpoint.class).values()); + /** + * Since all handler beans are passed into the constructor there is no need to detect + * anything here + */ + @Override + protected boolean isHandler(Class beanType) { + return false; } @Override - protected Object lookupHandler(String urlPath, HttpServletRequest request) - throws Exception { - Object handler = super.lookupHandler(urlPath, request); - if (handler != null) { - Object endpoint = (handler instanceof HandlerExecutionChain ? ((HandlerExecutionChain) handler) - .getHandler() : handler); - HttpMethod method = HttpMethod.valueOf(request.getMethod()); - if (endpoint instanceof Endpoint - && supportsMethod(((Endpoint) endpoint).methods(), method)) { - return endpoint; + protected void registerHandlerMethod(Object handler, Method method, + RequestMappingInfo mapping) { + + if (mapping == null) { + return; + } + + Set defaultPatterns = mapping.getPatternsCondition().getPatterns(); + String[] patterns = new String[defaultPatterns.isEmpty() ? 1 : defaultPatterns + .size()]; + + String path = ""; + Object bean = handler; + if (bean instanceof String) { + bean = getApplicationContext().getBean((String) handler); + } + if (bean instanceof MvcEndpoint) { + MvcEndpoint endpoint = (MvcEndpoint) bean; + path = endpoint.getPath(); + } + + int i = 0; + String prefix = StringUtils.hasText(this.prefix) ? this.prefix + path : path; + if (defaultPatterns.isEmpty()) { + patterns[0] = prefix; + } + else { + for (String pattern : defaultPatterns) { + patterns[i] = prefix + pattern; + i++; } } - return null; + PatternsRequestCondition patternsInfo = new PatternsRequestCondition(patterns); + + RequestMappingInfo modified = new RequestMappingInfo(patternsInfo, + mapping.getMethodsCondition(), mapping.getParamsCondition(), + mapping.getHeadersCondition(), mapping.getConsumesCondition(), + mapping.getProducesCondition(), mapping.getCustomCondition()); + + super.registerHandlerMethod(handler, method, modified); } /** @@ -128,19 +156,7 @@ public class EndpointHandlerMapping extends AbstractUrlHandlerMapping implements /** * Return the endpoints */ - public List> getEndpoints() { - return Collections.unmodifiableList(this.endpoints); - } - - private boolean supportsMethod(HttpMethod[] supportedMethods, - HttpMethod requestedMethod) { - Assert.notNull(supportedMethods, "SupportMethods must not be null"); - Assert.notNull(supportedMethods, "RequestedMethod must not be null"); - for (HttpMethod supportedMethod : supportedMethods) { - if (supportedMethod.equals(requestedMethod)) { - return true; - } - } - return false; + public Set getEndpoints() { + return this.endpoints; } } 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..1eacc19b752 --- /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.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * @author Dave Syer + */ +public class EnvironmentMvcEndpoint extends GenericMvcEndpoint implements + EnvironmentAware { + + private Environment environment; + + public EnvironmentMvcEndpoint(EnvironmentEndpoint delegate) { + super(delegate); + } + + @RequestMapping(value = "/{name:.*}", method = RequestMethod.GET) + @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..524200cbb77 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/GenericMvcEndpoint.java @@ -0,0 +1,59 @@ +/* + * 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 + */ +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.getId(); + } + + @Override + public boolean isSensitive() { + return this.delegate.isSensitive(); + } + + @Override + public Class getEndpointType() { + @SuppressWarnings("unchecked") + Class> type = (Class>) this.delegate + .getClass(); + return type; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/JolokiaEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpoint.java similarity index 51% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/JolokiaEndpoint.java rename to spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpoint.java index fbdc991c278..1d0473cdcb4 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/JolokiaEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpoint.java @@ -14,10 +14,13 @@ * limitations under the License. */ -package org.springframework.boot.actuate.endpoint; +package org.springframework.boot.actuate.endpoint.mvc; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.http.HttpMethod; /** * {@link Endpoint} implementation to register the Jolokia infrastructure with the Boot @@ -26,20 +29,49 @@ import org.springframework.http.HttpMethod; * @author Christian Dupuis */ @ConfigurationProperties(name = "endpoints.jolokia", ignoreUnknownFields = false) -public class JolokiaEndpoint extends AbstractEndpoint { +public class JolokiaMvcEndpoint implements MvcEndpoint { + + @NotNull + @Pattern(regexp = "/[^/]*", message = "Path must start with /") + private String path; + + private boolean sensitive; - public JolokiaEndpoint() { - super("/jolokia"); + private boolean enabled = true; + + public JolokiaMvcEndpoint() { + this.path = "/jolokia"; + } + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; } @Override - protected String doInvoke() { - return null; + public String getPath() { + return this.path; + } + + public void setPath(String path) { + this.path = path; + } + + @Override + public boolean isSensitive() { + return this.sensitive; + } + + public void setSensitive(boolean sensitive) { + this.sensitive = sensitive; } @Override - public HttpMethod[] methods() { - return NO_HTTP_METHOD; + public Class getEndpointType() { + return null; } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/ManagementErrorEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/ManagementErrorEndpoint.java new file mode 100644 index 00000000000..5b9e120f9cb --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/ManagementErrorEndpoint.java @@ -0,0 +1,67 @@ +/* + * 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 java.util.Map; + +import org.springframework.boot.actuate.web.ErrorController; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +/** + * Special endpoint for handling "/error" path when the management servlet is in a child + * context. The regular {@link ErrorController} should be available there but because of + * the way the handler mappings are set up it will not be detected. + * + * @author Dave Syer + */ +@ConfigurationProperties(name = "error") +public class ManagementErrorEndpoint implements MvcEndpoint { + + private final ErrorController controller; + private String path; + + public ManagementErrorEndpoint(String path, ErrorController controller) { + this.path = path; + this.controller = controller; + } + + @RequestMapping + @ResponseBody + public Map invoke() { + RequestAttributes attributes = RequestContextHolder.currentRequestAttributes(); + return this.controller.extract(attributes, false); + } + + @Override + public String getPath() { + return this.path; + } + + @Override + public boolean isSensitive() { + return false; + } + + @Override + public Class getEndpointType() { + return null; + } +} \ No newline at end of file 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..26800b5b892 --- /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.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * @author Dave Syer + */ +public class MetricsMvcEndpoint extends GenericMvcEndpoint { + + private MetricsEndpoint delegate; + + public MetricsMvcEndpoint(MetricsEndpoint delegate) { + super(delegate); + this.delegate = delegate; + } + + @RequestMapping(value = "/{name:.*}", method = RequestMethod.GET) + @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..7aa76420f7c --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoint.java @@ -0,0 +1,38 @@ +/* + * 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; + +/** + * A strategy for the MVC layer on top of an {@link Endpoint}. Implementations are allowed + * to use @RequestMapping and the full Spring MVC machinery, but should not + * use @Controller or @RequestMapping at the type level (since + * that would lead to a double mapping of paths, once by the regular MVC handler mappings + * and once by the {@link EndpointHandlerMapping}). + * + * @author Dave Syer + */ +public interface MvcEndpoint { + + String getPath(); + + boolean isSensitive(); + + Class getEndpointType(); + +} \ No newline at end of file diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoints.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoints.java new file mode 100644 index 00000000000..3e1801b45c8 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoints.java @@ -0,0 +1,88 @@ +/* + * 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 java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * A registry for all {@link MvcEndpoint} beans, and a factory for a set of generic ones + * wrapping existing {@link Endpoint} instances that are not already exposed as MVC + * endpoints. + * + * @author Dave Syer + */ +@Component +public class MvcEndpoints implements ApplicationContextAware, InitializingBean { + + private ApplicationContext applicationContext; + + private Set endpoints = new HashSet(); + + private Set> customTypes; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public void afterPropertiesSet() throws Exception { + Collection existing = this.applicationContext.getBeansOfType( + MvcEndpoint.class).values(); + this.endpoints.addAll(existing); + this.customTypes = findEndpointClasses(existing); + @SuppressWarnings("rawtypes") + Collection delegates = this.applicationContext.getBeansOfType( + Endpoint.class).values(); + for (Endpoint endpoint : delegates) { + if (isGenericEndpoint(endpoint.getClass())) { + this.endpoints.add(new GenericMvcEndpoint(endpoint)); + } + } + } + + private Set> findEndpointClasses(Collection existing) { + Set> types = new HashSet>(); + for (MvcEndpoint endpoint : existing) { + Class type = endpoint.getEndpointType(); + if (type != null) { + types.add(type); + } + } + return types; + } + + public Set getEndpoints() { + return this.endpoints; + } + + private boolean isGenericEndpoint(Class type) { + return !this.customTypes.contains(type) + && !MvcEndpoint.class.isAssignableFrom(type); + } + +} \ 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..0877ed6885e --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/ShutdownMvcEndpoint.java @@ -0,0 +1,39 @@ +/* + * 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 + */ +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/main/java/org/springframework/boot/actuate/web/BasicErrorController.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/BasicErrorController.java index 151a20ce2d9..5067aaaacca 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/BasicErrorController.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/BasicErrorController.java @@ -63,7 +63,7 @@ public class BasicErrorController implements ErrorController { @RequestMapping(value = "${error.path:/error}", produces = "text/html") public ModelAndView errorHtml(HttpServletRequest request) { - Map map = extract(new ServletRequestAttributes(request), false); + Map map = error(request); return new ModelAndView(ERROR_KEY, map); } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java index 76b70affe14..5f7ece55b54 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java @@ -24,8 +24,8 @@ import java.nio.charset.Charset; import org.junit.After; import org.junit.Test; import org.springframework.boot.TestUtils; -import org.springframework.boot.actuate.endpoint.AbstractEndpoint; import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; @@ -200,13 +200,8 @@ public class EndpointWebMvcAutoConfigurationTests { } @Bean - public Endpoint testEndpoint() { - return new AbstractEndpoint("/endpoint", false, true) { - @Override - public String doInvoke() { - return "endpointoutput"; - } - }; + public TestEndpoint testEndpoint() { + return new TestEndpoint(); } } @@ -245,4 +240,29 @@ public class EndpointWebMvcAutoConfigurationTests { } + public static class TestEndpoint implements MvcEndpoint { + + @RequestMapping + @ResponseBody + public String invoke() { + return "endpointoutput"; + } + + @Override + public String getPath() { + return "/endpoint"; + } + + @Override + public boolean isSensitive() { + return true; + } + + @Override + public Class getEndpointType() { + return Endpoint.class; + } + + } + } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java index efc159fc4ca..de66bf45d46 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java @@ -60,7 +60,9 @@ public class JolokiaAutoConfigurationTests { public void agentServletRegisteredWithAppContext() throws Exception { this.context = new AnnotationConfigEmbeddedWebApplicationContext(); this.context.register(Config.class, WebMvcAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, JolokiaAutoConfiguration.class); + ManagementServerPropertiesAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + JolokiaAutoConfiguration.class); this.context.refresh(); assertEquals(1, this.context.getBeanNamesForType(AgentServlet.class).length); } @@ -70,7 +72,9 @@ public class JolokiaAutoConfigurationTests { this.context = new AnnotationConfigEmbeddedWebApplicationContext(); TestUtils.addEnviroment(this.context, "endpoints.jolokia.enabled:false"); this.context.register(Config.class, WebMvcAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, JolokiaAutoConfiguration.class); + ManagementServerPropertiesAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + JolokiaAutoConfiguration.class); this.context.refresh(); assertEquals(0, this.context.getBeanNamesForType(AgentServlet.class).length); } @@ -79,7 +83,9 @@ public class JolokiaAutoConfigurationTests { public void agentServletRegisteredWithServletContainer() throws Exception { this.context = new AnnotationConfigEmbeddedWebApplicationContext(); this.context.register(Config.class, WebMvcAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, JolokiaAutoConfiguration.class); + ManagementServerPropertiesAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + JolokiaAutoConfiguration.class); this.context.refresh(); Servlet servlet = null; diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AbstractEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AbstractEndpointTests.java index 46abfb8ba64..bd6c43f75fb 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AbstractEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AbstractEndpointTests.java @@ -25,7 +25,6 @@ import org.springframework.boot.TestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; -import org.springframework.http.MediaType; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; @@ -43,22 +42,19 @@ public abstract class AbstractEndpointTests> { private final Class type; - private final String path; + private final String id; private final boolean sensitive; private final String property; - private MediaType[] produces; - - public AbstractEndpointTests(Class configClass, Class type, String path, - boolean sensitive, String property, MediaType... produces) { + public AbstractEndpointTests(Class configClass, Class type, String id, + boolean sensitive, String property) { this.configClass = configClass; this.type = type; - this.path = path; + this.id = id; this.sensitive = sensitive; this.property = property; - this.produces = produces; } @Before @@ -76,13 +72,8 @@ public abstract class AbstractEndpointTests> { } @Test - public void producesMediaType() { - assertThat(getEndpointBean().produces(), equalTo(this.produces)); - } - - @Test - public void getPath() throws Exception { - assertThat(getEndpointBean().getPath(), equalTo(this.path)); + public void getId() throws Exception { + assertThat(getEndpointBean().getId(), equalTo(this.id)); } @Test @@ -91,12 +82,12 @@ public abstract class AbstractEndpointTests> { } @Test - public void pathOverride() throws Exception { + public void idOverride() throws Exception { this.context = new AnnotationConfigApplicationContext(); - TestUtils.addEnviroment(this.context, this.property + ".path:/mypath"); + TestUtils.addEnviroment(this.context, this.property + ".id:myid"); this.context.register(this.configClass); this.context.refresh(); - assertThat(getEndpointBean().getPath(), equalTo("/mypath")); + assertThat(getEndpointBean().getId(), equalTo("myid")); } @Test diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AutoConfigurationReportEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AutoConfigurationReportEndpointTests.java index 56de89f77f8..a78e411489c 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AutoConfigurationReportEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AutoConfigurationReportEndpointTests.java @@ -42,7 +42,7 @@ public class AutoConfigurationReportEndpointTests extends AbstractEndpointTests { public AutoConfigurationReportEndpointTests() { - super(Config.class, AutoConfigurationReportEndpoint.class, "/autoconfig", true, + super(Config.class, AutoConfigurationReportEndpoint.class, "autoconfig", true, "endpoints.autoconfig"); } 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 04804d171d3..eaeedde7b05 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,15 +16,16 @@ package org.springframework.boot.actuate.endpoint; +import java.util.List; +import java.util.Map; + import org.junit.Test; -import org.springframework.boot.actuate.endpoint.BeansEndpoint; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.MediaType; -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}. @@ -34,13 +35,14 @@ import static org.junit.Assert.assertThat; public class BeansEndpointTests extends AbstractEndpointTests { public BeansEndpointTests() { - super(Config.class, BeansEndpoint.class, "/beans", true, "endpoints.beans", - MediaType.APPLICATION_JSON); + super(Config.class, BeansEndpoint.class, "beans", true, "endpoints.beans"); } @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/ConfigurationPropertiesReportEndpointParentTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointParentTests.java new file mode 100644 index 00000000000..e45923e9677 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointParentTests.java @@ -0,0 +1,103 @@ +/* + * Copyright 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; + +import java.util.Map; + +import org.junit.After; +import org.junit.Test; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ConfigurationPropertiesReportEndpointParentTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + if (this.context.getParent() != null) { + ((ConfigurableApplicationContext) this.context.getParent()).close(); + } + } + } + + @Test + public void testInvoke() throws Exception { + AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(); + parent.register(Parent.class); + parent.refresh(); + this.context = new AnnotationConfigApplicationContext(); + this.context.setParent(parent); + this.context.register(Config.class); + this.context.refresh(); + ConfigurationPropertiesReportEndpoint endpoint = this.context + .getBean(ConfigurationPropertiesReportEndpoint.class); + Map result = endpoint.invoke(); + assertTrue(result.containsKey("parent")); + assertEquals(3, result.size()); // the endpoint, the test props and the parent + // System.err.println(result); + } + + @Configuration + @EnableConfigurationProperties + public static class Parent { + @Bean + public TestProperties testProperties() { + return new TestProperties(); + } + } + + @Configuration + @EnableConfigurationProperties + public static class Config { + + @Bean + public ConfigurationPropertiesReportEndpoint endpoint() { + return new ConfigurationPropertiesReportEndpoint(); + } + + @Bean + public TestProperties testProperties() { + return new TestProperties(); + } + + } + + @ConfigurationProperties(name = "test") + public static class TestProperties { + + private String myTestProperty = "654321"; + + public String getMyTestProperty() { + return this.myTestProperty; + } + + public void setMyTestProperty(String myTestProperty) { + this.myTestProperty = myTestProperty; + } + + } +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java index 0e7edecb8a4..4d77d527137 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java @@ -33,7 +33,7 @@ public class ConfigurationPropertiesReportEndpointTests extends AbstractEndpointTests { public ConfigurationPropertiesReportEndpointTests() { - super(Config.class, ConfigurationPropertiesReportEndpoint.class, "/configprops", + super(Config.class, ConfigurationPropertiesReportEndpoint.class, "configprops", true, "endpoints.configprops"); } @@ -68,6 +68,15 @@ public class ConfigurationPropertiesReportEndpointTests extends assertEquals("******", nestedProperties.get("myTestProperty")); } + @Configuration + @EnableConfigurationProperties + public static class Parent { + @Bean + public TestProperties testProperties() { + return new TestProperties(); + } + } + @Configuration @EnableConfigurationProperties public static class Config { @@ -82,29 +91,30 @@ public class ConfigurationPropertiesReportEndpointTests extends return new TestProperties(); } - @ConfigurationProperties(name = "test") - public static class TestProperties { + } - private String dbPassword = "123456"; + @ConfigurationProperties(name = "test") + public static class TestProperties { - private String myTestProperty = "654321"; + private String dbPassword = "123456"; - public String getDbPassword() { - return this.dbPassword; - } + private String myTestProperty = "654321"; - public void setDbPassword(String dbPassword) { - this.dbPassword = dbPassword; - } + public String getDbPassword() { + return this.dbPassword; + } - public String getMyTestProperty() { - return this.myTestProperty; - } + public void setDbPassword(String dbPassword) { + this.dbPassword = dbPassword; + } - public void setMyTestProperty(String myTestProperty) { - this.myTestProperty = myTestProperty; - } + public String getMyTestProperty() { + return this.myTestProperty; + } + public void setMyTestProperty(String myTestProperty) { + this.myTestProperty = myTestProperty; } + } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/DumpEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/DumpEndpointTests.java index b0bbf05285e..e6a478e9eac 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/DumpEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/DumpEndpointTests.java @@ -20,7 +20,6 @@ import java.lang.management.ThreadInfo; import java.util.List; import org.junit.Test; -import org.springframework.boot.actuate.endpoint.DumpEndpoint; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -36,7 +35,7 @@ import static org.junit.Assert.assertThat; public class DumpEndpointTests extends AbstractEndpointTests { public DumpEndpointTests() { - super(Config.class, DumpEndpoint.class, "/dump", true, "endpoints.dump"); + super(Config.class, DumpEndpoint.class, "dump", true, "endpoints.dump"); } @Test diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/EnvironmentEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/EnvironmentEndpointTests.java index 133e897f68a..3ca7f893c4b 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/EnvironmentEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/EnvironmentEndpointTests.java @@ -17,7 +17,6 @@ package org.springframework.boot.actuate.endpoint; import org.junit.Test; -import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -33,7 +32,7 @@ import static org.junit.Assert.assertThat; public class EnvironmentEndpointTests extends AbstractEndpointTests { public EnvironmentEndpointTests() { - super(Config.class, EnvironmentEndpoint.class, "/env", true, "endpoints.env"); + super(Config.class, EnvironmentEndpoint.class, "env", true, "endpoints.env"); } @Test diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java index 05ed4853c28..e56f77380c6 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/HealthEndpointTests.java @@ -17,7 +17,6 @@ package org.springframework.boot.actuate.endpoint; import org.junit.Test; -import org.springframework.boot.actuate.endpoint.HealthEndpoint; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -34,7 +33,7 @@ import static org.junit.Assert.assertThat; public class HealthEndpointTests extends AbstractEndpointTests> { public HealthEndpointTests() { - super(Config.class, HealthEndpoint.class, "/health", false, "endpoints.health"); + super(Config.class, HealthEndpoint.class, "health", false, "endpoints.health"); } @Test diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/InfoEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/InfoEndpointTests.java index e08b53a4eb9..1bb88ec8841 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/InfoEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/InfoEndpointTests.java @@ -35,7 +35,7 @@ import static org.junit.Assert.assertThat; public class InfoEndpointTests extends AbstractEndpointTests { public InfoEndpointTests() { - super(Config.class, InfoEndpoint.class, "/info", false, "endpoints.info"); + super(Config.class, InfoEndpoint.class, "info", false, "endpoints.info"); } @Test diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/MetricsEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/MetricsEndpointTests.java index 76be87ac053..a05efee4319 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/MetricsEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/MetricsEndpointTests.java @@ -20,8 +20,6 @@ import java.util.Collection; import java.util.Collections; import org.junit.Test; -import org.springframework.boot.actuate.endpoint.MetricsEndpoint; -import org.springframework.boot.actuate.endpoint.PublicMetrics; import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -38,7 +36,7 @@ import static org.junit.Assert.assertThat; public class MetricsEndpointTests extends AbstractEndpointTests { public MetricsEndpointTests() { - super(Config.class, MetricsEndpoint.class, "/metrics", true, "endpoints.metrics"); + super(Config.class, MetricsEndpoint.class, "metrics", true, "endpoints.metrics"); } @Test diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ShutdownEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ShutdownEndpointTests.java index 49bca4fcc05..da2f64f8751 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ShutdownEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ShutdownEndpointTests.java @@ -35,7 +35,7 @@ import static org.junit.Assert.assertTrue; public class ShutdownEndpointTests extends AbstractEndpointTests { public ShutdownEndpointTests() { - super(Config.class, ShutdownEndpoint.class, "/shutdown", true, + super(Config.class, ShutdownEndpoint.class, "shutdown", true, "endpoints.shutdown"); } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/TraceEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/TraceEndpointTests.java index 13e1842fb66..9d0fe89fb24 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/TraceEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/TraceEndpointTests.java @@ -19,7 +19,6 @@ package org.springframework.boot.actuate.endpoint; import java.util.Collections; import org.junit.Test; -import org.springframework.boot.actuate.endpoint.TraceEndpoint; import org.springframework.boot.actuate.trace.InMemoryTraceRepository; import org.springframework.boot.actuate.trace.Trace; import org.springframework.boot.actuate.trace.TraceRepository; @@ -38,7 +37,7 @@ import static org.junit.Assert.assertThat; public class TraceEndpointTests extends AbstractEndpointTests { public TraceEndpointTests() { - super(Config.class, TraceEndpoint.class, "/trace", true, "endpoints.trace"); + super(Config.class, TraceEndpoint.class, "trace", true, "endpoints.trace"); } @Test diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java index 4497e3401de..1955345b6fb 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java @@ -167,11 +167,11 @@ public class EndpointMBeanExporterTests { public static class TestEndpoint extends AbstractEndpoint { public TestEndpoint() { - super("/test"); + super("test"); } @Override - protected String doInvoke() { + public String invoke() { return "hello world"; } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerAdapterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerAdapterTests.java deleted file mode 100644 index 5877f63a7d6..00000000000 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerAdapterTests.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 java.util.Collections; -import java.util.Map; - -import org.junit.Test; -import org.springframework.boot.actuate.endpoint.AbstractEndpoint; -import org.springframework.boot.actuate.endpoint.Endpoint; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link EndpointHandlerAdapter}. - * - * @author Phillip Webb - */ -public class EndpointHandlerAdapterTests { - - private EndpointHandlerAdapter adapter = new EndpointHandlerAdapter(); - private MockHttpServletRequest request = new MockHttpServletRequest(); - private MockHttpServletResponse response = new MockHttpServletResponse(); - - @Test - public void onlySupportsEndpoints() throws Exception { - assertTrue(this.adapter.supports(mock(Endpoint.class))); - assertFalse(this.adapter.supports(mock(Object.class))); - } - - @Test - public void rendersJson() throws Exception { - this.adapter.handle(this.request, this.response, - new AbstractEndpoint>("/foo") { - @Override - protected Map doInvoke() { - return Collections.singletonMap("hello", "world"); - } - }); - assertEquals("{\"hello\":\"world\"}", this.response.getContentAsString()); - } - - @Test - public void rendersString() throws Exception { - this.request.addHeader("Accept", "text/plain"); - this.adapter.handle(this.request, this.response, new AbstractEndpoint( - "/foo") { - @Override - protected String doInvoke() { - return "hello world"; - } - }); - assertEquals("hello world", this.response.getContentAsString()); - } -} 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 05311da42fb..d40f34b9c87 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 @@ -16,12 +16,19 @@ package org.springframework.boot.actuate.endpoint.mvc; +import java.lang.reflect.Method; import java.util.Arrays; +import org.junit.Before; import org.junit.Test; import org.springframework.boot.actuate.endpoint.AbstractEndpoint; -import org.springframework.http.HttpMethod; +import org.springframework.context.support.StaticApplicationContext; import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.util.ReflectionUtils; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.method.HandlerMethod; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; @@ -33,66 +40,94 @@ import static org.junit.Assert.assertThat; * Tests for {@link EndpointHandlerMapping}. * * @author Phillip Webb + * @author Dave Syer */ public class EndpointHandlerMappingTests { + private StaticApplicationContext context = new StaticApplicationContext(); + private Method method; + + @Before + public void init() throws Exception { + 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")); EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList( endpointA, endpointB)); + mapping.setApplicationContext(this.context); mapping.afterPropertiesSet(); assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")) - .getHandler(), equalTo((Object) endpointA)); + .getHandler(), + equalTo((Object) new HandlerMethod(endpointA, this.method))); assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/b")) - .getHandler(), equalTo((Object) endpointB)); + .getHandler(), + equalTo((Object) new HandlerMethod(endpointB, this.method))); assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/c")), nullValue()); } @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")); EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList( endpointA, endpointB)); + mapping.setApplicationContext(this.context); mapping.setPrefix("/a"); mapping.afterPropertiesSet(); assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a/a")) - .getHandler(), equalTo((Object) endpointA)); + .getHandler(), + equalTo((Object) new HandlerMethod(endpointA, this.method))); assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a/b")) - .getHandler(), equalTo((Object) endpointB)); + .getHandler(), + equalTo((Object) new HandlerMethod(endpointB, this.method))); assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")), nullValue()); } - @Test + @Test(expected = HttpRequestMethodNotSupportedException.class) public void onlyGetHttpMethodForNonActionEndpoints() throws Exception { - TestEndpoint endpoint = new TestEndpoint("/a"); + TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a")); EndpointHandlerMapping mapping = new EndpointHandlerMapping( Arrays.asList(endpoint)); + mapping.setApplicationContext(this.context); mapping.afterPropertiesSet(); assertNotNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a"))); assertNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a"))); } @Test + public void postHttpMethodForActionEndpoints() throws Exception { + TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a")); + EndpointHandlerMapping mapping = new EndpointHandlerMapping( + Arrays.asList(endpoint)); + mapping.setApplicationContext(this.context); + mapping.afterPropertiesSet(); + assertNotNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a"))); + } + + @Test(expected = HttpRequestMethodNotSupportedException.class) public void onlyPostHttpMethodForActionEndpoints() throws Exception { - TestEndpoint endpoint = new TestActionEndpoint("/a"); + TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a")); EndpointHandlerMapping mapping = new EndpointHandlerMapping( Arrays.asList(endpoint)); + mapping.setApplicationContext(this.context); mapping.afterPropertiesSet(); - assertNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a"))); assertNotNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a"))); + assertNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a"))); } @Test public void disabled() throws Exception { - TestEndpoint endpointA = new TestEndpoint("/a"); + TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("/a")); EndpointHandlerMapping mapping = new EndpointHandlerMapping( - Arrays.asList(endpointA)); + Arrays.asList(endpoint)); mapping.setDisabled(true); + mapping.setApplicationContext(this.context); mapping.afterPropertiesSet(); assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")), nullValue()); @@ -105,22 +140,32 @@ public class EndpointHandlerMappingTests { } @Override - public Object doInvoke() { + public Object invoke() { return null; } } - private static class TestActionEndpoint extends TestEndpoint { + private static class TestMvcEndpoint extends GenericMvcEndpoint { - public TestActionEndpoint(String path) { - super(path); + public TestMvcEndpoint(TestEndpoint delegate) { + super(delegate); + } + + } + + private static class TestActionEndpoint extends GenericMvcEndpoint { + + public TestActionEndpoint(TestEndpoint delegate) { + super(delegate); } @Override - public HttpMethod[] methods() { - return POST_HTTP_METHOD; + @RequestMapping(method = RequestMethod.POST) + public Object invoke() { + return null; } + } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java new file mode 100644 index 00000000000..79a9f4cd6e2 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java @@ -0,0 +1,93 @@ +/* + * 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.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.TestUtils; +import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration; +import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpointTests.TestConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalToIgnoringCase; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Dave Syer + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = { TestConfiguration.class }) +@WebAppConfiguration +public class EnvironmentMvcEndpointTests { + + @Autowired + private WebApplicationContext context; + + private MockMvc mvc; + + @Before + public void setUp() { + this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build(); + TestUtils.addEnviroment((ConfigurableApplicationContext) this.context, "foo:bar"); + } + + @Test + public void home() throws Exception { + this.mvc.perform(get("/env")).andExpect(status().isOk()) + .andExpect(content().string(containsString("systemProperties"))); + } + + @Test + public void sub() throws Exception { + this.mvc.perform(get("/env/foo")).andExpect(status().isOk()) + .andExpect(content().string(equalToIgnoringCase("bar"))); + } + + @Import(EndpointWebMvcAutoConfiguration.class) + @EnableWebMvc + @Configuration + public static class TestConfiguration { + + @Bean + public EnvironmentEndpoint endpoint() { + return new EnvironmentEndpoint(); + } + + @Bean + public EnvironmentMvcEndpoint mvcEndpoint() { + return new EnvironmentMvcEndpoint(endpoint()); + } + + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaEndpointTests.java new file mode 100644 index 00000000000..318a83adb70 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaEndpointTests.java @@ -0,0 +1,64 @@ +/* + * Copyright 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.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration; +import org.springframework.boot.actuate.endpoint.mvc.JolokiaEndpointTests.Config; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import static org.junit.Assert.assertEquals; + +/** + * @author Christian Dupuis + * @author Dave Syer + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = { Config.class }) +@WebAppConfiguration +public class JolokiaEndpointTests { + + @Autowired + private MvcEndpoints endpoints; + + @Test + public void endpointRegistered() throws Exception { + assertEquals(1, this.endpoints.getEndpoints().size()); + } + + @Configuration + @EnableConfigurationProperties + @EnableWebMvc + @Import(EndpointWebMvcAutoConfiguration.class) + public static class Config { + + @Bean + public JolokiaMvcEndpoint endpoint() { + return new JolokiaMvcEndpoint(); + } + + } +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerIntegrationTests.java index 7dbfefdf892..d23b10a670c 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerIntegrationTests.java @@ -26,8 +26,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; +import org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration; import org.springframework.boot.actuate.web.BasicErrorControllerIntegrationTests.TestConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -81,7 +83,8 @@ public class BasicErrorControllerIntegrationTests { } @Configuration - @EnableAutoConfiguration + @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, + ManagementSecurityAutoConfiguration.class }) public static class TestConfiguration { // For manual testing diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerSpecialIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerSpecialIntegrationTests.java index 7cabdf463d6..efbc69ff7f4 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerSpecialIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/BasicErrorControllerSpecialIntegrationTests.java @@ -19,7 +19,9 @@ package org.springframework.boot.actuate.web; import org.junit.After; import org.junit.Test; import org.springframework.boot.SpringApplication; +import org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; @@ -77,13 +79,15 @@ public class BasicErrorControllerSpecialIntegrationTests { } @Configuration - @EnableAutoConfiguration + @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, + ManagementSecurityAutoConfiguration.class }) protected static class ParentConfiguration { } @Configuration - @EnableAutoConfiguration + @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, + ManagementSecurityAutoConfiguration.class }) @EnableWebMvc protected static class WebMvcIncludedConfiguration { // For manual testing @@ -94,7 +98,19 @@ public class BasicErrorControllerSpecialIntegrationTests { } @Configuration - @EnableAutoConfiguration + @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, + ManagementSecurityAutoConfiguration.class }) + protected static class VanillaConfiguration { + // For manual testing + public static void main(String[] args) { + SpringApplication.run(VanillaConfiguration.class, args); + } + + } + + @Configuration + @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, + ManagementSecurityAutoConfiguration.class }) protected static class ChildConfiguration { // For manual testing diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index 752fe1ab226..0ee8f5348ce 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -16,6 +16,7 @@ spring-boot-sample-actuator spring-boot-sample-actuator-log4j + spring-boot-sample-actuator-noweb spring-boot-sample-actuator-ui spring-boot-sample-amqp spring-boot-sample-aop diff --git a/spring-boot-samples/spring-boot-sample-actuator-noweb/pom.xml b/spring-boot-samples/spring-boot-sample-actuator-noweb/pom.xml new file mode 100644 index 00000000000..de4eef79d0a --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-noweb/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 0.5.0.BUILD-SNAPSHOT + + spring-boot-sample-actuator-noweb + jar + + ${basedir}/../.. + + + + ${project.groupId} + spring-boot-starter-actuator + + + ${project.groupId} + spring-boot-starter-shell-remote + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/HelloWorldService.java b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/java/org/springframework/boot/sample/actuator/HelloWorldService.java similarity index 94% rename from spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/HelloWorldService.java rename to spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/java/org/springframework/boot/sample/actuator/HelloWorldService.java index 342c02a8850..baea40d432f 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/HelloWorldService.java +++ b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/java/org/springframework/boot/sample/actuator/HelloWorldService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/JolokiaEndpointTests.java b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/java/org/springframework/boot/sample/actuator/SampleActuatorNoWebApplication.java similarity index 56% rename from spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/JolokiaEndpointTests.java rename to spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/java/org/springframework/boot/sample/actuator/SampleActuatorNoWebApplication.java index 99485f02d57..eee9972708e 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/JolokiaEndpointTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/java/org/springframework/boot/sample/actuator/SampleActuatorNoWebApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * 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. @@ -14,29 +14,22 @@ * limitations under the License. */ -package org.springframework.boot.actuate.endpoint; +package org.springframework.boot.sample.actuator; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -/** - * @author Christian Dupuis - */ -public class JolokiaEndpointTests extends AbstractEndpointTests { +@Configuration +@EnableAutoConfiguration +@EnableConfigurationProperties +@ComponentScan +public class SampleActuatorNoWebApplication { - public JolokiaEndpointTests() { - super(Config.class, JolokiaEndpoint.class, "/jolokia", true, "endpoints.jolokia"); + public static void main(String[] args) throws Exception { + SpringApplication.run(SampleActuatorNoWebApplication.class, args); } - @Configuration - @EnableConfigurationProperties - public static class Config { - - @Bean - public JolokiaEndpoint endpoint() { - return new JolokiaEndpoint(); - } - - } } diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/ServiceProperties.java b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/java/org/springframework/boot/sample/actuator/ServiceProperties.java similarity index 95% rename from spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/ServiceProperties.java rename to spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/java/org/springframework/boot/sample/actuator/ServiceProperties.java index 83a119b8651..5b111e166ec 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/ServiceProperties.java +++ b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/java/org/springframework/boot/sample/actuator/ServiceProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; diff --git a/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/resources/application.properties new file mode 100644 index 00000000000..3df2e92ef3b --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/resources/application.properties @@ -0,0 +1,5 @@ +service.name: Phil +shell.ssh.enabled: true +shell.ssh.port: 2222 +shell.auth: simple +shell.auth.simple.user.password: password diff --git a/spring-boot-samples/spring-boot-sample-actuator-noweb/src/test/java/org/springframework/boot/sample/actuator/SampleActuatorNoWebApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/test/java/org/springframework/boot/sample/actuator/SampleActuatorNoWebApplicationTests.java new file mode 100644 index 00000000000..8711f8a4563 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/test/java/org/springframework/boot/sample/actuator/SampleActuatorNoWebApplicationTests.java @@ -0,0 +1,68 @@ +/* + * 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.sample.actuator; + +import static org.junit.Assert.assertNotNull; + +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.actuate.endpoint.MetricsEndpoint; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * Basic integration tests for service demo application. + * + * @author Dave Syer + */ +public class SampleActuatorNoWebApplicationTests { + + private static ConfigurableApplicationContext context; + + @BeforeClass + public static void start() throws Exception { + Future future = Executors + .newSingleThreadExecutor().submit( + new Callable() { + @Override + public ConfigurableApplicationContext call() throws Exception { + return SpringApplication + .run(SampleActuatorNoWebApplication.class); + } + }); + context = future.get(60, TimeUnit.SECONDS); + } + + @AfterClass + public static void stop() { + if (context != null) { + context.close(); + } + } + + @Test + public void endpointsExist() throws Exception { + assertNotNull(context.getBean(MetricsEndpoint.class)); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplication.java b/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/org/springframework/boot/sample/actuator/ui/SampleActuatorUiApplication.java similarity index 97% rename from spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplication.java rename to spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/org/springframework/boot/sample/actuator/ui/SampleActuatorUiApplication.java index 73d4cfa0946..927a2ba5228 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplication.java +++ b/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/org/springframework/boot/sample/actuator/ui/SampleActuatorUiApplication.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops.ui; +package org.springframework.boot.sample.actuator.ui; import java.util.Date; import java.util.Map; diff --git a/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplicationPortTests.java b/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/actuator/ui/SampleActuatorUiApplicationPortTests.java similarity index 96% rename from spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplicationPortTests.java rename to spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/actuator/ui/SampleActuatorUiApplicationPortTests.java index 95487b418ae..bf310ced5e9 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplicationPortTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/actuator/ui/SampleActuatorUiApplicationPortTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops.ui; +package org.springframework.boot.sample.actuator.ui; import static org.junit.Assert.assertEquals; @@ -29,6 +29,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; +import org.springframework.boot.sample.actuator.ui.SampleActuatorUiApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/actuator/ui/SampleActuatorUiApplicationTests.java similarity index 97% rename from spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplicationTests.java rename to spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/actuator/ui/SampleActuatorUiApplicationTests.java index 8960d3c2591..6d731abbdca 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator-ui/src/test/java/org/springframework/boot/sample/actuator/ui/SampleActuatorUiApplicationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops.ui; +package org.springframework.boot.sample.actuator.ui; import java.io.IOException; import java.util.Arrays; @@ -28,6 +28,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; +import org.springframework.boot.sample.actuator.ui.SampleActuatorUiApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointDisabledException.java b/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/HelloWorldService.java similarity index 58% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointDisabledException.java rename to spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/HelloWorldService.java index 2dce8a76f4d..baea40d432f 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointDisabledException.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/HelloWorldService.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * 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. @@ -14,13 +14,19 @@ * limitations under the License. */ -package org.springframework.boot.actuate.endpoint; +package org.springframework.boot.sample.actuator; -/** - * {@link RuntimeException} indicating an {@link Endpoint} implementation is not enabled. - * - * @author Christian Dupuis - */ -public class EndpointDisabledException extends RuntimeException { +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class HelloWorldService { + + @Autowired + private ServiceProperties configuration; + + public String getHelloMessage() { + return "Hello " + this.configuration.getName(); + } } diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/SampleActuatorApplication.java b/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/SampleActuatorApplication.java similarity index 96% rename from spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/SampleActuatorApplication.java rename to spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/SampleActuatorApplication.java index 1e747a7f9ce..c7e73fff1c3 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/SampleActuatorApplication.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/SampleActuatorApplication.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/SampleController.java b/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/SampleController.java similarity index 96% rename from spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/SampleController.java rename to spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/SampleController.java index 4c5f9950e25..ffb6a0c083d 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/ops/SampleController.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/SampleController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import java.util.Collections; import java.util.Map; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/ServiceProperties.java b/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/ServiceProperties.java new file mode 100644 index 00000000000..5b111e166ec --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator/src/main/java/org/springframework/boot/sample/actuator/ServiceProperties.java @@ -0,0 +1,36 @@ +/* + * 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.sample.actuator; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@ConfigurationProperties(name = "service", ignoreUnknownFields = false) +@Component +public class ServiceProperties { + + private String name = "World"; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/EndpointsPropertiesSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/EndpointsPropertiesSampleActuatorApplicationTests.java similarity index 97% rename from spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/EndpointsPropertiesSampleActuatorApplicationTests.java rename to spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/EndpointsPropertiesSampleActuatorApplicationTests.java index 5dad069baa8..b4991a7fe1d 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/EndpointsPropertiesSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/EndpointsPropertiesSampleActuatorApplicationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import java.io.IOException; import java.util.ArrayList; @@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Test; import org.springframework.boot.SpringApplication; +import org.springframework.boot.sample.actuator.SampleActuatorApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementAddressSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/ManagementAddressSampleActuatorApplicationTests.java similarity index 97% rename from spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementAddressSampleActuatorApplicationTests.java rename to spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/ManagementAddressSampleActuatorApplicationTests.java index 7456e49fc9f..b5019067469 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementAddressSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/ManagementAddressSampleActuatorApplicationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import static org.junit.Assert.assertEquals; @@ -32,6 +32,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.boot.sample.actuator.SampleActuatorApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/ManagementSampleActuatorApplicationTests.java similarity index 96% rename from spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementSampleActuatorApplicationTests.java rename to spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/ManagementSampleActuatorApplicationTests.java index e54fef2beeb..561b65d3171 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/ManagementSampleActuatorApplicationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import java.io.IOException; import java.util.Map; @@ -27,6 +27,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; +import org.springframework.boot.sample.actuator.SampleActuatorApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/NoManagementSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/NoManagementSampleActuatorApplicationTests.java similarity index 97% rename from spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/NoManagementSampleActuatorApplicationTests.java rename to spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/NoManagementSampleActuatorApplicationTests.java index 311b82005c1..3e11caf2c72 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/NoManagementSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/NoManagementSampleActuatorApplicationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import java.io.IOException; import java.util.ArrayList; @@ -30,6 +30,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.boot.sample.actuator.SampleActuatorApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/SampleActuatorApplicationTests.java similarity index 98% rename from spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java rename to spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/SampleActuatorApplicationTests.java index b5fffe09a48..28762898536 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/SampleActuatorApplicationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -36,6 +36,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.boot.sample.actuator.SampleActuatorApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ShutdownSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/ShutdownSampleActuatorApplicationTests.java similarity index 97% rename from spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ShutdownSampleActuatorApplicationTests.java rename to spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/ShutdownSampleActuatorApplicationTests.java index aa4bc9b0c7d..3d7f35471cc 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ShutdownSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/ShutdownSampleActuatorApplicationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import java.io.IOException; import java.util.ArrayList; @@ -30,6 +30,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.boot.sample.actuator.SampleActuatorApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/UnsecureManagementSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/UnsecureManagementSampleActuatorApplicationTests.java similarity index 96% rename from spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/UnsecureManagementSampleActuatorApplicationTests.java rename to spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/UnsecureManagementSampleActuatorApplicationTests.java index 48a17a4e886..dc619da376b 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/UnsecureManagementSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/UnsecureManagementSampleActuatorApplicationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -31,6 +31,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; +import org.springframework.boot.sample.actuator.SampleActuatorApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/UnsecureSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/UnsecureSampleActuatorApplicationTests.java similarity index 95% rename from spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/UnsecureSampleActuatorApplicationTests.java rename to spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/UnsecureSampleActuatorApplicationTests.java index 9a2516cfd85..bac04e3fce5 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/UnsecureSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/actuator/UnsecureSampleActuatorApplicationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.sample.ops; +package org.springframework.boot.sample.actuator; import java.io.IOException; import java.util.Map; @@ -27,6 +27,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; +import org.springframework.boot.sample.actuator.SampleActuatorApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/spring-boot-starters/spring-boot-starter-shell-remote/src/main/resources/commands/crash/autoconfig.groovy b/spring-boot-starters/spring-boot-starter-shell-remote/src/main/resources/commands/crash/autoconfig.groovy new file mode 100644 index 00000000000..3f4dc27beef --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-shell-remote/src/main/resources/commands/crash/autoconfig.groovy @@ -0,0 +1,29 @@ +package commands + +import org.springframework.boot.actuate.endpoint.AutoConfigurationReportEndpoint + +class autoconfig { + + @Usage("Display auto configuration report from ApplicationContext") + @Command + void main(InvocationContext context) { + context.attributes['spring.beanfactory'].getBeansOfType(AutoConfigurationReportEndpoint.class).each { name, endpoint -> + def report = endpoint.invoke() + out.println "Endpoint: " + name + "\n\nPositive Matches:\n================\n" + report.positiveMatches.each { key, list -> + out.println key + ":" + list.each { mandc -> + out.println " " + mandc.condition + ": " + mandc.message + } + } + out.println "\nNegative Matches\n================\n" + report.negativeMatches.each { key, list -> + out.println key + ":" + list.each { mandc -> + out.println " " + mandc.condition + ": " + mandc.message + } + } + } + } + +} \ No newline at end of file diff --git a/spring-boot-starters/spring-boot-starter-shell-remote/src/main/resources/commands/crash/beans.groovy b/spring-boot-starters/spring-boot-starter-shell-remote/src/main/resources/commands/crash/beans.groovy new file mode 100644 index 00000000000..67fea1cada5 --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-shell-remote/src/main/resources/commands/crash/beans.groovy @@ -0,0 +1,17 @@ +package commands + +import org.springframework.boot.actuate.endpoint.BeansEndpoint + +class beans { + + @Usage("Display beans in ApplicationContext") + @Command + def main(InvocationContext context) { + def result = [:] + context.attributes['spring.beanfactory'].getBeansOfType(BeansEndpoint.class).each { name, endpoint -> + result.put(name, endpoint.invoke()) + } + result.size() == 1 ? result.values()[0] : result + } + +} \ No newline at end of file