Commit 625d36d0 authored by Phillip Webb's avatar Phillip Webb

Merge branch 'framework-endpoint'

parents 5a978e2f 7c78b74a
...@@ -47,7 +47,6 @@ import org.springframework.boot.actuate.trace.TraceRepository; ...@@ -47,7 +47,6 @@ import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.boot.autoconfigure.AutoConfigurationReport; import org.springframework.boot.autoconfigure.AutoConfigurationReport;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 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.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.bind.PropertiesConfigurationFactory; import org.springframework.boot.bind.PropertiesConfigurationFactory;
...@@ -57,7 +56,6 @@ import org.springframework.core.env.ConfigurableEnvironment; ...@@ -57,7 +56,6 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.http.MediaType;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for common management * {@link EnableAutoConfiguration Auto-configuration} for common management
...@@ -68,7 +66,6 @@ import org.springframework.http.MediaType; ...@@ -68,7 +66,6 @@ import org.springframework.http.MediaType;
* @author Greg Turnquist * @author Greg Turnquist
*/ */
@Configuration @Configuration
@ConditionalOnClass(MediaType.class)
public class EndpointAutoConfiguration { public class EndpointAutoConfiguration {
@Autowired(required = false) @Autowired(required = false)
......
...@@ -29,18 +29,24 @@ import org.springframework.beans.BeansException; ...@@ -29,18 +29,24 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint; 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.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.actuate.properties.ManagementServerProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; 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.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; 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.ServerProperties;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
...@@ -85,21 +91,13 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, ...@@ -85,21 +91,13 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public EndpointHandlerMapping endpointHandlerMapping() { public EndpointHandlerMapping endpointHandlerMapping() {
EndpointHandlerMapping mapping = new EndpointHandlerMapping(); EndpointHandlerMapping mapping = new EndpointHandlerMapping(mvcEndpoints()
.getEndpoints());
mapping.setDisabled(ManagementServerPort.get(this.applicationContext) != ManagementServerPort.SAME); mapping.setDisabled(ManagementServerPort.get(this.applicationContext) != ManagementServerPort.SAME);
mapping.setPrefix(this.managementServerProperties.getContextPath()); mapping.setPrefix(this.managementServerProperties.getContextPath());
return mapping; return mapping;
} }
@Bean
@ConditionalOnMissingBean
public EndpointHandlerAdapter endpointHandlerAdapter(
final HttpMessageConverters messageConverters) {
EndpointHandlerAdapter adapter = new EndpointHandlerAdapter();
adapter.setMessageConverters(messageConverters.getConverters());
return adapter;
}
@Override @Override
public void setApplicationContext(ApplicationContext applicationContext) public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException { throws BeansException {
...@@ -130,6 +128,30 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, ...@@ -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() { private void createChildManagementContext() {
final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext(); final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext();
......
...@@ -16,7 +16,8 @@ ...@@ -16,7 +16,8 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.util.Map; import java.util.HashSet;
import java.util.Set;
import javax.servlet.Filter; import javax.servlet.Filter;
...@@ -26,15 +27,16 @@ import org.springframework.beans.factory.HierarchicalBeanFactory; ...@@ -26,15 +27,16 @@ import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; 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.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.properties.ManagementServerProperties;
import org.springframework.boot.actuate.web.ErrorController; import org.springframework.boot.actuate.web.ErrorController;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.SearchStrategy; 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.ConfigurableEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
...@@ -42,11 +44,10 @@ import org.springframework.boot.context.embedded.ErrorPage; ...@@ -42,11 +44,10 @@ import org.springframework.boot.context.embedded.ErrorPage;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 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.DispatcherServlet;
import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
/** /**
* Configuration triggered from {@link EndpointWebMvcAutoConfiguration} when a new * Configuration triggered from {@link EndpointWebMvcAutoConfiguration} when a new
...@@ -57,6 +58,9 @@ import org.springframework.web.servlet.HandlerMapping; ...@@ -57,6 +58,9 @@ import org.springframework.web.servlet.HandlerMapping;
@Configuration @Configuration
public class EndpointWebMvcChildContextConfiguration { public class EndpointWebMvcChildContextConfiguration {
@Value("${error.path:/error}")
private String errorPath = "/error";
@Configuration @Configuration
protected static class ServerCustomization implements protected static class ServerCustomization implements
EmbeddedServletContainerCustomizer { EmbeddedServletContainerCustomizer {
...@@ -100,13 +104,22 @@ public class EndpointWebMvcChildContextConfiguration { ...@@ -100,13 +104,22 @@ public class EndpointWebMvcChildContextConfiguration {
} }
@Bean @Bean
public HandlerMapping handlerMapping() { public HandlerAdapter handlerAdapter(HttpMessageConverters converters) {
return new EndpointHandlerMapping(); // TODO: maybe this needs more configuration for non-basic response use cases
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setMessageConverters(converters.getConverters());
return adapter;
} }
@Bean @Bean
public HandlerAdapter handlerAdapter() { public HandlerMapping handlerMapping(MvcEndpoints endpoints,
return new EndpointHandlerAdapter(); ListableBeanFactory beanFactory) {
Set<MvcEndpoint> set = new HashSet<MvcEndpoint>(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 { ...@@ -116,15 +129,8 @@ public class EndpointWebMvcChildContextConfiguration {
* endpoints. * endpoints.
*/ */
@Bean @Bean
public Endpoint<Map<String, Object>> errorEndpoint(final ErrorController controller) { public ManagementErrorEndpoint errorEndpoint(final ErrorController controller) {
return new AbstractEndpoint<Map<String, Object>>("/error", false, true) { return new ManagementErrorEndpoint(this.errorPath, controller);
@Override
protected Map<String, Object> doInvoke() {
RequestAttributes attributes = RequestContextHolder
.currentRequestAttributes();
return controller.extract(attributes, false);
}
};
} }
@Configuration @Configuration
......
...@@ -20,7 +20,8 @@ import java.util.Map; ...@@ -20,7 +20,8 @@ import java.util.Map;
import org.jolokia.http.AgentServlet; import org.jolokia.http.AgentServlet;
import org.springframework.beans.factory.annotation.Autowired; 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.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
...@@ -65,6 +66,9 @@ public class JolokiaAutoConfiguration { ...@@ -65,6 +66,9 @@ public class JolokiaAutoConfiguration {
private RelaxedPropertyResolver environment; private RelaxedPropertyResolver environment;
@Autowired
private ManagementServerProperties management;
@Autowired @Autowired
public void setEnvironment(Environment environment) { public void setEnvironment(Environment environment) {
this.environment = new RelaxedPropertyResolver(environment); this.environment = new RelaxedPropertyResolver(environment);
...@@ -77,19 +81,17 @@ public class JolokiaAutoConfiguration { ...@@ -77,19 +81,17 @@ public class JolokiaAutoConfiguration {
} }
@Bean @Bean
@ConditionalOnMissingBean() public ServletRegistrationBean jolokiaServletRegistration(AgentServlet servlet) {
public ServletRegistrationBean jolokiaServletRegistration() { ServletRegistrationBean registrationBean = new ServletRegistrationBean(servlet,
ServletRegistrationBean registrationBean = new ServletRegistrationBean( this.management.getContextPath() + jolokiaEndpoint().getPath() + "/*");
jolokiaServlet(), this.environment.getProperty("endpoints.jolokia.path",
"/jolokia") + "/*");
addInitParameters(registrationBean); addInitParameters(registrationBean);
return registrationBean; return registrationBean;
} }
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public JolokiaEndpoint jolokiaEndpoint() { public JolokiaMvcEndpoint jolokiaEndpoint() {
return new JolokiaEndpoint(); return new JolokiaMvcEndpoint();
} }
protected void addInitParameters(ServletRegistrationBean registrationBean) { protected void addInitParameters(ServletRegistrationBean registrationBean) {
......
...@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure; ...@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.servlet.Filter; import javax.servlet.Filter;
...@@ -26,6 +27,7 @@ import javax.servlet.Filter; ...@@ -26,6 +27,7 @@ import javax.servlet.Filter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; 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.properties.ManagementServerProperties;
import org.springframework.boot.actuate.web.ErrorController; import org.springframework.boot.actuate.web.ErrorController;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
...@@ -210,9 +212,9 @@ public class ManagementSecurityAutoConfiguration { ...@@ -210,9 +212,9 @@ public class ManagementSecurityAutoConfiguration {
return NO_PATHS; return NO_PATHS;
} }
List<Endpoint<?>> endpoints = endpointHandlerMapping.getEndpoints(); Set<? extends MvcEndpoint> endpoints = endpointHandlerMapping.getEndpoints();
List<String> paths = new ArrayList<String>(endpoints.size()); List<String> paths = new ArrayList<String>(endpoints.size());
for (Endpoint<?> endpoint : endpoints) { for (MvcEndpoint endpoint : endpoints) {
if (endpoint.isSensitive() == secure) { if (endpoint.isSensitive() == secure) {
paths.add(endpoint.getPath()); paths.add(endpoint.getPath());
} }
......
...@@ -19,61 +19,48 @@ package org.springframework.boot.actuate.endpoint; ...@@ -19,61 +19,48 @@ package org.springframework.boot.actuate.endpoint;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern; import javax.validation.constraints.Pattern;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
/** /**
* Abstract base for {@link Endpoint} implementations. * Abstract base for {@link Endpoint} implementations.
* <p> * <p>
* {@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 Phillip Webb
* @author Christian Dupuis * @author Christian Dupuis
*/ */
public abstract class AbstractEndpoint<T> implements Endpoint<T> { public abstract class AbstractEndpoint<T> implements Endpoint<T> {
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 @NotNull
@Pattern(regexp = "/[^/]*", message = "Path must start with /") @Pattern(regexp = "\\w+", message = "ID must only contains letters, numbers and '_'")
private String path; private String id;
private boolean sensitive; private boolean sensitive;
private boolean enabled = true; private boolean enabled = true;
public AbstractEndpoint(String path) { public AbstractEndpoint(String id) {
this(path, true, true); this(id, true, true);
} }
public AbstractEndpoint(String path, boolean sensitive, boolean enabled) { public AbstractEndpoint(String id, boolean sensitive, boolean enabled) {
this.path = path; this.id = id;
this.sensitive = sensitive; this.sensitive = sensitive;
this.enabled = enabled; this.enabled = enabled;
} }
public boolean isEnabled() { @Override
return this.enabled; public String getId() {
return this.id;
} }
public void setEnabled(boolean enabled) { public void setId(String id) {
this.enabled = enabled; this.id = id;
} }
@Override public boolean isEnabled() {
public String getPath() { return this.enabled;
return this.path;
} }
public void setPath(String path) { public void setEnabled(boolean enabled) {
this.path = path; this.enabled = enabled;
} }
@Override @Override
...@@ -85,23 +72,4 @@ public abstract class AbstractEndpoint<T> implements Endpoint<T> { ...@@ -85,23 +72,4 @@ public abstract class AbstractEndpoint<T> implements Endpoint<T> {
this.sensitive = sensitive; 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();
} }
...@@ -48,11 +48,11 @@ public class AutoConfigurationReportEndpoint extends AbstractEndpoint<Report> { ...@@ -48,11 +48,11 @@ public class AutoConfigurationReportEndpoint extends AbstractEndpoint<Report> {
private AutoConfigurationReport autoConfigurationReport; private AutoConfigurationReport autoConfigurationReport;
public AutoConfigurationReportEndpoint() { public AutoConfigurationReportEndpoint() {
super("/autoconfig"); super("autoconfig");
} }
@Override @Override
protected Report doInvoke() { public Report invoke() {
return new Report(this.autoConfigurationReport); return new Report(this.autoConfigurationReport);
} }
......
...@@ -16,13 +16,16 @@ ...@@ -16,13 +16,16 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.util.List;
import org.springframework.beans.BeansException; 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.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.LiveBeansView; import org.springframework.context.support.LiveBeansView;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
/** /**
* Exposes JSON view of Spring beans. If the {@link Environment} contains a key setting * Exposes JSON view of Spring beans. If the {@link Environment} contains a key setting
...@@ -33,13 +36,15 @@ import org.springframework.http.MediaType; ...@@ -33,13 +36,15 @@ import org.springframework.http.MediaType;
* @author Dave Syer * @author Dave Syer
*/ */
@ConfigurationProperties(name = "endpoints.beans", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.beans", ignoreUnknownFields = false)
public class BeansEndpoint extends AbstractEndpoint<String> implements public class BeansEndpoint extends AbstractEndpoint<List<Object>> implements
ApplicationContextAware { ApplicationContextAware {
private LiveBeansView liveBeansView = new LiveBeansView(); private LiveBeansView liveBeansView = new LiveBeansView();
private JsonParser parser = JsonParserFactory.getJsonParser();
public BeansEndpoint() { public BeansEndpoint() {
super("/beans"); super("beans");
} }
@Override @Override
...@@ -51,12 +56,7 @@ public class BeansEndpoint extends AbstractEndpoint<String> implements ...@@ -51,12 +56,7 @@ public class BeansEndpoint extends AbstractEndpoint<String> implements
} }
@Override @Override
public MediaType[] produces() { public List<Object> invoke() {
return new MediaType[] { MediaType.APPLICATION_JSON }; return this.parser.parseList(this.liveBeansView.getSnapshotAsJson());
}
@Override
protected String doInvoke() {
return this.liveBeansView.getSnapshotAsJson();
} }
} }
...@@ -31,8 +31,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; ...@@ -31,8 +31,9 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* annotated classes. * annotated classes.
* *
* <p> * <p>
* To protect sensitive information from being exposed, configure property names by using * To protect sensitive information from being exposed, certain property values are masked
* <code>endpoints.configprops.keys_to_sanitize</code>. * 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 * @author Christian Dupuis
*/ */
...@@ -45,7 +46,7 @@ public class ConfigurationPropertiesReportEndpoint extends ...@@ -45,7 +46,7 @@ public class ConfigurationPropertiesReportEndpoint extends
private ApplicationContext context; private ApplicationContext context;
public ConfigurationPropertiesReportEndpoint() { public ConfigurationPropertiesReportEndpoint() {
super("/configprops"); super("configprops");
} }
@Override @Override
...@@ -63,9 +64,15 @@ public class ConfigurationPropertiesReportEndpoint extends ...@@ -63,9 +64,15 @@ public class ConfigurationPropertiesReportEndpoint extends
} }
@Override @Override
public Map<String, Object> invoke() {
Map<String, Object> beans = extract(this.context);
return beans;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Map<String, Object> doInvoke() { private Map<String, Object> extract(ApplicationContext context) {
Map<String, Object> beans = this.context
Map<String, Object> beans = context
.getBeansWithAnnotation(ConfigurationProperties.class); .getBeansWithAnnotation(ConfigurationProperties.class);
// Serialize beans into map structure and sanitize values // Serialize beans into map structure and sanitize values
...@@ -75,6 +82,10 @@ public class ConfigurationPropertiesReportEndpoint extends ...@@ -75,6 +82,10 @@ public class ConfigurationPropertiesReportEndpoint extends
beans.put(entry.getKey(), sanitize(value)); beans.put(entry.getKey(), sanitize(value));
} }
if (context.getParent() != null) {
beans.put("parent", extract(context.getParent()));
}
return beans; return beans;
} }
......
...@@ -35,11 +35,11 @@ public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> { ...@@ -35,11 +35,11 @@ public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> {
* Create a new {@link DumpEndpoint} instance. * Create a new {@link DumpEndpoint} instance.
*/ */
public DumpEndpoint() { public DumpEndpoint() {
super("/dump"); super("dump");
} }
@Override @Override
protected List<ThreadInfo> doInvoke() { public List<ThreadInfo> invoke() {
return Arrays.asList(ManagementFactory.getThreadMXBean().dumpAllThreads(true, return Arrays.asList(ManagementFactory.getThreadMXBean().dumpAllThreads(true,
true)); true));
} }
......
...@@ -16,9 +16,6 @@ ...@@ -16,9 +16,6 @@
package org.springframework.boot.actuate.endpoint; 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 * 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. * exposed via Spring MVC but could also be exposed using some other technique.
...@@ -30,26 +27,21 @@ import org.springframework.http.MediaType; ...@@ -30,26 +27,21 @@ import org.springframework.http.MediaType;
public interface Endpoint<T> { public interface Endpoint<T> {
/** /**
* Returns the path of the endpoint. Must start with '/' and should not include * The logical ID of the endpoint. Must only contain simple letters, numbers and '_'
* wildcards. * characters (ie a {@literal "\w"} regex).
*/
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.
*/ */
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. * Called to invoke the endpoint.
......
...@@ -43,11 +43,11 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i ...@@ -43,11 +43,11 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
* Create a new {@link EnvironmentEndpoint} instance. * Create a new {@link EnvironmentEndpoint} instance.
*/ */
public EnvironmentEndpoint() { public EnvironmentEndpoint() {
super("/env"); super("env");
} }
@Override @Override
protected Map<String, Object> doInvoke() { public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>(); Map<String, Object> result = new LinkedHashMap<String, Object>();
result.put("profiles", this.environment.getActiveProfiles()); result.put("profiles", this.environment.getActiveProfiles());
for (PropertySource<?> source : getPropertySources()) { for (PropertySource<?> source : getPropertySources()) {
...@@ -71,7 +71,7 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i ...@@ -71,7 +71,7 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
return new StandardEnvironment().getPropertySources(); return new StandardEnvironment().getPropertySources();
} }
private Object sanitize(String name, Object object) { public static Object sanitize(String name, Object object) {
if (name.toLowerCase().endsWith("password") if (name.toLowerCase().endsWith("password")
|| name.toLowerCase().endsWith("secret")) { || name.toLowerCase().endsWith("secret")) {
return object == null ? null : "******"; return object == null ? null : "******";
......
...@@ -36,13 +36,13 @@ public class HealthEndpoint<T> extends AbstractEndpoint<T> { ...@@ -36,13 +36,13 @@ public class HealthEndpoint<T> extends AbstractEndpoint<T> {
* @param indicator the health indicator * @param indicator the health indicator
*/ */
public HealthEndpoint(HealthIndicator<? extends T> indicator) { public HealthEndpoint(HealthIndicator<? extends T> indicator) {
super("/health", false, true); super("health", false, true);
Assert.notNull(indicator, "Indicator must not be null"); Assert.notNull(indicator, "Indicator must not be null");
this.indicator = indicator; this.indicator = indicator;
} }
@Override @Override
protected T doInvoke() { public T invoke() {
return this.indicator.health(); return this.indicator.health();
} }
......
...@@ -39,13 +39,13 @@ public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> { ...@@ -39,13 +39,13 @@ public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
* @param info the info to expose * @param info the info to expose
*/ */
public InfoEndpoint(Map<String, ? extends Object> info) { public InfoEndpoint(Map<String, ? extends Object> info) {
super("/info", false, true); super("info", false, true);
Assert.notNull(info, "Info must not be null"); Assert.notNull(info, "Info must not be null");
this.info = info; this.info = info;
} }
@Override @Override
protected Map<String, Object> doInvoke() { public Map<String, Object> invoke() {
Map<String, Object> info = new LinkedHashMap<String, Object>(this.info); Map<String, Object> info = new LinkedHashMap<String, Object>(this.info);
info.putAll(getAdditionalInfo()); info.putAll(getAdditionalInfo());
return info; return info;
......
...@@ -39,13 +39,13 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> { ...@@ -39,13 +39,13 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
* @param metrics the metrics to expose * @param metrics the metrics to expose
*/ */
public MetricsEndpoint(PublicMetrics metrics) { public MetricsEndpoint(PublicMetrics metrics) {
super("/metrics"); super("metrics");
Assert.notNull(metrics, "Metrics must not be null"); Assert.notNull(metrics, "Metrics must not be null");
this.metrics = metrics; this.metrics = metrics;
} }
@Override @Override
protected Map<String, Object> doInvoke() { public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>(); Map<String, Object> result = new LinkedHashMap<String, Object>();
for (Metric metric : this.metrics.metrics()) { for (Metric metric : this.metrics.metrics()) {
result.put(metric.getName(), metric.getValue()); result.put(metric.getName(), metric.getValue());
......
...@@ -24,7 +24,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -24,7 +24,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpMethod;
/** /**
* {@link Endpoint} to shutdown the {@link ApplicationContext}. * {@link Endpoint} to shutdown the {@link ApplicationContext}.
...@@ -42,17 +41,23 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl ...@@ -42,17 +41,23 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl
* Create a new {@link ShutdownEndpoint} instance. * Create a new {@link ShutdownEndpoint} instance.
*/ */
public ShutdownEndpoint() { public ShutdownEndpoint() {
super("/shutdown", true, false); super("shutdown", true, false);
} }
@Override @Override
protected Map<String, Object> doInvoke() { public Map<String, Object> invoke() {
if (this.context == null) { if (this.context == null) {
return Collections.<String, Object> singletonMap("message", return Collections.<String, Object> singletonMap("message",
"No context to shutdown."); "No context to shutdown.");
} }
try {
return Collections.<String, Object> singletonMap("message",
"Shutting down, bye...");
}
finally {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -66,8 +71,7 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl ...@@ -66,8 +71,7 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl
} }
}).start(); }).start();
return Collections.<String, Object> singletonMap("message", }
"Shutting down, bye...");
} }
@Override @Override
...@@ -77,9 +81,4 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl ...@@ -77,9 +81,4 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl
} }
} }
@Override
public HttpMethod[] methods() {
return POST_HTTP_METHOD;
}
} }
...@@ -39,13 +39,13 @@ public class TraceEndpoint extends AbstractEndpoint<List<Trace>> { ...@@ -39,13 +39,13 @@ public class TraceEndpoint extends AbstractEndpoint<List<Trace>> {
* @param repository the trace repository * @param repository the trace repository
*/ */
public TraceEndpoint(TraceRepository repository) { public TraceEndpoint(TraceRepository repository) {
super("/trace"); super("trace");
Assert.notNull(repository, "Repository must not be null"); Assert.notNull(repository, "Repository must not be null");
this.repository = repository; this.repository = repository;
} }
@Override @Override
protected List<Trace> doInvoke() { public List<Trace> invoke() {
return this.repository.findAll(); return this.repository.findAll();
} }
} }
/*
* 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<HttpMessageConverter<?>> messageConverters;
private List<MediaType> 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<MediaType> 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<Object>) 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<MediaType> getMediaTypes(HttpServletRequest request,
Endpoint<?> endpoint, Class<?> resultClass)
throws HttpMediaTypeNotAcceptableException {
List<MediaType> requested = getAcceptableMediaTypes(request);
List<MediaType> producible = getProducibleMediaTypes(endpoint, resultClass);
Set<MediaType> compatible = new LinkedHashSet<MediaType>();
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<MediaType> mediaTypes = new ArrayList<MediaType>(compatible);
MediaType.sortBySpecificityAndQuality(mediaTypes);
return mediaTypes;
}
private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request)
throws HttpMediaTypeNotAcceptableException {
List<MediaType> mediaTypes = this.contentNegotiationManager
.resolveMediaTypes(new ServletWebRequest(request));
return mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL)
: mediaTypes;
}
private List<MediaType> 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<MediaType> result = new ArrayList<MediaType>();
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<MediaType> 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<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<MediaType>();
for (HttpMessageConverter<?> messageConverter : messageConverters) {
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
}
this.allSupportedMediaTypes = new ArrayList<MediaType>(allSupportedMediaTypes);
MediaType.sortBySpecificity(this.allSupportedMediaTypes);
}
/**
* Default conventions, taken from {@link WebMvcConfigurationSupport} with a few minor
* tweaks.
*/
private static class WebMvcConfigurationSupportConventions extends
WebMvcConfigurationSupport {
public List<HttpMessageConverter<?>> getDefaultHttpMessageConverters() {
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
addDefaultHttpMessageConverters(converters);
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter;
jacksonConverter.getObjectMapper().disable(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
}
return converters;
}
}
}
...@@ -16,36 +16,42 @@ ...@@ -16,36 +16,42 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.util.ArrayList; import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.HashSet;
import java.util.List; 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.boot.actuate.endpoint.Endpoint;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping; 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).
*
* <p>
* 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 Phillip Webb
* @author Christian Dupuis * @author Christian Dupuis
* @see EndpointHandlerAdapter * @author Dave Syer
*
*/ */
public class EndpointHandlerMapping extends AbstractUrlHandlerMapping implements public class EndpointHandlerMapping extends RequestMappingHandlerMapping implements
InitializingBean, ApplicationContextAware { ApplicationContextAware {
private List<Endpoint<?>> endpoints; private Set<? extends MvcEndpoint> endpoints;
private String prefix = ""; private String prefix = "";
...@@ -54,52 +60,74 @@ public class EndpointHandlerMapping extends AbstractUrlHandlerMapping implements ...@@ -54,52 +60,74 @@ public class EndpointHandlerMapping extends AbstractUrlHandlerMapping implements
/** /**
* Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be * Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be
* detected from the {@link ApplicationContext}. * detected from the {@link ApplicationContext}.
* @param endpoints
*/ */
public EndpointHandlerMapping() { public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
setOrder(HIGHEST_PRECEDENCE); this.endpoints = new HashSet<MvcEndpoint>(endpoints);
// By default the static resource handler mapping is LOWEST_PRECEDENCE - 1
setOrder(LOWEST_PRECEDENCE - 2);
}
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (!this.disabled) {
for (MvcEndpoint endpoint : this.endpoints) {
detectHandlerMethods(endpoint);
}
}
} }
/** /**
* Create a new {@link EndpointHandlerMapping} with the specified endpoints. * Since all handler beans are passed into the constructor there is no need to detect
* @param endpoints the endpoints * anything here
*/ */
public EndpointHandlerMapping(Collection<? extends Endpoint<?>> endpoints) { @Override
Assert.notNull(endpoints, "Endpoints must not be null"); protected boolean isHandler(Class<?> beanType) {
this.endpoints = new ArrayList<Endpoint<?>>(endpoints); return false;
} }
@Override @Override
public void afterPropertiesSet() throws Exception { protected void registerHandlerMethod(Object handler, Method method,
if (this.endpoints == null) { RequestMappingInfo mapping) {
this.endpoints = findEndpointBeans();
} if (mapping == null) {
if (!this.disabled) { return;
for (Endpoint<?> endpoint : this.endpoints) {
registerHandler(this.prefix + endpoint.getPath(), endpoint);
} }
Set<String> 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();
} }
@SuppressWarnings({ "rawtypes", "unchecked" }) int i = 0;
private List<Endpoint<?>> findEndpointBeans() { String prefix = StringUtils.hasText(this.prefix) ? this.prefix + path : path;
return new ArrayList(BeanFactoryUtils.beansOfTypeIncludingAncestors( if (defaultPatterns.isEmpty()) {
getApplicationContext(), Endpoint.class).values()); patterns[0] = prefix;
} }
else {
@Override for (String pattern : defaultPatterns) {
protected Object lookupHandler(String urlPath, HttpServletRequest request) patterns[i] = prefix + pattern;
throws Exception { i++;
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;
} }
} }
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 ...@@ -128,19 +156,7 @@ public class EndpointHandlerMapping extends AbstractUrlHandlerMapping implements
/** /**
* Return the endpoints * Return the endpoints
*/ */
public List<Endpoint<?>> getEndpoints() { public Set<? extends MvcEndpoint> getEndpoints() {
return Collections.unmodifiableList(this.endpoints); return 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;
} }
} }
/*
* 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);
}
}
}
/*
* 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<? extends Endpoint<?>> type = (Class<? extends Endpoint<?>>) this.delegate
.getClass();
return type;
}
}
...@@ -14,10 +14,13 @@ ...@@ -14,10 +14,13 @@
* limitations under the License. * 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.boot.context.properties.ConfigurationProperties;
import org.springframework.http.HttpMethod;
/** /**
* {@link Endpoint} implementation to register the Jolokia infrastructure with the Boot * {@link Endpoint} implementation to register the Jolokia infrastructure with the Boot
...@@ -26,20 +29,49 @@ import org.springframework.http.HttpMethod; ...@@ -26,20 +29,49 @@ import org.springframework.http.HttpMethod;
* @author Christian Dupuis * @author Christian Dupuis
*/ */
@ConfigurationProperties(name = "endpoints.jolokia", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.jolokia", ignoreUnknownFields = false)
public class JolokiaEndpoint extends AbstractEndpoint<String> { public class JolokiaMvcEndpoint implements MvcEndpoint {
@NotNull
@Pattern(regexp = "/[^/]*", message = "Path must start with /")
private String path;
private boolean sensitive;
public JolokiaEndpoint() { private boolean enabled = true;
super("/jolokia");
public JolokiaMvcEndpoint() {
this.path = "/jolokia";
}
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
} }
@Override @Override
protected String doInvoke() { public String getPath() {
return null; 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 @Override
public HttpMethod[] methods() { public Class<?> getEndpointType() {
return NO_HTTP_METHOD; return null;
} }
} }
/*
* 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<String, Object> 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
/*
* 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);
}
}
}
/*
* 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 <code>@RequestMapping</code> and the full Spring MVC machinery, but should not
* use <code>@Controller</code> or <code>@RequestMapping</code> 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
/*
* 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<MvcEndpoint> endpoints = new HashSet<MvcEndpoint>();
private Set<Class<?>> customTypes;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
Collection<MvcEndpoint> existing = this.applicationContext.getBeansOfType(
MvcEndpoint.class).values();
this.endpoints.addAll(existing);
this.customTypes = findEndpointClasses(existing);
@SuppressWarnings("rawtypes")
Collection<Endpoint> delegates = this.applicationContext.getBeansOfType(
Endpoint.class).values();
for (Endpoint<?> endpoint : delegates) {
if (isGenericEndpoint(endpoint.getClass())) {
this.endpoints.add(new GenericMvcEndpoint(endpoint));
}
}
}
private Set<Class<?>> findEndpointClasses(Collection<MvcEndpoint> existing) {
Set<Class<?>> types = new HashSet<Class<?>>();
for (MvcEndpoint endpoint : existing) {
Class<?> type = endpoint.getEndpointType();
if (type != null) {
types.add(type);
}
}
return types;
}
public Set<? extends MvcEndpoint> getEndpoints() {
return this.endpoints;
}
private boolean isGenericEndpoint(Class<?> type) {
return !this.customTypes.contains(type)
&& !MvcEndpoint.class.isAssignableFrom(type);
}
}
\ No newline at end of file
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint.mvc;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Dave Syer
*/
public class ShutdownMvcEndpoint extends GenericMvcEndpoint {
public ShutdownMvcEndpoint(ShutdownEndpoint delegate) {
super(delegate);
}
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
@Override
public Object invoke() {
return super.invoke();
}
}
...@@ -63,7 +63,7 @@ public class BasicErrorController implements ErrorController { ...@@ -63,7 +63,7 @@ public class BasicErrorController implements ErrorController {
@RequestMapping(value = "${error.path:/error}", produces = "text/html") @RequestMapping(value = "${error.path:/error}", produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request) { public ModelAndView errorHtml(HttpServletRequest request) {
Map<String, Object> map = extract(new ServletRequestAttributes(request), false); Map<String, Object> map = error(request);
return new ModelAndView(ERROR_KEY, map); return new ModelAndView(ERROR_KEY, map);
} }
......
...@@ -24,8 +24,8 @@ import java.nio.charset.Charset; ...@@ -24,8 +24,8 @@ import java.nio.charset.Charset;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.TestUtils; 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.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
...@@ -200,13 +200,8 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -200,13 +200,8 @@ public class EndpointWebMvcAutoConfigurationTests {
} }
@Bean @Bean
public Endpoint<String> testEndpoint() { public TestEndpoint testEndpoint() {
return new AbstractEndpoint<String>("/endpoint", false, true) { return new TestEndpoint();
@Override
public String doInvoke() {
return "endpointoutput";
}
};
} }
} }
...@@ -245,4 +240,29 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -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;
}
}
} }
...@@ -60,7 +60,9 @@ public class JolokiaAutoConfigurationTests { ...@@ -60,7 +60,9 @@ public class JolokiaAutoConfigurationTests {
public void agentServletRegisteredWithAppContext() throws Exception { public void agentServletRegisteredWithAppContext() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(); this.context = new AnnotationConfigEmbeddedWebApplicationContext();
this.context.register(Config.class, WebMvcAutoConfiguration.class, this.context.register(Config.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, JolokiaAutoConfiguration.class); ManagementServerPropertiesAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JolokiaAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
assertEquals(1, this.context.getBeanNamesForType(AgentServlet.class).length); assertEquals(1, this.context.getBeanNamesForType(AgentServlet.class).length);
} }
...@@ -70,7 +72,9 @@ public class JolokiaAutoConfigurationTests { ...@@ -70,7 +72,9 @@ public class JolokiaAutoConfigurationTests {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(); this.context = new AnnotationConfigEmbeddedWebApplicationContext();
TestUtils.addEnviroment(this.context, "endpoints.jolokia.enabled:false"); TestUtils.addEnviroment(this.context, "endpoints.jolokia.enabled:false");
this.context.register(Config.class, WebMvcAutoConfiguration.class, this.context.register(Config.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, JolokiaAutoConfiguration.class); ManagementServerPropertiesAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JolokiaAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
assertEquals(0, this.context.getBeanNamesForType(AgentServlet.class).length); assertEquals(0, this.context.getBeanNamesForType(AgentServlet.class).length);
} }
...@@ -79,7 +83,9 @@ public class JolokiaAutoConfigurationTests { ...@@ -79,7 +83,9 @@ public class JolokiaAutoConfigurationTests {
public void agentServletRegisteredWithServletContainer() throws Exception { public void agentServletRegisteredWithServletContainer() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(); this.context = new AnnotationConfigEmbeddedWebApplicationContext();
this.context.register(Config.class, WebMvcAutoConfiguration.class, this.context.register(Config.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, JolokiaAutoConfiguration.class); ManagementServerPropertiesAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JolokiaAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
Servlet servlet = null; Servlet servlet = null;
......
...@@ -25,7 +25,6 @@ import org.springframework.boot.TestUtils; ...@@ -25,7 +25,6 @@ import org.springframework.boot.TestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.http.MediaType;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
...@@ -43,22 +42,19 @@ public abstract class AbstractEndpointTests<T extends Endpoint<?>> { ...@@ -43,22 +42,19 @@ public abstract class AbstractEndpointTests<T extends Endpoint<?>> {
private final Class<?> type; private final Class<?> type;
private final String path; private final String id;
private final boolean sensitive; private final boolean sensitive;
private final String property; private final String property;
private MediaType[] produces; public AbstractEndpointTests(Class<?> configClass, Class<?> type, String id,
boolean sensitive, String property) {
public AbstractEndpointTests(Class<?> configClass, Class<?> type, String path,
boolean sensitive, String property, MediaType... produces) {
this.configClass = configClass; this.configClass = configClass;
this.type = type; this.type = type;
this.path = path; this.id = id;
this.sensitive = sensitive; this.sensitive = sensitive;
this.property = property; this.property = property;
this.produces = produces;
} }
@Before @Before
...@@ -76,13 +72,8 @@ public abstract class AbstractEndpointTests<T extends Endpoint<?>> { ...@@ -76,13 +72,8 @@ public abstract class AbstractEndpointTests<T extends Endpoint<?>> {
} }
@Test @Test
public void producesMediaType() { public void getId() throws Exception {
assertThat(getEndpointBean().produces(), equalTo(this.produces)); assertThat(getEndpointBean().getId(), equalTo(this.id));
}
@Test
public void getPath() throws Exception {
assertThat(getEndpointBean().getPath(), equalTo(this.path));
} }
@Test @Test
...@@ -91,12 +82,12 @@ public abstract class AbstractEndpointTests<T extends Endpoint<?>> { ...@@ -91,12 +82,12 @@ public abstract class AbstractEndpointTests<T extends Endpoint<?>> {
} }
@Test @Test
public void pathOverride() throws Exception { public void idOverride() throws Exception {
this.context = new AnnotationConfigApplicationContext(); 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.register(this.configClass);
this.context.refresh(); this.context.refresh();
assertThat(getEndpointBean().getPath(), equalTo("/mypath")); assertThat(getEndpointBean().getId(), equalTo("myid"));
} }
@Test @Test
......
...@@ -42,7 +42,7 @@ public class AutoConfigurationReportEndpointTests extends ...@@ -42,7 +42,7 @@ public class AutoConfigurationReportEndpointTests extends
AbstractEndpointTests<AutoConfigurationReportEndpoint> { AbstractEndpointTests<AutoConfigurationReportEndpoint> {
public AutoConfigurationReportEndpointTests() { public AutoConfigurationReportEndpointTests() {
super(Config.class, AutoConfigurationReportEndpoint.class, "/autoconfig", true, super(Config.class, AutoConfigurationReportEndpoint.class, "autoconfig", true,
"endpoints.autoconfig"); "endpoints.autoconfig");
} }
......
...@@ -16,15 +16,16 @@ ...@@ -16,15 +16,16 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.util.List;
import java.util.Map;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.BeansEndpoint;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue;
/** /**
* Tests for {@link BeansEndpoint}. * Tests for {@link BeansEndpoint}.
...@@ -34,13 +35,14 @@ import static org.junit.Assert.assertThat; ...@@ -34,13 +35,14 @@ import static org.junit.Assert.assertThat;
public class BeansEndpointTests extends AbstractEndpointTests<BeansEndpoint> { public class BeansEndpointTests extends AbstractEndpointTests<BeansEndpoint> {
public BeansEndpointTests() { public BeansEndpointTests() {
super(Config.class, BeansEndpoint.class, "/beans", true, "endpoints.beans", super(Config.class, BeansEndpoint.class, "beans", true, "endpoints.beans");
MediaType.APPLICATION_JSON);
} }
@Test @Test
public void invoke() throws Exception { public void invoke() throws Exception {
assertThat(getEndpointBean().invoke(), containsString("\"bean\": \"endpoint\"")); List<Object> result = getEndpointBean().invoke();
assertEquals(1, result.size());
assertTrue(result.get(0) instanceof Map);
} }
@Configuration @Configuration
......
/*
* 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<String, Object> 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;
}
}
}
...@@ -33,7 +33,7 @@ public class ConfigurationPropertiesReportEndpointTests extends ...@@ -33,7 +33,7 @@ public class ConfigurationPropertiesReportEndpointTests extends
AbstractEndpointTests<ConfigurationPropertiesReportEndpoint> { AbstractEndpointTests<ConfigurationPropertiesReportEndpoint> {
public ConfigurationPropertiesReportEndpointTests() { public ConfigurationPropertiesReportEndpointTests() {
super(Config.class, ConfigurationPropertiesReportEndpoint.class, "/configprops", super(Config.class, ConfigurationPropertiesReportEndpoint.class, "configprops",
true, "endpoints.configprops"); true, "endpoints.configprops");
} }
...@@ -68,6 +68,15 @@ public class ConfigurationPropertiesReportEndpointTests extends ...@@ -68,6 +68,15 @@ public class ConfigurationPropertiesReportEndpointTests extends
assertEquals("******", nestedProperties.get("myTestProperty")); assertEquals("******", nestedProperties.get("myTestProperty"));
} }
@Configuration
@EnableConfigurationProperties
public static class Parent {
@Bean
public TestProperties testProperties() {
return new TestProperties();
}
}
@Configuration @Configuration
@EnableConfigurationProperties @EnableConfigurationProperties
public static class Config { public static class Config {
...@@ -82,6 +91,8 @@ public class ConfigurationPropertiesReportEndpointTests extends ...@@ -82,6 +91,8 @@ public class ConfigurationPropertiesReportEndpointTests extends
return new TestProperties(); return new TestProperties();
} }
}
@ConfigurationProperties(name = "test") @ConfigurationProperties(name = "test")
public static class TestProperties { public static class TestProperties {
...@@ -106,5 +117,4 @@ public class ConfigurationPropertiesReportEndpointTests extends ...@@ -106,5 +117,4 @@ public class ConfigurationPropertiesReportEndpointTests extends
} }
} }
}
} }
...@@ -20,7 +20,6 @@ import java.lang.management.ThreadInfo; ...@@ -20,7 +20,6 @@ import java.lang.management.ThreadInfo;
import java.util.List; import java.util.List;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.DumpEndpoint;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -36,7 +35,7 @@ import static org.junit.Assert.assertThat; ...@@ -36,7 +35,7 @@ import static org.junit.Assert.assertThat;
public class DumpEndpointTests extends AbstractEndpointTests<DumpEndpoint> { public class DumpEndpointTests extends AbstractEndpointTests<DumpEndpoint> {
public DumpEndpointTests() { public DumpEndpointTests() {
super(Config.class, DumpEndpoint.class, "/dump", true, "endpoints.dump"); super(Config.class, DumpEndpoint.class, "dump", true, "endpoints.dump");
} }
@Test @Test
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -33,7 +32,7 @@ import static org.junit.Assert.assertThat; ...@@ -33,7 +32,7 @@ import static org.junit.Assert.assertThat;
public class EnvironmentEndpointTests extends AbstractEndpointTests<EnvironmentEndpoint> { public class EnvironmentEndpointTests extends AbstractEndpointTests<EnvironmentEndpoint> {
public EnvironmentEndpointTests() { public EnvironmentEndpointTests() {
super(Config.class, EnvironmentEndpoint.class, "/env", true, "endpoints.env"); super(Config.class, EnvironmentEndpoint.class, "env", true, "endpoints.env");
} }
@Test @Test
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -34,7 +33,7 @@ import static org.junit.Assert.assertThat; ...@@ -34,7 +33,7 @@ import static org.junit.Assert.assertThat;
public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint<String>> { public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint<String>> {
public HealthEndpointTests() { public HealthEndpointTests() {
super(Config.class, HealthEndpoint.class, "/health", false, "endpoints.health"); super(Config.class, HealthEndpoint.class, "health", false, "endpoints.health");
} }
@Test @Test
......
...@@ -35,7 +35,7 @@ import static org.junit.Assert.assertThat; ...@@ -35,7 +35,7 @@ import static org.junit.Assert.assertThat;
public class InfoEndpointTests extends AbstractEndpointTests<InfoEndpoint> { public class InfoEndpointTests extends AbstractEndpointTests<InfoEndpoint> {
public InfoEndpointTests() { public InfoEndpointTests() {
super(Config.class, InfoEndpoint.class, "/info", false, "endpoints.info"); super(Config.class, InfoEndpoint.class, "info", false, "endpoints.info");
} }
@Test @Test
......
...@@ -20,8 +20,6 @@ import java.util.Collection; ...@@ -20,8 +20,6 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import org.junit.Test; 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.actuate.metrics.Metric;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -38,7 +36,7 @@ import static org.junit.Assert.assertThat; ...@@ -38,7 +36,7 @@ import static org.junit.Assert.assertThat;
public class MetricsEndpointTests extends AbstractEndpointTests<MetricsEndpoint> { public class MetricsEndpointTests extends AbstractEndpointTests<MetricsEndpoint> {
public MetricsEndpointTests() { public MetricsEndpointTests() {
super(Config.class, MetricsEndpoint.class, "/metrics", true, "endpoints.metrics"); super(Config.class, MetricsEndpoint.class, "metrics", true, "endpoints.metrics");
} }
@Test @Test
......
...@@ -35,7 +35,7 @@ import static org.junit.Assert.assertTrue; ...@@ -35,7 +35,7 @@ import static org.junit.Assert.assertTrue;
public class ShutdownEndpointTests extends AbstractEndpointTests<ShutdownEndpoint> { public class ShutdownEndpointTests extends AbstractEndpointTests<ShutdownEndpoint> {
public ShutdownEndpointTests() { public ShutdownEndpointTests() {
super(Config.class, ShutdownEndpoint.class, "/shutdown", true, super(Config.class, ShutdownEndpoint.class, "shutdown", true,
"endpoints.shutdown"); "endpoints.shutdown");
} }
......
...@@ -19,7 +19,6 @@ package org.springframework.boot.actuate.endpoint; ...@@ -19,7 +19,6 @@ package org.springframework.boot.actuate.endpoint;
import java.util.Collections; import java.util.Collections;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.trace.InMemoryTraceRepository; import org.springframework.boot.actuate.trace.InMemoryTraceRepository;
import org.springframework.boot.actuate.trace.Trace; import org.springframework.boot.actuate.trace.Trace;
import org.springframework.boot.actuate.trace.TraceRepository; import org.springframework.boot.actuate.trace.TraceRepository;
...@@ -38,7 +37,7 @@ import static org.junit.Assert.assertThat; ...@@ -38,7 +37,7 @@ import static org.junit.Assert.assertThat;
public class TraceEndpointTests extends AbstractEndpointTests<TraceEndpoint> { public class TraceEndpointTests extends AbstractEndpointTests<TraceEndpoint> {
public TraceEndpointTests() { public TraceEndpointTests() {
super(Config.class, TraceEndpoint.class, "/trace", true, "endpoints.trace"); super(Config.class, TraceEndpoint.class, "trace", true, "endpoints.trace");
} }
@Test @Test
......
...@@ -167,11 +167,11 @@ public class EndpointMBeanExporterTests { ...@@ -167,11 +167,11 @@ public class EndpointMBeanExporterTests {
public static class TestEndpoint extends AbstractEndpoint<String> { public static class TestEndpoint extends AbstractEndpoint<String> {
public TestEndpoint() { public TestEndpoint() {
super("/test"); super("test");
} }
@Override @Override
protected String doInvoke() { public String invoke() {
return "hello world"; return "hello world";
} }
} }
......
/*
* 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<Map<String, String>>("/foo") {
@Override
protected Map<String, String> 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<String>(
"/foo") {
@Override
protected String doInvoke() {
return "hello world";
}
});
assertEquals("hello world", this.response.getContentAsString());
}
}
...@@ -16,12 +16,19 @@ ...@@ -16,12 +16,19 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint; 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.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.equalTo;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
...@@ -33,66 +40,94 @@ import static org.junit.Assert.assertThat; ...@@ -33,66 +40,94 @@ import static org.junit.Assert.assertThat;
* Tests for {@link EndpointHandlerMapping}. * Tests for {@link EndpointHandlerMapping}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer
*/ */
public class EndpointHandlerMappingTests { 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 @Test
public void withoutPrefix() throws Exception { public void withoutPrefix() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a"); TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a"));
TestEndpoint endpointB = new TestEndpoint("/b"); TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b"));
EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList( EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList(
endpointA, endpointB)); endpointA, endpointB));
mapping.setApplicationContext(this.context);
mapping.afterPropertiesSet(); mapping.afterPropertiesSet();
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")) 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")) 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")), assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/c")),
nullValue()); nullValue());
} }
@Test @Test
public void withPrefix() throws Exception { public void withPrefix() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a"); TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a"));
TestEndpoint endpointB = new TestEndpoint("/b"); TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b"));
EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList( EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList(
endpointA, endpointB)); endpointA, endpointB));
mapping.setApplicationContext(this.context);
mapping.setPrefix("/a"); mapping.setPrefix("/a");
mapping.afterPropertiesSet(); mapping.afterPropertiesSet();
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a/a")) 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")) 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")), assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")),
nullValue()); nullValue());
} }
@Test @Test(expected = HttpRequestMethodNotSupportedException.class)
public void onlyGetHttpMethodForNonActionEndpoints() throws Exception { public void onlyGetHttpMethodForNonActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestEndpoint("/a"); TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
EndpointHandlerMapping mapping = new EndpointHandlerMapping( EndpointHandlerMapping mapping = new EndpointHandlerMapping(
Arrays.asList(endpoint)); Arrays.asList(endpoint));
mapping.setApplicationContext(this.context);
mapping.afterPropertiesSet(); mapping.afterPropertiesSet();
assertNotNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a"))); assertNotNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a")));
assertNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a"))); assertNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a")));
} }
@Test @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 { public void onlyPostHttpMethodForActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestActionEndpoint("/a"); TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
EndpointHandlerMapping mapping = new EndpointHandlerMapping( EndpointHandlerMapping mapping = new EndpointHandlerMapping(
Arrays.asList(endpoint)); Arrays.asList(endpoint));
mapping.setApplicationContext(this.context);
mapping.afterPropertiesSet(); mapping.afterPropertiesSet();
assertNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a")));
assertNotNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a"))); assertNotNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a")));
assertNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a")));
} }
@Test @Test
public void disabled() throws Exception { public void disabled() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a"); TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("/a"));
EndpointHandlerMapping mapping = new EndpointHandlerMapping( EndpointHandlerMapping mapping = new EndpointHandlerMapping(
Arrays.asList(endpointA)); Arrays.asList(endpoint));
mapping.setDisabled(true); mapping.setDisabled(true);
mapping.setApplicationContext(this.context);
mapping.afterPropertiesSet(); mapping.afterPropertiesSet();
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")), assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")),
nullValue()); nullValue());
...@@ -105,22 +140,32 @@ public class EndpointHandlerMappingTests { ...@@ -105,22 +140,32 @@ public class EndpointHandlerMappingTests {
} }
@Override @Override
public Object doInvoke() { public Object invoke() {
return null; return null;
} }
} }
private static class TestActionEndpoint extends TestEndpoint { private static class TestMvcEndpoint extends GenericMvcEndpoint {
public TestMvcEndpoint(TestEndpoint delegate) {
super(delegate);
}
public TestActionEndpoint(String path) { }
super(path);
private static class TestActionEndpoint extends GenericMvcEndpoint {
public TestActionEndpoint(TestEndpoint delegate) {
super(delegate);
} }
@Override @Override
public HttpMethod[] methods() { @RequestMapping(method = RequestMethod.POST)
return POST_HTTP_METHOD; public Object invoke() {
return null;
} }
} }
} }
/*
* 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());
}
}
}
/*
* 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();
}
}
}
...@@ -26,8 +26,10 @@ import org.junit.Test; ...@@ -26,8 +26,10 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration;
import org.springframework.boot.actuate.web.BasicErrorControllerIntegrationTests.TestConfiguration; import org.springframework.boot.actuate.web.BasicErrorControllerIntegrationTests.TestConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -81,7 +83,8 @@ public class BasicErrorControllerIntegrationTests { ...@@ -81,7 +83,8 @@ public class BasicErrorControllerIntegrationTests {
} }
@Configuration @Configuration
@EnableAutoConfiguration @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class,
ManagementSecurityAutoConfiguration.class })
public static class TestConfiguration { public static class TestConfiguration {
// For manual testing // For manual testing
......
...@@ -19,7 +19,9 @@ package org.springframework.boot.actuate.web; ...@@ -19,7 +19,9 @@ package org.springframework.boot.actuate.web;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
...@@ -77,13 +79,15 @@ public class BasicErrorControllerSpecialIntegrationTests { ...@@ -77,13 +79,15 @@ public class BasicErrorControllerSpecialIntegrationTests {
} }
@Configuration @Configuration
@EnableAutoConfiguration @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class,
ManagementSecurityAutoConfiguration.class })
protected static class ParentConfiguration { protected static class ParentConfiguration {
} }
@Configuration @Configuration
@EnableAutoConfiguration @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class,
ManagementSecurityAutoConfiguration.class })
@EnableWebMvc @EnableWebMvc
protected static class WebMvcIncludedConfiguration { protected static class WebMvcIncludedConfiguration {
// For manual testing // For manual testing
...@@ -94,7 +98,19 @@ public class BasicErrorControllerSpecialIntegrationTests { ...@@ -94,7 +98,19 @@ public class BasicErrorControllerSpecialIntegrationTests {
} }
@Configuration @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 { protected static class ChildConfiguration {
// For manual testing // For manual testing
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<modules> <modules>
<module>spring-boot-sample-actuator</module> <module>spring-boot-sample-actuator</module>
<module>spring-boot-sample-actuator-log4j</module> <module>spring-boot-sample-actuator-log4j</module>
<module>spring-boot-sample-actuator-noweb</module>
<module>spring-boot-sample-actuator-ui</module> <module>spring-boot-sample-actuator-ui</module>
<module>spring-boot-sample-amqp</module> <module>spring-boot-sample-amqp</module>
<module>spring-boot-sample-aop</module> <module>spring-boot-sample-aop</module>
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>0.5.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-actuator-noweb</artifactId>
<packaging>jar</packaging>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-boot-starter-shell-remote</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -14,29 +14,22 @@ ...@@ -14,29 +14,22 @@
* limitations under the License. * 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.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
/** @Configuration
* @author Christian Dupuis @EnableAutoConfiguration
*/ @EnableConfigurationProperties
public class JolokiaEndpointTests extends AbstractEndpointTests<JolokiaEndpoint> { @ComponentScan
public class SampleActuatorNoWebApplication {
public JolokiaEndpointTests() { public static void main(String[] args) throws Exception {
super(Config.class, JolokiaEndpoint.class, "/jolokia", true, "endpoints.jolokia"); SpringApplication.run(SampleActuatorNoWebApplication.class, args);
} }
@Configuration
@EnableConfigurationProperties
public static class Config {
@Bean
public JolokiaEndpoint endpoint() {
return new JolokiaEndpoint();
}
}
} }
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * 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.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
......
service.name: Phil
shell.ssh.enabled: true
shell.ssh.port: 2222
shell.auth: simple
shell.auth.simple.user.password: password
/*
* 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<ConfigurableApplicationContext> future = Executors
.newSingleThreadExecutor().submit(
new Callable<ConfigurableApplicationContext>() {
@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));
}
}
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * 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.Date;
import java.util.Map; import java.util.Map;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.sample.ops.ui; package org.springframework.boot.sample.actuator.ui;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
...@@ -29,6 +29,7 @@ import org.junit.AfterClass; ...@@ -29,6 +29,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.ui.SampleActuatorUiApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.sample.ops.ui; package org.springframework.boot.sample.actuator.ui;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
...@@ -28,6 +28,7 @@ import org.junit.AfterClass; ...@@ -28,6 +28,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.ui.SampleActuatorUiApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -14,13 +14,19 @@ ...@@ -14,13 +14,19 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.sample.actuator;
/** import org.springframework.beans.factory.annotation.Autowired;
* {@link RuntimeException} indicating an {@link Endpoint} implementation is not enabled. import org.springframework.stereotype.Component;
*
* @author Christian Dupuis @Component
*/ public class HelloWorldService {
public class EndpointDisabledException extends RuntimeException {
@Autowired
private ServiceProperties configuration;
public String getHelloMessage() {
return "Hello " + this.configuration.getName();
}
} }
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * 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.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.sample.ops; package org.springframework.boot.sample.actuator;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
......
/*
* 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;
}
}
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.sample.ops; package org.springframework.boot.sample.actuator;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit; ...@@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest; import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * 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.assertEquals;
...@@ -32,6 +32,7 @@ import org.junit.BeforeClass; ...@@ -32,6 +32,7 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest; import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.sample.ops; package org.springframework.boot.sample.actuator;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
...@@ -27,6 +27,7 @@ import org.junit.AfterClass; ...@@ -27,6 +27,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.sample.ops; package org.springframework.boot.sample.actuator;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -30,6 +30,7 @@ import org.junit.BeforeClass; ...@@ -30,6 +30,7 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest; import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * 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.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
...@@ -36,6 +36,7 @@ import org.junit.BeforeClass; ...@@ -36,6 +36,7 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.sample.ops; package org.springframework.boot.sample.actuator;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -30,6 +30,7 @@ import org.junit.BeforeClass; ...@@ -30,6 +30,7 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest; import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * 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.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
...@@ -31,6 +31,7 @@ import org.junit.AfterClass; ...@@ -31,6 +31,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.sample.ops; package org.springframework.boot.sample.actuator;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
...@@ -27,6 +27,7 @@ import org.junit.AfterClass; ...@@ -27,6 +27,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
......
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
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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment