Support mixed case endpoint IDs with time-to-live

Update the endpoint time-to-live binding logic so that mixed case
endpoint IDs are supported. Prior to this commit an
`InvalidConfigurationPropertyNameException` would be thrown when using
a camel case endpoint ID.

See gh-14773
This commit is contained in:
Phillip Webb 2018-10-13 21:01:27 -07:00
parent 3105a38884
commit 138d85477d
8 changed files with 37 additions and 21 deletions

View File

@ -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);
}

View File

@ -73,8 +73,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);

View File

@ -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() {
}
}

View File

@ -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,9 +34,10 @@ 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;
}
@ -43,6 +45,12 @@ public class CachingOperationInvokerAdvisor implements OperationInvokerAdvisor {
@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) {

View File

@ -500,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)),

View File

@ -51,7 +51,7 @@ public class CachingOperationInvokerAdvisorTests {
private OperationInvoker invoker;
@Mock
private Function<String, Long> timeToLive;
private Function<EndpointId, Long> timeToLive;
private CachingOperationInvokerAdvisor advisor;
@ -85,7 +85,7 @@ public class CachingOperationInvokerAdvisorTests {
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
@ -95,7 +95,7 @@ public class CachingOperationInvokerAdvisorTests {
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

View File

@ -306,7 +306,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)) {

View File

@ -257,8 +257,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 {