Commit f540e7ea authored by Stephane Nicoll's avatar Stephane Nicoll

Merge branch '2.2.x'

Closes gh-19090
parents 30863eb0 0b4dcf96
...@@ -19,11 +19,13 @@ package org.springframework.boot.actuate.endpoint.invoker.cache; ...@@ -19,11 +19,13 @@ package org.springframework.boot.actuate.endpoint.invoker.cache;
import java.time.Duration; import java.time.Duration;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.endpoint.InvocationContext; import org.springframework.boot.actuate.endpoint.InvocationContext;
import org.springframework.boot.actuate.endpoint.http.ApiVersion;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker; import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
...@@ -46,7 +48,7 @@ public class CachingOperationInvoker implements OperationInvoker { ...@@ -46,7 +48,7 @@ public class CachingOperationInvoker implements OperationInvoker {
private final long timeToLive; private final long timeToLive;
private volatile CachedResponse cachedResponse; private final Map<ApiVersion, CachedResponse> cachedResponses;
/** /**
* Create a new instance with the target {@link OperationInvoker} to use to compute * Create a new instance with the target {@link OperationInvoker} to use to compute
...@@ -58,6 +60,7 @@ public class CachingOperationInvoker implements OperationInvoker { ...@@ -58,6 +60,7 @@ public class CachingOperationInvoker implements OperationInvoker {
Assert.isTrue(timeToLive > 0, "TimeToLive must be strictly positive"); Assert.isTrue(timeToLive > 0, "TimeToLive must be strictly positive");
this.invoker = invoker; this.invoker = invoker;
this.timeToLive = timeToLive; this.timeToLive = timeToLive;
this.cachedResponses = new ConcurrentHashMap<>();
} }
/** /**
...@@ -74,11 +77,12 @@ public class CachingOperationInvoker implements OperationInvoker { ...@@ -74,11 +77,12 @@ public class CachingOperationInvoker implements OperationInvoker {
return this.invoker.invoke(context); return this.invoker.invoke(context);
} }
long accessTime = System.currentTimeMillis(); long accessTime = System.currentTimeMillis();
CachedResponse cached = this.cachedResponse; ApiVersion contextApiVersion = context.getApiVersion();
CachedResponse cached = this.cachedResponses.get(contextApiVersion);
if (cached == null || cached.isStale(accessTime, this.timeToLive)) { if (cached == null || cached.isStale(accessTime, this.timeToLive)) {
Object response = this.invoker.invoke(context); Object response = this.invoker.invoke(context);
cached = createCachedResponse(response, accessTime); cached = createCachedResponse(response, accessTime);
this.cachedResponse = cached; this.cachedResponses.put(contextApiVersion, cached);
} }
return cached.getResponse(); return cached.getResponse();
} }
......
...@@ -21,6 +21,7 @@ import java.util.function.Function; ...@@ -21,6 +21,7 @@ import java.util.function.Function;
import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.OperationType; import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.SecurityContext; import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.http.ApiVersion;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker; import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor; import org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor;
import org.springframework.boot.actuate.endpoint.invoke.OperationParameter; import org.springframework.boot.actuate.endpoint.invoke.OperationParameter;
...@@ -54,7 +55,8 @@ public class CachingOperationInvokerAdvisor implements OperationInvokerAdvisor { ...@@ -54,7 +55,8 @@ public class CachingOperationInvokerAdvisor implements OperationInvokerAdvisor {
private boolean hasMandatoryParameter(OperationParameters parameters) { private boolean hasMandatoryParameter(OperationParameters parameters) {
for (OperationParameter parameter : parameters) { for (OperationParameter parameter : parameters) {
if (parameter.isMandatory() && !SecurityContext.class.isAssignableFrom(parameter.getType())) { if (parameter.isMandatory() && !ApiVersion.class.isAssignableFrom(parameter.getType())
&& !SecurityContext.class.isAssignableFrom(parameter.getType())) {
return true; return true;
} }
} }
......
...@@ -27,6 +27,7 @@ import org.mockito.MockitoAnnotations; ...@@ -27,6 +27,7 @@ import org.mockito.MockitoAnnotations;
import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.OperationType; import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.SecurityContext; import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.http.ApiVersion;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker; import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
import org.springframework.boot.actuate.endpoint.invoke.OperationParameters; import org.springframework.boot.actuate.endpoint.invoke.OperationParameters;
import org.springframework.boot.actuate.endpoint.invoke.reflect.OperationMethod; import org.springframework.boot.actuate.endpoint.invoke.reflect.OperationMethod;
...@@ -117,6 +118,13 @@ class CachingOperationInvokerAdvisorTests { ...@@ -117,6 +118,13 @@ class CachingOperationInvokerAdvisorTests {
assertAdviseIsApplied(parameters); assertAdviseIsApplied(parameters);
} }
@Test
void applyWithApiVersionShouldAddAdvise() {
OperationParameters parameters = getParameters("getWithApiVersion", ApiVersion.class, String.class);
given(this.timeToLive.apply(any())).willReturn(100L);
assertAdviseIsApplied(parameters);
}
private void assertAdviseIsApplied(OperationParameters parameters) { private void assertAdviseIsApplied(OperationParameters parameters) {
OperationInvoker advised = this.advisor.apply(EndpointId.of("foo"), OperationType.READ, parameters, OperationInvoker advised = this.advisor.apply(EndpointId.of("foo"), OperationType.READ, parameters,
this.invoker); this.invoker);
...@@ -152,6 +160,10 @@ class CachingOperationInvokerAdvisorTests { ...@@ -152,6 +160,10 @@ class CachingOperationInvokerAdvisorTests {
return ""; return "";
} }
String getWithApiVersion(ApiVersion apiVersion, @Nullable String bar) {
return "";
}
} }
} }
...@@ -28,6 +28,7 @@ import reactor.core.publisher.Mono; ...@@ -28,6 +28,7 @@ import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.endpoint.InvocationContext; import org.springframework.boot.actuate.endpoint.InvocationContext;
import org.springframework.boot.actuate.endpoint.SecurityContext; import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.http.ApiVersion;
import org.springframework.boot.actuate.endpoint.invoke.MissingParametersException; import org.springframework.boot.actuate.endpoint.invoke.MissingParametersException;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker; import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
...@@ -152,6 +153,26 @@ class CachingOperationInvokerTests { ...@@ -152,6 +153,26 @@ class CachingOperationInvokerTests {
verify(target, times(2)).invoke(context); verify(target, times(2)).invoke(context);
} }
@Test
void targetInvokedWithDifferentApiVersion() {
OperationInvoker target = mock(OperationInvoker.class);
Object expectedV2 = new Object();
Object expectedV3 = new Object();
InvocationContext contextV2 = new InvocationContext(ApiVersion.V2, mock(SecurityContext.class),
Collections.emptyMap());
InvocationContext contextV3 = new InvocationContext(ApiVersion.V3, mock(SecurityContext.class),
Collections.emptyMap());
given(target.invoke(contextV2)).willReturn(expectedV2);
given(target.invoke(contextV3)).willReturn(expectedV3);
CachingOperationInvoker invoker = new CachingOperationInvoker(target, 500L);
Object response = invoker.invoke(contextV2);
assertThat(response).isSameAs(expectedV2);
verify(target, times(1)).invoke(contextV2);
Object cachedResponse = invoker.invoke(contextV3);
assertThat(cachedResponse).isNotSameAs(response);
verify(target, times(1)).invoke(contextV3);
}
private static class MonoOperationInvoker implements OperationInvoker { private static class MonoOperationInvoker implements OperationInvoker {
static int invocations; static int invocations;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment