Simplify the Actuator's hypermedia support

This commit replaces the Acuator's support for hypermedia with a
single endpoint that returns HAL-formatted links to all of the
available endpoints. This is done without requiring Spring HATEOAS
to be on the classpath in a similar manner to the existing
CloudFoundry discovery endpoint.

Closes gh-9901
This commit is contained in:
Andy Wilkinson 2017-07-30 22:12:17 +01:00
parent 4a61e45644
commit 9f75da9a8f
61 changed files with 120 additions and 2763 deletions

View File

@ -1,331 +0,0 @@
/*
* Copyright 2012-2017 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.autoconfigure;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.ActuatorMediaTypes;
import org.springframework.boot.actuate.endpoint.mvc.HalBrowserMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HypermediaDisabled;
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.ResourceLoader;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.TypeUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
/**
* Configuration for hypermedia in HTTP endpoints.
*
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.3.0
*/
@ManagementContextConfiguration
@ConditionalOnClass(Link.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnBean(HttpMessageConverters.class)
@ConditionalOnEnabledEndpoint("actuator")
@EnableConfigurationProperties({ ResourceProperties.class,
ManagementServerProperties.class })
public class EndpointWebMvcHypermediaManagementContextConfiguration {
@Bean
public ManagementServletContext managementServletContext(
final ManagementServerProperties properties) {
return properties::getContextPath;
}
@Bean
public HalJsonMvcEndpoint halJsonMvcEndpoint(
ManagementServletContext managementServletContext,
ResourceProperties resources, ResourceLoader resourceLoader) {
if (HalBrowserMvcEndpoint.getHalBrowserLocation(resourceLoader) != null) {
return new HalBrowserMvcEndpoint(managementServletContext);
}
return new HalJsonMvcEndpoint(managementServletContext);
}
/**
* Controller advice that adds links to the actuator endpoint's path.
*/
@ControllerAdvice
public static class ActuatorEndpointLinksAdvice
implements ResponseBodyAdvice<Object> {
@Autowired
private MvcEndpoints endpoints;
@Autowired(required = false)
private HalJsonMvcEndpoint halJsonMvcEndpoint;
@Autowired
private ManagementServerProperties management;
private LinksEnhancer linksEnhancer;
@PostConstruct
public void init() {
this.linksEnhancer = new LinksEnhancer(this.management.getContextPath(),
this.endpoints);
}
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
returnType.increaseNestingLevel();
Type nestedType = returnType.getNestedGenericParameterType();
returnType.decreaseNestingLevel();
return ResourceSupport.class.isAssignableFrom(returnType.getParameterType())
|| TypeUtils.isAssignable(ResourceSupport.class, nestedType);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (request instanceof ServletServerHttpRequest) {
beforeBodyWrite(body, (ServletServerHttpRequest) request);
}
return body;
}
private void beforeBodyWrite(Object body, ServletServerHttpRequest request) {
Object pattern = request.getServletRequest()
.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
if (pattern != null && body instanceof ResourceSupport) {
beforeBodyWrite(pattern.toString(), (ResourceSupport) body);
}
}
private void beforeBodyWrite(String path, ResourceSupport body) {
if (isActuatorEndpointPath(path)) {
this.linksEnhancer.addEndpointLinks(body,
this.halJsonMvcEndpoint.getPath());
}
}
private boolean isActuatorEndpointPath(String path) {
if (this.halJsonMvcEndpoint != null) {
String toMatch = this.management.getContextPath()
+ this.halJsonMvcEndpoint.getPath();
return toMatch.equals(path) || (toMatch + "/").equals(path);
}
return false;
}
}
/**
* Controller advice that adds links to the existing Actuator endpoints. By default
* all the top-level resources are enhanced with a "self" link. Those resources that
* could not be enhanced (e.g. "/env/{name}") because their values are "primitive" are
* ignored.
*/
@ConditionalOnProperty(prefix = "endpoints.hypermedia", name = "enabled", matchIfMissing = false)
@ControllerAdvice(assignableTypes = MvcEndpoint.class)
static class MvcEndpointAdvice implements ResponseBodyAdvice<Object> {
private final List<RequestMappingHandlerAdapter> handlerAdapters;
private final Map<MediaType, HttpMessageConverter<?>> converterCache = new ConcurrentHashMap<>();
MvcEndpointAdvice(List<RequestMappingHandlerAdapter> handlerAdapters) {
this.handlerAdapters = handlerAdapters;
}
@PostConstruct
public void configureHttpMessageConverters() {
for (RequestMappingHandlerAdapter handlerAdapter : this.handlerAdapters) {
for (HttpMessageConverter<?> messageConverter : handlerAdapter
.getMessageConverters()) {
configureHttpMessageConverter(messageConverter);
}
}
}
private void configureHttpMessageConverter(
HttpMessageConverter<?> messageConverter) {
if (messageConverter instanceof TypeConstrainedMappingJackson2HttpMessageConverter) {
List<MediaType> supportedMediaTypes = new ArrayList<>(
messageConverter.getSupportedMediaTypes());
supportedMediaTypes.add(ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON);
((AbstractHttpMessageConverter<?>) messageConverter)
.setSupportedMediaTypes(supportedMediaTypes);
}
}
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
Class<?> controllerType = returnType.getDeclaringClass();
return !HalJsonMvcEndpoint.class.isAssignableFrom(controllerType);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (request instanceof ServletServerHttpRequest) {
return beforeBodyWrite(body, returnType, selectedContentType,
selectedConverterType, (ServletServerHttpRequest) request,
response);
}
return body;
}
private Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServletServerHttpRequest request, ServerHttpResponse response) {
if (body == null || body instanceof Resource) {
// Assume it already was handled or it already has its links
return body;
}
if (body instanceof Collection || body.getClass().isArray()) {
// We can't add links to a collection without wrapping it
return body;
}
HttpMessageConverter<Object> converter = findConverter(selectedConverterType,
selectedContentType);
if (converter == null || isHypermediaDisabled(returnType)) {
// Not a resource that can be enhanced with a link
return body;
}
String path = getPath(request);
try {
converter.write(new EndpointResource(body, path), selectedContentType,
response);
}
catch (IOException ex) {
throw new HttpMessageNotWritableException("Cannot write response", ex);
}
return null;
}
@SuppressWarnings("unchecked")
private HttpMessageConverter<Object> findConverter(
Class<? extends HttpMessageConverter<?>> selectedConverterType,
MediaType mediaType) {
HttpMessageConverter<Object> cached = (HttpMessageConverter<Object>) this.converterCache
.get(mediaType);
if (cached != null) {
return cached;
}
for (RequestMappingHandlerAdapter handlerAdapter : this.handlerAdapters) {
for (HttpMessageConverter<?> converter : handlerAdapter
.getMessageConverters()) {
if (selectedConverterType.isAssignableFrom(converter.getClass())
&& converter.canWrite(EndpointResource.class, mediaType)) {
this.converterCache.put(mediaType, converter);
return (HttpMessageConverter<Object>) converter;
}
}
}
return null;
}
private boolean isHypermediaDisabled(MethodParameter returnType) {
return AnnotationUtils.findAnnotation(returnType.getMethod(),
HypermediaDisabled.class) != null
|| AnnotationUtils.findAnnotation(
returnType.getMethod().getDeclaringClass(),
HypermediaDisabled.class) != null;
}
private String getPath(ServletServerHttpRequest request) {
String path = (String) request.getServletRequest()
.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
return (path == null ? "" : path);
}
}
@JsonInclude(content = Include.NON_NULL)
@JacksonXmlRootElement(localName = "resource")
private static class EndpointResource extends ResourceSupport {
private Object content;
private Map<String, Object> embedded;
@SuppressWarnings("unchecked")
EndpointResource(Object content, String path) {
this.content = content instanceof Map ? null : content;
this.embedded = (Map<String, Object>) (this.content == null ? content : null);
add(linkTo(Object.class).slash(path).withSelfRel());
}
@JsonUnwrapped
public Object getContent() {
return this.content;
}
@JsonAnyGetter
public Map<String, Object> getEmbedded() {
return this.embedded;
}
}
}

