diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointMvcAdapter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointMvcAdapter.java index a44815e9f0..11fb145689 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointMvcAdapter.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointMvcAdapter.java @@ -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 diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java index 1dec124911..770774a4bf 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HealthMvcEndpoint.java @@ -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>(Collections.singletonMap( - "message", "This endpoint is disabled"), HttpStatus.NOT_FOUND); + return new ResponseEntity>( + 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 diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoints.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoints.java index a437a6da35..de0e9fa0bf 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoints.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpoints.java @@ -58,11 +58,18 @@ public class MvcEndpoints implements ApplicationContextAware, InitializingBean { this.endpoints.addAll(existing); this.customTypes = findEndpointClasses(existing); @SuppressWarnings("rawtypes") - Collection delegates = BeanFactoryUtils.beansOfTypeIncludingAncestors( - this.applicationContext, Endpoint.class).values(); + Collection 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); } } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointsTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointsTests.java index af4fcc4bd2..d3d8bce645 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointsTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointsTests.java @@ -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 { public TestEndpoint() { diff --git a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index a477763be0..db667ac3c9 100644 --- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -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]]