Merge branch '2.0.x'

pull/14822/head
Phillip Webb 6 years ago
commit 1bf97e9b5e

@ -40,19 +40,19 @@ public enum AccessLevel {
public static final String REQUEST_ATTRIBUTE = "cloudFoundryAccessLevel";
private final List<String> endpointIds;
private final List<String> ids;
AccessLevel(String... endpointIds) {
this.endpointIds = Arrays.asList(endpointIds);
AccessLevel(String... ids) {
this.ids = Arrays.asList(ids);
}
/**
* Returns if the access level should allow access to the specified endpoint path.
* @param endpointId the endpoint ID to check
* Returns if the access level should allow access to the specified ID.
* @param id the ID to check
* @return {@code true} if access is allowed
*/
public boolean isAccessAllowed(String endpointId) {
return this.endpointIds.isEmpty() || this.endpointIds.contains(endpointId);
public boolean isAccessAllowed(String id) {
return this.ids.isEmpty() || this.ids.contains(id);
}
}

@ -59,7 +59,7 @@ class CloudFoundrySecurityInterceptor {
this.applicationId = applicationId;
}
Mono<SecurityResponse> preHandle(ServerWebExchange exchange, String endpointId) {
Mono<SecurityResponse> preHandle(ServerWebExchange exchange, String id) {
ServerHttpRequest request = exchange.getRequest();
if (CorsUtils.isPreFlightRequest(request)) {
return SUCCESS;
@ -72,7 +72,7 @@ class CloudFoundrySecurityInterceptor {
return Mono.error(new CloudFoundryAuthorizationException(
Reason.SERVICE_UNAVAILABLE, "Cloud controller URL is not available"));
}
return check(exchange, endpointId).then(SUCCESS).doOnError(this::logError)
return check(exchange, id).then(SUCCESS).doOnError(this::logError)
.onErrorResume(this::getErrorResponse);
}
@ -80,13 +80,13 @@ class CloudFoundrySecurityInterceptor {
logger.error(ex.getMessage(), ex);
}
private Mono<Void> check(ServerWebExchange exchange, String path) {
private Mono<Void> check(ServerWebExchange exchange, String id) {
try {
Token token = getToken(exchange.getRequest());
return this.tokenValidator.validate(token)
.then(this.cloudFoundrySecurityService
.getAccessLevel(token.toString(), this.applicationId))
.filter((accessLevel) -> accessLevel.isAccessAllowed(path))
.filter((accessLevel) -> accessLevel.isAccessAllowed(id))
.switchIfEmpty(Mono.error(new CloudFoundryAuthorizationException(
Reason.ACCESS_DENIED, "Access denied")))
.doOnSuccess((accessLevel) -> exchange.getAttributes()

@ -27,6 +27,7 @@ import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
@ -70,7 +71,7 @@ class CloudFoundryWebFluxEndpointHandlerMapping
protected ReactiveWebOperation wrapReactiveWebOperation(ExposableWebEndpoint endpoint,
WebOperation operation, ReactiveWebOperation reactiveWebOperation) {
return new SecureReactiveWebOperation(reactiveWebOperation,
this.securityInterceptor, endpoint.getId());
this.securityInterceptor, endpoint.getEndpointId());
}
@Override
@ -113,10 +114,11 @@ class CloudFoundryWebFluxEndpointHandlerMapping
private final CloudFoundrySecurityInterceptor securityInterceptor;
private final String endpointId;
private final EndpointId endpointId;
SecureReactiveWebOperation(ReactiveWebOperation delegate,
CloudFoundrySecurityInterceptor securityInterceptor, String endpointId) {
CloudFoundrySecurityInterceptor securityInterceptor,
EndpointId endpointId) {
this.delegate = delegate;
this.securityInterceptor = securityInterceptor;
this.endpointId = endpointId;
@ -125,7 +127,8 @@ class CloudFoundryWebFluxEndpointHandlerMapping
@Override
public Mono<ResponseEntity<Object>> handle(ServerWebExchange exchange,
Map<String, String> body) {
return this.securityInterceptor.preHandle(exchange, this.endpointId)
return this.securityInterceptor
.preHandle(exchange, this.endpointId.toLowerCaseString())
.flatMap((securityResponse) -> flatMapResponse(exchange, body,
securityResponse));
}

@ -28,6 +28,7 @@ import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryA
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.Token;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
@ -59,7 +60,7 @@ class CloudFoundrySecurityInterceptor {
this.applicationId = applicationId;
}
SecurityResponse preHandle(HttpServletRequest request, String endpointId) {
SecurityResponse preHandle(HttpServletRequest request, EndpointId endpointId) {
if (CorsUtils.isPreFlightRequest(request)) {
return SecurityResponse.success();
}
@ -90,12 +91,14 @@ class CloudFoundrySecurityInterceptor {
return SecurityResponse.success();
}
private void check(HttpServletRequest request, String endpointId) throws Exception {
private void check(HttpServletRequest request, EndpointId endpointId)
throws Exception {
Token token = getToken(request);
this.tokenValidator.validate(token);
AccessLevel accessLevel = this.cloudFoundrySecurityService
.getAccessLevel(token.toString(), this.applicationId);
if (!accessLevel.isAccessAllowed(endpointId)) {
if (!accessLevel.isAccessAllowed(
(endpointId != null) ? endpointId.toLowerCaseString() : "")) {
throw new CloudFoundryAuthorizationException(Reason.ACCESS_DENIED,
"Access denied");
}

@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
@ -68,7 +69,7 @@ class CloudFoundryWebEndpointServletHandlerMapping
protected ServletWebOperation wrapServletWebOperation(ExposableWebEndpoint endpoint,
WebOperation operation, ServletWebOperation servletWebOperation) {
return new SecureServletWebOperation(servletWebOperation,
this.securityInterceptor, endpoint.getId());
this.securityInterceptor, endpoint.getEndpointId());
}
@Override
@ -76,7 +77,7 @@ class CloudFoundryWebEndpointServletHandlerMapping
protected Map<String, Map<String, Link>> links(HttpServletRequest request,
HttpServletResponse response) {
SecurityResponse securityResponse = this.securityInterceptor.preHandle(request,
"");
null);
if (!securityResponse.getStatus().equals(HttpStatus.OK)) {
sendFailureResponse(response, securityResponse);
}
@ -115,10 +116,11 @@ class CloudFoundryWebEndpointServletHandlerMapping
private final CloudFoundrySecurityInterceptor securityInterceptor;
private final String endpointId;
private final EndpointId endpointId;
SecureServletWebOperation(ServletWebOperation delegate,
CloudFoundrySecurityInterceptor securityInterceptor, String endpointId) {
CloudFoundrySecurityInterceptor securityInterceptor,
EndpointId endpointId) {
this.delegate = delegate;
this.securityInterceptor = securityInterceptor;
this.endpointId = endpointId;

@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.endpoint;
import java.time.Duration;
import java.util.function.Function;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Bindable;
@ -33,7 +34,7 @@ import org.springframework.core.env.PropertyResolver;
* @author Stephane Nicoll
* @author Phillip Webb
*/
class EndpointIdTimeToLivePropertyFunction implements Function<String, Long> {
class EndpointIdTimeToLivePropertyFunction implements Function<EndpointId, Long> {
private static final Bindable<Duration> DURATION = Bindable.of(Duration.class);
@ -48,9 +49,9 @@ class EndpointIdTimeToLivePropertyFunction implements Function<String, Long> {
}
@Override
public Long apply(String endpointId) {
public Long apply(EndpointId endpointId) {
String name = String.format("management.endpoint.%s.cache.time-to-live",
endpointId);
endpointId.toLowerCaseString());
BindResult<Duration> duration = Binder.get(this.environment).bind(name, DURATION);
return duration.map(Duration::toMillis).orElse(null);
}

@ -20,11 +20,13 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.boot.actuate.endpoint.EndpointFilter;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
@ -73,10 +75,19 @@ public class ExposeExcludePropertyEndpointFilter<E extends ExposableEndpoint<?>>
}
private Set<String> bind(Binder binder, String name) {
return asSet(binder.bind(name, Bindable.listOf(String.class))
return asSet(binder.bind(name, Bindable.listOf(String.class)).map(this::cleanup)
.orElseGet(ArrayList::new));
}
private List<String> cleanup(List<String> values) {
return values.stream().map(this::cleanup).collect(Collectors.toList());
}
private String cleanup(String value) {
return "*".equals(value) ? "*"
: EndpointId.fromPropertyValue(value).toLowerCaseString();
}
private Set<String> asSet(Collection<String> items) {
if (items == null) {
return Collections.emptySet();
@ -109,7 +120,7 @@ public class ExposeExcludePropertyEndpointFilter<E extends ExposableEndpoint<?>>
}
private boolean contains(Set<String> items, ExposableEndpoint<?> endpoint) {
return items.contains(endpoint.getId().toLowerCase(Locale.ENGLISH));
return items.contains(endpoint.getEndpointId().toLowerCaseString());
}
}

@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.condition;
import java.util.Map;
import java.util.Optional;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
@ -54,8 +55,8 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
AnnotationAttributes attributes = getEndpointAttributes(context, metadata);
String id = attributes.getString("id");
String key = "management.endpoint." + id + ".enabled";
EndpointId id = EndpointId.of(attributes.getString("id"));
String key = "management.endpoint." + id.toLowerCaseString() + ".enabled";
Boolean userDefinedEnabled = environment.getProperty(key, Boolean.class);
if (userDefinedEnabled != null) {
return new ConditionOutcome(userDefinedEnabled,

@ -72,7 +72,8 @@ class DefaultEndpointObjectNameFactory implements EndpointObjectNameFactory {
throws MalformedObjectNameException {
StringBuilder builder = new StringBuilder(this.properties.getDomain());
builder.append(":type=Endpoint");
builder.append(",name=").append(StringUtils.capitalize(endpoint.getId()));
builder.append(",name=")
.append(StringUtils.capitalize(endpoint.getEndpointId().toString()));
String baseName = builder.toString();
if (this.mBeanServer != null && hasMBean(baseName)) {
builder.append(",context=").append(this.contextId);

@ -16,8 +16,10 @@
package org.springframework.boot.actuate.autoconfigure.endpoint.web;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.web.PathMapper;
/**
@ -28,15 +30,23 @@ import org.springframework.boot.actuate.endpoint.web.PathMapper;
*/
class MappingWebEndpointPathMapper implements PathMapper {
private final Map<String, String> pathMapping;
private final Map<EndpointId, String> pathMapping;
MappingWebEndpointPathMapper(Map<String, String> pathMapping) {
this.pathMapping = pathMapping;
this.pathMapping = new HashMap<>();
pathMapping.forEach((id, path) -> this.pathMapping
.put(EndpointId.fromPropertyValue(id), path));
}
@Override
@Deprecated
public String getRootPath(String endpointId) {
return this.pathMapping.getOrDefault(endpointId, endpointId);
return getRootPath(EndpointId.of(endpointId));
}
@Override
public String getRootPath(EndpointId endpointId) {
return this.pathMapping.getOrDefault(endpointId, endpointId.toLowerCaseString());
}
}

@ -19,7 +19,6 @@ package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.Meter.Id;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.instrument.config.MeterFilterReply;
@ -29,8 +28,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
/**
* {@link MeterFilter} to log only once a warning message and deny a {@link Meter}
* {@link Id}.
* {@link MeterFilter} to log only once a warning message and deny a {@link Id Meter.Id}.
*
* @author Jon Schneider
* @author Dmytro Nosan

@ -31,6 +31,7 @@ import reactor.core.publisher.Mono;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
import org.springframework.boot.security.reactive.ApplicationContextServerWebExchangeMatcher;
@ -208,9 +209,12 @@ public final class EndpointRequest {
.map(pathMappedEndpoints::getPath);
}
private String getEndpointId(Object source) {
private EndpointId getEndpointId(Object source) {
if (source instanceof EndpointId) {
return (EndpointId) source;
}
if (source instanceof String) {
return (String) source;
return (EndpointId.of((String) source));
}
if (source instanceof Class) {
return getEndpointId((Class<?>) source);
@ -218,12 +222,12 @@ public final class EndpointRequest {
throw new IllegalStateException("Unsupported source " + source);
}
private String getEndpointId(Class<?> source) {
private EndpointId getEndpointId(Class<?> source) {
Endpoint annotation = AnnotatedElementUtils.getMergedAnnotation(source,
Endpoint.class);
Assert.state(annotation != null,
() -> "Class " + source + " is not annotated with @Endpoint");
return annotation.id();
return EndpointId.of(annotation.id());
}
private List<ServerWebExchangeMatcher> getDelegateMatchers(Set<String> paths) {

@ -31,6 +31,7 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
import org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider;
@ -256,9 +257,12 @@ public final class EndpointRequest {
.map(pathMappedEndpoints::getPath);
}
private String getEndpointId(Object source) {
private EndpointId getEndpointId(Object source) {
if (source instanceof EndpointId) {
return (EndpointId) source;
}
if (source instanceof String) {
return (String) source;
return (EndpointId.of((String) source));
}
if (source instanceof Class) {
return getEndpointId((Class<?>) source);
@ -266,12 +270,12 @@ public final class EndpointRequest {
throw new IllegalStateException("Unsupported source " + source);
}
private String getEndpointId(Class<?> source) {
private EndpointId getEndpointId(Class<?> source) {
Endpoint annotation = AnnotatedElementUtils.getMergedAnnotation(source,
Endpoint.class);
Assert.state(annotation != null,
() -> "Class " + source + " is not annotated with @Endpoint");
return annotation.id();
return EndpointId.of(annotation.id());
}
private List<RequestMatcher> getDelegateMatchers(

@ -23,6 +23,7 @@ import java.util.function.Function;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.InvocationContext;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
@ -57,7 +58,7 @@ public class CloudFoundryWebEndpointDiscovererTests {
Collection<ExposableWebEndpoint> endpoints = discoverer.getEndpoints();
assertThat(endpoints.size()).isEqualTo(2);
for (ExposableWebEndpoint endpoint : endpoints) {
if (endpoint.getId().equals("health")) {
if (endpoint.getEndpointId().equals(EndpointId.of("health"))) {
WebOperation operation = findMainReadOperation(endpoint);
assertThat(operation.invoke(new InvocationContext(
mock(SecurityContext.class), Collections.emptyMap())))
@ -82,8 +83,8 @@ public class CloudFoundryWebEndpointDiscovererTests {
this.load((id) -> null, (id) -> id, configuration, consumer);
}
private void load(Function<String, Long> timeToLive, PathMapper endpointPathMapper,
Class<?> configuration,
private void load(Function<EndpointId, Long> timeToLive,
PathMapper endpointPathMapper, Class<?> configuration,
Consumer<CloudFoundryWebEndpointDiscoverer> consumer) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
configuration);

@ -32,6 +32,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAu
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
@ -240,9 +241,10 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
context);
Collection<ExposableWebEndpoint> endpoints = handlerMapping
.getEndpoints();
List<String> endpointIds = endpoints.stream()
.map(ExposableEndpoint::getId).collect(Collectors.toList());
assertThat(endpointIds).contains("test");
List<EndpointId> endpointIds = endpoints.stream()
.map(ExposableEndpoint::getEndpointId)
.collect(Collectors.toList());
assertThat(endpointIds).contains(EndpointId.of("test"));
});
}
@ -258,7 +260,8 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
Collection<ExposableWebEndpoint> endpoints = handlerMapping
.getEndpoints();
ExposableWebEndpoint endpoint = endpoints.stream()
.filter((candidate) -> "test".equals(candidate.getId()))
.filter((candidate) -> EndpointId.of("test")
.equals(candidate.getEndpointId()))
.findFirst().get();
assertThat(endpoint.getOperations()).hasSize(1);
WebOperation operation = endpoint.getOperations().iterator().next();

@ -27,6 +27,7 @@ import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoC
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
@ -236,7 +237,8 @@ public class CloudFoundryActuatorAutoConfigurationTests {
Collection<ExposableWebEndpoint> endpoints = handlerMapping
.getEndpoints();
assertThat(endpoints.stream()
.filter((candidate) -> "test".equals(candidate.getId()))
.filter((candidate) -> EndpointId.of("test")
.equals(candidate.getEndpointId()))
.findFirst()).isNotEmpty();
});
}
@ -254,7 +256,8 @@ public class CloudFoundryActuatorAutoConfigurationTests {
Collection<ExposableWebEndpoint> endpoints = handlerMapping
.getEndpoints();
ExposableWebEndpoint endpoint = endpoints.stream()
.filter((candidate) -> "test".equals(candidate.getId()))
.filter((candidate) -> EndpointId.of("test")
.equals(candidate.getEndpointId()))
.findFirst().get();
Collection<WebOperation> operations = endpoint.getOperations();
assertThat(operations).hasSize(1);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -26,6 +26,7 @@ import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.Token;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletRequest;
@ -65,13 +66,15 @@ public class CloudFoundrySecurityInterceptorTests {
this.request.setMethod("OPTIONS");
this.request.addHeader(HttpHeaders.ORIGIN, "http://example.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
SecurityResponse response = this.interceptor.preHandle(this.request, "/a");
SecurityResponse response = this.interceptor.preHandle(this.request,
EndpointId.of("test"));
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK);
}
@Test
public void preHandleWhenTokenIsMissingShouldReturnFalse() {
SecurityResponse response = this.interceptor.preHandle(this.request, "/a");
SecurityResponse response = this.interceptor.preHandle(this.request,
EndpointId.of("test"));
assertThat(response.getStatus())
.isEqualTo(Reason.MISSING_AUTHORIZATION.getStatus());
}
@ -79,7 +82,8 @@ public class CloudFoundrySecurityInterceptorTests {
@Test
public void preHandleWhenTokenIsNotBearerShouldReturnFalse() {
this.request.addHeader("Authorization", mockAccessToken());
SecurityResponse response = this.interceptor.preHandle(this.request, "/a");
SecurityResponse response = this.interceptor.preHandle(this.request,
EndpointId.of("test"));
assertThat(response.getStatus())
.isEqualTo(Reason.MISSING_AUTHORIZATION.getStatus());
}
@ -89,7 +93,8 @@ public class CloudFoundrySecurityInterceptorTests {
this.interceptor = new CloudFoundrySecurityInterceptor(this.tokenValidator,
this.securityService, null);
this.request.addHeader("Authorization", "bearer " + mockAccessToken());
SecurityResponse response = this.interceptor.preHandle(this.request, "/a");
SecurityResponse response = this.interceptor.preHandle(this.request,
EndpointId.of("test"));
assertThat(response.getStatus())
.isEqualTo(Reason.SERVICE_UNAVAILABLE.getStatus());
}
@ -99,7 +104,8 @@ public class CloudFoundrySecurityInterceptorTests {
this.interceptor = new CloudFoundrySecurityInterceptor(this.tokenValidator, null,
"my-app-id");
this.request.addHeader("Authorization", "bearer " + mockAccessToken());
SecurityResponse response = this.interceptor.preHandle(this.request, "/a");
SecurityResponse response = this.interceptor.preHandle(this.request,
EndpointId.of("test"));
assertThat(response.getStatus())
.isEqualTo(Reason.SERVICE_UNAVAILABLE.getStatus());
}
@ -110,7 +116,8 @@ public class CloudFoundrySecurityInterceptorTests {
this.request.addHeader("Authorization", "bearer " + accessToken);
given(this.securityService.getAccessLevel(accessToken, "my-app-id"))
.willReturn(AccessLevel.RESTRICTED);
SecurityResponse response = this.interceptor.preHandle(this.request, "/a");
SecurityResponse response = this.interceptor.preHandle(this.request,
EndpointId.of("test"));
assertThat(response.getStatus()).isEqualTo(Reason.ACCESS_DENIED.getStatus());
}
@ -120,7 +127,8 @@ public class CloudFoundrySecurityInterceptorTests {
this.request.addHeader("Authorization", "Bearer " + accessToken);
given(this.securityService.getAccessLevel(accessToken, "my-app-id"))
.willReturn(AccessLevel.FULL);
SecurityResponse response = this.interceptor.preHandle(this.request, "/a");
SecurityResponse response = this.interceptor.preHandle(this.request,
EndpointId.of("test"));
ArgumentCaptor<Token> tokenArgumentCaptor = ArgumentCaptor.forClass(Token.class);
verify(this.tokenValidator).validate(tokenArgumentCaptor.capture());
Token token = tokenArgumentCaptor.getValue();
@ -136,7 +144,8 @@ public class CloudFoundrySecurityInterceptorTests {
this.request.addHeader("Authorization", "Bearer " + accessToken);
given(this.securityService.getAccessLevel(accessToken, "my-app-id"))
.willReturn(AccessLevel.RESTRICTED);
SecurityResponse response = this.interceptor.preHandle(this.request, "info");
SecurityResponse response = this.interceptor.preHandle(this.request,
EndpointId.of("info"));
ArgumentCaptor<Token> tokenArgumentCaptor = ArgumentCaptor.forClass(Token.class);
verify(this.tokenValidator).validate(tokenArgumentCaptor.capture());
Token token = tokenArgumentCaptor.getValue();

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -20,6 +20,7 @@ import java.util.function.Function;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
@ -34,21 +35,26 @@ public class EndpointIdTimeToLivePropertyFunctionTests {
private final MockEnvironment environment = new MockEnvironment();
private final Function<String, Long> timeToLive = new EndpointIdTimeToLivePropertyFunction(
private final Function<EndpointId, Long> timeToLive = new EndpointIdTimeToLivePropertyFunction(
this.environment);
@Test
public void defaultConfiguration() {
Long result = this.timeToLive.apply("test");
Long result = this.timeToLive.apply(EndpointId.of("test"));
assertThat(result).isNull();
}
@Test
public void userConfiguration() {
this.environment.setProperty("management.endpoint.test.cache.time-to-live",
"500");
Long result = this.timeToLive.apply("test");
this.environment.setProperty(
"management.endpoint.another-test.cache.time-to-live", "500");
Long result = this.timeToLive.apply(EndpointId.of("anotherTest"));
assertThat(result).isEqualTo(500L);
}
@Test
public void mixedCaseUserConfiguration() {
}
}

@ -21,6 +21,7 @@ import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.actuate.endpoint.EndpointFilter;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.mock.env.MockEnvironment;
@ -79,43 +80,43 @@ public class ExposeExcludePropertyEndpointFilterTests {
@Test
public void matchWhenExposeIsEmptyAndExcludeIsEmptyAndInDefaultShouldMatch() {
setupFilter("", "");
assertThat(match("def")).isTrue();
assertThat(match(EndpointId.of("def"))).isTrue();
}
@Test
public void matchWhenExposeIsEmptyAndExcludeIsEmptyAndNotInDefaultShouldNotMatch() {
setupFilter("", "");
assertThat(match("bar")).isFalse();
assertThat(match(EndpointId.of("bar"))).isFalse();
}
@Test
public void matchWhenExposeMatchesAndExcludeIsEmptyShouldMatch() {
setupFilter("bar", "");
assertThat(match("bar")).isTrue();
assertThat(match(EndpointId.of("bar"))).isTrue();
}
@Test
public void matchWhenExposeDoesNotMatchAndExcludeIsEmptyShouldNotMatch() {
setupFilter("bar", "");
assertThat(match("baz")).isFalse();
assertThat(match(EndpointId.of("baz"))).isFalse();
}
@Test
public void matchWhenExposeMatchesAndExcludeMatchesShouldNotMatch() {
setupFilter("bar,baz", "baz");
assertThat(match("baz")).isFalse();
assertThat(match(EndpointId.of("baz"))).isFalse();
}
@Test
public void matchWhenExposeMatchesAndExcludeDoesNotMatchShouldMatch() {
setupFilter("bar,baz", "buz");
assertThat(match("baz")).isTrue();
assertThat(match(EndpointId.of("baz"))).isTrue();
}
@Test
public void matchWhenExposeMatchesWithDifferentCaseShouldMatch() {
setupFilter("bar", "");
assertThat(match("bAr")).isTrue();
assertThat(match(EndpointId.of("bAr"))).isTrue();
}
@Test
@ -125,23 +126,29 @@ public class ExposeExcludePropertyEndpointFilterTests {
environment.setProperty("foo.exclude", "");
this.filter = new ExposeExcludePropertyEndpointFilter<>(
DifferentTestExposableWebEndpoint.class, environment, "foo");
assertThat(match("baz")).isTrue();
assertThat(match(EndpointId.of("baz"))).isTrue();
}
@Test
public void matchWhenIncludeIsAsteriskShouldMatchAll() {
setupFilter("*", "buz");
assertThat(match("bar")).isTrue();
assertThat(match("baz")).isTrue();
assertThat(match("buz")).isFalse();
assertThat(match(EndpointId.of("bar"))).isTrue();
assertThat(match(EndpointId.of("baz"))).isTrue();
assertThat(match(EndpointId.of("buz"))).isFalse();
}
@Test
public void matchWhenExcludeIsAsteriskShouldMatchNone() {
setupFilter("bar,baz,buz", "*");
assertThat(match("bar")).isFalse();
assertThat(match("baz")).isFalse();
assertThat(match("buz")).isFalse();
assertThat(match(EndpointId.of("bar"))).isFalse();
assertThat(match(EndpointId.of("baz"))).isFalse();
assertThat(match(EndpointId.of("buz"))).isFalse();
}
@Test
public void matchWhenMixedCaseShouldMatch() {
setupFilter("foo-bar", "");
assertThat(match(EndpointId.of("fooBar"))).isTrue();
}
private void setupFilter(String include, String exclude) {
@ -153,9 +160,9 @@ public class ExposeExcludePropertyEndpointFilterTests {
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private boolean match(String id) {
private boolean match(EndpointId id) {
ExposableEndpoint<?> endpoint = mock(TestExposableWebEndpoint.class);
given(endpoint.getId()).willReturn(id);
given(endpoint.getEndpointId()).willReturn(id);
return ((EndpointFilter) this.filter).match(endpoint);
}

@ -140,6 +140,14 @@ public class ConditionalOnEnabledEndpointTests {
});
}
@Test
public void outcomeWhenEndpointEnabledPropertyIsTrueAndMixedCaseShouldMatch() {
this.contextRunner.withPropertyValues("management.endpoint.foo-bar.enabled=true")
.withUserConfiguration(
FooBarEndpointEnabledByDefaultFalseConfiguration.class)
.run((context) -> assertThat(context).hasBean("fooBar"));
}
@Endpoint(id = "foo", enableByDefault = true)
static class FooEndpointEnabledByDefaultTrue {
@ -150,6 +158,11 @@ public class ConditionalOnEnabledEndpointTests {
}
@Endpoint(id = "fooBar", enableByDefault = false)
static class FooBarEndpointEnabledByDefaultFalse {
}
@EndpointExtension(endpoint = FooEndpointEnabledByDefaultTrue.class, filter = TestFilter.class)
static class FooEndpointExtensionEnabledByDefaultTrue {
@ -191,6 +204,17 @@ public class ConditionalOnEnabledEndpointTests {
}
@Configuration
static class FooBarEndpointEnabledByDefaultFalseConfiguration {
@Bean
@ConditionalOnEnabledEndpoint
public FooBarEndpointEnabledByDefaultFalse fooBar() {
return new FooBarEndpointEnabledByDefaultFalse();
}
}
@Configuration
static class FooEndpointAndExtensionEnabledByDefaultTrueConfiguration {

@ -24,6 +24,7 @@ import javax.management.ObjectName;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.util.ObjectUtils;
@ -51,22 +52,23 @@ public class DefaultEndpointObjectNameFactoryTests {
@Test
public void generateObjectName() {
ObjectName objectName = generateObjectName(endpoint("Test"));
ObjectName objectName = generateObjectName(endpoint(EndpointId.of("test")));
assertThat(objectName.toString())
.isEqualTo("org.springframework.boot:type=Endpoint,name=Test");
}
@Test
public void generateObjectNameWithCapitalizedId() {
ObjectName objectName = generateObjectName(endpoint("test"));
ObjectName objectName = generateObjectName(
endpoint(EndpointId.of("testEndpoint")));
assertThat(objectName.toString())
.isEqualTo("org.springframework.boot:type=Endpoint,name=Test");
.isEqualTo("org.springframework.boot:type=Endpoint,name=TestEndpoint");
}
@Test
public void generateObjectNameWithCustomDomain() {
this.properties.setDomain("com.example.acme");
ObjectName objectName = generateObjectName(endpoint("test"));
ObjectName objectName = generateObjectName(endpoint(EndpointId.of("test")));
assertThat(objectName.toString())
.isEqualTo("com.example.acme:type=Endpoint,name=Test");
}
@ -85,7 +87,7 @@ public class DefaultEndpointObjectNameFactoryTests {
}
private void assertUniqueObjectName() {
ExposableJmxEndpoint endpoint = endpoint("test");
ExposableJmxEndpoint endpoint = endpoint(EndpointId.of("test"));
String id = ObjectUtils.getIdentityHexString(endpoint);
ObjectName objectName = generateObjectName(endpoint);
assertThat(objectName.toString()).isEqualTo(
@ -98,7 +100,7 @@ public class DefaultEndpointObjectNameFactoryTests {
this.environment.setProperty("spring.jmx.unique-names", "false");
this.properties.setUniqueNames(true);
assertThatIllegalArgumentException()
.isThrownBy(() -> generateObjectName(endpoint("test")))
.isThrownBy(() -> generateObjectName(endpoint(EndpointId.of("test"))))
.withMessageContaining("spring.jmx.unique-names")
.withMessageContaining("management.endpoints.jmx.unique-names");
}
@ -107,7 +109,7 @@ public class DefaultEndpointObjectNameFactoryTests {
public void generateObjectNameWithStaticNames() {
this.properties.getStaticNames().setProperty("counter", "42");
this.properties.getStaticNames().setProperty("foo", "bar");
ObjectName objectName = generateObjectName(endpoint("test"));
ObjectName objectName = generateObjectName(endpoint(EndpointId.of("test")));
assertThat(objectName.getKeyProperty("counter")).isEqualTo("42");
assertThat(objectName.getKeyProperty("foo")).isEqualTo("bar");
assertThat(objectName.toString())
@ -122,7 +124,7 @@ public class DefaultEndpointObjectNameFactoryTests {
null)).willReturn(
Collections.singleton(new ObjectName(
"org.springframework.boot:type=Endpoint,name=Test")));
ObjectName objectName = generateObjectName(endpoint("test"));
ObjectName objectName = generateObjectName(endpoint(EndpointId.of("test")));
assertThat(objectName.toString()).isEqualTo(
"org.springframework.boot:type=Endpoint,name=Test,context=testContext");
@ -138,9 +140,9 @@ public class DefaultEndpointObjectNameFactoryTests {
}
}
private ExposableJmxEndpoint endpoint(String id) {
private ExposableJmxEndpoint endpoint(EndpointId id) {
ExposableJmxEndpoint endpoint = mock(ExposableJmxEndpoint.class);
given(endpoint.getId()).willReturn(id);
given(endpoint.getEndpointId()).willReturn(id);
return endpoint;
}

@ -20,6 +20,8 @@ import java.util.Collections;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import static org.assertj.core.api.Assertions.assertThat;
/**
@ -33,14 +35,29 @@ public class MappingWebEndpointPathMapperTests {
public void defaultConfiguration() {
MappingWebEndpointPathMapper mapper = new MappingWebEndpointPathMapper(
Collections.emptyMap());
assertThat(mapper.getRootPath("test")).isEqualTo("test");
assertThat(mapper.getRootPath(EndpointId.of("test"))).isEqualTo("test");
}
@Test
public void userConfiguration() {
MappingWebEndpointPathMapper mapper = new MappingWebEndpointPathMapper(
Collections.singletonMap("test", "custom"));
assertThat(mapper.getRootPath("test")).isEqualTo("custom");
assertThat(mapper.getRootPath(EndpointId.of("test"))).isEqualTo("custom");
}
@Test
public void mixedCaseDefaultConfiguration() {
MappingWebEndpointPathMapper mapper = new MappingWebEndpointPathMapper(
Collections.emptyMap());
assertThat(mapper.getRootPath(EndpointId.of("testEndpoint")))
.isEqualTo("testendpoint");
}
@Test
public void mixedCaseUserConfiguration() {
MappingWebEndpointPathMapper mapper = new MappingWebEndpointPathMapper(
Collections.singletonMap("test-endpoint", "custom"));
assertThat(mapper.getRootPath(EndpointId.of("testEndpoint"))).isEqualTo("custom");
}
}

@ -20,6 +20,7 @@ import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.PathMapper;
@ -65,7 +66,7 @@ public class WebEndpointAutoConfigurationTests {
.run((context) -> {
assertThat(context).hasSingleBean(PathMapper.class);
String pathMapping = context.getBean(PathMapper.class)
.getRootPath("health");
.getRootPath(EndpointId.of("health"));
assertThat(pathMapping).isEqualTo("healthcheck");
});
}

@ -23,6 +23,7 @@ import org.assertj.core.api.AssertDelegateTarget;
import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.Operation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
@ -129,9 +130,9 @@ public class EndpointRequestTests {
ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint()
.excluding(FooEndpoint.class, BazServletEndpoint.class);
List<ExposableEndpoint<?>> endpoints = new ArrayList<>();
endpoints.add(mockEndpoint("foo", "foo"));
endpoints.add(mockEndpoint("bar", "bar"));
endpoints.add(mockEndpoint("baz", "baz"));
endpoints.add(mockEndpoint(EndpointId.of("foo"), "foo"));
endpoints.add(mockEndpoint(EndpointId.of("bar"), "bar"));
endpoints.add(mockEndpoint(EndpointId.of("baz"), "baz"));
PathMappedEndpoints pathMappedEndpoints = new PathMappedEndpoints("/actuator",
() -> endpoints);
assertMatcher(matcher, pathMappedEndpoints).doesNotMatch("/actuator/foo");
@ -202,14 +203,14 @@ public class EndpointRequestTests {
private PathMappedEndpoints mockPathMappedEndpoints(String basePath) {
List<ExposableEndpoint<?>> endpoints = new ArrayList<>();
endpoints.add(mockEndpoint("foo", "foo"));
endpoints.add(mockEndpoint("bar", "bar"));
endpoints.add(mockEndpoint(EndpointId.of("foo"), "foo"));
endpoints.add(mockEndpoint(EndpointId.of("bar"), "bar"));
return new PathMappedEndpoints(basePath, () -> endpoints);
}
private TestEndpoint mockEndpoint(String id, String rootPath) {
private TestEndpoint mockEndpoint(EndpointId id, String rootPath) {
TestEndpoint endpoint = mock(TestEndpoint.class);
given(endpoint.getId()).willReturn(id);
given(endpoint.getEndpointId()).willReturn(id);
given(endpoint.getRootPath()).willReturn(rootPath);
return endpoint;
}

@ -25,6 +25,7 @@ import org.assertj.core.api.AssertDelegateTarget;
import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.Operation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
@ -163,9 +164,9 @@ public class EndpointRequestTests {
RequestMatcher matcher = EndpointRequest.toAnyEndpoint()
.excluding(FooEndpoint.class, BazServletEndpoint.class);
List<ExposableEndpoint<?>> endpoints = new ArrayList<>();
endpoints.add(mockEndpoint("foo", "foo"));
endpoints.add(mockEndpoint("bar", "bar"));
endpoints.add(mockEndpoint("baz", "baz"));
endpoints.add(mockEndpoint(EndpointId.of("foo"), "foo"));
endpoints.add(mockEndpoint(EndpointId.of("bar"), "bar"));
endpoints.add(mockEndpoint(EndpointId.of("baz"), "baz"));
PathMappedEndpoints pathMappedEndpoints = new PathMappedEndpoints("/actuator",
() -> endpoints);
assertMatcher(matcher, pathMappedEndpoints).doesNotMatch("/actuator/foo");
@ -258,14 +259,14 @@ public class EndpointRequestTests {
private PathMappedEndpoints mockPathMappedEndpoints(String basePath) {
List<ExposableEndpoint<?>> endpoints = new ArrayList<>();
endpoints.add(mockEndpoint("foo", "foo"));
endpoints.add(mockEndpoint("bar", "bar"));
endpoints.add(mockEndpoint(EndpointId.of("foo"), "foo"));
endpoints.add(mockEndpoint(EndpointId.of("bar"), "bar"));
return new PathMappedEndpoints(basePath, () -> endpoints);
}
private TestEndpoint mockEndpoint(String id, String rootPath) {
private TestEndpoint mockEndpoint(EndpointId id, String rootPath) {
TestEndpoint endpoint = mock(TestEndpoint.class);
given(endpoint.getId()).willReturn(id);
given(endpoint.getEndpointId()).willReturn(id);
given(endpoint.getRootPath()).willReturn(rootPath);
return endpoint;
}

@ -33,7 +33,7 @@ import org.springframework.util.Assert;
public abstract class AbstractExposableEndpoint<O extends Operation>
implements ExposableEndpoint<O> {
private final String id;
private final EndpointId id;
private boolean enabledByDefault;
@ -44,9 +44,24 @@ public abstract class AbstractExposableEndpoint<O extends Operation>
* @param id the endpoint id
* @param enabledByDefault if the endpoint is enabled by default
* @param operations the endpoint operations
* @deprecated since 2.0.6 in favor of
* {@link #AbstractExposableEndpoint(EndpointId, boolean, Collection)}
*/
@Deprecated
public AbstractExposableEndpoint(String id, boolean enabledByDefault,
Collection<? extends O> operations) {
this(EndpointId.of(id), enabledByDefault, operations);
}
/**
* Create a new {@link AbstractExposableEndpoint} instance.
* @param id the endpoint id
* @param enabledByDefault if the endpoint is enabled by default
* @param operations the endpoint operations
* @since 2.0.6
*/
public AbstractExposableEndpoint(EndpointId id, boolean enabledByDefault,
Collection<? extends O> operations) {
Assert.notNull(id, "ID must not be null");
Assert.notNull(operations, "Operations must not be null");
this.id = id;
@ -56,7 +71,7 @@ public abstract class AbstractExposableEndpoint<O extends Operation>
@Override
public String getId() {
return this.id;
return this.id.toString();
}
@Override

@ -0,0 +1,100 @@
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
import java.util.Locale;
import java.util.regex.Pattern;
import org.springframework.util.Assert;
/**
* An identifier for an actuator endpoint. Endpoint IDs may contain only letters and
* numbers and must begin with a lower-case letter. Case is ignored when comparing
* endpoint IDs.
*
* @author Phillip Webb
* @since 2.0.6
*/
public final class EndpointId {
private static final Pattern ALPHA_NUMERIC = Pattern.compile("[a-zA-Z0-9]+");
private final String value;
private final String lowerCaseValue;
private EndpointId(String value) {
Assert.hasText(value, "Value must not be empty");
Assert.isTrue(ALPHA_NUMERIC.matcher(value).matches(),
"Value must be alpha-numeric");
Assert.isTrue(!Character.isDigit(value.charAt(0)),
"Value must not start with a number");
Assert.isTrue(!Character.isUpperCase(value.charAt(0)),
"Value must not start with an uppercase letter");
this.value = value;
this.lowerCaseValue = value.toLowerCase(Locale.ENGLISH);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return toLowerCaseString().equals(((EndpointId) obj).toLowerCaseString());
}
@Override
public int hashCode() {
return toLowerCaseString().hashCode();
}
/**
* Return a lower-case version of the endpoint ID.
* @return the lower-case endpoint ID
*/
public String toLowerCaseString() {
return this.lowerCaseValue;
}
@Override
public String toString() {
return this.value;
}
/**
* Factory method to create a new {@link EndpointId} of the specified value.
* @param value the endpoint ID value
* @return an {@link EndpointId} instance
*/
public static EndpointId of(String value) {
return new EndpointId(value);
}
/**
* Factory method to create a new {@link EndpointId} from a property value. Is more
* lenient that {@link #of(String)} to allow for common "relaxed" property variants.
* @param value the property value to convert
* @return an {@link EndpointId} instance
*/
public static EndpointId fromPropertyValue(String value) {
return new EndpointId(value.replace("-", ""));
}
}

@ -31,9 +31,20 @@ public interface ExposableEndpoint<O extends Operation> {
/**
* Returns the id of the endpoint.
* @return the id
* @deprecated since 2.0.6 in favor of {@link #getEndpointId()}
*/
@Deprecated
String getId();
/**
* Return the endpoint ID.
* @return the endpoint ID
* @since 2.0.6
*/
default EndpointId getEndpointId() {
return EndpointId.of(getId());
}
/**
* Returns if the endpoint is enabled by default.
* @return if the endpoint is enabled by default

@ -19,6 +19,7 @@ package org.springframework.boot.actuate.endpoint.annotation;
import java.util.Collection;
import org.springframework.boot.actuate.endpoint.AbstractExposableEndpoint;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.Operation;
import org.springframework.core.style.ToStringCreator;
@ -46,10 +47,28 @@ public abstract class AbstractDiscoveredEndpoint<O extends Operation>
* @param id the ID of the endpoint
* @param enabledByDefault if the endpoint is enabled by default
* @param operations the endpoint operations
* @deprecated since 2.0.6 in favor of
* {@link #AbstractDiscoveredEndpoint(EndpointDiscoverer, Object, EndpointId, boolean, Collection)}
*/
@Deprecated
public AbstractDiscoveredEndpoint(EndpointDiscoverer<?, ?> discoverer,
Object endpointBean, String id, boolean enabledByDefault,
Collection<? extends O> operations) {
this(discoverer, endpointBean, EndpointId.of(id), enabledByDefault, operations);
}
/**
* Create a new {@link AbstractDiscoveredEndpoint} instance.
* @param discoverer the discoverer that discovered the endpoint
* @param endpointBean the primary source bean
* @param id the ID of the endpoint
* @param enabledByDefault if the endpoint is enabled by default
* @param operations the endpoint operations
* @since 2.0.6
*/
public AbstractDiscoveredEndpoint(EndpointDiscoverer<?, ?> discoverer,
Object endpointBean, EndpointId id, boolean enabledByDefault,
Collection<? extends O> operations) {
super(id, enabledByDefault, operations);
Assert.notNull(discoverer, "Discoverer must not be null");
Assert.notNull(endpointBean, "EndpointBean must not be null");

@ -24,6 +24,7 @@ import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.Operation;
import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
@ -68,20 +69,20 @@ abstract class DiscoveredOperationsFactory<O extends Operation> {
this.invokerAdvisors = invokerAdvisors;
}
public Collection<O> createOperations(String id, Object target) {
public Collection<O> createOperations(EndpointId id, Object target) {
return MethodIntrospector.selectMethods(target.getClass(),
(MetadataLookup<O>) (method) -> createOperation(id, target, method))
.values();
}
private O createOperation(String endpointId, Object target, Method method) {
private O createOperation(EndpointId endpointId, Object target, Method method) {
return OPERATION_TYPES.entrySet().stream()
.map((entry) -> createOperation(endpointId, target, method,
entry.getKey(), entry.getValue()))
.filter(Objects::nonNull).findFirst().orElse(null);
}
private O createOperation(String endpointId, Object target, Method method,
private O createOperation(EndpointId endpointId, Object target, Method method,
OperationType operationType, Class<? extends Annotation> annotationType) {
AnnotationAttributes annotationAttributes = AnnotatedElementUtils
.getMergedAnnotationAttributes(method, annotationType);
@ -96,7 +97,7 @@ abstract class DiscoveredOperationsFactory<O extends Operation> {
return createOperation(endpointId, operationMethod, invoker);
}
private OperationInvoker applyAdvisors(String endpointId,
private OperationInvoker applyAdvisors(EndpointId endpointId,
OperationMethod operationMethod, OperationInvoker invoker) {
if (this.invokerAdvisors != null) {
for (OperationInvokerAdvisor advisor : this.invokerAdvisors) {
@ -107,7 +108,7 @@ abstract class DiscoveredOperationsFactory<O extends Operation> {
return invoker;
}
protected abstract O createOperation(String endpointId,
protected abstract O createOperation(EndpointId endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker);
}

@ -22,6 +22,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.actuate.endpoint.EndpointId;
/**
* Identifies a type as being an actuator endpoint that provides information about the
* running application. Endpoints can be exposed over a variety of technologies including
@ -52,8 +54,9 @@ import java.lang.annotation.Target;
public @interface Endpoint {
/**
* The id of the endpoint.
* The id of the endpoint (must follow {@link EndpointId} rules).
* @return the id
* @see EndpointId
*/
String id() default "";

@ -32,6 +32,7 @@ import java.util.stream.Collectors;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.boot.actuate.endpoint.EndpointFilter;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.EndpointsSupplier;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.Operation;
@ -101,7 +102,7 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
return new DiscoveredOperationsFactory<O>(parameterValueMapper, invokerAdvisors) {
@Override
protected O createOperation(String endpointId,
protected O createOperation(EndpointId endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
return EndpointDiscoverer.this.createOperation(endpointId,
operationMethod, invoker);
@ -125,7 +126,7 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
}
private Collection<EndpointBean> createEndpointBeans() {
Map<String, EndpointBean> byId = new LinkedHashMap<>();
Map<EndpointId, EndpointBean> byId = new LinkedHashMap<>();
String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(
this.applicationContext, Endpoint.class);
for (String beanName : beanNames) {
@ -145,7 +146,7 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
}
private void addExtensionBeans(Collection<EndpointBean> endpointBeans) {
Map<String, EndpointBean> byId = endpointBeans.stream()
Map<EndpointId, EndpointBean> byId = endpointBeans.stream()
.collect(Collectors.toMap(EndpointBean::getId, (bean) -> bean));
String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(
this.applicationContext, EndpointExtension.class);
@ -189,7 +190,7 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
private E convertToEndpoint(EndpointBean endpointBean) {
MultiValueMap<OperationKey, O> indexed = new LinkedMultiValueMap<>();
String id = endpointBean.getId();
EndpointId id = endpointBean.getId();
addOperations(indexed, id, endpointBean.getBean(), false);
if (endpointBean.getExtensions().size() > 1) {
String extensionBeans = endpointBean.getExtensions().stream()
@ -209,7 +210,7 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
endpointBean.isEnabledByDefault(), operations);
}
private void addOperations(MultiValueMap<OperationKey, O> indexed, String id,
private void addOperations(MultiValueMap<OperationKey, O> indexed, EndpointId id,
Object target, boolean replaceLast) {
Set<OperationKey> replacedLast = new HashSet<>();
Collection<O> operations = this.operationsFactory.createOperations(id, target);
@ -339,7 +340,25 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
* @param enabledByDefault if the endpoint is enabled by default
* @param operations the endpoint operations
* @return a created endpoint (a {@link DiscoveredEndpoint} is recommended)
* @since 2.0.6
*/
protected E createEndpoint(Object endpointBean, EndpointId id,
boolean enabledByDefault, Collection<O> operations) {
return createEndpoint(endpointBean, (id != null) ? id.toString() : null,
enabledByDefault, operations);
}
/**
* Factory method called to create the {@link ExposableEndpoint endpoint}.
* @param endpointBean the source endpoint bean
* @param id the ID of the endpoint
* @param enabledByDefault if the endpoint is enabled by default
* @param operations the endpoint operations
* @return a created endpoint (a {@link DiscoveredEndpoint} is recommended)
* @deprecated Since 2.0.6 in favor of
* {@link #createEndpoint(Object, EndpointId, boolean, Collection)}
*/
@Deprecated
protected abstract E createEndpoint(Object endpointBean, String id,
boolean enabledByDefault, Collection<O> operations);
@ -349,7 +368,24 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
* @param operationMethod the operation method
* @param invoker the invoker to use
* @return a created operation
* @since 2.0.6
*/
protected O createOperation(EndpointId endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
return createOperation((endpointId != null) ? endpointId.toString() : null,
operationMethod, invoker);
}
/**
* Factory method to create an {@link Operation endpoint operation}.
* @param endpointId the endpoint id
* @param operationMethod the operation method
* @param invoker the invoker to use
* @return a created operation
* @deprecated since 2.0.6 in favor of
* {@link #createOperation(EndpointId, DiscoveredOperationMethod, OperationInvoker)}
*/
@Deprecated
protected abstract O createOperation(String endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker);
@ -414,7 +450,7 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
private final Object bean;
private final String id;
private final EndpointId id;
private boolean enabledByDefault;
@ -426,14 +462,15 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
AnnotationAttributes attributes = AnnotatedElementUtils
.findMergedAnnotationAttributes(bean.getClass(), Endpoint.class, true,
true);
String id = attributes.getString("id");
Assert.state(StringUtils.hasText(id),
() -> "No @Endpoint id attribute specified for "
+ bean.getClass().getName());
this.beanName = beanName;
this.bean = bean;
this.id = attributes.getString("id");
this.id = EndpointId.of(id);
this.enabledByDefault = (Boolean) attributes.get("enableByDefault");
this.filter = getFilter(this.bean.getClass());
Assert.state(StringUtils.hasText(this.id),
() -> "No @Endpoint id attribute specified for "
+ bean.getClass().getName());
}
public void addExtension(ExtensionBean extensionBean) {
@ -461,7 +498,7 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
return this.bean;
}
public String getId() {
public EndpointId getId() {
return this.id;
}
@ -484,7 +521,7 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
private final Object bean;
private final String endpointId;
private final EndpointId endpointId;
private final Class<?> filter;
@ -500,7 +537,7 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
true);
Assert.state(endpointAttributes != null, () -> "Extension "
+ endpointType.getName() + " does not specify an endpoint");
this.endpointId = endpointAttributes.getString("id");
this.endpointId = EndpointId.of(endpointAttributes.getString("id"));
this.filter = attributes.getClass("filter");
}
@ -512,7 +549,7 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
return this.bean;
}
public String getEndpointId() {
public EndpointId getEndpointId() {
return this.endpointId;
}

@ -16,6 +16,7 @@
package org.springframework.boot.actuate.endpoint.invoke;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.OperationType;
/**
@ -34,7 +35,25 @@ public interface OperationInvokerAdvisor {
* @param parameters the operation parameters
* @param invoker the invoker to advise
* @return an potentially new operation invoker with support for additional features
* @since 2.0.6
*/
default OperationInvoker apply(EndpointId endpointId, OperationType operationType,
OperationParameters parameters, OperationInvoker invoker) {
return apply((endpointId != null) ? endpointId.toString() : null, operationType,
parameters, invoker);
}
/**
* Apply additional functionality to the given invoker.
* @param endpointId the endpoint ID
* @param operationType the operation type
* @param parameters the operation parameters
* @param invoker the invoker to advise
* @return an potentially new operation invoker with support for additional features
* @deprecated since 2.0.6 in favor of
* {@link #apply(EndpointId, OperationType, OperationParameters, OperationInvoker)}
*/
@Deprecated
OperationInvoker apply(String endpointId, OperationType operationType,
OperationParameters parameters, OperationInvoker invoker);

@ -18,6 +18,7 @@ package org.springframework.boot.actuate.endpoint.invoker.cache;
import java.util.function.Function;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
@ -33,15 +34,23 @@ import org.springframework.boot.actuate.endpoint.invoke.OperationParameters;
*/
public class CachingOperationInvokerAdvisor implements OperationInvokerAdvisor {
private final Function<String, Long> endpointIdTimeToLive;
private final Function<EndpointId, Long> endpointIdTimeToLive;
public CachingOperationInvokerAdvisor(Function<String, Long> endpointIdTimeToLive) {
public CachingOperationInvokerAdvisor(
Function<EndpointId, Long> endpointIdTimeToLive) {
this.endpointIdTimeToLive = endpointIdTimeToLive;
}
@Override
@Deprecated
public OperationInvoker apply(String endpointId, OperationType operationType,
OperationParameters parameters, OperationInvoker invoker) {
return apply(EndpointId.of(endpointId), operationType, parameters, invoker);
}
@Override
public OperationInvoker apply(EndpointId endpointId, OperationType operationType,
OperationParameters parameters, OperationInvoker invoker) {
if (operationType == OperationType.READ && !hasMandatoryParameter(parameters)) {
Long timeToLive = this.endpointIdTimeToLive.apply(endpointId);
if (timeToLive != null && timeToLive > 0) {

@ -89,7 +89,7 @@ public class EndpointMBean implements DynamicMBean {
throws MBeanException, ReflectionException {
JmxOperation operation = this.operations.get(actionName);
if (operation == null) {
String message = "Endpoint with id '" + this.endpoint.getId()
String message = "Endpoint with id '" + this.endpoint.getEndpointId()
+ "' has no operation named " + actionName;
throw new ReflectionException(new IllegalArgumentException(message), message);
}

@ -136,7 +136,7 @@ public class JmxEndpointExporter
}
private String getEndpointDescription(ExposableJmxEndpoint endpoint) {
return "endpoint '" + endpoint.getId() + "'";
return "endpoint '" + endpoint.getEndpointId() + "'";
}
}

@ -58,7 +58,7 @@ class MBeanInfoFactory {
}
private String getDescription(ExposableJmxEndpoint endpoint) {
return "MBean operations for endpoint " + endpoint.getId();
return "MBean operations for endpoint " + endpoint.getEndpointId();
}
private ModelMBeanOperationInfo[] getMBeanOperations(ExposableJmxEndpoint endpoint) {

@ -18,6 +18,7 @@ package org.springframework.boot.actuate.endpoint.jmx.annotation;
import java.util.Collection;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer;
import org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint;
@ -32,7 +33,8 @@ class DiscoveredJmxEndpoint extends AbstractDiscoveredEndpoint<JmxOperation>
implements ExposableJmxEndpoint {
DiscoveredJmxEndpoint(EndpointDiscoverer<?, ?> discoverer, Object endpointBean,
String id, boolean enabledByDefault, Collection<JmxOperation> operations) {
EndpointId id, boolean enabledByDefault,
Collection<JmxOperation> operations) {
super(discoverer, endpointBean, id, enabledByDefault, operations);
}

@ -26,6 +26,7 @@ import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation;
import org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
@ -59,8 +60,8 @@ class DiscoveredJmxOperation extends AbstractDiscoveredOperation implements JmxO
private final List<JmxOperationParameter> parameters;
DiscoveredJmxOperation(String endpointId, DiscoveredOperationMethod operationMethod,
OperationInvoker invoker) {
DiscoveredJmxOperation(EndpointId endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
super(operationMethod, invoker);
Method method = operationMethod.getMethod();
this.name = method.getName();

@ -19,6 +19,7 @@ package org.springframework.boot.actuate.endpoint.jmx.annotation;
import java.util.Collection;
import org.springframework.boot.actuate.endpoint.EndpointFilter;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod;
import org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
@ -54,15 +55,30 @@ public class JmxEndpointDiscoverer
}
@Override
@Deprecated
protected ExposableJmxEndpoint createEndpoint(Object endpointBean, String id,
boolean enabledByDefault, Collection<JmxOperation> operations) {
return createEndpoint(endpointBean, EndpointId.of(id), enabledByDefault,
operations);
}
@Override
protected ExposableJmxEndpoint createEndpoint(Object endpointBean, EndpointId id,
boolean enabledByDefault, Collection<JmxOperation> operations) {
return new DiscoveredJmxEndpoint(this, endpointBean, id, enabledByDefault,
operations);
}
@Override
@Deprecated
protected JmxOperation createOperation(String endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
return createOperation(EndpointId.of(endpointId), operationMethod, invoker);
}
@Override
protected JmxOperation createOperation(EndpointId endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
return new DiscoveredJmxOperation(endpointId, operationMethod, invoker);
}

@ -76,8 +76,9 @@ public class EndpointLinksResolver {
collectLinks(links, (ExposableWebEndpoint) endpoint, normalizedUrl);
}
else if (endpoint instanceof PathMappedEndpoint) {
links.put(endpoint.getId(), createLink(normalizedUrl,
((PathMappedEndpoint) endpoint).getRootPath()));
String rootPath = ((PathMappedEndpoint) endpoint).getRootPath();
Link link = createLink(normalizedUrl, rootPath);
links.put(endpoint.getEndpointId().toLowerCaseString(), link);
}
}
return links;

@ -25,6 +25,7 @@ import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.EndpointsSupplier;
import org.springframework.util.Assert;
@ -37,7 +38,7 @@ public class PathMappedEndpoints implements Iterable<PathMappedEndpoint> {
private final String basePath;
private final Map<String, PathMappedEndpoint> endpoints;
private final Map<EndpointId, PathMappedEndpoint> endpoints;
/**
* Create a new {@link PathMappedEndpoints} instance for the given supplier.
@ -62,13 +63,14 @@ public class PathMappedEndpoints implements Iterable<PathMappedEndpoint> {
this.endpoints = getEndpoints(suppliers);
}
private Map<String, PathMappedEndpoint> getEndpoints(
private Map<EndpointId, PathMappedEndpoint> getEndpoints(
Collection<EndpointsSupplier<?>> suppliers) {
Map<String, PathMappedEndpoint> endpoints = new LinkedHashMap<>();
Map<EndpointId, PathMappedEndpoint> endpoints = new LinkedHashMap<>();
suppliers.forEach((supplier) -> {
supplier.getEndpoints().forEach((endpoint) -> {
if (endpoint instanceof PathMappedEndpoint) {
endpoints.put(endpoint.getId(), (PathMappedEndpoint) endpoint);
endpoints.put(endpoint.getEndpointId(),
(PathMappedEndpoint) endpoint);
}
});
});
@ -88,8 +90,21 @@ public class PathMappedEndpoints implements Iterable<PathMappedEndpoint> {
* endpoint cannot be found.
* @param endpointId the endpoint ID
* @return the root path or {@code null}
* @deprecated since 2.0.6 in favor of {@link #getRootPath(EndpointId)}
*/
@Deprecated
public String getRootPath(String endpointId) {
return getRootPath(EndpointId.of(endpointId));
}
/**
* Return the root path for the endpoint with the given ID or {@code null} if the
* endpoint cannot be found.
* @param endpointId the endpoint ID
* @return the root path or {@code null}
* @since 2.0.6
*/
public String getRootPath(EndpointId endpointId) {
PathMappedEndpoint endpoint = getEndpoint(endpointId);
return (endpoint != null) ? endpoint.getRootPath() : null;
}
@ -99,8 +114,21 @@ public class PathMappedEndpoints implements Iterable<PathMappedEndpoint> {
* endpoint cannot be found.
* @param endpointId the endpoint ID
* @return the full path or {@code null}
* @deprecated since 2.0.6 in favor of {@link #getPath(EndpointId)}
*/
@Deprecated
public String getPath(String endpointId) {
return getPath(EndpointId.of(endpointId));
}
/**
* Return the full path for the endpoint with the given ID or {@code null} if the
* endpoint cannot be found.
* @param endpointId the endpoint ID
* @return the full path or {@code null}
* @since 2.0.6
*/
public String getPath(EndpointId endpointId) {
return getPath(getEndpoint(endpointId));
}
@ -125,8 +153,21 @@ public class PathMappedEndpoints implements Iterable<PathMappedEndpoint> {
* endpoint cannot be found.
* @param endpointId the endpoint ID
* @return the path mapped endpoint or {@code null}
* @deprecated since 2.0.6 in favor of {@link #getEndpoint(EndpointId)}
*/
@Deprecated
public PathMappedEndpoint getEndpoint(String endpointId) {
return getEndpoint(EndpointId.of(endpointId));
}
/**
* Return the {@link PathMappedEndpoint} with the given ID or {@code null} if the
* endpoint cannot be found.
* @param endpointId the endpoint ID
* @return the path mapped endpoint or {@code null}
* @since 2.0.6
*/
public PathMappedEndpoint getEndpoint(EndpointId endpointId) {
return this.endpoints.get(endpointId);
}

@ -16,6 +16,8 @@
package org.springframework.boot.actuate.endpoint.web;
import org.springframework.boot.actuate.endpoint.EndpointId;
/**
* Strategy interface used to provide a mapping between an endpoint ID and the root path
* where it will be exposed.
@ -31,15 +33,41 @@ public interface PathMapper {
* Resolve the root path for the endpoint with the specified {@code endpointId}.
* @param endpointId the id of an endpoint
* @return the path of the endpoint
* @since 2.0.6
*/
default String getRootPath(EndpointId endpointId) {
return getRootPath((endpointId != null) ? endpointId.toString() : null);
}
/**
* Resolve the root path for the endpoint with the specified {@code endpointId}.
* @param endpointId the id of an endpoint
* @return the path of the endpoint
* @deprecated since 2.0.6 in favor of {@link #getRootPath(EndpointId)}
*/
@Deprecated
String getRootPath(String endpointId);
/**
* Returns an {@link PathMapper} that uses the endpoint ID as the path.
* @return an {@link PathMapper} that uses the endpoint ID as the path
* @return an {@link PathMapper} that uses the lowercase endpoint ID as the path
*/
static PathMapper useEndpointId() {
return (endpointId) -> endpointId;
return new PathMapper() {
@Override
@Deprecated
public String getRootPath(String endpointId) {
return getRootPath(EndpointId.of(endpointId));
}
@Override
public String getRootPath(EndpointId endpointId) {
return endpointId.toLowerCaseString();
}
};
}
}

@ -67,7 +67,7 @@ public class ServletEndpointRegistrar implements ServletContextInitializer {
private void register(ServletContext servletContext,
ExposableServletEndpoint endpoint) {
String name = endpoint.getId() + "-actuator-endpoint";
String name = endpoint.getEndpointId().toLowerCaseString() + "-actuator-endpoint";
String path = this.basePath + "/" + endpoint.getRootPath();
String urlMapping = path.endsWith("/") ? path + "*" : path + "/*";
EndpointServlet endpointServlet = endpoint.getEndpointServlet();

@ -20,6 +20,7 @@ import java.util.Collection;
import java.util.Collections;
import org.springframework.boot.actuate.endpoint.EndpointFilter;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.Operation;
import org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod;
import org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer;
@ -67,16 +68,31 @@ public class ControllerEndpointDiscoverer
}
@Override
@Deprecated
protected ExposableControllerEndpoint createEndpoint(Object endpointBean, String id,
boolean enabledByDefault, Collection<Operation> operations) {
return createEndpoint(endpointBean, (id != null) ? EndpointId.of(id) : null,
enabledByDefault, operations);
}
@Override
protected ExposableControllerEndpoint createEndpoint(Object endpointBean,
EndpointId id, boolean enabledByDefault, Collection<Operation> operations) {
String rootPath = this.endpointPathMapper.getRootPath(id);
return new DiscoveredControllerEndpoint(this, endpointBean, id, rootPath,
enabledByDefault);
}
@Override
@Deprecated
protected Operation createOperation(String endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
return createOperation(EndpointId.of(endpointId), operationMethod, invoker);
}
@Override
protected Operation createOperation(EndpointId endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
throw new IllegalStateException(
"ControllerEndpoints must not declare operations");
}

@ -18,6 +18,7 @@ package org.springframework.boot.actuate.endpoint.web.annotation;
import java.util.Collections;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.Operation;
import org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer;
@ -33,7 +34,7 @@ class DiscoveredControllerEndpoint extends AbstractDiscoveredEndpoint<Operation>
private final String rootPath;
DiscoveredControllerEndpoint(EndpointDiscoverer<?, ?> discoverer, Object endpointBean,
String id, String rootPath, boolean enabledByDefault) {
EndpointId id, String rootPath, boolean enabledByDefault) {
super(discoverer, endpointBean, id, enabledByDefault, Collections.emptyList());
this.rootPath = rootPath;
}

@ -19,6 +19,7 @@ package org.springframework.boot.actuate.endpoint.web.annotation;
import java.util.Collections;
import java.util.function.Supplier;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.Operation;
import org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer;
@ -39,7 +40,7 @@ class DiscoveredServletEndpoint extends AbstractDiscoveredEndpoint<Operation>
private final EndpointServlet endpointServlet;
DiscoveredServletEndpoint(EndpointDiscoverer<?, ?> discoverer, Object endpointBean,
String id, String rootPath, boolean enabledByDefault) {
EndpointId id, String rootPath, boolean enabledByDefault) {
super(discoverer, endpointBean, id, enabledByDefault, Collections.emptyList());
String beanType = endpointBean.getClass().getName();
Assert.state(endpointBean instanceof Supplier,

@ -18,6 +18,7 @@ package org.springframework.boot.actuate.endpoint.web.annotation;
import java.util.Collection;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer;
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
@ -34,7 +35,7 @@ class DiscoveredWebEndpoint extends AbstractDiscoveredEndpoint<WebOperation>
private final String rootPath;
DiscoveredWebEndpoint(EndpointDiscoverer<?, ?> discoverer, Object endpointBean,
String id, String rootPath, boolean enabledByDefault,
EndpointId id, String rootPath, boolean enabledByDefault,
Collection<WebOperation> operations) {
super(discoverer, endpointBean, id, enabledByDefault, operations);
this.rootPath = rootPath;

@ -23,6 +23,7 @@ import java.util.stream.Stream;
import org.reactivestreams.Publisher;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation;
import org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
@ -51,8 +52,9 @@ class DiscoveredWebOperation extends AbstractDiscoveredOperation implements WebO
private final WebOperationRequestPredicate requestPredicate;
DiscoveredWebOperation(String endpointId, DiscoveredOperationMethod operationMethod,
OperationInvoker invoker, WebOperationRequestPredicate requestPredicate) {
DiscoveredWebOperation(EndpointId endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker,
WebOperationRequestPredicate requestPredicate) {
super(operationMethod, invoker);
Method method = operationMethod.getMethod();
this.id = getId(endpointId, method);
@ -60,7 +62,7 @@ class DiscoveredWebOperation extends AbstractDiscoveredOperation implements WebO
this.requestPredicate = requestPredicate;
}
private String getId(String endpointId, Method method) {
private String getId(EndpointId endpointId, Method method) {
return endpointId + Stream.of(method.getParameters()).filter(this::hasSelector)
.map(this::dashName).collect(Collectors.joining());
}

@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
@ -50,7 +51,7 @@ class RequestPredicateFactory {
this.endpointMediaTypes = endpointMediaTypes;
}
public WebOperationRequestPredicate getRequestPredicate(String endpointId,
public WebOperationRequestPredicate getRequestPredicate(EndpointId endpointId,
String rootPath, DiscoveredOperationMethod operationMethod) {
Method method = operationMethod.getMethod();
String path = getPath(rootPath, method);

@ -20,6 +20,7 @@ import java.util.Collection;
import java.util.Collections;
import org.springframework.boot.actuate.endpoint.EndpointFilter;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.Operation;
import org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod;
import org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer;
@ -66,16 +67,31 @@ public class ServletEndpointDiscoverer
}
@Override
@Deprecated
protected ExposableServletEndpoint createEndpoint(Object endpointBean, String id,
boolean enabledByDefault, Collection<Operation> operations) {
return createEndpoint(endpointBean, EndpointId.of(id), enabledByDefault,
operations);
}
@Override
protected ExposableServletEndpoint createEndpoint(Object endpointBean, EndpointId id,
boolean enabledByDefault, Collection<Operation> operations) {
String rootPath = this.endpointPathMapper.getRootPath(id);
return new DiscoveredServletEndpoint(this, endpointBean, id, rootPath,
enabledByDefault);
}
@Override
@Deprecated
protected Operation createOperation(String endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
return createOperation(EndpointId.of(endpointId), operationMethod, invoker);
}
@Override
protected Operation createOperation(EndpointId endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
throw new IllegalStateException("ServletEndpoints must not declare operations");
}

@ -19,6 +19,7 @@ package org.springframework.boot.actuate.endpoint.web.annotation;
import java.util.Collection;
import org.springframework.boot.actuate.endpoint.EndpointFilter;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod;
import org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
@ -68,16 +69,31 @@ public class WebEndpointDiscoverer
}
@Override
@Deprecated
protected ExposableWebEndpoint createEndpoint(Object endpointBean, String id,
boolean enabledByDefault, Collection<WebOperation> operations) {
return createEndpoint(endpointBean, EndpointId.of(id), enabledByDefault,
operations);
}
@Override
protected ExposableWebEndpoint createEndpoint(Object endpointBean, EndpointId id,
boolean enabledByDefault, Collection<WebOperation> operations) {
String rootPath = this.endpointPathMapper.getRootPath(id);
return new DiscoveredWebEndpoint(this, endpointBean, id, rootPath,
enabledByDefault, operations);
}
@Override
@Deprecated
protected WebOperation createOperation(String endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
return createOperation(EndpointId.of(endpointId), operationMethod, invoker);
}
@Override
protected WebOperation createOperation(EndpointId endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
String rootPath = this.endpointPathMapper.getRootPath(endpointId);
WebOperationRequestPredicate requestPredicate = this.requestPredicateFactory
.getRequestPredicate(endpointId, rootPath, operationMethod);

@ -0,0 +1,92 @@
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link EndpointId}.
*
* @author Phillip Webb
*/
public class EndpointIdTests {
@Test
public void ofWhenNullThorowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> EndpointId.of(null))
.withMessage("Value must not be empty");
}
@Test
public void ofWhenEmptyThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> EndpointId.of(""))
.withMessage("Value must not be empty");
}
@Test
public void ofWhenContainsDashThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> EndpointId.of("foo-bar"))
.withMessage("Value must be alpha-numeric");
}
@Test
public void ofWhenHasBadCharThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> EndpointId.of("foo!bar"))
.withMessage("Value must be alpha-numeric");
}
@Test
public void ofWhenStartsWithNumberThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> EndpointId.of("1foo"))
.withMessage("Value must not start with a number");
}
@Test
public void ofWhenStartsWithUppercaseLetterThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> EndpointId.of("Foo"))
.withMessage("Value must not start with an uppercase letter");
}
@Test
public void equalsAndHashCode() {
EndpointId one = EndpointId.of("foobar");
EndpointId two = EndpointId.of("fooBar");
EndpointId three = EndpointId.of("barfoo");
assertThat(one.hashCode()).isEqualTo(two.hashCode());
assertThat(one).isEqualTo(one).isEqualTo(two).isNotEqualTo(three);
}
@Test
public void toLowerCaseStringReturnsLowercase() {
assertThat(EndpointId.of("fooBar").toLowerCaseString()).isEqualTo("foobar");
}
@Test
public void toStringReturnsString() {
assertThat(EndpointId.of("fooBar").toString()).isEqualTo("fooBar");
}
@Test
public void fromPropertyValueStripsDashes() {
EndpointId fromPropertyValue = EndpointId.fromPropertyValue("foo-bar");
assertThat(fromPropertyValue).isEqualTo(EndpointId.of("fooBar"));
}
}

@ -25,6 +25,7 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.InvocationContext;
import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.SecurityContext;
@ -60,8 +61,8 @@ public class DiscoveredOperationsFactoryTests {
@Test
public void createOperationsWhenHasReadMethodShouldCreateOperation() {
Collection<TestOperation> operations = this.factory.createOperations("test",
new ExampleRead());
Collection<TestOperation> operations = this.factory
.createOperations(EndpointId.of("test"), new ExampleRead());
assertThat(operations).hasSize(1);
TestOperation operation = getFirst(operations);
assertThat(operation.getType()).isEqualTo(OperationType.READ);
@ -69,8 +70,8 @@ public class DiscoveredOperationsFactoryTests {
@Test
public void createOperationsWhenHasWriteMethodShouldCreateOperation() {
Collection<TestOperation> operations = this.factory.createOperations("test",
new ExampleWrite());
Collection<TestOperation> operations = this.factory
.createOperations(EndpointId.of("test"), new ExampleWrite());
assertThat(operations).hasSize(1);
TestOperation operation = getFirst(operations);
assertThat(operation.getType()).isEqualTo(OperationType.WRITE);
@ -78,8 +79,8 @@ public class DiscoveredOperationsFactoryTests {
@Test
public void createOperationsWhenHasDeleteMethodShouldCreateOperation() {
Collection<TestOperation> operations = this.factory.createOperations("test",
new ExampleDelete());
Collection<TestOperation> operations = this.factory
.createOperations(EndpointId.of("test"), new ExampleDelete());
assertThat(operations).hasSize(1);
TestOperation operation = getFirst(operations);
assertThat(operation.getType()).isEqualTo(OperationType.DELETE);
@ -87,8 +88,8 @@ public class DiscoveredOperationsFactoryTests {
@Test
public void createOperationsWhenMultipleShouldReturnMultiple() {
Collection<TestOperation> operations = this.factory.createOperations("test",
new ExampleMultiple());
Collection<TestOperation> operations = this.factory
.createOperations(EndpointId.of("test"), new ExampleMultiple());
assertThat(operations).hasSize(2);
assertThat(operations.stream().map(TestOperation::getType))
.containsOnly(OperationType.READ, OperationType.WRITE);
@ -96,8 +97,8 @@ public class DiscoveredOperationsFactoryTests {
@Test
public void createOperationsShouldProvideOperationMethod() {
TestOperation operation = getFirst(
this.factory.createOperations("test", new ExampleWithParams()));
TestOperation operation = getFirst(this.factory
.createOperations(EndpointId.of("test"), new ExampleWithParams()));
OperationMethod operationMethod = operation.getOperationMethod();
assertThat(operationMethod.getMethod().getName()).isEqualTo("read");
assertThat(operationMethod.getParameters().hasParameters()).isTrue();
@ -105,8 +106,8 @@ public class DiscoveredOperationsFactoryTests {
@Test
public void createOperationsShouldProviderInvoker() {
TestOperation operation = getFirst(
this.factory.createOperations("test", new ExampleWithParams()));
TestOperation operation = getFirst(this.factory
.createOperations(EndpointId.of("test"), new ExampleWithParams()));
Map<String, Object> params = Collections.singletonMap("name", 123);
Object result = operation
.invoke(new InvocationContext(mock(SecurityContext.class), params));
@ -118,10 +119,10 @@ public class DiscoveredOperationsFactoryTests {
TestOperationInvokerAdvisor advisor = new TestOperationInvokerAdvisor();
this.invokerAdvisors.add(advisor);
TestOperation operation = getFirst(
this.factory.createOperations("test", new ExampleRead()));
this.factory.createOperations(EndpointId.of("test"), new ExampleRead()));
operation.invoke(new InvocationContext(mock(SecurityContext.class),
Collections.emptyMap()));
assertThat(advisor.getEndpointId()).isEqualTo("test");
assertThat(advisor.getEndpointId()).isEqualTo(EndpointId.of("test"));
assertThat(advisor.getOperationType()).isEqualTo(OperationType.READ);
assertThat(advisor.getParameters()).isEmpty();
}
@ -189,7 +190,7 @@ public class DiscoveredOperationsFactoryTests {
}
@Override
protected TestOperation createOperation(String endpointId,
protected TestOperation createOperation(EndpointId endpointId,
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
return new TestOperation(endpointId, operationMethod, invoker);
}
@ -198,7 +199,7 @@ public class DiscoveredOperationsFactoryTests {
static class TestOperation extends AbstractDiscoveredOperation {
TestOperation(String endpointId, DiscoveredOperationMethod operationMethod,
TestOperation(EndpointId endpointId, DiscoveredOperationMethod operationMethod,
OperationInvoker invoker) {
super(operationMethod, invoker);
}
@ -207,14 +208,14 @@ public class DiscoveredOperationsFactoryTests {
static class TestOperationInvokerAdvisor implements OperationInvokerAdvisor {
private String endpointId;
private EndpointId endpointId;
private OperationType operationType;
private OperationParameters parameters;
@Override
public OperationInvoker apply(String endpointId, OperationType operationType,
public OperationInvoker apply(EndpointId endpointId, OperationType operationType,
OperationParameters parameters, OperationInvoker invoker) {
this.endpointId = endpointId;
this.operationType = operationType;
@ -222,7 +223,14 @@ public class DiscoveredOperationsFactoryTests {
return invoker;
}
public String getEndpointId() {
@Override
@Deprecated
public OperationInvoker apply(String endpointId, OperationType operationType,
OperationParameters parameters, OperationInvoker invoker) {
throw new IllegalStateException();
}
public EndpointId getEndpointId() {
return this.endpointId;
}

@ -35,6 +35,7 @@ import java.util.function.Function;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointFilter;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.Operation;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
@ -125,10 +126,11 @@ public class EndpointDiscovererTests {
public void getEndpointsWhenHasSubclassedEndpointShouldReturnEndpoint() {
load(TestEndpointSubclassConfiguration.class, (context) -> {
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context);
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
Map<EndpointId, TestExposableEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
Map<Method, TestOperation> operations = mapOperations(endpoints.get("test"));
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
Map<Method, TestOperation> operations = mapOperations(
endpoints.get(EndpointId.of("test")));
assertThat(operations).hasSize(5);
assertThat(operations).containsKeys(testEndpointMethods());
assertThat(operations).containsKeys(ReflectionUtils.findMethod(
@ -151,10 +153,11 @@ public class EndpointDiscovererTests {
load(TestEndpointConfiguration.class, (context) -> {
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context,
(endpointId) -> 0L);
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
Map<EndpointId, TestExposableEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
Map<Method, TestOperation> operations = mapOperations(endpoints.get("test"));
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
Map<Method, TestOperation> operations = mapOperations(
endpoints.get(EndpointId.of("test")));
operations.values().forEach((operation) -> assertThat(operation.getInvoker())
.isNotInstanceOf(CachingOperationInvoker.class));
});
@ -165,10 +168,11 @@ public class EndpointDiscovererTests {
load(TestEndpointConfiguration.class, (context) -> {
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context,
(endpointId) -> (endpointId.equals("foo") ? 500L : 0L));
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
Map<EndpointId, TestExposableEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
Map<Method, TestOperation> operations = mapOperations(endpoints.get("test"));
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
Map<Method, TestOperation> operations = mapOperations(
endpoints.get(EndpointId.of("test")));
operations.values().forEach((operation) -> assertThat(operation.getInvoker())
.isNotInstanceOf(CachingOperationInvoker.class));
});
@ -178,11 +182,13 @@ public class EndpointDiscovererTests {
public void getEndpointsWhenTtlSetByIdAndIdMatchesShouldCacheInvokeCalls() {
load(TestEndpointConfiguration.class, (context) -> {
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context,
(endpointId) -> (endpointId.equals("test") ? 500L : 0L));
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
(endpointId) -> (endpointId.equals(EndpointId.of("test")) ? 500L
: 0L));
Map<EndpointId, TestExposableEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
Map<Method, TestOperation> operations = mapOperations(endpoints.get("test"));
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
Map<Method, TestOperation> operations = mapOperations(
endpoints.get(EndpointId.of("test")));
TestOperation getAll = operations.get(findTestEndpointMethod("getAll"));
TestOperation getOne = operations
.get(findTestEndpointMethod("getOne", String.class));
@ -201,9 +207,9 @@ public class EndpointDiscovererTests {
public void getEndpointsWhenHasSpecializedFiltersInNonSpecializedDiscovererShouldFilterEndpoints() {
load(SpecializedEndpointsConfiguration.class, (context) -> {
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context);
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
Map<EndpointId, TestExposableEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
});
}
@ -212,9 +218,10 @@ public class EndpointDiscovererTests {
load(SpecializedEndpointsConfiguration.class, (context) -> {
SpecializedEndpointDiscoverer discoverer = new SpecializedEndpointDiscoverer(
context);
Map<String, SpecializedExposableEndpoint> endpoints = mapEndpoints(
Map<EndpointId, SpecializedExposableEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test", "specialized");
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"),
EndpointId.of("specialized"));
});
}
@ -223,10 +230,10 @@ public class EndpointDiscovererTests {
load(SpecializedEndpointsConfiguration.class, (context) -> {
SpecializedEndpointDiscoverer discoverer = new SpecializedEndpointDiscoverer(
context);
Map<String, SpecializedExposableEndpoint> endpoints = mapEndpoints(
Map<EndpointId, SpecializedExposableEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
Map<Method, SpecializedOperation> operations = mapOperations(
endpoints.get("specialized"));
endpoints.get(EndpointId.of("specialized")));
assertThat(operations).containsKeys(
ReflectionUtils.findMethod(SpecializedExtension.class, "getSpecial"));
@ -238,10 +245,10 @@ public class EndpointDiscovererTests {
load(SubSpecializedEndpointsConfiguration.class, (context) -> {
SpecializedEndpointDiscoverer discoverer = new SpecializedEndpointDiscoverer(
context);
Map<String, SpecializedExposableEndpoint> endpoints = mapEndpoints(
Map<EndpointId, SpecializedExposableEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
Map<Method, SpecializedOperation> operations = mapOperations(
endpoints.get("specialized"));
endpoints.get(EndpointId.of("specialized")));
assertThat(operations).containsKeys(
ReflectionUtils.findMethod(SpecializedTestEndpoint.class, "getAll"));
assertThat(operations).containsKeys(ReflectionUtils.findMethod(
@ -261,18 +268,19 @@ public class EndpointDiscovererTests {
};
SpecializedEndpointDiscoverer discoverer = new SpecializedEndpointDiscoverer(
context, Collections.singleton(filter));
Map<String, SpecializedExposableEndpoint> endpoints = mapEndpoints(
Map<EndpointId, SpecializedExposableEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
});
}
private void hasTestEndpoint(AnnotationConfigApplicationContext context) {
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context);
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
Map<EndpointId, TestExposableEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
Map<Method, TestOperation> operations = mapOperations(endpoints.get("test"));
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
Map<Method, TestOperation> operations = mapOperations(
endpoints.get(EndpointId.of("test")));
assertThat(operations).hasSize(4);
assertThat(operations).containsKeys();
}
@ -290,14 +298,15 @@ public class EndpointDiscovererTests {
return ReflectionUtils.findMethod(TestEndpoint.class, name, paramTypes);
}
private <E extends ExposableEndpoint<?>> Map<String, E> mapEndpoints(
private <E extends ExposableEndpoint<?>> Map<EndpointId, E> mapEndpoints(
Collection<E> endpoints) {
Map<String, E> byId = new LinkedHashMap<>();
Map<EndpointId, E> byId = new LinkedHashMap<>();
endpoints.forEach((endpoint) -> {
E existing = byId.put(endpoint.getId(), endpoint);
E existing = byId.put(endpoint.getEndpointId(), endpoint);
if (existing != null) {
throw new AssertionError(String.format(
"Found endpoints with duplicate id '%s'", endpoint.getId()));
throw new AssertionError(
String.format("Found endpoints with duplicate id '%s'",
endpoint.getEndpointId()));
}
});
return byId;
@ -491,12 +500,12 @@ public class EndpointDiscovererTests {
}
TestEndpointDiscoverer(ApplicationContext applicationContext,
Function<String, Long> timeToLive) {
Function<EndpointId, Long> timeToLive) {
this(applicationContext, timeToLive, Collections.emptyList());
}
TestEndpointDiscoverer(ApplicationContext applicationContext,
Function<String, Long> timeToLive,
Function<EndpointId, Long> timeToLive,
Collection<EndpointFilter<TestExposableEndpoint>> filters) {
this(applicationContext, new ConversionServiceParameterValueMapper(),
Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)),
@ -513,6 +522,12 @@ public class EndpointDiscovererTests {
@Override
protected TestExposableEndpoint createEndpoint(Object endpointBean, String id,
boolean enabledByDefault, Collection<TestOperation> operations) {
throw new IllegalStateException();
}
@Override
protected TestExposableEndpoint createEndpoint(Object endpointBean, EndpointId id,
boolean enabledByDefault, Collection<TestOperation> operations) {
return new TestExposableEndpoint(this, endpointBean, id, enabledByDefault,
operations);
}
@ -548,6 +563,13 @@ public class EndpointDiscovererTests {
protected SpecializedExposableEndpoint createEndpoint(Object endpointBean,
String id, boolean enabledByDefault,
Collection<SpecializedOperation> operations) {
throw new IllegalStateException();
}
@Override
protected SpecializedExposableEndpoint createEndpoint(Object endpointBean,
EndpointId id, boolean enabledByDefault,
Collection<SpecializedOperation> operations) {
return new SpecializedExposableEndpoint(this, endpointBean, id,
enabledByDefault, operations);
}
@ -569,7 +591,7 @@ public class EndpointDiscovererTests {
static class TestExposableEndpoint extends AbstractDiscoveredEndpoint<TestOperation> {
TestExposableEndpoint(EndpointDiscoverer<?, ?> discoverer, Object endpointBean,
String id, boolean enabledByDefault,
EndpointId id, boolean enabledByDefault,
Collection<? extends TestOperation> operations) {
super(discoverer, endpointBean, id, enabledByDefault, operations);
}
@ -580,7 +602,7 @@ public class EndpointDiscovererTests {
extends AbstractDiscoveredEndpoint<SpecializedOperation> {
SpecializedExposableEndpoint(EndpointDiscoverer<?, ?> discoverer,
Object endpointBean, String id, boolean enabledByDefault,
Object endpointBean, EndpointId id, boolean enabledByDefault,
Collection<? extends SpecializedOperation> operations) {
super(discoverer, endpointBean, id, enabledByDefault, operations);
}

@ -24,6 +24,7 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
@ -49,7 +50,7 @@ public class CachingOperationInvokerAdvisorTests {
private OperationInvoker invoker;
@Mock
private Function<String, Long> timeToLive;
private Function<EndpointId, Long> timeToLive;
private CachingOperationInvokerAdvisor advisor;
@ -62,8 +63,8 @@ public class CachingOperationInvokerAdvisorTests {
@Test
public void applyWhenOperationIsNotReadShouldNotAddAdvise() {
OperationParameters parameters = getParameters("get");
OperationInvoker advised = this.advisor.apply("foo", OperationType.WRITE,
parameters, this.invoker);
OperationInvoker advised = this.advisor.apply(EndpointId.of("foo"),
OperationType.WRITE, parameters, this.invoker);
assertThat(advised).isSameAs(this.invoker);
}
@ -71,8 +72,8 @@ public class CachingOperationInvokerAdvisorTests {
public void applyWhenHasAtLeaseOneMandatoryParameterShouldNotAddAdvise() {
OperationParameters parameters = getParameters("getWithParameters", String.class,
String.class);
OperationInvoker advised = this.advisor.apply("foo", OperationType.READ,
parameters, this.invoker);
OperationInvoker advised = this.advisor.apply(EndpointId.of("foo"),
OperationType.READ, parameters, this.invoker);
assertThat(advised).isSameAs(this.invoker);
}
@ -80,20 +81,20 @@ public class CachingOperationInvokerAdvisorTests {
public void applyWhenTimeToLiveReturnsNullShouldNotAddAdvise() {
OperationParameters parameters = getParameters("get");
given(this.timeToLive.apply(any())).willReturn(null);
OperationInvoker advised = this.advisor.apply("foo", OperationType.READ,
parameters, this.invoker);
OperationInvoker advised = this.advisor.apply(EndpointId.of("foo"),
OperationType.READ, parameters, this.invoker);
assertThat(advised).isSameAs(this.invoker);
verify(this.timeToLive).apply("foo");
verify(this.timeToLive).apply(EndpointId.of("foo"));
}
@Test
public void applyWhenTimeToLiveIsZeroShouldNotAddAdvise() {
OperationParameters parameters = getParameters("get");
given(this.timeToLive.apply(any())).willReturn(0L);
OperationInvoker advised = this.advisor.apply("foo", OperationType.READ,
parameters, this.invoker);
OperationInvoker advised = this.advisor.apply(EndpointId.of("foo"),
OperationType.READ, parameters, this.invoker);
assertThat(advised).isSameAs(this.invoker);
verify(this.timeToLive).apply("foo");
verify(this.timeToLive).apply(EndpointId.of("foo"));
}
@Test
@ -120,8 +121,8 @@ public class CachingOperationInvokerAdvisorTests {
}
private void assertAdviseIsApplied(OperationParameters parameters) {
OperationInvoker advised = this.advisor.apply("foo", OperationType.READ,
parameters, this.invoker);
OperationInvoker advised = this.advisor.apply(EndpointId.of("foo"),
OperationType.READ, parameters, this.invoker);
assertThat(advised).isInstanceOf(CachingOperationInvoker.class);
assertThat(advised).hasFieldOrPropertyWithValue("invoker", this.invoker);
assertThat(advised).hasFieldOrPropertyWithValue("timeToLive", 100L);

@ -187,9 +187,8 @@ public class JmxEndpointExporterTests {
@Override
public ObjectName getObjectName(ExposableJmxEndpoint endpoint)
throws MalformedObjectNameException {
return (endpoint != null)
? new ObjectName("boot:type=Endpoint,name=" + endpoint.getId())
: null;
return (endpoint != null) ? new ObjectName(
"boot:type=Endpoint,name=" + endpoint.getEndpointId()) : null;
}
}

@ -25,6 +25,7 @@ import java.util.Map;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
@ -126,8 +127,8 @@ public class DiscoveredJmxOperationTests {
annotationAttributes.put("produces", "application/xml");
DiscoveredOperationMethod operationMethod = new DiscoveredOperationMethod(method,
OperationType.READ, annotationAttributes);
DiscoveredJmxOperation operation = new DiscoveredJmxOperation("test",
operationMethod, mock(OperationInvoker.class));
DiscoveredJmxOperation operation = new DiscoveredJmxOperation(
EndpointId.of("test"), operationMethod, mock(OperationInvoker.class));
return operation;
}

@ -26,6 +26,7 @@ import java.util.function.Function;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
@ -66,10 +67,10 @@ public class JmxEndpointDiscovererTests {
@Test
public void getEndpointsShouldDiscoverStandardEndpoints() {
load(TestEndpoint.class, (discoverer) -> {
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
assertThat(endpoints).containsOnlyKeys("test");
Map<EndpointId, ExposableJmxEndpoint> endpoints = discover(discoverer);
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
Map<String, JmxOperation> operationByName = mapOperations(
endpoints.get("test").getOperations());
endpoints.get(EndpointId.of("test")).getOperations());
assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
"update", "deleteSomething");
JmxOperation getAll = operationByName.get("getAll");
@ -102,8 +103,9 @@ public class JmxEndpointDiscovererTests {
@Test
public void getEndpointsWhenHasFilteredEndpointShouldOnlyDiscoverJmxEndpoints() {
load(MultipleEndpointsConfiguration.class, (discoverer) -> {
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
assertThat(endpoints).containsOnlyKeys("test", "jmx");
Map<EndpointId, ExposableJmxEndpoint> endpoints = discover(discoverer);
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"),
EndpointId.of("jmx"));
});
}
@ -118,19 +120,19 @@ public class JmxEndpointDiscovererTests {
@Test
public void getEndpointsWhenHasJmxExtensionShouldOverrideStandardEndpoint() {
load(OverriddenOperationJmxEndpointConfiguration.class, (discoverer) -> {
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
assertThat(endpoints).containsOnlyKeys("test");
assertJmxTestEndpoint(endpoints.get("test"));
Map<EndpointId, ExposableJmxEndpoint> endpoints = discover(discoverer);
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
assertJmxTestEndpoint(endpoints.get(EndpointId.of("test")));
});
}
@Test
public void getEndpointsWhenHasJmxExtensionWithNewOperationAddsExtraOperation() {
load(AdditionalOperationJmxEndpointConfiguration.class, (discoverer) -> {
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
assertThat(endpoints).containsOnlyKeys("test");
Map<EndpointId, ExposableJmxEndpoint> endpoints = discover(discoverer);
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
Map<String, JmxOperation> operationByName = mapOperations(
endpoints.get("test").getOperations());
endpoints.get(EndpointId.of("test")).getOperations());
assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
"update", "deleteSomething", "getAnother");
JmxOperation getAnother = operationByName.get("getAnother");
@ -143,10 +145,10 @@ public class JmxEndpointDiscovererTests {
@Test
public void getEndpointsWhenHasCacheWithTtlShouldCacheReadOperationWithTtlValue() {
load(TestEndpoint.class, (id) -> 500L, (discoverer) -> {
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
assertThat(endpoints).containsOnlyKeys("test");
Map<EndpointId, ExposableJmxEndpoint> endpoints = discover(discoverer);
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
Map<String, JmxOperation> operationByName = mapOperations(
endpoints.get("test").getOperations());
endpoints.get(EndpointId.of("test")).getOperations());
assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
"update", "deleteSomething");
JmxOperation getAll = operationByName.get("getAll");
@ -160,10 +162,11 @@ public class JmxEndpointDiscovererTests {
public void getEndpointsShouldCacheReadOperations() {
load(AdditionalOperationJmxEndpointConfiguration.class, (id) -> 500L,
(discoverer) -> {
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
assertThat(endpoints).containsOnlyKeys("test");
Map<EndpointId, ExposableJmxEndpoint> endpoints = discover(
discoverer);
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
Map<String, JmxOperation> operationByName = mapOperations(
endpoints.get("test").getOperations());
endpoints.get(EndpointId.of("test")).getOperations());
assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
"update", "deleteSomething", "getAnother");
JmxOperation getAll = operationByName.get("getAll");
@ -269,10 +272,11 @@ public class JmxEndpointDiscovererTests {
assertThat(parameter.getType()).isEqualTo(type);
}
private Map<String, ExposableJmxEndpoint> discover(JmxEndpointDiscoverer discoverer) {
Map<String, ExposableJmxEndpoint> byId = new HashMap<>();
private Map<EndpointId, ExposableJmxEndpoint> discover(
JmxEndpointDiscoverer discoverer) {
Map<EndpointId, ExposableJmxEndpoint> byId = new HashMap<>();
discoverer.getEndpoints()
.forEach((endpoint) -> byId.put(endpoint.getId(), endpoint));
.forEach((endpoint) -> byId.put(endpoint.getEndpointId(), endpoint));
return byId;
}
@ -286,7 +290,7 @@ public class JmxEndpointDiscovererTests {
load(configuration, (id) -> null, consumer);
}
private void load(Class<?> configuration, Function<String, Long> timeToLive,
private void load(Class<?> configuration, Function<EndpointId, Long> timeToLive,
Consumer<JmxEndpointDiscoverer> consumer) {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
configuration)) {

@ -24,6 +24,7 @@ import java.util.Map;
import org.assertj.core.api.Condition;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.web.annotation.ExposableControllerEndpoint;
@ -62,7 +63,7 @@ public class EndpointLinksResolverTests {
operations.add(operationWithPath("/alpha", "alpha"));
operations.add(operationWithPath("/alpha/{name}", "alpha-name"));
ExposableWebEndpoint endpoint = mock(ExposableWebEndpoint.class);
given(endpoint.getId()).willReturn("alpha");
given(endpoint.getEndpointId()).willReturn(EndpointId.of("alpha"));
given(endpoint.isEnableByDefault()).willReturn(true);
given(endpoint.getOperations()).willReturn(operations);
String requestUrl = "https://api.example.com/actuator";
@ -80,7 +81,7 @@ public class EndpointLinksResolverTests {
@Test
public void resolvedLinksContainsALinkForServletEndpoint() {
ExposableServletEndpoint servletEndpoint = mock(ExposableServletEndpoint.class);
given(servletEndpoint.getId()).willReturn("alpha");
given(servletEndpoint.getEndpointId()).willReturn(EndpointId.of("alpha"));
given(servletEndpoint.isEnableByDefault()).willReturn(true);
given(servletEndpoint.getRootPath()).willReturn("alpha");
String requestUrl = "https://api.example.com/actuator";
@ -97,7 +98,7 @@ public class EndpointLinksResolverTests {
public void resolvedLinksContainsALinkForControllerEndpoint() {
ExposableControllerEndpoint controllerEndpoint = mock(
ExposableControllerEndpoint.class);
given(controllerEndpoint.getId()).willReturn("alpha");
given(controllerEndpoint.getEndpointId()).willReturn(EndpointId.of("alpha"));
given(controllerEndpoint.isEnableByDefault()).willReturn(true);
given(controllerEndpoint.getRootPath()).willReturn("alpha");
String requestUrl = "https://api.example.com/actuator";

@ -22,6 +22,7 @@ import java.util.List;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.EndpointsSupplier;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.Operation;
@ -58,38 +59,41 @@ public class PathMappedEndpointsTests {
public void iteratorShouldReturnPathMappedEndpoints() {
PathMappedEndpoints mapped = createTestMapped(null);
assertThat(mapped).hasSize(2);
assertThat(mapped).extracting("id").containsExactly("e2", "e3");
assertThat(mapped).extracting("endpointId").containsExactly(EndpointId.of("e2"),
EndpointId.of("e3"));
}
@Test
public void streamShouldReturnPathMappedEndpoints() {
PathMappedEndpoints mapped = createTestMapped(null);
assertThat(mapped.stream()).hasSize(2);
assertThat(mapped.stream()).extracting("id").containsExactly("e2", "e3");
assertThat(mapped.stream()).extracting("endpointId")
.containsExactly(EndpointId.of("e2"), EndpointId.of("e3"));
}
@Test
public void getRootPathWhenContainsIdShouldReturnRootPath() {
PathMappedEndpoints mapped = createTestMapped(null);
assertThat(mapped.getRootPath("e2")).isEqualTo("p2");
assertThat(mapped.getRootPath(EndpointId.of("e2"))).isEqualTo("p2");
}
@Test
public void getRootPathWhenMissingIdShouldReturnNull() {
PathMappedEndpoints mapped = createTestMapped(null);
assertThat(mapped.getRootPath("xx")).isNull();
assertThat(mapped.getRootPath(EndpointId.of("xx"))).isNull();
}
@Test
public void getPathWhenContainsIdShouldReturnRootPath() {
assertThat(createTestMapped(null).getPath("e2")).isEqualTo("/p2");
assertThat(createTestMapped("/x").getPath("e2")).isEqualTo("/x/p2");
assertThat(createTestMapped(null).getPath(EndpointId.of("e2"))).isEqualTo("/p2");
assertThat(createTestMapped("/x").getPath(EndpointId.of("e2")))
.isEqualTo("/x/p2");
}
@Test
public void getPathWhenMissingIdShouldReturnNull() {
PathMappedEndpoints mapped = createTestMapped(null);
assertThat(mapped.getRootPath("xx")).isNull();
assertThat(mapped.getPath(EndpointId.of("xx"))).isNull();
}
@Test
@ -108,34 +112,34 @@ public class PathMappedEndpointsTests {
@Test
public void getEndpointWhenContainsIdShouldReturnPathMappedEndpoint() {
PathMappedEndpoints mapped = createTestMapped(null);
assertThat(mapped.getEndpoint("e2").getRootPath()).isEqualTo("p2");
assertThat(mapped.getEndpoint(EndpointId.of("e2")).getRootPath()).isEqualTo("p2");
}
@Test
public void getEndpointWhenMissingIdShouldReturnNull() {
PathMappedEndpoints mapped = createTestMapped(null);
assertThat(mapped.getEndpoint("xx")).isNull();
assertThat(mapped.getEndpoint(EndpointId.of("xx"))).isNull();
}
private PathMappedEndpoints createTestMapped(String basePath) {
List<ExposableEndpoint<?>> endpoints = new ArrayList<>();
endpoints.add(mockEndpoint("e1"));
endpoints.add(mockEndpoint("e2", "p2"));
endpoints.add(mockEndpoint("e3", "p3"));
endpoints.add(mockEndpoint("e4"));
endpoints.add(mockEndpoint(EndpointId.of("e1")));
endpoints.add(mockEndpoint(EndpointId.of("e2"), "p2"));
endpoints.add(mockEndpoint(EndpointId.of("e3"), "p3"));
endpoints.add(mockEndpoint(EndpointId.of("e4")));
return new PathMappedEndpoints(basePath, () -> endpoints);
}
private TestPathMappedEndpoint mockEndpoint(String id, String rootPath) {
private TestPathMappedEndpoint mockEndpoint(EndpointId id, String rootPath) {
TestPathMappedEndpoint endpoint = mock(TestPathMappedEndpoint.class);
given(endpoint.getId()).willReturn(id);
given(endpoint.getEndpointId()).willReturn(id);
given(endpoint.getRootPath()).willReturn(rootPath);
return endpoint;
}
private TestEndpoint mockEndpoint(String id) {
private TestEndpoint mockEndpoint(EndpointId id) {
TestEndpoint endpoint = mock(TestEndpoint.class);
given(endpoint.getId()).willReturn(id);
given(endpoint.getEndpointId()).willReturn(id);
return endpoint;
}

@ -33,6 +33,8 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.actuate.endpoint.EndpointId;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.any;
@ -120,7 +122,7 @@ public class ServletEndpointRegistrarTests {
private ExposableServletEndpoint mockEndpoint(EndpointServlet endpointServlet) {
ExposableServletEndpoint endpoint = mock(ExposableServletEndpoint.class);
given(endpoint.getId()).willReturn("test");
given(endpoint.getEndpointId()).willReturn(EndpointId.of("test"));
given(endpoint.getEndpointServlet()).willReturn(endpointServlet);
given(endpoint.getRootPath()).willReturn("test");
return endpoint;

@ -24,6 +24,7 @@ import java.util.stream.Collectors;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.DiscoveredEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
@ -66,7 +67,8 @@ public class ControllerEndpointDiscovererTests {
.getEndpoints();
assertThat(endpoints).hasSize(1);
ExposableControllerEndpoint endpoint = endpoints.iterator().next();
assertThat(endpoint.getId()).isEqualTo("testcontroller");
assertThat(endpoint.getEndpointId())
.isEqualTo(EndpointId.of("testcontroller"));
assertThat(endpoint.getController())
.isInstanceOf(TestControllerEndpoint.class);
assertThat(endpoint).isInstanceOf(DiscoveredEndpoint.class);
@ -83,7 +85,8 @@ public class ControllerEndpointDiscovererTests {
.getEndpoints();
assertThat(endpoints).hasSize(1);
ExposableControllerEndpoint endpoint = endpoints.iterator().next();
assertThat(endpoint.getId()).isEqualTo("testcontroller");
assertThat(endpoint.getEndpointId())
.isEqualTo(EndpointId.of("testcontroller"));
assertThat(endpoint.getController())
.isInstanceOf(TestProxyControllerEndpoint.class);
assertThat(endpoint).isInstanceOf(DiscoveredEndpoint.class);
@ -98,7 +101,8 @@ public class ControllerEndpointDiscovererTests {
.getEndpoints();
assertThat(endpoints).hasSize(1);
ExposableControllerEndpoint endpoint = endpoints.iterator().next();
assertThat(endpoint.getId()).isEqualTo("testrestcontroller");
assertThat(endpoint.getEndpointId())
.isEqualTo(EndpointId.of("testrestcontroller"));
assertThat(endpoint.getController())
.isInstanceOf(TestRestControllerEndpoint.class);
}));
@ -114,7 +118,8 @@ public class ControllerEndpointDiscovererTests {
.getEndpoints();
assertThat(endpoints).hasSize(1);
ExposableControllerEndpoint endpoint = endpoints.iterator().next();
assertThat(endpoint.getId()).isEqualTo("testrestcontroller");
assertThat(endpoint.getEndpointId())
.isEqualTo(EndpointId.of("testrestcontroller"));
assertThat(endpoint.getController())
.isInstanceOf(TestProxyRestControllerEndpoint.class);
assertThat(endpoint).isInstanceOf(DiscoveredEndpoint.class);
@ -127,9 +132,11 @@ public class ControllerEndpointDiscovererTests {
.run(assertDiscoverer((discoverer) -> {
Collection<ExposableControllerEndpoint> endpoints = discoverer
.getEndpoints();
List<String> ids = endpoints.stream().map(ExposableEndpoint::getId)
List<EndpointId> ids = endpoints.stream()
.map(ExposableEndpoint::getEndpointId)
.collect(Collectors.toList());
assertThat(ids).containsOnly("testcontroller", "testrestcontroller");
assertThat(ids).containsOnly(EndpointId.of("testcontroller"),
EndpointId.of("testrestcontroller"));
}));
}

@ -31,6 +31,7 @@ import javax.servlet.ServletResponse;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.DiscoveredEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
@ -75,7 +76,8 @@ public class ServletEndpointDiscovererTests {
.getEndpoints();
assertThat(endpoints).hasSize(1);
ExposableServletEndpoint endpoint = endpoints.iterator().next();
assertThat(endpoint.getId()).isEqualTo("testservlet");
assertThat(endpoint.getEndpointId())
.isEqualTo(EndpointId.of("testservlet"));
assertThat(endpoint.getEndpointServlet()).isNotNull();
assertThat(endpoint).isInstanceOf(DiscoveredEndpoint.class);
}));
@ -91,7 +93,8 @@ public class ServletEndpointDiscovererTests {
.getEndpoints();
assertThat(endpoints).hasSize(1);
ExposableServletEndpoint endpoint = endpoints.iterator().next();
assertThat(endpoint.getId()).isEqualTo("testservlet");
assertThat(endpoint.getEndpointId())
.isEqualTo(EndpointId.of("testservlet"));
assertThat(endpoint.getEndpointServlet()).isNotNull();
assertThat(endpoint).isInstanceOf(DiscoveredEndpoint.class);
}));
@ -103,9 +106,10 @@ public class ServletEndpointDiscovererTests {
.run(assertDiscoverer((discoverer) -> {
Collection<ExposableServletEndpoint> endpoints = discoverer
.getEndpoints();
List<String> ids = endpoints.stream().map(ExposableEndpoint::getId)
List<EndpointId> ids = endpoints.stream()
.map(ExposableEndpoint::getEndpointId)
.collect(Collectors.toList());
assertThat(ids).containsOnly("testservlet");
assertThat(ids).containsOnly(EndpointId.of("testservlet"));
}));
}

@ -31,6 +31,7 @@ import java.util.stream.Stream;
import org.assertj.core.api.Condition;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
@ -85,19 +86,19 @@ public class WebEndpointDiscovererTests {
@Test
public void getEndpointsWhenHasFilteredEndpointShouldOnlyDiscoverWebEndpoints() {
load(MultipleEndpointsConfiguration.class, (discoverer) -> {
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
Map<EndpointId, ExposableWebEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
});
}
@Test
public void getEndpointsWhenHasWebExtensionShouldOverrideStandardEndpoint() {
load(OverriddenOperationWebEndpointExtensionConfiguration.class, (discoverer) -> {
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
Map<EndpointId, ExposableWebEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
ExposableWebEndpoint endpoint = endpoints.get("test");
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
ExposableWebEndpoint endpoint = endpoints.get(EndpointId.of("test"));
assertThat(requestPredicates(endpoint)).has(
requestPredicates(path("test").httpMethod(WebEndpointHttpMethod.GET)
.consumes().produces("application/json")));
@ -107,10 +108,10 @@ public class WebEndpointDiscovererTests {
@Test
public void getEndpointsWhenExtensionAddsOperationShouldHaveBothOperations() {
load(AdditionalOperationWebEndpointConfiguration.class, (discoverer) -> {
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
Map<EndpointId, ExposableWebEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
ExposableWebEndpoint endpoint = endpoints.get("test");
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
ExposableWebEndpoint endpoint = endpoints.get(EndpointId.of("test"));
assertThat(requestPredicates(endpoint)).has(requestPredicates(
path("test").httpMethod(WebEndpointHttpMethod.GET).consumes()
.produces("application/json"),
@ -122,10 +123,10 @@ public class WebEndpointDiscovererTests {
@Test
public void getEndpointsWhenPredicateForWriteOperationThatReturnsVoidShouldHaveNoProducedMediaTypes() {
load(VoidWriteOperationEndpointConfiguration.class, (discoverer) -> {
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
Map<EndpointId, ExposableWebEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("voidwrite");
ExposableWebEndpoint endpoint = endpoints.get("voidwrite");
assertThat(endpoints).containsOnlyKeys(EndpointId.of("voidwrite"));
ExposableWebEndpoint endpoint = endpoints.get(EndpointId.of("voidwrite"));
assertThat(requestPredicates(endpoint)).has(requestPredicates(
path("voidwrite").httpMethod(WebEndpointHttpMethod.POST).produces()
.consumes("application/json")));
@ -182,10 +183,10 @@ public class WebEndpointDiscovererTests {
@Test
public void getEndpointsWhenHasCacheWithTtlShouldCacheReadOperationWithTtlValue() {
load((id) -> 500L, (id) -> id, TestEndpointConfiguration.class, (discoverer) -> {
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
Map<EndpointId, ExposableWebEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
ExposableWebEndpoint endpoint = endpoints.get("test");
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
ExposableWebEndpoint endpoint = endpoints.get(EndpointId.of("test"));
assertThat(endpoint.getOperations()).hasSize(1);
WebOperation operation = endpoint.getOperations().iterator().next();
Object invoker = ReflectionTestUtils.getField(operation, "invoker");
@ -198,10 +199,10 @@ public class WebEndpointDiscovererTests {
@Test
public void getEndpointsWhenOperationReturnsResourceShouldProduceApplicationOctetStream() {
load(ResourceEndpointConfiguration.class, (discoverer) -> {
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
Map<EndpointId, ExposableWebEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("resource");
ExposableWebEndpoint endpoint = endpoints.get("resource");
assertThat(endpoints).containsOnlyKeys(EndpointId.of("resource"));
ExposableWebEndpoint endpoint = endpoints.get(EndpointId.of("resource"));
assertThat(requestPredicates(endpoint)).has(requestPredicates(
path("resource").httpMethod(WebEndpointHttpMethod.GET).consumes()
.produces("application/octet-stream")));
@ -211,10 +212,11 @@ public class WebEndpointDiscovererTests {
@Test
public void getEndpointsWhenHasCustomMediaTypeShouldProduceCustomMediaType() {
load(CustomMediaTypesEndpointConfiguration.class, (discoverer) -> {
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
Map<EndpointId, ExposableWebEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("custommediatypes");
ExposableWebEndpoint endpoint = endpoints.get("custommediatypes");
assertThat(endpoints).containsOnlyKeys(EndpointId.of("custommediatypes"));
ExposableWebEndpoint endpoint = endpoints
.get(EndpointId.of("custommediatypes"));
assertThat(requestPredicates(endpoint)).has(requestPredicates(
path("custommediatypes").httpMethod(WebEndpointHttpMethod.GET)
.consumes().produces("text/plain"),
@ -229,10 +231,10 @@ public class WebEndpointDiscovererTests {
public void getEndpointsWhenHasCustomPathShouldReturnCustomPath() {
load((id) -> null, (id) -> "custom/" + id,
AdditionalOperationWebEndpointConfiguration.class, (discoverer) -> {
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
Map<EndpointId, ExposableWebEndpoint> endpoints = mapEndpoints(
discoverer.getEndpoints());
assertThat(endpoints).containsOnlyKeys("test");
ExposableWebEndpoint endpoint = endpoints.get("test");
assertThat(endpoints).containsOnlyKeys(EndpointId.of("test"));
ExposableWebEndpoint endpoint = endpoints.get(EndpointId.of("test"));
Condition<List<? extends WebOperationRequestPredicate>> expected = requestPredicates(
path("custom/test").httpMethod(WebEndpointHttpMethod.GET)
.consumes().produces("application/json"),
@ -246,8 +248,9 @@ public class WebEndpointDiscovererTests {
this.load((id) -> null, (id) -> id, configuration, consumer);
}
private void load(Function<String, Long> timeToLive, PathMapper endpointPathMapper,
Class<?> configuration, Consumer<WebEndpointDiscoverer> consumer) {
private void load(Function<EndpointId, Long> timeToLive,
PathMapper endpointPathMapper, Class<?> configuration,
Consumer<WebEndpointDiscoverer> consumer) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
configuration);
try {
@ -267,10 +270,11 @@ public class WebEndpointDiscovererTests {
}
}
private Map<String, ExposableWebEndpoint> mapEndpoints(
private Map<EndpointId, ExposableWebEndpoint> mapEndpoints(
Collection<ExposableWebEndpoint> endpoints) {
Map<String, ExposableWebEndpoint> endpointById = new HashMap<>();
endpoints.forEach((endpoint) -> endpointById.put(endpoint.getId(), endpoint));
Map<EndpointId, ExposableWebEndpoint> endpointById = new HashMap<>();
endpoints.forEach(
(endpoint) -> endpointById.put(endpoint.getEndpointId(), endpoint));
return endpointById;
}

@ -20,6 +20,7 @@ import java.util.Arrays;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint;
import org.springframework.boot.actuate.endpoint.web.annotation.ExposableControllerEndpoint;
@ -117,22 +118,22 @@ public class ControllerEndpointHandlerMappingTests {
}
private ExposableControllerEndpoint firstEndpoint() {
return mockEndpoint("first", new FirstTestMvcEndpoint());
return mockEndpoint(EndpointId.of("first"), new FirstTestMvcEndpoint());
}
private ExposableControllerEndpoint secondEndpoint() {
return mockEndpoint("second", new SecondTestMvcEndpoint());
return mockEndpoint(EndpointId.of("second"), new SecondTestMvcEndpoint());
}
private ExposableControllerEndpoint pathlessEndpoint() {
return mockEndpoint("pathless", new PathlessControllerEndpoint());
return mockEndpoint(EndpointId.of("pathless"), new PathlessControllerEndpoint());
}
private ExposableControllerEndpoint mockEndpoint(String id, Object controller) {
private ExposableControllerEndpoint mockEndpoint(EndpointId id, Object controller) {
ExposableControllerEndpoint endpoint = mock(ExposableControllerEndpoint.class);
given(endpoint.getId()).willReturn(id);
given(endpoint.getEndpointId()).willReturn(id);
given(endpoint.getController()).willReturn(controller);
given(endpoint.getRootPath()).willReturn(id);
given(endpoint.getRootPath()).willReturn(id.toString());
return endpoint;
}

@ -20,6 +20,7 @@ import java.util.Arrays;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint;
import org.springframework.boot.actuate.endpoint.web.annotation.ExposableControllerEndpoint;
@ -109,22 +110,22 @@ public class ControllerEndpointHandlerMappingTests {
}
private ExposableControllerEndpoint firstEndpoint() {
return mockEndpoint("first", new FirstTestMvcEndpoint());
return mockEndpoint(EndpointId.of("first"), new FirstTestMvcEndpoint());
}
private ExposableControllerEndpoint secondEndpoint() {
return mockEndpoint("second", new SecondTestMvcEndpoint());
return mockEndpoint(EndpointId.of("second"), new SecondTestMvcEndpoint());
}
private ExposableControllerEndpoint pathlessEndpoint() {
return mockEndpoint("pathless", new PathlessControllerEndpoint());
return mockEndpoint(EndpointId.of("pathless"), new PathlessControllerEndpoint());
}
private ExposableControllerEndpoint mockEndpoint(String id, Object controller) {
private ExposableControllerEndpoint mockEndpoint(EndpointId id, Object controller) {
ExposableControllerEndpoint endpoint = mock(ExposableControllerEndpoint.class);
given(endpoint.getId()).willReturn(id);
given(endpoint.getEndpointId()).willReturn(id);
given(endpoint.getController()).willReturn(controller);
given(endpoint.getRootPath()).willReturn(id);
given(endpoint.getRootPath()).willReturn(id.toString());
return endpoint;
}

@ -48,7 +48,7 @@ public class MetricsWebFilterTests {
MockClock clock = new MockClock();
this.registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, clock);
this.webFilter = new MetricsWebFilter(this.registry,
new DefaultWebFluxTagsProvider(), REQUEST_METRICS_NAME);
new DefaultWebFluxTagsProvider(), REQUEST_METRICS_NAME, true);
}
@Test

Loading…
Cancel
Save