View File

@ -29,6 +29,7 @@ import org.springframework.beans.factory.ObjectProvider;
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.endpoint.mvc.NamedMvcEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -379,7 +380,7 @@ public class ManagementWebSecurityAutoConfiguration {
if (endpointHandlerMapping == null) {
// Maybe there are actually no endpoints (e.g. management.port=-1)
endpointHandlerMapping = new EndpointHandlerMapping(
Collections.<MvcEndpoint>emptySet());
Collections.<NamedMvcEndpoint>emptySet());
}
return endpointHandlerMapping;
}

View File

@ -21,7 +21,6 @@ import java.util.Set;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint;
@ -50,10 +49,7 @@ class CloudFoundryEndpointHandlerMapping
HealthMvcEndpoint healthMvcEndpoint = null;
while (iterator.hasNext()) {
NamedMvcEndpoint endpoint = iterator.next();
if (endpoint instanceof HalJsonMvcEndpoint) {
iterator.remove();
}
else if (endpoint instanceof HealthMvcEndpoint) {
if (endpoint instanceof HealthMvcEndpoint) {
iterator.remove();
healthMvcEndpoint = (HealthMvcEndpoint) endpoint;
}

View File

@ -17,9 +17,17 @@
package org.springframework.boot.actuate.endpoint.mvc;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.HandlerMapping;
@ -63,4 +71,67 @@ public class EndpointHandlerMapping extends AbstractEndpointHandlerMapping<MvcEn
super(endpoints, corsConfiguration);
}
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
detectHandlerMethods(new EndpointLinksMvcEndpoint(
getEndpoints().stream().filter(NamedMvcEndpoint.class::isInstance)
.map(NamedMvcEndpoint.class::cast).collect(Collectors.toSet())));
}
/**
* {@link MvcEndpoint} to provide HAL-formatted links to all the
* {@link NamedMvcEndpoint named endpoints}.
*
* @author Madhura Bhave
* @author Andy Wilkinson
*/
private static final class EndpointLinksMvcEndpoint extends AbstractMvcEndpoint {
private final Set<NamedMvcEndpoint> endpoints;
private EndpointLinksMvcEndpoint(Set<NamedMvcEndpoint> endpoints) {
super("", false);
this.endpoints = endpoints;
}
@ResponseBody
@ActuatorGetMapping
public Map<String, Map<String, Link>> links(HttpServletRequest request) {
Map<String, Link> links = new LinkedHashMap<>();
String url = request.getRequestURL().toString();
if (url.endsWith("/")) {
url = url.substring(0, url.length() - 1);
}
links.put("self", Link.withHref(url));
for (NamedMvcEndpoint endpoint : this.endpoints) {
links.put(endpoint.getName(),
Link.withHref(url + "/" + endpoint.getName()));
}
return Collections.singletonMap("_links", links);
}
}
/**
* Details for a link in the HAL response.
*/
static final class Link {
private final String href;
private Link(String href) {
this.href = href;
}
public String getHref() {
return this.href;
}
static Link withHref(Object href) {
return new Link(href.toString());
}
}
}

View File

