|
|
|
@ -18,6 +18,7 @@ package org.springframework.boot.actuate.endpoint.invoker.cache;
|
|
|
|
|
|
|
|
|
|
import java.security.Principal;
|
|
|
|
|
import java.time.Duration;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.Iterator;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Map.Entry;
|
|
|
|
@ -28,7 +29,11 @@ import reactor.core.publisher.Mono;
|
|
|
|
|
|
|
|
|
|
import org.springframework.boot.actuate.endpoint.ApiVersion;
|
|
|
|
|
import org.springframework.boot.actuate.endpoint.InvocationContext;
|
|
|
|
|
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
|
|
|
|
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
|
|
|
|
|
import org.springframework.boot.actuate.endpoint.invoke.OperationParameter;
|
|
|
|
|
import org.springframework.boot.actuate.endpoint.invoke.OperationParameters;
|
|
|
|
|
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
|
|
|
|
|
import org.springframework.util.Assert;
|
|
|
|
|
import org.springframework.util.ClassUtils;
|
|
|
|
|
import org.springframework.util.ConcurrentReferenceHashMap;
|
|
|
|
@ -85,9 +90,7 @@ public class CachingOperationInvoker implements OperationInvoker {
|
|
|
|
|
if (this.cachedResponses.size() > CACHE_CLEANUP_THRESHOLD) {
|
|
|
|
|
cleanExpiredCachedResponses(accessTime);
|
|
|
|
|
}
|
|
|
|
|
ApiVersion contextApiVersion = context.resolveArgument(ApiVersion.class);
|
|
|
|
|
Principal principal = context.resolveArgument(Principal.class);
|
|
|
|
|
CacheKey cacheKey = new CacheKey(contextApiVersion, principal);
|
|
|
|
|
CacheKey cacheKey = getCacheKey(context);
|
|
|
|
|
CachedResponse cached = this.cachedResponses.get(cacheKey);
|
|
|
|
|
if (cached == null || cached.isStale(accessTime, this.timeToLive)) {
|
|
|
|
|
Object response = this.invoker.invoke(context);
|
|
|
|
@ -97,6 +100,13 @@ public class CachingOperationInvoker implements OperationInvoker {
|
|
|
|
|
return cached.getResponse();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private CacheKey getCacheKey(InvocationContext context) {
|
|
|
|
|
ApiVersion contextApiVersion = context.resolveArgument(ApiVersion.class);
|
|
|
|
|
Principal principal = context.resolveArgument(Principal.class);
|
|
|
|
|
WebServerNamespace serverNamespace = context.resolveArgument(WebServerNamespace.class);
|
|
|
|
|
return new CacheKey(contextApiVersion, principal, serverNamespace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void cleanExpiredCachedResponses(long accessTime) {
|
|
|
|
|
try {
|
|
|
|
|
Iterator<Entry<CacheKey, CachedResponse>> iterator = this.cachedResponses.entrySet().iterator();
|
|
|
|
@ -126,6 +136,15 @@ public class CachingOperationInvoker implements OperationInvoker {
|
|
|
|
|
return new CachedResponse(response, accessTime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static boolean isApplicable(OperationParameters parameters) {
|
|
|
|
|
for (OperationParameter parameter : parameters) {
|
|
|
|
|
if (parameter.isMandatory() && !CacheKey.containsType(parameter.getType())) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A cached response that encapsulates the response itself and the time at which it
|
|
|
|
|
* was created.
|
|
|
|
@ -174,13 +193,23 @@ public class CachingOperationInvoker implements OperationInvoker {
|
|
|
|
|
|
|
|
|
|
private static final class CacheKey {
|
|
|
|
|
|
|
|
|
|
public static final Class<?>[] CACHEABLE_TYPES = new Class[] { ApiVersion.class, SecurityContext.class,
|
|
|
|
|
WebServerNamespace.class };
|
|
|
|
|
|
|
|
|
|
private final ApiVersion apiVersion;
|
|
|
|
|
|
|
|
|
|
private final Principal principal;
|
|
|
|
|
|
|
|
|
|
private CacheKey(ApiVersion apiVersion, Principal principal) {
|
|
|
|
|
private final WebServerNamespace serverNamespace;
|
|
|
|
|
|
|
|
|
|
private CacheKey(ApiVersion apiVersion, Principal principal, WebServerNamespace serverNamespace) {
|
|
|
|
|
this.principal = principal;
|
|
|
|
|
this.apiVersion = apiVersion;
|
|
|
|
|
this.serverNamespace = serverNamespace;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean containsType(Class<?> type) {
|
|
|
|
|
return Arrays.stream(CacheKey.CACHEABLE_TYPES).anyMatch((c) -> c.isAssignableFrom(type));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@ -193,7 +222,8 @@ public class CachingOperationInvoker implements OperationInvoker {
|
|
|
|
|
}
|
|
|
|
|
CacheKey other = (CacheKey) obj;
|
|
|
|
|
return this.apiVersion.equals(other.apiVersion)
|
|
|
|
|
&& ObjectUtils.nullSafeEquals(this.principal, other.principal);
|
|
|
|
|
&& ObjectUtils.nullSafeEquals(this.principal, other.principal)
|
|
|
|
|
&& ObjectUtils.nullSafeEquals(this.serverNamespace, other.serverNamespace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@ -202,6 +232,7 @@ public class CachingOperationInvoker implements OperationInvoker {
|
|
|
|
|
int result = 1;
|
|
|
|
|
result = prime * result + this.apiVersion.hashCode();
|
|
|
|
|
result = prime * result + ObjectUtils.nullSafeHashCode(this.principal);
|
|
|
|
|
result = prime * result + ObjectUtils.nullSafeHashCode(this.serverNamespace);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|