package org.onap.vid.aai.util; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.util.concurrent.UncheckedExecutionException; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.NotNull; import org.onap.vid.properties.Features; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.togglz.core.manager.FeatureManager; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Function; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; @Component public class CacheProviderWithLoadingCache implements CacheProvider { private final ExecutorService cacheReloadPool; private final FeatureManager featureManager; private final CacheConfigProvider cacheConfigProvider; private final ConcurrentHashMap caches; @Autowired public CacheProviderWithLoadingCache(FeatureManager featureManager, CacheConfigProvider cacheConfigProvider) { this.featureManager = featureManager; this.cacheConfigProvider = cacheConfigProvider; this.cacheReloadPool = Executors.newFixedThreadPool(3); this.caches = new ConcurrentHashMap<>(); } /* Returns the cache associated with given name; creates one if wasn't any */ @Override public Cache aaiClientCacheFor(String name, Function loader) { return (Cache) caches.computeIfAbsent(name, s -> buildAaiClientCacheFrom(loader, name)); } @Override public void resetCache(String name) { caches.remove(name); } /* Creates and returns a Cache that use provided `loader` to fetch values for search keys, and stores the result for reuse over a certain time. The cache will not use any stored key if FLAG_1810_AAI_LOCAL_CACHE is turned off. In that case, `loader` will be invoked for any `get()` request from the cache. The cache adheres the flag in real-time; so no restart is required. */ protected Cache buildAaiClientCacheFrom(Function loader, String name) { final LoadingCache activeCache = buildAaiClientActiveCacheFrom(loader, name); // this works because Cache interface has only a single method: "get()" // can be replaced with new anonimous class; e.g.: // return new Cache() { ... } return key -> { if (featureManager.isActive(Features.FLAG_1810_AAI_LOCAL_CACHE) && defaultIfNull(cacheConfigProvider.getCacheConfig(name).isActive(), true)) { try { return activeCache.getUnchecked(key); } catch (UncheckedExecutionException exception) { return ExceptionUtils.rethrow(exception.getCause()); } } else { activeCache.invalidateAll(); activeCache.cleanUp(); return loader.apply(key); } }; } private LoadingCache buildAaiClientActiveCacheFrom(Function loader, String name) { return createCacheBuilder(name).build(createAsyncReloadingCacheLoaderFrom(loader)); } @NotNull protected CacheBuilder createCacheBuilder(String name) { CacheConfig cacheConfig = cacheConfigProvider.getCacheConfig(name); return CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(cacheConfig.getExpireAfterWriteHours(), TimeUnit.HOURS) .refreshAfterWrite(cacheConfig.getRefreshAfterWriteSeconds(), TimeUnit.SECONDS); } private CacheLoader createAsyncReloadingCacheLoaderFrom(Function loader) { return CacheLoader.asyncReloading(CacheLoader.from(loader::apply), cacheReloadPool); } }