Commit 7f1264bb authored by Dave Syer's avatar Dave Syer Committed by Phillip Webb

Replace @FrameworkEndpoint with MvcEndpoint interface

parent 87e00cfa
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter; import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
...@@ -30,23 +28,20 @@ import javax.servlet.http.HttpServletResponse; ...@@ -30,23 +28,20 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint; import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint; import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint; import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.GenericMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint; 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.endpoint.mvc.ShutdownMvcEndpoint;
import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
...@@ -63,7 +58,6 @@ import org.springframework.context.annotation.Bean; ...@@ -63,7 +58,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
...@@ -97,7 +91,8 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, ...@@ -97,7 +91,8 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public EndpointHandlerMapping endpointHandlerMapping() { public EndpointHandlerMapping endpointHandlerMapping() {
EndpointHandlerMapping mapping = new EndpointHandlerMapping(); EndpointHandlerMapping mapping = new EndpointHandlerMapping(mvcEndpoints()
.getEndpoints());
mapping.setDisabled(ManagementServerPort.get(this.applicationContext) != ManagementServerPort.SAME); mapping.setDisabled(ManagementServerPort.get(this.applicationContext) != ManagementServerPort.SAME);
mapping.setPrefix(this.managementServerProperties.getContextPath()); mapping.setPrefix(this.managementServerProperties.getContextPath());
return mapping; return mapping;
...@@ -133,48 +128,28 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, ...@@ -133,48 +128,28 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware,
}; };
} }
@Component @Bean
protected static class GenericEndpointPostProcessor implements @ConditionalOnMissingBean
BeanDefinitionRegistryPostProcessor { public MvcEndpoints mvcEndpoints() {
return new MvcEndpoints();
private BeanDefinitionRegistry registry; }
private Map<Class<? extends Endpoint<?>>, Class<?>> endpointTypes = new HashMap<Class<? extends Endpoint<?>>, Class<?>>();
public GenericEndpointPostProcessor() {
this.endpointTypes.put(EnvironmentEndpoint.class,
EnvironmentMvcEndpoint.class);
this.endpointTypes.put(MetricsEndpoint.class, MetricsMvcEndpoint.class);
this.endpointTypes.put(ShutdownEndpoint.class, ShutdownMvcEndpoint.class);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
for (String name : beanFactory.getBeanNamesForType(Endpoint.class)) {
Class<?> type = getTypeForEndpoint(beanFactory.getType(name));
BeanDefinitionBuilder bean = BeanDefinitionBuilder
.genericBeanDefinition(type);
bean.addConstructorArgReference(name);
this.registry.registerBeanDefinition("mvc." + name,
bean.getBeanDefinition());
}
}
protected Class<?> getTypeForEndpoint(Class<?> endpoint) { @Bean
Class<?> type = GenericMvcEndpoint.class; @ConditionalOnBean(EnvironmentEndpoint.class)
if (this.endpointTypes.containsKey(endpoint)) { public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
type = this.endpointTypes.get(endpoint); return new EnvironmentMvcEndpoint(delegate);
} }
return type;
}
@Override @Bean
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) @ConditionalOnBean(MetricsEndpoint.class)
throws BeansException { public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) {
this.registry = registry; return new MetricsMvcEndpoint(delegate);
} }
@Bean
@ConditionalOnBean(ShutdownEndpoint.class)
public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
return new ShutdownMvcEndpoint(delegate);
} }
private void createChildManagementContext() { private void createChildManagementContext() {
......
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.Filter; import javax.servlet.Filter;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
...@@ -24,13 +27,16 @@ import org.springframework.beans.factory.HierarchicalBeanFactory; ...@@ -24,13 +27,16 @@ import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.endpoint.ManagementErrorEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.ManagementErrorEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.boot.actuate.web.ErrorController; import org.springframework.boot.actuate.web.ErrorController;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
...@@ -38,7 +44,6 @@ import org.springframework.boot.context.embedded.ErrorPage; ...@@ -38,7 +44,6 @@ import org.springframework.boot.context.embedded.ErrorPage;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.HandlerMapping;
...@@ -99,16 +104,19 @@ public class EndpointWebMvcChildContextConfiguration { ...@@ -99,16 +104,19 @@ public class EndpointWebMvcChildContextConfiguration {
} }
@Bean @Bean
public HandlerAdapter handlerAdapter() { public HandlerAdapter handlerAdapter(HttpMessageConverters converters) {
// TODO: maybe this needs more configuration for non-basic response use cases // TODO: maybe this needs more configuration for non-basic response use cases
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter(); RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setMessageConverters(new RestTemplate().getMessageConverters()); adapter.setMessageConverters(converters.getConverters());
return adapter; return adapter;
} }
@Bean @Bean
public HandlerMapping handlerMapping() { public HandlerMapping handlerMapping(MvcEndpoints endpoints,
EndpointHandlerMapping mapping = new EndpointHandlerMapping(); 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 // In a child context we definitely want to see the parent endpoints
mapping.setDetectHandlerMethodsInAncestorContexts(true); mapping.setDetectHandlerMethodsInAncestorContexts(true);
return mapping; return mapping;
......
...@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure; ...@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.servlet.Filter; import javax.servlet.Filter;
...@@ -210,7 +211,7 @@ public class ManagementSecurityAutoConfiguration { ...@@ -210,7 +211,7 @@ public class ManagementSecurityAutoConfiguration {
return NO_PATHS; return NO_PATHS;
} }
List<Endpoint<?>> endpoints = endpointHandlerMapping.getEndpoints(); Set<? extends Endpoint<?>> endpoints = endpointHandlerMapping.getEndpoints();
List<String> paths = new ArrayList<String>(endpoints.size()); List<String> paths = new ArrayList<String>(endpoints.size());
for (Endpoint<?> endpoint : endpoints) { for (Endpoint<?> endpoint : endpoints) {
if (endpoint.isSensitive() == secure) { if (endpoint.isSensitive() == secure) {
......
...@@ -21,7 +21,6 @@ import java.util.Map; ...@@ -21,7 +21,6 @@ import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.AutoConfigurationReportEndpoint.Report; import org.springframework.boot.actuate.endpoint.AutoConfigurationReportEndpoint.Report;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigurationReport; import org.springframework.boot.autoconfigure.AutoConfigurationReport;
import org.springframework.boot.autoconfigure.AutoConfigurationReport.ConditionAndOutcome; import org.springframework.boot.autoconfigure.AutoConfigurationReport.ConditionAndOutcome;
import org.springframework.boot.autoconfigure.AutoConfigurationReport.ConditionAndOutcomes; import org.springframework.boot.autoconfigure.AutoConfigurationReport.ConditionAndOutcomes;
...@@ -32,8 +31,6 @@ import org.springframework.util.ClassUtils; ...@@ -32,8 +31,6 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonPropertyOrder;
...@@ -45,7 +42,6 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; ...@@ -45,7 +42,6 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
* @author Dave Syer * @author Dave Syer
*/ */
@ConfigurationProperties(name = "endpoints.autoconfig", ignoreUnknownFields = false) @ConfigurationProperties(name = "endpoints.autoconfig", ignoreUnknownFields = false)
@FrameworkEndpoint
public class AutoConfigurationReportEndpoint extends AbstractEndpoint<Report> { public class AutoConfigurationReportEndpoint extends AbstractEndpoint<Report> {
@Autowired @Autowired
...@@ -56,8 +52,6 @@ public class AutoConfigurationReportEndpoint extends AbstractEndpoint<Report> { ...@@ -56,8 +52,6 @@ public class AutoConfigurationReportEndpoint extends AbstractEndpoint<Report> {
} }
@Override @Override
@RequestMapping
@ResponseBody
public Report invoke() { public Report invoke() {
return new Report(this.autoConfigurationReport); return new Report(this.autoConfigurationReport);
} }
......
...@@ -17,17 +17,13 @@ ...@@ -17,17 +17,13 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.Collection;
import java.util.Collections; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.HandlerMapping;
...@@ -56,9 +52,9 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl ...@@ -56,9 +52,9 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
* *
*/ */
public class EndpointHandlerMapping extends RequestMappingHandlerMapping implements public class EndpointHandlerMapping extends RequestMappingHandlerMapping implements
InitializingBean, ApplicationContextAware { ApplicationContextAware {
private List<Endpoint<?>> endpoints; private Set<? extends MvcEndpoint> endpoints;
private String prefix = ""; private String prefix = "";
...@@ -67,8 +63,10 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme ...@@ -67,8 +63,10 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme
/** /**
* Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be * Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be
* detected from the {@link ApplicationContext}. * detected from the {@link ApplicationContext}.
* @param endpoints
*/ */
public EndpointHandlerMapping() { public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
this.endpoints = new HashSet<MvcEndpoint>(endpoints);
// By default the static resource handler mapping is LOWEST_PRECEDENCE - 1 // By default the static resource handler mapping is LOWEST_PRECEDENCE - 1
setOrder(LOWEST_PRECEDENCE - 2); setOrder(LOWEST_PRECEDENCE - 2);
} }
...@@ -76,28 +74,20 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme ...@@ -76,28 +74,20 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme
@Override @Override
public void afterPropertiesSet() { public void afterPropertiesSet() {
super.afterPropertiesSet(); super.afterPropertiesSet();
if (this.endpoints == null) { if (!this.disabled) {
this.endpoints = findEndpointBeans(); for (MvcEndpoint endpoint : this.endpoints) {
detectHandlerMethods(endpoint);
}
} }
} }
@SuppressWarnings({ "rawtypes", "unchecked" })
private List<Endpoint<?>> findEndpointBeans() {
return new ArrayList(BeanFactoryUtils.beansOfTypeIncludingAncestors(
getApplicationContext(), Endpoint.class).values());
}
/** /**
* Detects &#64;FrameworkEndpoint annotations in handler beans. * Since all handler beans are passed into the constructor there is no need to detect
* * anything here
* @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#isHandler(java.lang.Class)
*/ */
@Override @Override
protected boolean isHandler(Class<?> beanType) { protected boolean isHandler(Class<?> beanType) {
if (this.disabled) { return false;
return false;
}
return AnnotationUtils.findAnnotation(beanType, FrameworkEndpoint.class) != null;
} }
@Override @Override
...@@ -169,7 +159,7 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme ...@@ -169,7 +159,7 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme
/** /**
* Return the endpoints * Return the endpoints
*/ */
public List<Endpoint<?>> getEndpoints() { public Set<? extends Endpoint<?>> getEndpoints() {
return Collections.unmodifiableList(this.endpoints); return this.endpoints;
} }
} }
...@@ -22,13 +22,13 @@ import org.springframework.core.env.Environment; ...@@ -22,13 +22,13 @@ import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; 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.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
/** /**
* @author Dave Syer * @author Dave Syer
*/ */
@FrameworkEndpoint
public class EnvironmentMvcEndpoint extends GenericMvcEndpoint implements public class EnvironmentMvcEndpoint extends GenericMvcEndpoint implements
EnvironmentAware { EnvironmentAware {
...@@ -38,7 +38,7 @@ public class EnvironmentMvcEndpoint extends GenericMvcEndpoint implements ...@@ -38,7 +38,7 @@ public class EnvironmentMvcEndpoint extends GenericMvcEndpoint implements
super(delegate); super(delegate);
} }
@RequestMapping("/{name:.*}") @RequestMapping(value = "/{name:.*}", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public Object value(@PathVariable String name) { public Object value(@PathVariable String name) {
String result = this.environment.getProperty(name); String result = this.environment.getProperty(name);
......
/*
* 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.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
/**
* @author Dave Syer
*/
@Component
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FrameworkEndpoint {
}
\ No newline at end of file
...@@ -24,7 +24,6 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -24,7 +24,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
/** /**
* @author Dave Syer * @author Dave Syer
*/ */
@FrameworkEndpoint
public class GenericMvcEndpoint implements MvcEndpoint { public class GenericMvcEndpoint implements MvcEndpoint {
private Endpoint<?> delegate; private Endpoint<?> delegate;
...@@ -44,4 +43,17 @@ public class GenericMvcEndpoint implements MvcEndpoint { ...@@ -44,4 +43,17 @@ public class GenericMvcEndpoint implements MvcEndpoint {
return this.delegate.getPath(); return this.delegate.getPath();
} }
@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,12 +14,10 @@ ...@@ -14,12 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint.mvc;
import java.util.Map; import java.util.Map;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.web.ErrorController; import org.springframework.boot.actuate.web.ErrorController;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -35,7 +33,6 @@ import org.springframework.web.context.request.RequestContextHolder; ...@@ -35,7 +33,6 @@ import org.springframework.web.context.request.RequestContextHolder;
* @author Dave Syer * @author Dave Syer
*/ */
@ConfigurationProperties(name = "error") @ConfigurationProperties(name = "error")
@FrameworkEndpoint
public class ManagementErrorEndpoint implements MvcEndpoint { public class ManagementErrorEndpoint implements MvcEndpoint {
private final ErrorController controller; private final ErrorController controller;
...@@ -57,4 +54,14 @@ public class ManagementErrorEndpoint implements MvcEndpoint { ...@@ -57,4 +54,14 @@ public class ManagementErrorEndpoint implements MvcEndpoint {
public String getPath() { public String getPath() {
return this.path; return this.path;
} }
@Override
public boolean isSensitive() {
return false;
}
@Override
public Class<?> getEndpointType() {
return null;
}
} }
\ No newline at end of file
...@@ -20,13 +20,13 @@ import org.springframework.boot.actuate.endpoint.MetricsEndpoint; ...@@ -20,13 +20,13 @@ import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; 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.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
/** /**
* @author Dave Syer * @author Dave Syer
*/ */
@FrameworkEndpoint
public class MetricsMvcEndpoint extends GenericMvcEndpoint { public class MetricsMvcEndpoint extends GenericMvcEndpoint {
private MetricsEndpoint delegate; private MetricsEndpoint delegate;
...@@ -36,7 +36,7 @@ public class MetricsMvcEndpoint extends GenericMvcEndpoint { ...@@ -36,7 +36,7 @@ public class MetricsMvcEndpoint extends GenericMvcEndpoint {
this.delegate = delegate; this.delegate = delegate;
} }
@RequestMapping("/{name:.*}") @RequestMapping(value = "/{name:.*}", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public Object value(@PathVariable String name) { public Object value(@PathVariable String name) {
Object value = this.delegate.invoke().get(name); Object value = this.delegate.invoke().get(name);
......
...@@ -16,11 +16,19 @@ ...@@ -16,11 +16,19 @@
package org.springframework.boot.actuate.endpoint.mvc; 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 * @author Dave Syer
*/ */
public interface MvcEndpoint { public interface MvcEndpoint extends Endpoint<Object> {
String getPath(); 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
...@@ -24,7 +24,6 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -24,7 +24,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
/** /**
* @author Dave Syer * @author Dave Syer
*/ */
@FrameworkEndpoint
public class ShutdownMvcEndpoint extends GenericMvcEndpoint { public class ShutdownMvcEndpoint extends GenericMvcEndpoint {
public ShutdownMvcEndpoint(ShutdownEndpoint delegate) { public ShutdownMvcEndpoint(ShutdownEndpoint delegate) {
......
...@@ -24,8 +24,8 @@ import java.nio.charset.Charset; ...@@ -24,8 +24,8 @@ import java.nio.charset.Charset;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.TestUtils; import org.springframework.boot.TestUtils;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint; import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.FrameworkEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.boot.actuate.properties.ManagementServerProperties;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
...@@ -240,20 +240,29 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -240,20 +240,29 @@ public class EndpointWebMvcAutoConfigurationTests {
} }
@FrameworkEndpoint public static class TestEndpoint implements MvcEndpoint {
public static class TestEndpoint extends AbstractEndpoint<String> {
public TestEndpoint() {
super("/endpoint", false, true);
}
@Override
@RequestMapping @RequestMapping
@ResponseBody @ResponseBody
public String invoke() { public String invoke() {
return "endpointoutput"; return "endpointoutput";
} }
@Override
public String getPath() {
return "/endpoint";
}
@Override
public boolean isSensitive() {
return true;
}
@Override
public Class<?> getEndpointType() {
return Endpoint.class;
}
} }
} }
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
...@@ -44,14 +45,10 @@ import static org.junit.Assert.assertThat; ...@@ -44,14 +45,10 @@ import static org.junit.Assert.assertThat;
public class EndpointHandlerMappingTests { public class EndpointHandlerMappingTests {
private StaticApplicationContext context = new StaticApplicationContext(); private StaticApplicationContext context = new StaticApplicationContext();
private EndpointHandlerMapping mapping = new EndpointHandlerMapping();
private Method method; private Method method;
@Before @Before
public void init() throws Exception { public void init() throws Exception {
this.context.getDefaultListableBeanFactory().registerSingleton("mapping",
this.mapping);
this.mapping.setApplicationContext(this.context);
this.method = ReflectionUtils.findMethod(TestMvcEndpoint.class, "invoke"); this.method = ReflectionUtils.findMethod(TestMvcEndpoint.class, "invoke");
} }
...@@ -59,18 +56,17 @@ public class EndpointHandlerMappingTests { ...@@ -59,18 +56,17 @@ public class EndpointHandlerMappingTests {
public void withoutPrefix() throws Exception { public void withoutPrefix() throws Exception {
TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a")); TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a"));
TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b")); TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b"));
this.context.getDefaultListableBeanFactory().registerSingleton( EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList(
endpointA.getPath(), endpointA); endpointA, endpointB));
this.context.getDefaultListableBeanFactory().registerSingleton( mapping.setApplicationContext(this.context);
endpointB.getPath(), endpointB); mapping.afterPropertiesSet();
this.mapping.afterPropertiesSet(); assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a"))
assertThat(this.mapping.getHandler(new MockHttpServletRequest("GET", "/a"))
.getHandler(), .getHandler(),
equalTo((Object) new HandlerMethod(endpointA, this.method))); equalTo((Object) new HandlerMethod(endpointA, this.method)));
assertThat(this.mapping.getHandler(new MockHttpServletRequest("GET", "/b")) assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/b"))
.getHandler(), .getHandler(),
equalTo((Object) new HandlerMethod(endpointB, this.method))); equalTo((Object) new HandlerMethod(endpointB, this.method)));
assertThat(this.mapping.getHandler(new MockHttpServletRequest("GET", "/c")), assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/c")),
nullValue()); nullValue());
} }
...@@ -78,59 +74,62 @@ public class EndpointHandlerMappingTests { ...@@ -78,59 +74,62 @@ public class EndpointHandlerMappingTests {
public void withPrefix() throws Exception { public void withPrefix() throws Exception {
TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a")); TestMvcEndpoint endpointA = new TestMvcEndpoint(new TestEndpoint("/a"));
TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b")); TestMvcEndpoint endpointB = new TestMvcEndpoint(new TestEndpoint("/b"));
this.context.getDefaultListableBeanFactory().registerSingleton( EndpointHandlerMapping mapping = new EndpointHandlerMapping(Arrays.asList(
endpointA.getPath(), endpointA); endpointA, endpointB));
this.context.getDefaultListableBeanFactory().registerSingleton( mapping.setApplicationContext(this.context);
endpointB.getPath(), endpointB); mapping.setPrefix("/a");
this.mapping.setPrefix("/a"); mapping.afterPropertiesSet();
this.mapping.afterPropertiesSet(); assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a/a"))
assertThat(this.mapping.getHandler(new MockHttpServletRequest("GET", "/a/a"))
.getHandler(), .getHandler(),
equalTo((Object) new HandlerMethod(endpointA, this.method))); equalTo((Object) new HandlerMethod(endpointA, this.method)));
assertThat(this.mapping.getHandler(new MockHttpServletRequest("GET", "/a/b")) assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a/b"))
.getHandler(), .getHandler(),
equalTo((Object) new HandlerMethod(endpointB, this.method))); equalTo((Object) new HandlerMethod(endpointB, this.method)));
assertThat(this.mapping.getHandler(new MockHttpServletRequest("GET", "/a")), assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")),
nullValue()); nullValue());
} }
@Test(expected = HttpRequestMethodNotSupportedException.class) @Test(expected = HttpRequestMethodNotSupportedException.class)
public void onlyGetHttpMethodForNonActionEndpoints() throws Exception { public void onlyGetHttpMethodForNonActionEndpoints() throws Exception {
TestMvcEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a")); TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton( EndpointHandlerMapping mapping = new EndpointHandlerMapping(
endpoint.getPath(), endpoint); Arrays.asList(endpoint));
this.mapping.afterPropertiesSet(); mapping.setApplicationContext(this.context);
assertNotNull(this.mapping.getHandler(new MockHttpServletRequest("GET", "/a"))); mapping.afterPropertiesSet();
assertNull(this.mapping.getHandler(new MockHttpServletRequest("POST", "/a"))); assertNotNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a")));
assertNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a")));
} }
@Test @Test
public void postHttpMethodForActionEndpoints() throws Exception { public void postHttpMethodForActionEndpoints() throws Exception {
TestMvcEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a")); TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton( EndpointHandlerMapping mapping = new EndpointHandlerMapping(
endpoint.getPath(), endpoint); Arrays.asList(endpoint));
this.mapping.afterPropertiesSet(); mapping.setApplicationContext(this.context);
assertNotNull(this.mapping.getHandler(new MockHttpServletRequest("POST", "/a"))); mapping.afterPropertiesSet();
assertNotNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a")));
} }
@Test(expected = HttpRequestMethodNotSupportedException.class) @Test(expected = HttpRequestMethodNotSupportedException.class)
public void onlyPostHttpMethodForActionEndpoints() throws Exception { public void onlyPostHttpMethodForActionEndpoints() throws Exception {
TestMvcEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a")); TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton( EndpointHandlerMapping mapping = new EndpointHandlerMapping(
endpoint.getPath(), endpoint); Arrays.asList(endpoint));
this.mapping.afterPropertiesSet(); mapping.setApplicationContext(this.context);
assertNotNull(this.mapping.getHandler(new MockHttpServletRequest("POST", "/a"))); mapping.afterPropertiesSet();
assertNull(this.mapping.getHandler(new MockHttpServletRequest("GET", "/a"))); assertNotNull(mapping.getHandler(new MockHttpServletRequest("POST", "/a")));
assertNull(mapping.getHandler(new MockHttpServletRequest("GET", "/a")));
} }
@Test @Test
public void disabled() throws Exception { public void disabled() throws Exception {
TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("/a")); TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("/a"));
this.context.getDefaultListableBeanFactory().registerSingleton( EndpointHandlerMapping mapping = new EndpointHandlerMapping(
endpoint.getPath(), endpoint); Arrays.asList(endpoint));
this.mapping.setDisabled(true); mapping.setDisabled(true);
this.mapping.afterPropertiesSet(); mapping.setApplicationContext(this.context);
assertThat(this.mapping.getHandler(new MockHttpServletRequest("GET", "/a")), mapping.afterPropertiesSet();
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a")),
nullValue()); nullValue());
} }
...@@ -141,14 +140,12 @@ public class EndpointHandlerMappingTests { ...@@ -141,14 +140,12 @@ public class EndpointHandlerMappingTests {
} }
@Override @Override
@RequestMapping(method = RequestMethod.GET)
public Object invoke() { public Object invoke() {
return null; return null;
} }
} }
@FrameworkEndpoint
private static class TestMvcEndpoint extends GenericMvcEndpoint { private static class TestMvcEndpoint extends GenericMvcEndpoint {
public TestMvcEndpoint(TestEndpoint delegate) { public TestMvcEndpoint(TestEndpoint delegate) {
...@@ -157,8 +154,7 @@ public class EndpointHandlerMappingTests { ...@@ -157,8 +154,7 @@ public class EndpointHandlerMappingTests {
} }
@FrameworkEndpoint private static class TestActionEndpoint extends GenericMvcEndpoint {
private static class TestActionEndpoint extends TestMvcEndpoint {
public TestActionEndpoint(TestEndpoint delegate) { public TestActionEndpoint(TestEndpoint delegate) {
super(delegate); super(delegate);
...@@ -169,6 +165,7 @@ public class EndpointHandlerMappingTests { ...@@ -169,6 +165,7 @@ public class EndpointHandlerMappingTests {
public Object invoke() { public Object invoke() {
return null; 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());
}
}
}
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