Skip to content
Snippets Groups Projects

Iss #73 - Add support to view cache entries through the cache resource

5 files
+ 167
44
Compare changes
  • Side-by-side
  • Inline
Files
5
package org.eclipsefoundation.core.resource;
import java.io.IOException;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
@@ -11,6 +16,7 @@ import javax.inject.Inject;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@@ -32,10 +38,12 @@ import org.eclipsefoundation.core.service.impl.DefaultLoadingCacheManager.Loadin
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.google.auto.value.AutoValue;
import io.quarkus.cache.CaffeineCache;
import io.quarkus.qute.Location;
import io.quarkus.qute.Template;
@@ -55,6 +63,8 @@ public class CacheResource {
CachingService service;
@Inject
LoadingCacheManager lcm;
@Inject
ObjectMapper mapper;
@GET
@Path("keys")
@@ -78,43 +88,93 @@ public class CacheResource {
.build();
}
@GET
@Path("{cacheKey}/value")
@KeySecured
@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN })
@OptionalPath(MicroprofilePropertyNames.CACHE_RESOURCE_ENABLED)
public Response getCacheValue(@PathParam("cacheKey") String cacheKey, @QueryParam("cache_request") String compressedCacheRequest)
throws InterruptedException, ExecutionException, TimeoutException, IOException {
// decode the compressed cache request
CacheRequest request = mapper.readValue(Base64.getDecoder().decode(compressedCacheRequest), CacheRequest.class);
// retrieve the matching key
ParameterizedCacheKey matchingKey = getMatchingKey(cacheKey, request);
if (STANDARD_CACHE_TYPE.equals(request.getCacheType())) {
return buildContentAwareReturn(service.getCache().as(CaffeineCache.class).getIfPresent(matchingKey).get(5, TimeUnit.SECONDS));
} else if (LOADING_CACHE_TYPE.equals(request.getCacheType())) {
return buildContentAwareReturn(
getLoadingCacheForRequest(request).getCache().getIfPresent(matchingKey).get(5, TimeUnit.SECONDS));
}
throw handleBadCacheType(request);
}
@POST
@Path("{cacheKey}/clear")
@KeySecured
@OptionalPath(MicroprofilePropertyNames.CACHE_RESOURCE_ENABLED)
public Response clearForKey(@PathParam("cacheKey") String cacheKey, @HeaderParam("x-cache-key") String passedKey,
CacheClearRequest request) {
CacheRequest request) {
ParameterizedCacheKey matchingKey = getMatchingKey(cacheKey, request);
if (STANDARD_CACHE_TYPE.equals(request.getCacheType())) {
// remove the given keys from the cache
List<ParameterizedCacheKey> keys = service
.getCacheKeys()
.stream()
.filter(k -> k.getId().equals(cacheKey) && checkParams(request.getParams(), k.getParams()))
.collect(Collectors.toList());
keys.forEach(k -> service.remove(k));
service.remove(matchingKey);
return Response.ok().build();
} else if (LOADING_CACHE_TYPE.equals(request.getCacheType())) {
Optional<LoadingCacheWrapper<?>> matchingLoadingCache = lcm
.getManagedCaches()
getLoadingCacheForRequest(request).getCache().synchronous().refresh(matchingKey);
return Response.ok().build();
}
throw handleBadCacheType(request);
}
/**
* Using the cache ID key and the incoming request, retrieve the matching active cache keys given the filters and return
* them.
*
* @param cacheKey the ID key for the cache entry
* @param request the cache request containing additional cache info
* @return
*/
private ParameterizedCacheKey getMatchingKey(String cacheKey, CacheRequest request) {
if (STANDARD_CACHE_TYPE.equals(request.getCacheType())) {
return service
.getCacheKeys()
.stream()
.filter(c -> c.getInnerType().getName().equals(request.getClazz()))
.findFirst();
if (matchingLoadingCache.isEmpty()) {
// if we don't have a match, throw as we can't handle the inner class type
throw new BadRequestException("Passed cache class is invalid: " + LoggingHelper.format(request.getClazz()));
}
// for each of the matching keys, request that cache entry to refresh
LoadingCacheWrapper<?> wrapper = matchingLoadingCache.get();
wrapper
.filter(k -> k.getId().equals(cacheKey) && checkParams(request.getParams(), k.getParams())
&& k.getClazz().getName().equals(request.getClazz()))
.findFirst()
.orElseThrow(() -> new NotFoundException("No cache entry found with given parameters"));
} else if (LOADING_CACHE_TYPE.equals(request.getCacheType())) {
return getLoadingCacheForRequest(request)
.getCacheKeys()
.stream()
.filter(k -> k.getId().equals(cacheKey) && checkParams(request.getParams(), k.getParams()))
.forEach(k -> wrapper.getCache().synchronous().refresh(k));
return Response.ok().build();
.filter(k -> k.getId().equals(cacheKey) && checkParams(request.getParams(), k.getParams())
&& k.getClazz().getName().equals(request.getClazz()))
.findFirst()
.orElseThrow(() -> new NotFoundException("No cache entry found with given parameters"));
}
// if we don't have a match, throw as we can't handle the cache type
throw new BadRequestException("Passed cache type is invalid: " + LoggingHelper.format(request.getCacheType()));
throw handleBadCacheType(request);
}
/**
* Using the cache request, load and return the corresponding cache wrapper for use in looking up keys or minor cache
* modification.
*
* @param request the current cache request
* @return a loading cache wrapper for the given loading cache if it exists, or throws a bad request exception if none
* found.
*/
private LoadingCacheWrapper<?> getLoadingCacheForRequest(CacheRequest request) {
// iterate over the available loading caches to find one with a matching class
Optional<LoadingCacheWrapper<?>> matchingLoadingCache = lcm
.getManagedCaches()
.stream()
.filter(c -> c.getInnerType().getName().equals(request.getClazz()))
.findFirst();
if (matchingLoadingCache.isEmpty()) {
throw handleBadCacheType(request);
}
// for each of the matching keys, request that cache entry to refresh
return matchingLoadingCache.get();
}
/**
@@ -147,6 +207,31 @@ public class CacheResource {
return passedParams.entrySet().stream().allMatch(e -> CollectionUtils.isEqualCollection(e.getValue(), entryParams.get(e.getKey())));
}
/**
* Centralized to reduce repetition when handling bad cache calls, as the message is same across the board.
*
* @param r the bad cache request.
* @return the error to be thrown
*/
private BadRequestException handleBadCacheType(CacheRequest r) {
// if we don't have a match, throw as we can't handle the cache type
return new BadRequestException("Passed cache parameters are not valid: " + LoggingHelper.format(r.toString()));
}
/**
* Builds the response object for the given content, using a plain text response for non-structured data.
*
* @param data the cache data to return.
* @return response to return to the user with proper Content-Type.
*/
private Response buildContentAwareReturn(Object data) {
Response.ResponseBuilder r = Response.ok(data);
if (data instanceof String) {
return r.header("Content-Type", MediaType.TEXT_PLAIN).build();
}
return r.build();
}
/**
* Cache key plus its expiration to be displayed on the view more page.
*
@@ -177,8 +262,8 @@ public class CacheResource {
}
@AutoValue
@JsonDeserialize(builder = AutoValue_CacheResource_CacheClearRequest.Builder.class)
public abstract static class CacheClearRequest {
@JsonDeserialize(builder = AutoValue_CacheResource_CacheRequest.Builder.class)
public abstract static class CacheRequest {
public abstract String getClazz();
public abstract String getCacheType();
@@ -186,7 +271,7 @@ public class CacheResource {
public abstract Map<String, List<String>> getParams();
public static Builder builder() {
return new AutoValue_CacheResource_CacheClearRequest.Builder();
return new AutoValue_CacheResource_CacheRequest.Builder();
}
@AutoValue.Builder
@@ -198,7 +283,7 @@ public class CacheResource {
public abstract Builder setParams(Map<String, List<String>> params);
public abstract CacheClearRequest build();
public abstract CacheRequest build();
}
}
}
Loading