Commit 97255785 authored by Dave Syer's avatar Dave Syer

All MVC endpoint paths to be separately customized from the id

This change applies only to "standard" MVC endpoints (not the extended
ones like /env and /jolokia which already have this feature). Allows
users to supply an endpoints.{name}.path.

Fixes gh-2790
parent 434d46f5
......@@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
public class EndpointMvcAdapter implements MvcEndpoint {
private final Endpoint<?> delegate;
private String path;
/**
* Create a new {@link EndpointMvcAdapter}.
......@@ -58,7 +59,17 @@ public class EndpointMvcAdapter implements MvcEndpoint {
@Override
public String getPath() {
return "/" + this.delegate.getId();
return this.path != null ? this.path : "/" + this.delegate.getId();
}
public void setPath(String path) {
if (!path.startsWith("/")) {
path = "/" + path;
}
while (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
this.path = path;
}
@Override
......
......@@ -58,6 +58,8 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware {
private Health cached;
private String path;
public HealthMvcEndpoint(HealthEndpoint delegate) {
this(delegate, true);
}
......@@ -125,8 +127,9 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware {
public Object invoke(Principal principal) {
if (!this.delegate.isEnabled()) {
// Shouldn't happen because the request mapping should not be registered
return new ResponseEntity<Map<String, String>>(Collections.singletonMap(
"message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
return new ResponseEntity<Map<String, String>>(
Collections.singletonMap("message", "This endpoint is disabled"),
HttpStatus.NOT_FOUND);
}
Health health = getHealth(principal);
HttpStatus status = getStatus(health);
......@@ -174,8 +177,8 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware {
}
private boolean isSecure(Principal principal) {
return (principal != null && !principal.getClass().getName()
.contains("Anonymous"));
return (principal != null
&& !principal.getClass().getName().contains("Anonymous"));
}
private boolean isUnrestricted() {
......@@ -185,7 +188,17 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware {
@Override
public String getPath() {
return "/" + this.delegate.getId();
return this.path != null ? this.path : "/" + this.delegate.getId();
}
public void setPath(String path) {
if (!path.startsWith("/")) {
path = "/" + path;
}
while (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
this.path = path;
}
@Override
......
......@@ -58,11 +58,18 @@ public class MvcEndpoints implements ApplicationContextAware, InitializingBean {
this.endpoints.addAll(existing);
this.customTypes = findEndpointClasses(existing);
@SuppressWarnings("rawtypes")
Collection<Endpoint> delegates = BeanFactoryUtils.beansOfTypeIncludingAncestors(
this.applicationContext, Endpoint.class).values();
Collection<Endpoint> delegates = BeanFactoryUtils
.beansOfTypeIncludingAncestors(this.applicationContext, Endpoint.class)
.values();
for (Endpoint<?> endpoint : delegates) {
if (isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()) {
this.endpoints.add(new EndpointMvcAdapter(endpoint));
EndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint);
String path = this.applicationContext.getEnvironment()
.getProperty("endpoints." + endpoint.getId() + ".path");
if (path != null) {
adapter.setPath(path);
}
this.endpoints.add(adapter);
}
}
}
......
......@@ -18,6 +18,7 @@ package org.springframework.boot.actuate.endpoint.mvc;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.support.StaticApplicationContext;
import static org.junit.Assert.assertEquals;
......@@ -62,6 +63,19 @@ public class MvcEndpointsTests {
assertEquals(1, this.endpoints.getEndpoints().size());
}
@Test
public void changesPath() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"endpoints.test.path=/foo/bar/");
this.context.getDefaultListableBeanFactory().registerSingleton("endpoint",
new TestEndpoint());
this.endpoints.setApplicationContext(this.context);
this.endpoints.afterPropertiesSet();
assertEquals(1, this.endpoints.getEndpoints().size());
assertEquals("/foo/bar",
this.endpoints.getEndpoints().iterator().next().getPath());
}
protected static class TestEndpoint extends AbstractEndpoint<String> {
public TestEndpoint() {
......
......@@ -166,8 +166,6 @@ For example, the following will disable _all_ endpoints except for `info`:
endpoints.info.enabled=true
----
[[production-ready-endpoint-hypermedia]]
=== Hypermedia for actuator MVC endpoints
If http://projects.spring.io/spring-hateoas[Spring HATEOAS] is on the classpath (e.g.
......@@ -502,7 +500,7 @@ you should carefully consider which endpoints you enable. See
[[production-ready-customizing-management-server-context-path]]
=== Customizing the management server context path
=== Customizing the management endpoint paths
Sometimes it is useful to group all management endpoints under a single path. For example,
your application might already use `/info` for another purpose. You can use the
`management.context-path` property to set a prefix for your management endpoint:
......@@ -515,6 +513,23 @@ your application might already use `/info` for another purpose. You can use the
The `application.properties` example above will change the endpoint from `/{id}` to
`/manage/{id}` (e.g. `/manage/info`).
You can also change the "id" of an endpoint (using
`endpoints.{name}.id`) which then changes the default resource path
for the MVC endpoint. Legal endpoint ids are composed only of
alphanumeric characters (because they can be exposed in a number of
places, including JMX object names, where special characters are
forbidden). The MVC path can be changed separately by configuring
`endpoints.{name}.path`, and there is no validation on those values
(so you can use anything that is legel in a URL path). For example, to
change the location of the `/health` endpoint to `/ping/me` you can
set `endpoints.health.path=/ping/me`.
TIP: If you provide a custom `MvcEndpoint` remember to include a
settable `path` property, and default it to `/{id}` if you want your
code to behave like the standard MVC endpoints. (Take a look at the
`HealthMvcEndpoint` to see how you might do that.) If your custom
endpoint is an `Endpoint` (not an `MvcEndpoint`) then Spring Boot will
take care of the path for you.
[[production-ready-customizing-management-server-port]]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment