aboutsummaryrefslogtreecommitdiffstats
path: root/vid-app-common/src/main/java/org/onap/vid/aai/util/CacheProviderWithLoadingCache.java
blob: 26a3ebf250223237622d72e2b6d396e125cb5169 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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<String, Cache> 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 <K, V> Cache<K, V> aaiClientCacheFor(String name, Function<K, V> loader) {
        return (Cache<K, V>) 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 <K, V> Cache<K, V> buildAaiClientCacheFrom(Function<K, V> loader, String name) {
        final LoadingCache<K, V> 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 <K, V> LoadingCache<K, V> buildAaiClientActiveCacheFrom(Function<K, V> loader, String name) {
        return createCacheBuilder(name).build(createAsyncReloadingCacheLoaderFrom(loader));

    }

    @NotNull
    protected CacheBuilder<Object, Object> createCacheBuilder(String name) {
        CacheConfig cacheConfig = cacheConfigProvider.getCacheConfig(name);
        return CacheBuilder.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(cacheConfig.getExpireAfterWriteHours(), TimeUnit.HOURS)
                .refreshAfterWrite(cacheConfig.getRefreshAfterWriteSeconds(), TimeUnit.SECONDS);
    }

    private <K, V> CacheLoader<K, V> createAsyncReloadingCacheLoaderFrom(Function<K, V> loader) {
        return CacheLoader.asyncReloading(CacheLoader.from(loader::apply), cacheReloadPool);
    }

}