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;
import org.springframework.boot.autoconfigure.AutoConfigurationReport;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
......@@ -57,7 +56,6 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.http.MediaType;
/**
* {@link EnableAutoConfiguration Auto-configuration} for common management
......@@ -68,7 +66,6 @@ import org.springframework.http.MediaType;
* @author Greg Turnquist
*/
@Configuration
@ConditionalOnClass(MediaType.class)
public class EndpointAutoConfiguration {
@Autowired(required = false)
......
......@@ -29,18 +29,24 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerAdapter;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint;
import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
......@@ -85,21 +91,13 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
@Bean
@ConditionalOnMissingBean
public EndpointHandlerMapping endpointHandlerMapping() {
EndpointHandlerMapping mapping = new EndpointHandlerMapping();
EndpointHandlerMapping mapping = new EndpointHandlerMapping(mvcEndpoints()
.getEndpoints());
mapping.setDisabled(ManagementServerPort.get(this.applicationContext) != ManagementServerPort.SAME);
mapping.setPrefix(this.managementServerProperties.getContextPath());
return mapping;
}
@Bean
@ConditionalOnMissingBean
public EndpointHandlerAdapter endpointHandlerAdapter(
final HttpMessageConverters messageConverters) {
EndpointHandlerAdapter adapter = new EndpointHandlerAdapter();
adapter.setMessageConverters(messageConverters.getConverters());
return adapter;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
......@@ -130,6 +128,30 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
};
}
@Bean
@ConditionalOnMissingBean
public MvcEndpoints mvcEndpoints() {
return new MvcEndpoints();
}
@Bean
@ConditionalOnBean(EnvironmentEndpoint.class)
public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
return new EnvironmentMvcEndpoint(delegate);
}
@Bean
@ConditionalOnBean(MetricsEndpoint.class)
public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) {
return new MetricsMvcEndpoint(delegate);
}
@Bean
@ConditionalOnBean(ShutdownEndpoint.class)
public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
return new ShutdownMvcEndpoint(delegate);
}
private void createChildManagementContext() {
final AnnotationConfigEmbeddedWebApplicationContext childContext = new AnnotationConfigEmbeddedWebApplicationContext();
......
......@@ -16,7 +16,8 @@
package org.springframework.boot.actuate.autoconfigure;
import java.util.Map;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.Filter;
......@@ -26,15 +27,16 @@ import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerAdapter;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.ManagementErrorEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.boot.actuate.web.ErrorController;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
......@@ -42,11 +44,10 @@ import org.springframework.boot.context.embedded.ErrorPage;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
/**
* Configuration triggered from {@link EndpointWebMvcAutoConfiguration} when a new
......@@ -57,6 +58,9 @@ import org.springframework.web.servlet.HandlerMapping;
@Configuration
public class EndpointWebMvcChildContextConfiguration {
@Value("${error.path:/error}")
private String errorPath = "/error";
@Configuration
protected static class ServerCustomization implements
EmbeddedServletContainerCustomizer {
......@@ -100,13 +104,22 @@ public class EndpointWebMvcChildContextConfiguration {
}
@Bean
public HandlerMapping handlerMapping() {
return new EndpointHandlerMapping();
public HandlerAdapter handlerAdapter(HttpMessageConverters converters) {
// TODO: maybe this needs more configuration for non-basic response use cases
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setMessageConverters(converters.getConverters());
return adapter;
}
@Bean
public HandlerAdapter handlerAdapter() {
return new EndpointHandlerAdapter();
public HandlerMapping handlerMapping(MvcEndpoints endpoints,
ListableBeanFactory beanFactory) {
Set<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 {
* endpoints.
*/
@Bean
public Endpoint<Map<String, Object>> errorEndpoint(final ErrorController controller) {
return new AbstractEndpoint<Map<String, Object>>("/error", false, true) {
@Override
protected Map<String, Object> doInvoke() {
RequestAttributes attributes = RequestContextHolder
.currentRequestAttributes();
return controller.extract(attributes, false);
}
};
public ManagementErrorEndpoint errorEndpoint(final ErrorController controller) {
return new ManagementErrorEndpoint(this.errorPath, controller);
}
@Configuration
......
......@@ -20,7 +20,8 @@ import java.util.Map;
import org.jolokia.http.AgentServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.JolokiaEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint;
import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
......@@ -65,6 +66,9 @@ public class JolokiaAutoConfiguration {
private RelaxedPropertyResolver environment;
@Autowired
private ManagementServerProperties management;
@Autowired
public void setEnvironment(Environment environment) {
this.environment = new RelaxedPropertyResolver(environment);
......@@ -77,19 +81,17 @@ public class JolokiaAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean()
public ServletRegistrationBean jolokiaServletRegistration() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(
jolokiaServlet(), this.environment.getProperty("endpoints.jolokia.path",
"/jolokia") + "/*");
public ServletRegistrationBean jolokiaServletRegistration(AgentServlet servlet) {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(servlet,
this.management.getContextPath() + jolokiaEndpoint().getPath() + "/*");
addInitParameters(registrationBean);
return registrationBean;
}
@Bean
@ConditionalOnMissingBean
public JolokiaEndpoint jolokiaEndpoint() {
return new JolokiaEndpoint();
public JolokiaMvcEndpoint jolokiaEndpoint() {
return new JolokiaMvcEndpoint();
}
protected void addInitParameters(ServletRegistrationBean registrationBean) {
......
......@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.servlet.Filter;
......@@ -26,6 +27,7 @@ import javax.servlet.Filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.boot.actuate.web.ErrorController;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
......@@ -210,9 +212,9 @@ public class ManagementSecurityAutoConfiguration {
return NO_PATHS;
}
List<Endpoint<?>> endpoints = endpointHandlerMapping.getEndpoints();
Set<? extends MvcEndpoint> endpoints = endpointHandlerMapping.getEndpoints();
List<String> paths = new ArrayList<String>(endpoints.size());
for (Endpoint<?> endpoint : endpoints) {
for (MvcEndpoint endpoint : endpoints) {
if (endpoint.isSensitive() == secure) {
paths.add(endpoint.getPath());
}
......
......@@ -19,61 +19,48 @@ package org.springframework.boot.actuate.endpoint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
/**
* Abstract base for {@link Endpoint} implementations.
* <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 Christian Dupuis
*/
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
@Pattern(regexp = "/[^/]*", message = "Path must start with /")
private String path;
@Pattern(regexp = "\\w+", message = "ID must only contains letters, numbers and '_'")
private String id;
private boolean sensitive;
private boolean enabled = true;
public AbstractEndpoint(String path) {
this(path, true, true);
public AbstractEndpoint(String id) {
this(id, true, true);
}
public AbstractEndpoint(String path, boolean sensitive, boolean enabled) {
this.path = path;
public AbstractEndpoint(String id, boolean sensitive, boolean enabled) {
this.id = id;
this.sensitive = sensitive;
this.enabled = enabled;
}
public boolean isEnabled() {
return this.enabled;
@Override
public String getId() {
return this.id;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
public void setId(String id) {
this.id = id;
}
@Override
public String getPath() {
return this.path;
public boolean isEnabled() {
return this.enabled;
}
public void setPath(String path) {
this.path = path;
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
......@@ -85,23 +72,4 @@ public abstract class AbstractEndpoint<T> implements Endpoint<T> {
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> {
private AutoConfigurationReport autoConfigurationReport;
public AutoConfigurationReportEndpoint() {
super("/autoconfig");
super("autoconfig");
}
@Override
protected Report doInvoke() {
public Report invoke() {
return new Report(this.autoConfigurationReport);
}
......
......@@ -16,13 +16,16 @@
package org.springframework.boot.actuate.endpoint;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.boot.config.JsonParser;
import org.springframework.boot.config.JsonParserFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.LiveBeansView;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
/**
* Exposes JSON view of Spring beans. If the {@link Environment} contains a key setting
......@@ -33,13 +36,15 @@ import org.springframework.http.MediaType;
* @author Dave Syer
*/
@ConfigurationProperties(name = "endpoints.beans", ignoreUnknownFields = false)
public class BeansEndpoint extends AbstractEndpoint<String> implements
public class BeansEndpoint extends AbstractEndpoint<List<Object>> implements
ApplicationContextAware {
private LiveBeansView liveBeansView = new LiveBeansView();
private JsonParser parser = JsonParserFactory.getJsonParser();
public BeansEndpoint() {
super("/beans");
super("beans");
}
@Override
......@@ -51,12 +56,7 @@ public class BeansEndpoint extends AbstractEndpoint<String> implements
}
@Override
public MediaType[] produces() {
return new MediaType[] { MediaType.APPLICATION_JSON };
}
@Override
protected String doInvoke() {
return this.liveBeansView.getSnapshotAsJson();
public List<Object> invoke() {
return this.parser.parseList(this.liveBeansView.getSnapshotAsJson());
}
}
......@@ -31,8 +31,9 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* annotated classes.
*
* <p>
* To protect sensitive information from being exposed, configure property names by using
* <code>endpoints.configprops.keys_to_sanitize</code>.
* To protect sensitive information from being exposed, certain property values are masked
* if their names end with a set of configurable values (default "password" and "secret").
* Configure property names by using {@link #setKeysToSanitize(String[])}.
*
* @author Christian Dupuis
*/
......@@ -45,7 +46,7 @@ public class ConfigurationPropertiesReportEndpoint extends
private ApplicationContext context;
public ConfigurationPropertiesReportEndpoint() {
super("/configprops");
super("configprops");
}
@Override
......@@ -63,9 +64,15 @@ public class ConfigurationPropertiesReportEndpoint extends
}
@Override
public Map<String, Object> invoke() {
Map<String, Object> beans = extract(this.context);
return beans;
}
@SuppressWarnings("unchecked")
protected Map<String, Object> doInvoke() {
Map<String, Object> beans = this.context
private Map<String, Object> extract(ApplicationContext context) {
Map<String, Object> beans = context
.getBeansWithAnnotation(ConfigurationProperties.class);
// Serialize beans into map structure and sanitize values
......@@ -75,6 +82,10 @@ public class ConfigurationPropertiesReportEndpoint extends
beans.put(entry.getKey(), sanitize(value));
}
if (context.getParent() != null) {
beans.put("parent", extract(context.getParent()));
}
return beans;
}
......
......@@ -35,11 +35,11 @@ public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> {
* Create a new {@link DumpEndpoint} instance.
*/
public DumpEndpoint() {
super("/dump");
super("dump");
}
@Override
protected List<ThreadInfo> doInvoke() {
public List<ThreadInfo> invoke() {
return Arrays.asList(ManagementFactory.getThreadMXBean().dumpAllThreads(true,
true));
}
......
......@@ -16,9 +16,6 @@
package org.springframework.boot.actuate.endpoint;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
/**
* An endpoint that can be used to expose useful information to operations. Usually
* exposed via Spring MVC but could also be exposed using some other technique.
......@@ -30,26 +27,21 @@ import org.springframework.http.MediaType;
public interface Endpoint<T> {
/**
* Returns the path of the endpoint. Must start with '/' and should not include
* wildcards.
*/
String getPath();
/**
* Returns if the endpoint is sensitive, i.e. may return data that the average user
* should not see. Mappings can use this as a security hint.
* The logical ID of the endpoint. Must only contain simple letters, numbers and '_'
* characters (ie a {@literal "\w"} regex).
*/
boolean isSensitive();
String getId();
/**
* Returns the {@link MediaType}s that this endpoint produces or {@code null}.
* Return if the endpoint is enabled.
*/
MediaType[] produces();
boolean isEnabled();
/**
* Returns the {@link HttpMethod}s that this endpoint supports.
* Return if the endpoint is sensitive, i.e. may return data that the average user
* should not see. Mappings can use this as a security hint.
*/
HttpMethod[] methods();
boolean isSensitive();
/**
* Called to invoke the endpoint.
......
......@@ -43,11 +43,11 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
* Create a new {@link EnvironmentEndpoint} instance.
*/
public EnvironmentEndpoint() {
super("/env");
super("env");
}
@Override
protected Map<String, Object> doInvoke() {
public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>();
result.put("profiles", this.environment.getActiveProfiles());
for (PropertySource<?> source : getPropertySources()) {
......@@ -71,7 +71,7 @@ public class EnvironmentEndpoint extends AbstractEndpoint<Map<String, Object>> i
return new StandardEnvironment().getPropertySources();
}
private Object sanitize(String name, Object object) {
public static Object sanitize(String name, Object object) {
if (name.toLowerCase().endsWith("password")
|| name.toLowerCase().endsWith("secret")) {
return object == null ? null : "******";
......
......@@ -36,13 +36,13 @@ public class HealthEndpoint<T> extends AbstractEndpoint<T> {
* @param indicator the health indicator
*/
public HealthEndpoint(HealthIndicator<? extends T> indicator) {
super("/health", false, true);
super("health", false, true);
Assert.notNull(indicator, "Indicator must not be null");
this.indicator = indicator;
}
@Override
protected T doInvoke() {
public T invoke() {
return this.indicator.health();
}
......
......@@ -39,13 +39,13 @@ public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
* @param info the info to expose
*/
public InfoEndpoint(Map<String, ? extends Object> info) {
super("/info", false, true);
super("info", false, true);
Assert.notNull(info, "Info must not be null");
this.info = info;
}
@Override
protected Map<String, Object> doInvoke() {
public Map<String, Object> invoke() {
Map<String, Object> info = new LinkedHashMap<String, Object>(this.info);
info.putAll(getAdditionalInfo());
return info;
......
......@@ -39,13 +39,13 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
* @param metrics the metrics to expose
*/
public MetricsEndpoint(PublicMetrics metrics) {
super("/metrics");
super("metrics");
Assert.notNull(metrics, "Metrics must not be null");
this.metrics = metrics;
}
@Override
protected Map<String, Object> doInvoke() {
public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>();
for (Metric metric : this.metrics.metrics()) {
result.put(metric.getName(), metric.getValue());
......
......@@ -24,7 +24,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpMethod;
/**
* {@link Endpoint} to shutdown the {@link ApplicationContext}.
......@@ -42,32 +41,37 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> impl
* Create a new {@link ShutdownEndpoint} instance.
*/
public ShutdownEndpoint() {
super("/shutdown", true, false);
super("shutdown", true, false);
}
@Override
protected Map<String, Object> doInvoke() {
public Map<String, Object> invoke() {
if (this.context == null) {
return Collections.<String, Object> singletonMap("message",
"No context to shutdown.");
}
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500L);
}
catch (InterruptedException ex) {
// Swallow exception and continue
try {
return Collections.<String, Object> singletonMap("message",
"Shutting down, bye...");
}
finally {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500L);
}
catch (InterruptedException ex) {
// Swallow exception and continue
}
ShutdownEndpoint.this.context.close();
}
ShutdownEndpoint.this.context.close();
}
}).start();
}).start();
return Collections.<String, Object> singletonMap("message",
"Shutting down, bye...");
}
}
@Override
......@@ -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>> {
* @param repository the trace repository
*/
public TraceEndpoint(TraceRepository repository) {
super("/trace");
super("trace");
Assert.notNull(repository, "Repository must not be null");
this.repository = repository;
}
@Override
protected List<Trace> doInvoke() {
public List<Trace> invoke() {
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 @@
package org.springframework.boot.actuate.endpoint.mvc;
import java.util.ArrayList;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
/**
* {@link HandlerMapping} to map {@link Endpoint}s to URLs via {@link Endpoint#getPath()}.
* {@link HandlerMapping} to map {@link Endpoint}s to URLs via {@link Endpoint#getId()}.
* The semantics of {@code @RequestMapping} should be identical to a normal
* {@code @Controller}, but the endpoints should not be annotated as {@code @Controller}
* (otherwise they will be mapped by the normal MVC mechanisms).
*
* <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 Christian Dupuis
* @see EndpointHandlerAdapter
* @author Dave Syer
*
*/
public class EndpointHandlerMapping extends AbstractUrlHandlerMapping implements
InitializingBean, ApplicationContextAware {
public class EndpointHandlerMapping extends RequestMappingHandlerMapping implements
ApplicationContextAware {
private List<Endpoint<?>> endpoints;
private Set<? extends MvcEndpoint> endpoints;
private String prefix = "";
......@@ -54,52 +60,74 @@ public class EndpointHandlerMapping extends AbstractUrlHandlerMapping implements
/**
* Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be
* detected from the {@link ApplicationContext}.
* @param endpoints
*/
public EndpointHandlerMapping() {
setOrder(HIGHEST_PRECEDENCE);
}
/**
* Create a new {@link EndpointHandlerMapping} with the specified endpoints.
* @param endpoints the endpoints
*/
public EndpointHandlerMapping(Collection<? extends Endpoint<?>> endpoints) {
Assert.notNull(endpoints, "Endpoints must not be null");
this.endpoints = new ArrayList<Endpoint<?>>(endpoints);
public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
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() throws Exception {
if (this.endpoints == null) {
this.endpoints = findEndpointBeans();
}
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (!this.disabled) {
for (Endpoint<?> endpoint : this.endpoints) {
registerHandler(this.prefix + endpoint.getPath(), endpoint);
for (MvcEndpoint endpoint : this.endpoints) {
detectHandlerMethods(endpoint);
}
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private List<Endpoint<?>> findEndpointBeans() {
return new ArrayList(BeanFactoryUtils.beansOfTypeIncludingAncestors(
getApplicationContext(), Endpoint.class).values());
/**
* Since all handler beans are passed into the constructor there is no need to detect
* anything here
*/
@Override
protected boolean isHandler(Class<?> beanType) {
return false;
}
@Override
protected Object lookupHandler(String urlPath, HttpServletRequest request)
throws Exception {
Object handler = super.lookupHandler(urlPath, request);
if (handler != null) {
Object endpoint = (handler instanceof HandlerExecutionChain ? ((HandlerExecutionChain) handler)
.getHandler() : handler);
HttpMethod method = HttpMethod.valueOf(request.getMethod());
if (endpoint instanceof Endpoint
&& supportsMethod(((Endpoint<?>) endpoint).methods(), method)) {
return endpoint;
protected void registerHandlerMethod(Object handler, Method method,
RequestMappingInfo mapping) {
if (mapping == null) {
return;
}
Set<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();
}
int i = 0;
String prefix = StringUtils.hasText(this.prefix) ? this.prefix + path : path;
if (defaultPatterns.isEmpty()) {
patterns[0] = prefix;
}
else {
for (String pattern : defaultPatterns) {
patterns[i] = prefix + pattern;
i++;
}
}
return null;
PatternsRequestCondition patternsInfo = new PatternsRequestCondition(patterns);
RequestMappingInfo modified = new RequestMappingInfo(patternsInfo,
mapping.getMethodsCondition(), mapping.getParamsCondition(),
mapping.getHeadersCondition(), mapping.getConsumesCondition(),
mapping.getProducesCondition(), mapping.getCustomCondition());
super.registerHandlerMethod(handler, method, modified);
}
/**
......@@ -128,19 +156,7 @@ public class EndpointHandlerMapping extends AbstractUrlHandlerMapping implements
/**
* Return the endpoints
*/
public List<Endpoint<?>> getEndpoints() {
return Collections.unmodifiableList(this.endpoints);
}
private boolean supportsMethod(HttpMethod[] supportedMethods,
HttpMethod requestedMethod) {
Assert.notNull(supportedMethods, "SupportMethods must not be null");
Assert.notNull(supportedMethods, "RequestedMethod must not be null");
for (HttpMethod supportedMethod : supportedMethods) {
if (supportedMethod.equals(requestedMethod)) {
return true;
}
}
return false;
public Set<? extends MvcEndpoint> getEndpoints() {
return this.endpoints;
}
}
/*
* 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 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
package org.springframework.boot.actuate.endpoint.mvc;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.HttpMethod;
/**
* {@link Endpoint} implementation to register the Jolokia infrastructure with the Boot
......@@ -26,20 +29,49 @@ import org.springframework.http.HttpMethod;
* @author Christian Dupuis
*/
@ConfigurationProperties(name = "endpoints.jolokia", ignoreUnknownFields = false)
public class JolokiaEndpoint extends AbstractEndpoint<String> {
public class JolokiaMvcEndpoint implements MvcEndpoint {
@NotNull
@Pattern(regexp = "/[^/]*", message = "Path must start with /")
private String path;
private boolean sensitive;
public JolokiaEndpoint() {
super("/jolokia");
private boolean enabled = true;
public JolokiaMvcEndpoint() {
this.path = "/jolokia";
}
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
protected String doInvoke() {
return null;
public String getPath() {
return this.path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public boolean isSensitive() {
return this.sensitive;
}
public void setSensitive(boolean sensitive) {
this.sensitive = sensitive;
}
@Override
public HttpMethod[] methods() {
return NO_HTTP_METHOD;
public Class<?> getEndpointType() {
return null;
}
}
/*
* 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 {
@RequestMapping(value = "${error.path:/error}", produces = "text/html")
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);
}
......
......@@ -24,8 +24,8 @@ import java.nio.charset.Charset;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.TestUtils;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
......@@ -200,13 +200,8 @@ public class EndpointWebMvcAutoConfigurationTests {
}
@Bean
public Endpoint<String> testEndpoint() {
return new AbstractEndpoint<String>("/endpoint", false, true) {
@Override
public String doInvoke() {
return "endpointoutput";
}
};
public TestEndpoint testEndpoint() {
return new TestEndpoint();
}
}
......@@ -245,4 +240,29 @@ public class EndpointWebMvcAutoConfigurationTests {
}
public static class TestEndpoint implements MvcEndpoint {
@RequestMapping
@ResponseBody
public String invoke() {
return "endpointoutput";
}
@Override
public String getPath() {
return "/endpoint";
}
@Override
public boolean isSensitive() {
return true;
}
@Override
public Class<?> getEndpointType() {
return Endpoint.class;
}
}
}
......@@ -60,7 +60,9 @@ public class JolokiaAutoConfigurationTests {
public void agentServletRegisteredWithAppContext() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext();
this.context.register(Config.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, JolokiaAutoConfiguration.class);
ManagementServerPropertiesAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JolokiaAutoConfiguration.class);
this.context.refresh();
assertEquals(1, this.context.getBeanNamesForType(AgentServlet.class).length);
}
......@@ -70,7 +72,9 @@ public class JolokiaAutoConfigurationTests {
this.context = new AnnotationConfigEmbeddedWebApplicationContext();
TestUtils.addEnviroment(this.context, "endpoints.jolokia.enabled:false");
this.context.register(Config.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, JolokiaAutoConfiguration.class);
ManagementServerPropertiesAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JolokiaAutoConfiguration.class);
this.context.refresh();
assertEquals(0, this.context.getBeanNamesForType(AgentServlet.class).length);
}
......@@ -79,7 +83,9 @@ public class JolokiaAutoConfigurationTests {
public void agentServletRegisteredWithServletContainer() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext();
this.context.register(Config.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, JolokiaAutoConfiguration.class);
ManagementServerPropertiesAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JolokiaAutoConfiguration.class);
this.context.refresh();
Servlet servlet = null;
......
......@@ -25,7 +25,6 @@ import org.springframework.boot.TestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.http.MediaType;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
......@@ -43,22 +42,19 @@ public abstract class AbstractEndpointTests<T extends Endpoint<?>> {
private final Class<?> type;
private final String path;
private final String id;
private final boolean sensitive;
private final String property;
private MediaType[] produces;
public AbstractEndpointTests(Class<?> configClass, Class<?> type, String path,
boolean sensitive, String property, MediaType... produces) {
public AbstractEndpointTests(Class<?> configClass, Class<?> type, String id,
boolean sensitive, String property) {
this.configClass = configClass;
this.type = type;
this.path = path;
this.id = id;
this.sensitive = sensitive;
this.property = property;
this.produces = produces;
}
@Before
......@@ -76,13 +72,8 @@ public abstract class AbstractEndpointTests<T extends Endpoint<?>> {
}
@Test
public void producesMediaType() {
assertThat(getEndpointBean().produces(), equalTo(this.produces));
}
@Test
public void getPath() throws Exception {
assertThat(getEndpointBean().getPath(), equalTo(this.path));
public void getId() throws Exception {
assertThat(getEndpointBean().getId(), equalTo(this.id));
}
@Test
......@@ -91,12 +82,12 @@ public abstract class AbstractEndpointTests<T extends Endpoint<?>> {
}
@Test
public void pathOverride() throws Exception {
public void idOverride() throws Exception {
this.context = new AnnotationConfigApplicationContext();
TestUtils.addEnviroment(this.context, this.property + ".path:/mypath");
TestUtils.addEnviroment(this.context, this.property + ".id:myid");
this.context.register(this.configClass);
this.context.refresh();
assertThat(getEndpointBean().getPath(), equalTo("/mypath"));
assertThat(getEndpointBean().getId(), equalTo("myid"));
}
@Test
......
......@@ -42,7 +42,7 @@ public class AutoConfigurationReportEndpointTests extends
AbstractEndpointTests<AutoConfigurationReportEndpoint> {
public AutoConfigurationReportEndpointTests() {
super(Config.class, AutoConfigurationReportEndpoint.class, "/autoconfig", true,
super(Config.class, AutoConfigurationReportEndpoint.class, "autoconfig", true,
"endpoints.autoconfig");
}
......
......@@ -16,15 +16,16 @@
package org.springframework.boot.actuate.endpoint;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.BeansEndpoint;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link BeansEndpoint}.
......@@ -34,13 +35,14 @@ import static org.junit.Assert.assertThat;
public class BeansEndpointTests extends AbstractEndpointTests<BeansEndpoint> {
public BeansEndpointTests() {
super(Config.class, BeansEndpoint.class, "/beans", true, "endpoints.beans",
MediaType.APPLICATION_JSON);
super(Config.class, BeansEndpoint.class, "beans", true, "endpoints.beans");
}
@Test
public void invoke() throws Exception {
assertThat(getEndpointBean().invoke(), containsString("\"bean\": \"endpoint\""));
List<Object> result = getEndpointBean().invoke();
assertEquals(1, result.size());
assertTrue(result.get(0) instanceof Map);
}
@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
AbstractEndpointTests<ConfigurationPropertiesReportEndpoint> {
public ConfigurationPropertiesReportEndpointTests() {
super(Config.class, ConfigurationPropertiesReportEndpoint.class, "/configprops",
super(Config.class, ConfigurationPropertiesReportEndpoint.class, "configprops",
true, "endpoints.configprops");
}
......@@ -68,6 +68,15 @@ public class ConfigurationPropertiesReportEndpointTests extends
assertEquals("******", nestedProperties.get("myTestProperty"));
}
@Configuration
@EnableConfigurationProperties
public static class Parent {
@Bean
public TestProperties testProperties() {
return new TestProperties();
}
}
@Configuration
@EnableConfigurationProperties
public static class Config {
......@@ -82,29 +91,30 @@ public class ConfigurationPropertiesReportEndpointTests extends
return new TestProperties();
}
@ConfigurationProperties(name = "test")
public static class TestProperties {
}
private String dbPassword = "123456";
@ConfigurationProperties(name = "test")
public static class TestProperties {
private String myTestProperty = "654321";
private String dbPassword = "123456";
public String getDbPassword() {
return this.dbPassword;
}
private String myTestProperty = "654321";
public void setDbPassword(String dbPassword) {
this.dbPassword = dbPassword;
}
public String getDbPassword() {
return this.dbPassword;
}
public String getMyTestProperty() {
return this.myTestProperty;
}
public void setDbPassword(String dbPassword) {
this.dbPassword = dbPassword;
}
public void setMyTestProperty(String myTestProperty) {
this.myTestProperty = myTestProperty;
}
public String getMyTestProperty() {
return this.myTestProperty;
}
public void setMyTestProperty(String myTestProperty) {
this.myTestProperty = myTestProperty;
}
}
}
......@@ -20,7 +20,6 @@ import java.lang.management.ThreadInfo;
import java.util.List;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.DumpEndpoint;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -36,7 +35,7 @@ import static org.junit.Assert.assertThat;
public class DumpEndpointTests extends AbstractEndpointTests<DumpEndpoint> {
public DumpEndpointTests() {
super(Config.class, DumpEndpoint.class, "/dump", true, "endpoints.dump");
super(Config.class, DumpEndpoint.class, "dump", true, "endpoints.dump");
}
@Test
......
......@@ -17,7 +17,6 @@
package org.springframework.boot.actuate.endpoint;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -33,7 +32,7 @@ import static org.junit.Assert.assertThat;
public class EnvironmentEndpointTests extends AbstractEndpointTests<EnvironmentEndpoint> {
public EnvironmentEndpointTests() {
super(Config.class, EnvironmentEndpoint.class, "/env", true, "endpoints.env");
super(Config.class, EnvironmentEndpoint.class, "env", true, "endpoints.env");
}
@Test
......
......@@ -17,7 +17,6 @@
package org.springframework.boot.actuate.endpoint;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
......@@ -34,7 +33,7 @@ import static org.junit.Assert.assertThat;
public class HealthEndpointTests extends AbstractEndpointTests<HealthEndpoint<String>> {
public HealthEndpointTests() {
super(Config.class, HealthEndpoint.class, "/health", false, "endpoints.health");
super(Config.class, HealthEndpoint.class, "health", false, "endpoints.health");
}
@Test
......
......@@ -35,7 +35,7 @@ import static org.junit.Assert.assertThat;
public class InfoEndpointTests extends AbstractEndpointTests<InfoEndpoint> {
public InfoEndpointTests() {
super(Config.class, InfoEndpoint.class, "/info", false, "endpoints.info");
super(Config.class, InfoEndpoint.class, "info", false, "endpoints.info");
}
@Test
......
......@@ -20,8 +20,6 @@ import java.util.Collection;
import java.util.Collections;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.PublicMetrics;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
......@@ -38,7 +36,7 @@ import static org.junit.Assert.assertThat;
public class MetricsEndpointTests extends AbstractEndpointTests<MetricsEndpoint> {
public MetricsEndpointTests() {
super(Config.class, MetricsEndpoint.class, "/metrics", true, "endpoints.metrics");
super(Config.class, MetricsEndpoint.class, "metrics", true, "endpoints.metrics");
}
@Test
......
......@@ -35,7 +35,7 @@ import static org.junit.Assert.assertTrue;
public class ShutdownEndpointTests extends AbstractEndpointTests<ShutdownEndpoint> {
public ShutdownEndpointTests() {
super(Config.class, ShutdownEndpoint.class, "/shutdown", true,
super(Config.class, ShutdownEndpoint.class, "shutdown", true,
"endpoints.shutdown");
}
......
......@@ -19,7 +19,6 @@ package org.springframework.boot.actuate.endpoint;
import java.util.Collections;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.trace.InMemoryTraceRepository;
import org.springframework.boot.actuate.trace.Trace;
import org.springframework.boot.actuate.trace.TraceRepository;
......@@ -38,7 +37,7 @@ import static org.junit.Assert.assertThat;
public class TraceEndpointTests extends AbstractEndpointTests<TraceEndpoint> {
public TraceEndpointTests() {
super(Config.class, TraceEndpoint.class, "/trace", true, "endpoints.trace");
super(Config.class, TraceEndpoint.class, "trace", true, "endpoints.trace");
}
@Test
......
......@@ -167,11 +167,11 @@ public class EndpointMBeanExporterTests {
public static class TestEndpoint extends AbstractEndpoint<String> {
public TestEndpoint() {
super("/test");
super("test");
}
@Override
protected String doInvoke() {
public String invoke() {
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 @@
package org.springframework.boot.actuate.endpoint.mvc;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.http.HttpMethod;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
......@@ -33,66 +40,94 @@ import static org.junit.Assert.assertThat;
* Tests for {@link EndpointHandlerMapping}.
*
* @author Phillip Webb
* @author Dave Syer
*/
public class EndpointHandlerMappingTests {
private StaticApplicationContext context = new StaticApplicationContext();
private Method method;
@Before
public void init() throws Exception {
this.method = ReflectionUtils.findMethod(TestMvcEndpoint.class, "invoke");
}
@Test
public void withoutPrefix() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a");
TestEndpoint endpointB = new TestEndpoint("/b");
TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a"));
TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b"));
EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList(
endpointA, endpointB));
mapping.setApplicationContext(this.context);
mapping.afterPropertiesSet();
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a"))
.getHandler(), equalTo((Object) endpointA));
.getHandler(),
equalTo((Object) new HandlerMethod(endpointA, this.method)));
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/b"))
.getHandler(), equalTo((Object) endpointB));
.getHandler(),
equalTo((Object) new HandlerMethod(endpointB, this.method)));
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/c")),
nullValue());
}
@Test
public void withPrefix() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a");
TestEndpoint endpointB = new TestEndpoint("/b");
TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a"));
TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b"));
EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList(
endpointA, endpointB));
mapping.setApplicationContext(this.context);
mapping.setPrefix("/a");
mapping.afterPropertiesSet();
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a/a"))
.getHandler(), equalTo((Object) endpointA));
.getHandler(),
equalTo((Object) new HandlerMethod(endpointA, this.method)));
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a/b"))
.getHandler(), equalTo((Object) endpointB));
.getHandler(),
equalTo((Object) new HandlerMethod(endpointB, this.method)));
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")),
nullValue());
}
@Test
@Test(expected = HttpRequestMethodNotSupportedException.class)
public void onlyGetHttpMethodForNonActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestEndpoint("/a");
TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
EndpointHandlerMapping mapping = new EndpointHandlerMapping(
Arrays.asList(endpoint));
mapping.setApplicationContext(this.context);
mapping.afterPropertiesSet();
assertNotNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a")));
assertNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a")));
}
@Test
public void postHttpMethodForActionEndpoints() throws Exception {
TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
EndpointHandlerMapping mapping = new EndpointHandlerMapping(
Arrays.asList(endpoint));
mapping.setApplicationContext(this.context);
mapping.afterPropertiesSet();
assertNotNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a")));
}
@Test(expected = HttpRequestMethodNotSupportedException.class)
public void onlyPostHttpMethodForActionEndpoints() throws Exception {
TestEndpoint endpoint = new TestActionEndpoint("/a");
TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
EndpointHandlerMapping mapping = new EndpointHandlerMapping(
Arrays.asList(endpoint));
mapping.setApplicationContext(this.context);
mapping.afterPropertiesSet();
assertNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a")));
assertNotNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a")));
assertNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a")));
}
@Test
public void disabled() throws Exception {
TestEndpoint endpointA = new TestEndpoint("/a");
TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("/a"));
EndpointHandlerMapping mapping = new EndpointHandlerMapping(
Arrays.asList(endpointA));
Arrays.asList(endpoint));
mapping.setDisabled(true);
mapping.setApplicationContext(this.context);
mapping.afterPropertiesSet();
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")),
nullValue());
......@@ -105,22 +140,32 @@ public class EndpointHandlerMappingTests {
}
@Override
public Object doInvoke() {
public Object invoke() {
return null;
}
}
private static class TestActionEndpoint extends TestEndpoint {
private static class TestMvcEndpoint extends GenericMvcEndpoint {
public TestActionEndpoint(String path) {
super(path);
public TestMvcEndpoint(TestEndpoint delegate) {
super(delegate);
}
}
private static class TestActionEndpoint extends GenericMvcEndpoint {
public TestActionEndpoint(TestEndpoint delegate) {
super(delegate);
}
@Override
public HttpMethod[] methods() {
return POST_HTTP_METHOD;
@RequestMapping(method = RequestMethod.POST)
public Object invoke() {
return null;
}
}
}
/*
* 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;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration;
import org.springframework.boot.actuate.web.BasicErrorControllerIntegrationTests.TestConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -81,7 +83,8 @@ public class BasicErrorControllerIntegrationTests {
}
@Configuration
@EnableAutoConfiguration
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class,
ManagementSecurityAutoConfiguration.class })
public static class TestConfiguration {
// For manual testing
......
......@@ -19,7 +19,9 @@ package org.springframework.boot.actuate.web;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
......@@ -77,13 +79,15 @@ public class BasicErrorControllerSpecialIntegrationTests {
}
@Configuration
@EnableAutoConfiguration
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class,
ManagementSecurityAutoConfiguration.class })
protected static class ParentConfiguration {
}
@Configuration
@EnableAutoConfiguration
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class,
ManagementSecurityAutoConfiguration.class })
@EnableWebMvc
protected static class WebMvcIncludedConfiguration {
// For manual testing
......@@ -94,7 +98,19 @@ public class BasicErrorControllerSpecialIntegrationTests {
}
@Configuration
@EnableAutoConfiguration
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class,
ManagementSecurityAutoConfiguration.class })
protected static class VanillaConfiguration {
// For manual testing
public static void main(String[] args) {
SpringApplication.run(VanillaConfiguration.class, args);
}
}
@Configuration
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class,
ManagementSecurityAutoConfiguration.class })
protected static class ChildConfiguration {
// For manual testing
......
......@@ -16,6 +16,7 @@
<modules>
<module>spring-boot-sample-actuator</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-amqp</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 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......
/*
* Copyright 2013 the original author or authors.
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -14,29 +14,22 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
package org.springframework.boot.sample.actuator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author Christian Dupuis
*/
public class JolokiaEndpointTests extends AbstractEndpointTests<JolokiaEndpoint> {
@Configuration
@EnableAutoConfiguration
@EnableConfigurationProperties
@ComponentScan
public class SampleActuatorNoWebApplication {
public JolokiaEndpointTests() {
super(Config.class, JolokiaEndpoint.class, "/jolokia", true, "endpoints.jolokia");
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleActuatorNoWebApplication.class, args);
}
@Configuration
@EnableConfigurationProperties
public static class Config {
@Bean
public JolokiaEndpoint endpoint() {
return new JolokiaEndpoint();
}
}
}
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
......
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 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops.ui;
package org.springframework.boot.sample.actuator.ui;
import java.util.Date;
import java.util.Map;
......
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops.ui;
package org.springframework.boot.sample.actuator.ui;
import static org.junit.Assert.assertEquals;
......@@ -29,6 +29,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.ui.SampleActuatorUiApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
......
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops.ui;
package org.springframework.boot.sample.actuator.ui;
import java.io.IOException;
import java.util.Arrays;
......@@ -28,6 +28,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.ui.SampleActuatorUiApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
......
/*
* Copyright 2013 the original author or authors.
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -14,13 +14,19 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
package org.springframework.boot.sample.actuator;
/**
* {@link RuntimeException} indicating an {@link Endpoint} implementation is not enabled.
*
* @author Christian Dupuis
*/
public class EndpointDisabledException extends RuntimeException {
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class HelloWorldService {
@Autowired
private ServiceProperties configuration;
public String getHelloMessage() {
return "Hello " + this.configuration.getName();
}
}
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
......
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import java.util.Collections;
import java.util.Map;
......
/*
* 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 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import java.io.IOException;
import java.util.ArrayList;
......@@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
......
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import static org.junit.Assert.assertEquals;
......@@ -32,6 +32,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
......
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import java.io.IOException;
import java.util.Map;
......@@ -27,6 +27,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
......
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import java.io.IOException;
import java.util.ArrayList;
......@@ -30,6 +30,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
......
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
......@@ -36,6 +36,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
......
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import java.io.IOException;
import java.util.ArrayList;
......@@ -30,6 +30,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
......
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
......@@ -31,6 +31,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
......
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.sample.ops;
package org.springframework.boot.sample.actuator;
import java.io.IOException;
import java.util.Map;
......@@ -27,6 +27,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.sample.actuator.SampleActuatorApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
......
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