@ -48,7 +48,6 @@ public class EnvironmentMvcEndpoint extends EndpointMvcAdapter
@ActuatorGetMapping("/{name:.*}")
@ResponseBody
@HypermediaDisabled
public Object value(@PathVariable String name) {
if (!getDelegate().isEnabled()) {
// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's

View File

@ -1,170 +0,0 @@
/*
* Copyright 2012-2017 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.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.resource.ResourceTransformer;
import org.springframework.web.servlet.resource.ResourceTransformerChain;
import org.springframework.web.servlet.resource.TransformedResource;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
/**
* {@link MvcEndpoint} to expose a HAL browser.
*
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @author Stephane Nicoll
* @since 1.3.0
*/
public class HalBrowserMvcEndpoint extends HalJsonMvcEndpoint
implements ResourceLoaderAware {
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private static HalBrowserLocation[] HAL_BROWSER_RESOURCE_LOCATIONS = {
new HalBrowserLocation("classpath:/META-INF/spring-data-rest/hal-browser/",
"index.html"),
new HalBrowserLocation(
"classpath:/META-INF/resources/webjars/hal-browser/3325375/",
"browser.html") };
private HalBrowserLocation location;
public HalBrowserMvcEndpoint(ManagementServletContext managementServletContext) {
super(managementServletContext);
}
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public String browse(HttpServletRequest request) {
ServletUriComponentsBuilder builder = ServletUriComponentsBuilder
.fromRequest(request);
String uriString = builder.build().toUriString();
return "redirect:" + uriString + (uriString.endsWith("/") ? "" : "/")
+ this.location.getHtmlFile();
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.location = getHalBrowserLocation(resourceLoader);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Make sure the root path is not cached so the browser comes back for the JSON
// and add a transformer to set the initial link
if (this.location != null) {
String start = getManagementServletContext().getContextPath() + getPath();
registry.addResourceHandler(start + "/", start + "/**")
.addResourceLocations(this.location.getResourceLocation())
.setCachePeriod(0).resourceChain(true)
.addTransformer(new InitialUrlTransformer());
}
}
public static HalBrowserLocation getHalBrowserLocation(
ResourceLoader resourceLoader) {
for (HalBrowserLocation candidate : HAL_BROWSER_RESOURCE_LOCATIONS) {
try {
Resource resource = resourceLoader.getResource(candidate.toString());
if (resource != null && resource.exists()) {
return candidate;
}
}
catch (Exception ex) {
// Ignore
}
}
return null;
}
/**
* HAL Browser properties.
*/
public static class HalBrowserLocation {
private final String resourceLocation;
private final String htmlFile;
public HalBrowserLocation(String resourceLocation, String html) {
this.resourceLocation = resourceLocation;
this.htmlFile = html;
}
public String getResourceLocation() {
return this.resourceLocation;
}
public String getHtmlFile() {
return this.htmlFile;
}
@Override
public String toString() {
return this.resourceLocation + this.htmlFile;
}
}
/**
* {@link ResourceTransformer} to change the initial link location.
*/
private class InitialUrlTransformer implements ResourceTransformer {
@Override
public Resource transform(HttpServletRequest request, Resource resource,
ResourceTransformerChain transformerChain) throws IOException {
resource = transformerChain.transform(request, resource);
if (resource.getFilename().equalsIgnoreCase(
HalBrowserMvcEndpoint.this.location.getHtmlFile())) {
return replaceInitialLink(request, resource);
}
return resource;
}
private Resource replaceInitialLink(HttpServletRequest request, Resource resource)
throws IOException {
byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
String content = new String(bytes, DEFAULT_CHARSET);
List<String> pathSegments = new ArrayList<>(ServletUriComponentsBuilder
.fromRequest(request).build().getPathSegments());
pathSegments.remove(pathSegments.size() - 1);
String initial = "/"
+ StringUtils.collectionToDelimitedString(pathSegments, "/");
content = content.replace("entryPoint: '/'", "entryPoint: '" + initial + "'");
return new TransformedResource(resource, content.getBytes(DEFAULT_CHARSET));
}
}
}

View File

@ -1,60 +0,0 @@
/*
* Copyright 2012-2017 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.context.properties.ConfigurationProperties;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* {@link MvcEndpoint} to expose HAL-formatted JSON.
*
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.3.0
*/
@ConfigurationProperties(prefix = "endpoints.actuator")
public class HalJsonMvcEndpoint extends AbstractNamedMvcEndpoint {
private final ManagementServletContext managementServletContext;
public HalJsonMvcEndpoint(ManagementServletContext managementServletContext) {
super("actuator", getDefaultPath(managementServletContext), false);
this.managementServletContext = managementServletContext;
}
private static String getDefaultPath(
ManagementServletContext managementServletContext) {
if (StringUtils.hasText(managementServletContext.getContextPath())) {
return "";
}
return "/application";
}
@ActuatorGetMapping
@ResponseBody
public ResourceSupport links() {
return new ResourceSupport();
}
protected final ManagementServletContext getManagementServletContext() {
return this.managementServletContext;
}
}

View File

@ -55,7 +55,6 @@ import org.springframework.web.bind.annotation.ResponseStatus;
* @since 1.4.0
*/
@ConfigurationProperties(prefix = "endpoints.heapdump")
@HypermediaDisabled
public class HeapdumpMvcEndpoint extends AbstractNamedMvcEndpoint {
private final long timeout;

View File

@ -1,39 +0,0 @@
/*
* Copyright 2012-2015 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.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Annotation to indicate that an {@link MvcEndpoint} class or {@link RequestMapping}
* method shouldn't generate a hypermedia response.
*
* @author Dave Syer
* @since 1.3.0
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HypermediaDisabled {
}

View File

@ -44,7 +44,6 @@ import org.springframework.web.util.UrlPathHelper;
* @author Andy Wilkinson
*/
@ConfigurationProperties(prefix = "endpoints.jolokia", ignoreUnknownFields = false)
@HypermediaDisabled
public class JolokiaMvcEndpoint extends AbstractNamedMvcEndpoint implements
InitializingBean, ApplicationContextAware, ServletContextAware, DisposableBean {

View File

@ -47,7 +47,6 @@ public class LoggersMvcEndpoint extends EndpointMvcAdapter {
@ActuatorGetMapping("/{name:.*}")
@ResponseBody
@HypermediaDisabled
public Object get(@PathVariable String name) {
if (!this.delegate.isEnabled()) {
// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's
@ -60,7 +59,6 @@ public class LoggersMvcEndpoint extends EndpointMvcAdapter {
@ActuatorPostMapping("/{name:.*}")
@ResponseBody
@HypermediaDisabled
public Object set(@PathVariable String name,
@RequestBody Map<String, String> configuration) {
if (!this.delegate.isEnabled()) {

View File

@ -44,7 +44,6 @@ public class MetricsMvcEndpoint extends EndpointMvcAdapter {
@ActuatorGetMapping("/{name:.*}")
@ResponseBody
@HypermediaDisabled
public Object value(@PathVariable String name) {
if (!this.delegate.isEnabled()) {
// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's

View File

@ -47,12 +47,6 @@
"vcap_services"
]
},
{
"name": "endpoints.hypermedia.enabled",
"type": "java.lang.Boolean",
"description": "Enable hypermedia support for endpoints.",
"defaultValue": false
},
{
"name": "endpoints.info.path",
"type": "java.lang.String",

View File

@ -19,5 +19,4 @@ org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration,\
org.springframework.boot.actuate.cloudfoundry.CloudFoundryActuatorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration

View File

@ -45,7 +45,6 @@ import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCustomizer;
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
@ -437,7 +436,7 @@ public class EndpointWebMvcAutoConfigurationTests {
this.applicationContext.refresh();
// /health, /metrics, /loggers, /env, /actuator, /heapdump, /auditevents
// (/shutdown is disabled by default)
assertThat(this.applicationContext.getBeansOfType(MvcEndpoint.class)).hasSize(7);
assertThat(this.applicationContext.getBeansOfType(MvcEndpoint.class)).hasSize(6);
}
@Test
@ -500,18 +499,6 @@ public class EndpointWebMvcAutoConfigurationTests {
.hasSize(1);
}
@Test
public void actuatorEndpointEnabledIndividually() {
this.applicationContext.register(RootConfig.class, BaseConfiguration.class,
EndpointWebMvcAutoConfiguration.class);
TestPropertyValues
.of("endpoints.enabled:false", "endpoints.actuator.enabled:true")
.applyTo(this.applicationContext);
this.applicationContext.refresh();
assertThat(this.applicationContext.getBeansOfType(HalJsonMvcEndpoint.class))
.hasSize(1);
}
@Test
public void managementSpecificSslUsingDifferentPort() throws Exception {
TestPropertyValues

View File

@ -1,95 +0,0 @@
/*
* Copyright 2012-2017 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.autoconfigure;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.hal.DefaultCurieProvider;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link EndpointWebMvcHypermediaManagementContextConfiguration}.
*
* @author Andy Wilkinson
*/
public class EndpointWebMvcHypermediaManagementContextConfigurationTests {
private AnnotationConfigWebApplicationContext context;
@Before
public void setRequestAttributes() {
RequestContextHolder.setRequestAttributes(
new ServletRequestAttributes(new MockHttpServletRequest()));
}
@After
public void resetRequestAttributes() {
RequestContextHolder.resetRequestAttributes();
}
@After
public void closeContext() {
this.context.close();
}
@Test
public void basicConfiguration() {
load();
assertThat(this.context.getBeansOfType(ManagementServletContext.class))
.hasSize(1);
assertThat(this.context.getBeansOfType(HalJsonMvcEndpoint.class)).hasSize(1);
assertThat(this.context.getBeansOfType(DefaultCurieProvider.class)).isEmpty();
}
private void load(String... properties) {
this.context = new AnnotationConfigWebApplicationContext();
TestPropertyValues.of(properties).applyTo(this.context);
this.context.register(TestConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcHypermediaManagementContextConfiguration.class);
this.context.refresh();
}
@Configuration
@EnableConfigurationProperties({ ManagementServerProperties.class,
ServerProperties.class })
static class TestConfiguration {
@Bean
public MvcEndpoints mvcEndpoints() {
return new MvcEndpoints();
}
}
}

View File

@ -25,8 +25,8 @@ import org.junit.Test;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpointSecurityInterceptor;
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint;
import org.springframework.boot.actuate.servlet.MockServletWebServerFactory;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
@ -138,7 +138,7 @@ public class JolokiaAutoConfigurationTests {
@Bean
public EndpointHandlerMapping endpointHandlerMapping(
Collection<? extends MvcEndpoint> endpoints) {
Collection<NamedMvcEndpoint> endpoints) {
EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints);
mapping.setSecurityInterceptor(new MvcEndpointSecurityInterceptor(false,
Collections.<String>emptyList()));

View File

@ -1,54 +0,0 @@
/*
* Copyright 2012-2017 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.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.context.annotation.Configuration;
/**
* Minimal configuration required to run the Actuator with hypermedia.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@ImportAutoConfiguration({ ServletWebServerFactoryAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, WebMvcAutoConfiguration.class,
HypermediaAutoConfiguration.class, EndpointAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, AuditAutoConfiguration.class })
public @interface MinimalActuatorHypermediaApplication {
}

View File

@ -38,7 +38,6 @@ import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.AuditEventsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint;
@ -82,7 +81,7 @@ public class MvcEndpointPathConfigurationTests {
@Parameters(name = "{0}")
public static Object[] parameters() {
return new Object[] { new Object[] { "actuator", HalJsonMvcEndpoint.class },
return new Object[] {
new Object[] { "auditevents", AuditEventsMvcEndpoint.class },
new Object[] { "autoconfig", AutoConfigurationReportEndpoint.class },
new Object[] { "beans", BeansEndpoint.class },

View File

@ -24,7 +24,6 @@ import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointHandlerMappingTests;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint;
import org.springframework.boot.actuate.health.HealthIndicator;
@ -54,13 +53,6 @@ public class CloudFoundryEndpointHandlerMappingTests
assertThat(handlerMapping.getPath(testMvcEndpoint)).isEqualTo("/a");
}
@Test
public void doesNotRegisterHalJsonMvcEndpoint() throws Exception {
CloudFoundryEndpointHandlerMapping handlerMapping = new CloudFoundryEndpointHandlerMapping(
Collections.singleton(new TestHalJsonMvcEndpoint()), null, null);
assertThat(handlerMapping.getEndpoints()).hasSize(0);
}
@Test
public void registersCloudFoundryDiscoveryEndpoint() throws Exception {
StaticApplicationContext context = new StaticApplicationContext();
@ -114,14 +106,6 @@ public class CloudFoundryEndpointHandlerMappingTests
}
private static class TestHalJsonMvcEndpoint extends HalJsonMvcEndpoint {
TestHalJsonMvcEndpoint() {
super(() -> "");
}
}
private static class TestHealthMvcEndpoint extends HealthMvcEndpoint {
TestHealthMvcEndpoint(HealthEndpoint delegate) {

View File

@ -89,6 +89,7 @@ public class RequestMappingEndpointTests {
assertThat(map.get("bean")).isEqualTo("scopedTarget.mapping");
}
@SuppressWarnings("unchecked")
@Test
public void beanMethodMappings() {
StaticApplicationContext context = new StaticApplicationContext();
@ -99,14 +100,21 @@ public class RequestMappingEndpointTests {
context.getDefaultListableBeanFactory().registerSingleton("mapping", mapping);
this.endpoint.setApplicationContext(context);
Map<String, Object> result = this.endpoint.invoke();
assertThat(result).hasSize(1);
assertThat(result.keySet().iterator().next().contains("/dump")).isTrue();
@SuppressWarnings("unchecked")
Map<String, Object> handler = (Map<String, Object>) result.values().iterator()
.next();
assertThat(handler.containsKey("method")).isTrue();
assertThat(result).hasSize(2);
assertThat(result.keySet())
.filteredOn((key) -> key.contains("/dump || /dump.json"))
.hasOnlyOneElementSatisfying((key) -> {
assertThat((Map<String, Object>) result.get(key))
.containsOnlyKeys("bean", "method");
});
assertThat(result.keySet()).filteredOn((key) -> key.contains(" || /.json"))
.hasOnlyOneElementSatisfying((key) -> {
assertThat((Map<String, Object>) result.get(key))
.containsOnlyKeys("bean", "method");
});
}
@SuppressWarnings("unchecked")
@Test
public void concreteMethodMappings() {
EndpointHandlerMapping mapping = new EndpointHandlerMapping(
@ -116,12 +124,18 @@ public class RequestMappingEndpointTests {
this.endpoint.setMethodMappings(
Collections.<AbstractHandlerMethodMapping<?>>singletonList(mapping));
Map<String, Object> result = this.endpoint.invoke();
assertThat(result).hasSize(1);
assertThat(result.keySet().iterator().next().contains("/dump")).isTrue();
@SuppressWarnings("unchecked")
Map<String, Object> handler = (Map<String, Object>) result.values().iterator()
.next();
assertThat(handler.containsKey("method")).isTrue();
assertThat(result).hasSize(2);
assertThat(result.keySet())
.filteredOn((key) -> key.contains("/dump || /dump.json"))
.hasOnlyOneElementSatisfying((key) -> {
assertThat((Map<String, Object>) result.get(key))
.containsOnlyKeys("method");
});
assertThat(result.keySet()).filteredOn((key) -> key.contains(" || /.json"))
.hasOnlyOneElementSatisfying((key) -> {
assertThat((Map<String, Object>) result.get(key))
.containsOnlyKeys("method");
});
}
@Configuration

View File

@ -62,6 +62,8 @@ public class EndpointHandlerMappingTests extends AbstractEndpointHandlerMappingT
assertThat(mapping.getHandler(request("GET", "/b")).getHandler())
.isEqualTo(new HandlerMethod(endpointB, this.method));
assertThat(mapping.getHandler(request("GET", "/c"))).isNull();
assertThat((((HandlerMethod) mapping.getHandler(request("GET", "/")).getHandler())
.getMethod().getName())).isEqualTo("links");
}
@Test
@ -77,7 +79,12 @@ public class EndpointHandlerMappingTests extends AbstractEndpointHandlerMappingT
.getHandler()).isEqualTo(new HandlerMethod(endpointA, this.method));
assertThat(mapping.getHandler(new MockHttpServletRequest("GET", "/a/b"))
.getHandler()).isEqualTo(new HandlerMethod(endpointB, this.method));
assertThat(mapping.getHandler(request("GET", "/a"))).isNull();
assertThat(
(((HandlerMethod) mapping.getHandler(request("GET", "/a")).getHandler())
.getMethod().getName())).isEqualTo("links");
assertThat(
(((HandlerMethod) mapping.getHandler(request("GET", "/a/")).getHandler())
.getMethod().getName())).isEqualTo("links");
}
@Test(expected = HttpRequestMethodNotSupportedException.class)

View File

@ -1,83 +0,0 @@
/*
* Copyright 2012-2017 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.actuate.autoconfigure.MinimalActuatorHypermediaApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration tests for {@link HalBrowserMvcEndpoint}'s support for producing text/html
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(properties = "endpoints.actuator.path=/actuator")
@DirtiesContext
public class HalBrowserMvcEndpointBrowserPathIntegrationTests {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void requestWithTrailingSlashIsRedirectedToBrowserHtml() throws Exception {
this.mockMvc.perform(get("/application/actuator/").accept(MediaType.TEXT_HTML))
.andExpect(status().isFound())
.andExpect(header().string(HttpHeaders.LOCATION,
"http://localhost/application/actuator/browser.html"));
}
@Test
public void requestWithoutTrailingSlashIsRedirectedToBrowserHtml() throws Exception {
this.mockMvc.perform(get("/application/actuator").accept(MediaType.TEXT_HTML))
.andExpect(status().isFound()).andExpect(header().string("location",
"http://localhost/application/actuator/browser.html"));
}
@MinimalActuatorHypermediaApplication
@Configuration
public static class SpringBootHypermediaApplication {
}
}

View File

@ -1,112 +0,0 @@
/*
* Copyright 2012-2017 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 com.google.common.net.HttpHeaders;
import org.junit.Before;
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.MinimalActuatorHypermediaApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration tests for {@link HalBrowserMvcEndpoint}
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(properties = "management.security.enabled=false")
@DirtiesContext
public class HalBrowserMvcEndpointDisabledIntegrationTests {
@Autowired
private WebApplicationContext context;
@Autowired
private MvcEndpoints mvcEndpoints;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void linksOnActuator() throws Exception {
this.mockMvc.perform(get("/application").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists())
.andExpect(header().doesNotExist("cache-control"));
}
@Test
public void browserRedirect() throws Exception {
this.mockMvc.perform(get("/application/").accept(MediaType.TEXT_HTML))
.andExpect(status().isFound())
.andExpect(header().string(HttpHeaders.LOCATION,
"http://localhost/application/browser.html"));
}
@Test
public void endpointsDoNotHaveLinks() throws Exception {
for (MvcEndpoint endpoint : this.mvcEndpoints.getEndpoints()) {
String path = endpoint.getPath();
if ("".equals(path) || endpoint instanceof HeapdumpMvcEndpoint) {
continue;
}
path = "/application" + (path.length() > 0 ? path : "/");
MockHttpServletRequestBuilder requestBuilder = get(path);
if (endpoint instanceof AuditEventsMvcEndpoint) {
requestBuilder.param("after", "2016-01-01T12:00:00+00:00");
}
this.mockMvc.perform(requestBuilder.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links").doesNotExist());
}
}
@MinimalActuatorHypermediaApplication
@Configuration
public static class SpringBootHypermediaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHypermediaApplication.class,
"--endpoints.hypermedia.enabled=false");
}
}
}

View File

@ -1,99 +0,0 @@
/*
* Copyright 2012-2016 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.actuate.autoconfigure.MinimalActuatorHypermediaApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration tests for {@link HalBrowserMvcEndpoint} when endpoints are disabled.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(properties = "endpoints.enabled=false")
@DirtiesContext
public class HalBrowserMvcEndpointEndpointsDisabledIntegrationTests {
@Autowired
private WebApplicationContext context;
@Autowired
private MvcEndpoints mvcEndpoints;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void links() throws Exception {
this.mockMvc.perform(get("/actuator").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound());
}
@Test
public void browser() throws Exception {
this.mockMvc.perform(get("/actuator/").accept(MediaType.TEXT_HTML))
.andExpect(status().isNotFound());
}
@Test
public void trace() throws Exception {
this.mockMvc.perform(get("/trace").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound());
}
@Test
public void envValue() throws Exception {
this.mockMvc.perform(get("/env/user.home").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound());
}
@Test
public void endpointsAllDisabled() throws Exception {
assertThat(this.mvcEndpoints.getEndpoints()).isEmpty();
}
@MinimalActuatorHypermediaApplication
@Configuration
public static class SpringBootHypermediaApplication {
}
}

View File

@ -1,137 +0,0 @@
/*
* Copyright 2012-2016 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.actuate.autoconfigure.MinimalActuatorHypermediaApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import static org.hamcrest.CoreMatchers.containsString;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
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.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration tests for {@link HalBrowserMvcEndpoint} when a custom management context
* path has been configured.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(properties = { "management.contextPath:/admin",
"management.security.enabled=false" })
@DirtiesContext
public class HalBrowserMvcEndpointManagementContextPathIntegrationTests {
@Autowired
private WebApplicationContext context;
@Autowired
private MvcEndpoints mvcEndpoints;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void actuatorHomeJson() throws Exception {
this.mockMvc.perform(get("/admin").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
}
@Test
public void actuatorHomeWithTrailingSlashJson() throws Exception {
this.mockMvc.perform(get("/admin/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
}
@Test
public void actuatorHomeHtml() throws Exception {
this.mockMvc.perform(get("/admin/").accept(MediaType.TEXT_HTML))
.andExpect(status().isFound()).andExpect(header().string(
HttpHeaders.LOCATION, "http://localhost/admin/browser.html"));
}
@Test
public void actuatorBrowserHtml() throws Exception {
this.mockMvc
.perform(get("/admin/browser.html").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(containsString("entryPoint: '/admin'")));
}
@Test
public void trace() throws Exception {
this.mockMvc.perform(get("/admin/trace").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").doesNotExist())
.andExpect(jsonPath("$").isArray());
}
@Test
public void endpointsAllListed() throws Exception {
for (MvcEndpoint endpoint : this.mvcEndpoints.getEndpoints()) {
String path = endpoint.getPath();
if ("/actuator".equals(path)) {
continue;
}
path = path.startsWith("/") ? path.substring(1) : path;
path = path.length() > 0 ? path : "self";
this.mockMvc.perform(get("/admin").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links.%s.href", path)
.value("http://localhost/admin" + endpoint.getPath()));
}
}
@MinimalActuatorHypermediaApplication
@RestController
public static class SpringBootHypermediaApplication {
@RequestMapping("")
public ResourceSupport home() {
ResourceSupport resource = new ResourceSupport();
resource.add(linkTo(SpringBootHypermediaApplication.class).slash("/")
.withSelfRel());
return resource;
}
}
}

View File

@ -1,131 +0,0 @@
/*
* Copyright 2012-2017 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.net.URI;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.actuate.autoconfigure.MinimalActuatorHypermediaApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
/**
* Integration tests for {@link HalBrowserMvcEndpoint} when a custom server context path
* has been configured.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"server.servlet.contextPath=/spring" })
@DirtiesContext
public class HalBrowserMvcEndpointServerContextPathIntegrationTests {
@LocalServerPort
private int port;
@Test
public void linksAddedToHomePage() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/spring/", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"_links\":");
}
@Test
public void actuatorBrowserRedirect() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/spring/application/", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
assertThat(entity.getHeaders().getLocation()).isEqualTo(URI.create(
"http://localhost:" + this.port + "/spring/application/browser.html"));
}
@Test
public void actuatorBrowserEntryPoint() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/spring/application/browser.html",
HttpMethod.GET, new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("entryPoint: '/spring/application'");
}
@Test
public void actuatorLinks() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/spring/application", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"_links\":");
}
@Test
public void actuatorLinksWithTrailingSlash() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/spring/application/", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"_links\":");
}
@MinimalActuatorHypermediaApplication
@RestController
public static class SpringBootHypermediaApplication {
@RequestMapping("")
public ResourceSupport home() {
ResourceSupport resource = new ResourceSupport();
resource.add(linkTo(SpringBootHypermediaApplication.class).slash("/")
.withSelfRel());
return resource;
}
}
}

View File

@ -1,111 +0,0 @@
/*
* Copyright 2012-2017 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.net.URI;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.MinimalActuatorHypermediaApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
/**
* Integration tests for {@link HalBrowserMvcEndpoint} when a custom server port has been
* configured.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"management.port=0" })
@DirtiesContext
public class HalBrowserMvcEndpointServerPortIntegrationTests {
@Value("${local.management.port}")
private int port;
@Test
public void links() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/application", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"_links\":");
assertThat(entity.getBody()).contains(":" + this.port);
}
@Test
public void linksWithTrailingSlash() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/application/", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"_links\":");
assertThat(entity.getBody()).contains(":" + this.port);
}
@Test
public void browserRedirect() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/application/", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
assertThat(entity.getHeaders().getLocation()).isEqualTo(URI
.create("http://localhost:" + this.port + "/application/browser.html"));
}
@MinimalActuatorHypermediaApplication
@RestController
public static class SpringBootHypermediaApplication {
@RequestMapping("")
public ResourceSupport home() {
ResourceSupport resource = new ResourceSupport();
resource.add(linkTo(SpringBootHypermediaApplication.class).slash("/")
.withSelfRel());
return resource;
}
}
}

View File

@ -1,131 +0,0 @@
/*
* Copyright 2012-2017 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.net.URI;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.actuate.autoconfigure.MinimalActuatorHypermediaApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
/**
* Integration tests for {@link HalBrowserMvcEndpoint} when a custom server servlet path
* has been configured.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"server.servlet.path=/spring" })
@DirtiesContext
public class HalBrowserMvcEndpointServerServletPathIntegrationTests {
@LocalServerPort
private int port;
@Test
public void linksAddedToHomePage() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/spring/", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"_links\":");
}
@Test
public void actuatorBrowserRedirect() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/spring/application/", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
assertThat(entity.getHeaders().getLocation()).isEqualTo(URI.create(
"http://localhost:" + this.port + "/spring/application/browser.html"));
}
@Test
public void actuatorBrowserEntryPoint() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/spring/application/browser.html",
HttpMethod.GET, new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("entryPoint: '/spring/application'");
}
@Test
public void actuatorLinks() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/spring/application", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"_links\":");
}
@Test
public void actuatorLinksWithTrailingSlash() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = new TestRestTemplate().exchange(
"http://localhost:" + this.port + "/spring/application/", HttpMethod.GET,
new HttpEntity<Void>(null, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"_links\":");
}
@MinimalActuatorHypermediaApplication
@RestController
public static class SpringBootHypermediaApplication {
@RequestMapping("")
public ResourceSupport home() {
ResourceSupport resource = new ResourceSupport();
resource.add(linkTo(SpringBootHypermediaApplication.class).slash("/")
.withSelfRel());
return resource;
}
}
}

View File

@ -1,145 +0,0 @@
/*
* Copyright 2012-2017 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.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.MinimalActuatorHypermediaApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration tests for {@link HalBrowserMvcEndpoint}
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@DirtiesContext
@TestPropertySource(properties = { "endpoints.hypermedia.enabled=true",
"management.security.enabled=false" })
public class HalBrowserMvcEndpointVanillaIntegrationTests {
@Autowired
private WebApplicationContext context;
@Autowired
private MvcEndpoints mvcEndpoints;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void links() throws Exception {
this.mockMvc.perform(get("/application").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists())
.andExpect(header().doesNotExist("cache-control"));
}
@Test
public void linksWithTrailingSlash() throws Exception {
this.mockMvc.perform(get("/application/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists())
.andExpect(header().doesNotExist("cache-control"));
}
@Test
public void browser() throws Exception {
this.mockMvc.perform(get("/application/").accept(MediaType.TEXT_HTML))
.andExpect(status().isFound())
.andExpect(header().string(HttpHeaders.LOCATION,
"http://localhost/application/browser.html"));
}
@Test
public void trace() throws Exception {
this.mockMvc.perform(get("/application/trace").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").doesNotExist())
.andExpect(jsonPath("$").isArray());
}
@Test
public void envValue() throws Exception {
this.mockMvc
.perform(get("/application/env/user.home")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links").doesNotExist());
}
@Test
public void endpointsAllListed() throws Exception {
for (MvcEndpoint endpoint : this.mvcEndpoints.getEndpoints()) {
String path = endpoint.getPath();
if ("/application".equals(path)) {
continue;
}
path = path.startsWith("/") ? path.substring(1) : path;
this.mockMvc.perform(get("/application").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links.%s.href", path).exists());
}
}
@Test
public void endpointsEachHaveSelf() throws Exception {
Set<String> collections = new HashSet<>(Arrays.asList("/trace", "/beans", "/dump",
"/heapdump", "/loggers", "/auditevents"));
for (MvcEndpoint endpoint : this.mvcEndpoints.getEndpoints()) {
String path = endpoint.getPath();
if (collections.contains(path)) {
continue;
}
path = "/application" + (path.length() > 0 ? path : "/");
this.mockMvc.perform(get(path).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links.self.href")
.value("http://localhost/application" + endpoint.getPath()));
}
}
@MinimalActuatorHypermediaApplication
@Configuration
public static class SpringBootHypermediaApplication {
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright 2012-2017 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.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link HalJsonMvcEndpoint}.
*
* @author Madhura Bhave
*/
public class HalJsonMvcEndpointTests {
@Mock
ManagementServletContext managementServletContext;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void halJsonEndpointPathWhenManagementContextPathPresent() throws Exception {
Mockito.when(this.managementServletContext.getContextPath()).thenReturn("my-app");
HalJsonMvcEndpoint endpoint = new HalJsonMvcEndpoint(
this.managementServletContext);
assertThat(endpoint.getPath()).isEqualTo("");
}
@Test
public void halJsonEndpointPathWhenManagementContextPathNotPresent()
throws Exception {
Mockito.when(this.managementServletContext.getContextPath()).thenReturn("");
HalJsonMvcEndpoint endpoint = new HalJsonMvcEndpoint(
this.managementServletContext);
assertThat(endpoint.getPath()).isEqualTo("/application");
}
}

View File

@ -1085,9 +1085,6 @@ content into your application; rather pick only the properties that you need.
# ENDPOINTS ({sc-spring-boot-actuator}/endpoint/AbstractEndpoint.{sc-ext}[AbstractEndpoint] subclasses)
endpoints.enabled=true # Enable endpoints.
endpoints.sensitive= # Default endpoint sensitive setting.
endpoints.actuator.enabled=true # Enable the endpoint.
endpoints.actuator.path= # Endpoint URL path.
endpoints.actuator.sensitive=false # Enable security on the endpoint.
endpoints.auditevents.enabled= # Enable the endpoint.
endpoints.auditevents.path= # Endpoint path.
endpoints.auditevents.sensitive=false # Enable security on the endpoint.
@ -1129,7 +1126,6 @@ content into your application; rather pick only the properties that you need.
endpoints.heapdump.enabled= # Enable the endpoint.
endpoints.heapdump.path= # Endpoint path.
endpoints.heapdump.sensitive= # Mark if the endpoint exposes sensitive information.
endpoints.hypermedia.enabled=false # Enable hypermedia support for endpoints.
endpoints.info.enabled= # Enable the endpoint.
endpoints.info.id= # Endpoint identifier.
endpoints.info.path= # Endpoint path.

View File

@ -69,11 +69,6 @@ The following technology agnostic endpoints are available:
|===
| ID | Description | Sensitive Default
|`actuator`
|Provides a hypermedia-based "`discovery page`" for the other endpoints. Requires Spring
HATEOAS to be on the classpath.
|true
|`auditevents`
|Exposes audit events information for the current application.
|true
@ -207,25 +202,14 @@ For example, to mark _all_ endpoints as sensitive except `info`:
[[production-ready-endpoint-hypermedia]]
=== Hypermedia for actuator MVC endpoints
If `endpoints.hypermedia.enabled` is set to `true` and
http://projects.spring.io/spring-hateoas[Spring HATEOAS] is on the classpath (e.g.
through the `spring-boot-starter-hateoas` or if you are using
http://projects.spring.io/spring-data-rest[Spring Data REST]) then the HTTP endpoints
from the Actuator are enhanced with hypermedia links, and a "`discovery page`" is added
with links to all the endpoints. The "`discovery page`" is available on `/application` by
default. It is implemented as an endpoint, allowing properties to be used to configure
its path (`endpoints.actuator.path`) and whether or not it is enabled
(`endpoints.actuator.enabled`).
A "`discovery page`" is added with links to all the endpoints. The "`discovery page`" is
available on `/application` by default.
When a custom management context path is configured, the "`discovery page`" will
automatically move from `/application` to the root of the management context. For example,
if the management context path is `/management` then the discovery page will be available
from `/management`.
If the https://github.com/mikekelly/hal-browser[HAL Browser] is on the classpath
via its webjar (`org.webjars:hal-browser`), or via the `spring-data-rest-hal-browser` then
an HTML "`discovery page`", in the form of the HAL Browser, is also provided.
[[production-ready-endpoint-cors]]

View File

@ -80,22 +80,6 @@ The following sample applications are provided:
| link:spring-boot-sample-hateoas[spring-boot-sample-hateoas]
| RESTful API built using Spring Hateoas
| link:spring-boot-sample-hypermedia[spring-boot-sample-hypermedia]
| Demonstrates Actuator's hypermedia support, including HAL Browser
| link:spring-boot-sample-hypermedia-gson[spring-boot-sample-hypermedia-gson]
| Demonstrates Actuator's hypermedia support using GSON in place of Jackson
| link:spring-boot-sample-hypermedia-jpa[spring-boot-sample-hypermedia-jpa]
| Demonstrates Actuator's hypermedia support alongside Spring Data JPA and Spring Data
REST
| link:spring-boot-sample-hypermedia-ui-secure[spring-boot-sample-hypermedia-ui-secure]
| Demonstrates Actuator's hypermedia support alongside a static secure web UI
| link:spring-boot-sample-hypermedia-ui[spring-boot-sample-hypermedia-ui]
| Demonstrates Actuator's hypermedia support alongside a static web UI
| link:spring-boot-sample-integration[spring-boot-sample-integration]
| Integration application built using Spring Integration and its Java DSL

View File

@ -46,11 +46,6 @@
<module>spring-boot-sample-devtools</module>
<module>spring-boot-sample-flyway</module>
<module>spring-boot-sample-hateoas</module>
<module>spring-boot-sample-hypermedia</module>
<module>spring-boot-sample-hypermedia-gson</module>
<module>spring-boot-sample-hypermedia-jpa</module>
<module>spring-boot-sample-hypermedia-ui</module>
<module>spring-boot-sample-hypermedia-ui-secure</module>
<module>spring-boot-sample-integration</module>
<module>spring-boot-sample-jersey</module>
<module>spring-boot-sample-jersey1</module>

View File

@ -28,10 +28,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>

View File

@ -1,7 +1,7 @@
# logging.file=/tmp/logs/app.log
# logging.level.org.springframework.security=DEBUG
management.address=127.0.0.1
#management.port=8181
management.context-path=/
management.info.build.mode=full
@ -20,3 +20,5 @@ management.trace.include=REQUEST_HEADERS,RESPONSE_HEADERS,ERRORS,PATH_INFO,\
PATH_TRANSLATED,CONTEXT_PATH,USER_PRINCIPAL,PARAMETERS,QUERY_STRING,AUTH_TYPE,\
REMOTE_ADDRESS,SESSION_ID,REMOTE_USER
management.security.enabled = false

View File

@ -1,54 +0,0 @@
<?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>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-hypermedia-gson</artifactId>
<name>Spring Boot Hypermedia GSON Sample</name>
<description>Spring Boot Hypermedia GSON Sample</description>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,29 +0,0 @@
/*
* Copyright 2012-2015 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 sample.hypermedia.gson;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SampleHypermediaGsonApplication {
public static void main(String[] args) {
SpringApplication.run(SampleHypermediaGsonApplication.class, args);
}
}

View File

@ -1,4 +0,0 @@
# management.context-path=/admin
management.security.enabled=false
spring.http.converters.preferred-json-mapper=gson

View File

@ -1,73 +0,0 @@
/*
* Copyright 2012-2017 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 sample.hypermedia.gson;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(properties = "endpoints.hypermedia.enabled: true")
public class SampleHypermediaGsonApplicationTests {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void health() throws Exception {
this.mockMvc.perform(get("/application/health").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.links[0].href").value("http://localhost/application/health"))
.andExpect(jsonPath("$.content.status").exists());
}
@Test
public void trace() throws Exception {
this.mockMvc.perform(get("/application/trace").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$.links").doesNotExist())
.andExpect(jsonPath("$").isArray());
}
@Test
public void envValue() throws Exception {
this.mockMvc.perform(get("/application/env/user.home").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links").doesNotExist());
}
}

View File

@ -1,66 +0,0 @@
<?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>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-hypermedia-jpa</artifactId>
<name>Spring Boot Hypermedia JPA Sample</name>
<description>Spring Boot Hypermedia JPA Sample</description>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-hal-browser</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,40 +0,0 @@
/*
* Copyright 2012-2015 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 sample.hypermedia.jpa;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
private String title;
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
}

View File

@ -1,23 +0,0 @@
/*
* Copyright 2012-2015 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 sample.hypermedia.jpa;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book, Long> {
}

View File

@ -1,29 +0,0 @@
/*
* Copyright 2012-2015 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 sample.hypermedia.jpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SampleHypermediaJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SampleHypermediaJpaApplication.class, args);
}
}

View File

@ -1,2 +0,0 @@
management.context-path=/admin
endpoints.docs.curies.enabled=true

View File

@ -1,70 +0,0 @@
/*
* Copyright 2012-2016 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 sample.hypermedia.jpa;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SampleHypermediaJpaApplicationIntegrationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void links() throws Exception {
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
}
@Test
public void health() throws Exception {
this.mockMvc.perform(get("/admin/health").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$._links").doesNotExist());
}
@Test
public void adminLinks() throws Exception {
this.mockMvc.perform(get("/admin").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$._links").exists());
}
@Test
public void browser() throws Exception {
MvcResult response = this.mockMvc.perform(get("/").accept(MediaType.TEXT_HTML))
.andExpect(status().isFound()).andReturn();
assertThat(response.getResponse().getHeaders("location").get(0))
.isEqualTo("http://localhost/browser/index.html#/");
}
}

View File

@ -1,49 +0,0 @@
<?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">
<parent>
<artifactId>spring-boot-samples</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-sample-hypermedia-ui-secure</artifactId>
<name>Spring Boot Hypermedia UI Secure Sample</name>
<description>Spring Boot Hypermedia UI Secure Sample</description>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,29 +0,0 @@
/*
* Copyright 2012-2017 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 sample.hypermedia.ui.secure;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SampleHypermediaUiSecureApplication {
public static void main(String[] args) {
SpringApplication.run(SampleHypermediaUiSecureApplication.class, args);
}
}

View File

@ -1,5 +0,0 @@
<html>
<body>
<h1>Hello World!</h1>
</body>
</html>

View File

@ -1,62 +0,0 @@
/*
* Copyright 2012-2017 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 sample.hypermedia.ui.secure;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
"endpoints.env.sensitive=false", "foo=bar" })
public class SampleHypermediaUiSecureApplicationTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void links() {
String response = this.restTemplate.getForObject("/application", String.class);
assertThat(response).contains("\"_links\":");
}
@Test
public void testInsecureNestedPath() throws Exception {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/application/env",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
ResponseEntity<String> user = this.restTemplate
.getForEntity("/application/env/foo", String.class);
assertThat(user.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(user.getBody()).contains("{\"foo\":");
}
@Test
public void testSecurePath() throws Exception {
ResponseEntity<String> entity = this.restTemplate
.getForEntity("/application/metrics", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
}

View File

@ -1,52 +0,0 @@
/*
* Copyright 2012-2017 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 sample.hypermedia.ui.secure;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
"management.context-path=/admin" })
public class SampleHypermediaUiSecureApplicationWithContextPathTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void links() {
String response = this.restTemplate.getForObject("/admin", String.class);
assertThat(response).contains("\"_links\":");
}
@Test
public void testSecurePath() throws Exception {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/admin/metrics",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
}

View File

@ -1,45 +0,0 @@
<?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>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-hypermedia-ui</artifactId>
<name>Spring Boot Hypermedia UI Sample</name>
<description>Spring Boot Hypermedia UI Sample</description>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,28 +0,0 @@
/*
* Copyright 2012-2015 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 sample.hypermedia.ui;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SampleHypermediaUiApplication {
public static void main(String[] args) {
SpringApplication.run(SampleHypermediaUiApplication.class, args);
}
}

View File

@ -1,3 +0,0 @@
management.security.enabled=false
management.context-path=/admin

View File

@ -1,5 +0,0 @@
<html>
<body>
<h1>Hello World!</h1>
</body>
</html>

View File

@ -1,75 +0,0 @@
/*
* Copyright 2012-2017 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 sample.hypermedia.ui;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"management.context-path=" })
public class SampleHypermediaUiApplicationTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void home() {
String response = this.restTemplate.getForObject("/", String.class);
assertThat(response).contains("Hello World");
}
@Test
public void links() {
String response = this.restTemplate.getForObject("/application", String.class);
assertThat(response).contains("\"_links\":");
}
@Test
public void linksWithJson() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> response = this.restTemplate.exchange("/application",
HttpMethod.GET, new HttpEntity<Void>(headers), String.class);
assertThat(response.getBody()).contains("\"_links\":");
}
@Test
public void homeWithHtml() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> response = this.restTemplate.exchange("/", HttpMethod.GET,
new HttpEntity<Void>(headers), String.class);
assertThat(response.getBody()).contains("Hello World");
}
}

View File

@ -1,50 +0,0 @@
<?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>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-hypermedia</artifactId>
<name>Spring Boot Hypermedia Sample</name>
<description>Spring Boot Hypermedia Sample</description>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>hal-browser</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,29 +0,0 @@
/*
* Copyright 2012-2015 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 sample.hypermedia;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SampleHypermediaApplication {
public static void main(String[] args) {
SpringApplication.run(SampleHypermediaApplication.class, args);
}
}

View File

@ -1,4 +0,0 @@
# management.context-path=/admin
management.security.enabled=false
endpoints.docs.curies.enabled=true

View File

@ -1,68 +0,0 @@
/*
* Copyright 2012-2017 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 sample.hypermedia;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SampleHypermediaApplicationHomePageTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void home() {
String response = this.restTemplate.getForObject("/", String.class);
assertThat(response).contains("404");
}
@Test
public void linksWithJson() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> response = this.restTemplate.exchange("/application",
HttpMethod.GET, new HttpEntity<Void>(headers), String.class);
assertThat(response.getBody()).contains("\"_links\":");
}
@Test
public void halWithHtml() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> response = this.restTemplate.exchange("/application/",
HttpMethod.GET, new HttpEntity<Void>(headers), String.class);
assertThat(response.getBody()).contains("HAL Browser");
}
}