package org.springframework.boot.actuate.cache;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.lang.Nullable;
@Endpoint(id = "caches")
public class CachesEndpoint {
private final Map<String, CacheManager> cacheManagers;
public CachesEndpoint(Map<String, CacheManager> cacheManagers) {
this.cacheManagers = new LinkedHashMap<>(cacheManagers);
}
@ReadOperation
public CachesReport caches() {
Map<String, Map<String, CacheDescriptor>> descriptors = new LinkedHashMap<>();
getCacheEntries(matchAll(), matchAll()).forEach((entry) -> {
String cacheName = entry.getName();
String cacheManager = entry.getCacheManager();
Map<String, CacheDescriptor> cacheManagerDescriptors = descriptors.computeIfAbsent(cacheManager,
(key) -> new LinkedHashMap<>());
cacheManagerDescriptors.put(cacheName, new CacheDescriptor(entry.getTarget()));
});
Map<String, CacheManagerDescriptor> cacheManagerDescriptors = new LinkedHashMap<>();
descriptors.forEach((name, entries) -> cacheManagerDescriptors.put(name, new CacheManagerDescriptor(entries)));
return new CachesReport(cacheManagerDescriptors);
}
@ReadOperation
public CacheEntry cache(@Selector String cache, @Nullable String cacheManager) {
return extractUniqueCacheEntry(cache, getCacheEntries((name) -> name.equals(cache), isNameMatch(cacheManager)));
}
@DeleteOperation
public void clearCaches() {
getCacheEntries(matchAll(), matchAll()).forEach(this::clearCache);
}
@DeleteOperation
public boolean clearCache(@Selector String cache, @Nullable String cacheManager) {
CacheEntry entry = extractUniqueCacheEntry(cache,
getCacheEntries((name) -> name.equals(cache), isNameMatch(cacheManager)));
return (entry != null && clearCache(entry));
}
private List<CacheEntry> getCacheEntries(Predicate<String> cacheNamePredicate,
Predicate<String> cacheManagerNamePredicate) {
return this.cacheManagers.keySet().stream().filter(cacheManagerNamePredicate)
.flatMap((cacheManagerName) -> getCacheEntries(cacheManagerName, cacheNamePredicate).stream())
.collect(Collectors.toList());
}
private List<CacheEntry> getCacheEntries(String cacheManagerName, Predicate<String> cacheNamePredicate) {
CacheManager cacheManager = this.cacheManagers.get(cacheManagerName);
return cacheManager.getCacheNames().stream().filter(cacheNamePredicate).map(cacheManager::getCache)
.filter(Objects::nonNull).map((cache) -> new CacheEntry(cache, cacheManagerName))
.collect(Collectors.toList());
}
private CacheEntry (String cache, List<CacheEntry> entries) {
if (entries.size() > 1) {
throw new NonUniqueCacheException(cache,
entries.stream().map(CacheEntry::getCacheManager).distinct().collect(Collectors.toList()));
}
return (!entries.isEmpty() ? entries.get(0) : null);
}
private boolean clearCache(CacheEntry entry) {
String cacheName = entry.getName();
String cacheManager = entry.getCacheManager();
Cache cache = this.cacheManagers.get(cacheManager).getCache(cacheName);
if (cache != null) {
cache.clear();
return true;
}
return false;
}
private Predicate<String> isNameMatch(String name) {
return (name != null) ? ((requested) -> requested.equals(name)) : matchAll();
}
private Predicate<String> matchAll() {
return (name) -> true;
}
public static final class CachesReport {
private final Map<String, CacheManagerDescriptor> cacheManagers;
public CachesReport(Map<String, CacheManagerDescriptor> cacheManagers) {
this.cacheManagers = cacheManagers;
}
public Map<String, CacheManagerDescriptor> getCacheManagers() {
return this.cacheManagers;
}
}
public static final class CacheManagerDescriptor {
private final Map<String, CacheDescriptor> caches;
public CacheManagerDescriptor(Map<String, CacheDescriptor> caches) {
this.caches = caches;
}
public Map<String, CacheDescriptor> getCaches() {
return this.caches;
}
}
public static class CacheDescriptor {
private final String target;
public CacheDescriptor(String target) {
this.target = target;
}
public String getTarget() {
return this.target;
}
}
public static final class CacheEntry extends CacheDescriptor {
private final String name;
private final String cacheManager;
public CacheEntry(Cache cache, String cacheManager) {
super(cache.getNativeCache().getClass().getName());
this.name = cache.getName();
this.cacheManager = cacheManager;
}
public String getName() {
return this.name;
}
public String getCacheManager() {
return this.cacheManager;
}
}
}