All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.turbospaces.ebean.MockCacheManager Maven / Gradle / Ivy

There is a newer version: 2.0.33
Show newest version
package com.turbospaces.ebean;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.jgroups.util.RspList;

import com.google.common.cache.Cache;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.netflix.archaius.api.Config;
import com.turbospaces.cache.BlockhoundCacheWrapper;
import com.turbospaces.cfg.ApplicationProperties;

import io.ebean.BackgroundExecutor;
import io.ebean.DatabaseBuilder;
import io.ebean.cache.QueryCacheEntryValidate;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheConfig;
import io.ebean.cache.ServerCacheFactory;
import io.ebean.cache.ServerCacheNotification;
import io.ebean.cache.ServerCacheNotify;
import io.ebean.cache.ServerCacheOptions;
import io.ebean.cache.ServerCacheType;
import io.ebean.config.CurrentTenantProvider;
import io.ebeaninternal.server.cache.DefaultServerCacheConfig;
import io.ebeaninternal.server.cache.DefaultServerQueryCache;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MockCacheManager implements CacheManager {
    private final ApplicationProperties props;
    private final Map caches = Maps.newConcurrentMap();
    private BackgroundExecutor executor;

    public MockCacheManager(ApplicationProperties props) {
        this.props = Objects.requireNonNull(props);
    }
    @Override
    public void setBackgroundExecutor(BackgroundExecutor executor) {
        this.executor = Objects.requireNonNull(executor);
    }
    @Override
    public ServerCacheFactory create(DatabaseBuilder databaseBuilder, BackgroundExecutor backgroupExecutor) {
        setBackgroundExecutor(backgroupExecutor);
        return this;
    }
    @Override
    public ServerCacheNotify createCacheNotify(ServerCacheNotify listener) {
        return new ServerCacheNotify() {
            @Override
            public void notify(ServerCacheNotification notification) {

            }
        };
    }
    @Override
    public ServerCache createCache(ServerCacheConfig config) {
        String cacheKey = config.getCacheKey();
        String shortName = config.getShortName();

        ServerCacheOptions cacheOptions = config.getCacheOptions();
        ServerCacheType cacheType = config.getType();
        CurrentTenantProvider tenantProvider = config.getTenantProvider();
        QueryCacheEntryValidate queryCacheValidate = config.getQueryCacheEntryValidate();

        ServerCache cache = caches.get(cacheKey);
        if (Objects.isNull(cache)) {
            Config prefixedView = props.cfg().getPrefixedView(cacheKey);

            Map configMap = new HashMap<>();
            for (String key : prefixedView.keys()) {
                Object rawProperty = prefixedView.getRawProperty(key);
                configMap.put(key, rawProperty);
            }

            int maxTtl = prefixedView.getInteger(EbeanCacheConfigurer.MAX_TTL, cacheOptions.getMaxSecsToLive());
            int maxIdle = prefixedView.getInteger(EbeanCacheConfigurer.MAX_IDLE, cacheOptions.getMaxIdleSecs());
            int maxSize = prefixedView.getInteger(EbeanCacheConfigurer.MAX_SIZE, cacheOptions.getMaxSize());
            int trimFrequency = (int) props.APP_TIMER_INTERVAL.get().toSeconds();

            ServerCacheOptions options = new ServerCacheOptions();
            options.setMaxSecsToLive(maxTtl);
            options.setMaxIdleSecs(maxIdle);
            options.setMaxSize(maxSize);
            options.setTrimFrequency(trimFrequency);

            ServerCacheConfig scc = new ServerCacheConfig(
                    cacheType,
                    cacheKey,
                    shortName,
                    options,
                    tenantProvider,
                    queryCacheValidate //
            );

            if (config.isQueryCache()) {
                cache = new DefaultServerQueryCache(new DefaultServerCacheConfig(scc));
                ServerCache prev = caches.putIfAbsent(cacheKey, cache);
                if (Objects.nonNull(prev)) {
                    cache = prev;
                } else {
                    log.debug("created query cache: {} using cfg {}",
                            cacheKey,
                            ToStringBuilder.reflectionToString(options, ToStringStyle.SHORT_PREFIX_STYLE) //
                    );
                }
            } else {
                //
                // ~ fail fast in development mode and raise sentry error in production mode otherwise
                //
                if (BooleanUtils.isFalse(prefixedView.containsKey(EbeanCacheConfigurer.CACHE_MODE_LOCAL))) {
                    if (props.isProdMode()) {
                        log.error("cache {} is not configured", cacheKey);
                    } else {
                        throw new IllegalStateException("cache %s is not configured".formatted(cacheKey));
                    }
                }

                ToStringStyle nameStyle = ToStringStyle.NO_CLASS_NAME_STYLE;
                Cache holder = LocalCache.cache(cacheKey, new DefaultServerCacheConfig(scc));
                BlockhoundCacheWrapper cacheWrapper = new BlockhoundCacheWrapper<>(holder);

                cache = new LocalEbeanCache(props, cacheKey, cacheWrapper, tenantProvider, scc, new BroadcastChannel() {
                    private final RspList list = new RspList<>();
                    private final CompletableFuture> completable = CompletableFuture.completedFuture(list);
                    private final ListenableFuture>> toReturn = Futures.immediateFuture(completable);

                    @Override
                    public FluentFuture>> broadcastPutAsync(String cacheName, ServerCacheType type, String key, Object obj) {
                        return FluentFuture.from(toReturn);
                    }
                    @Override
                    public FluentFuture>> broadcastRemoveAsync(String cacheName, String key) {
                        return FluentFuture.from(toReturn);
                    }
                    @Override
                    public FluentFuture>> broadcastClearAllAsync(String cacheName) {
                        return FluentFuture.from(toReturn);
                    }
                    @Override
                    public FluentFuture>> broadcastClearAllAsync() {
                        return FluentFuture.from(toReturn);
                    }
                    @Override
                    public FluentFuture>> broadcastAsync(ServerCacheNotification notification, String data) {
                        return FluentFuture.from(toReturn);
                    }
                });

                ServerCache prev = caches.putIfAbsent(cacheKey, cache);
                if (Objects.nonNull(prev)) {
                    cache = prev;
                } else {
                    log.debug("created local cache: {} using cfg {}", cacheKey, ToStringBuilder.reflectionToString(options, nameStyle));
                }
            }

            // ~ cleanUp
            ServerCache tmp = Objects.requireNonNull(cache);
            executor.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    if (tmp instanceof LocalEbeanCache) {
                        ((LocalEbeanCache) tmp).cleanUp();
                    } else if (tmp instanceof ReplicatedCache) {
                        ((ReplicatedCache) tmp).cleanUp();
                    } else if (tmp instanceof DefaultServerQueryCache) {
                        ((DefaultServerQueryCache) tmp).runEviction();
                    }
                }
            }, trimFrequency, trimFrequency, TimeUnit.SECONDS);
        }

        return cache;
    }
    @Override
    public ServerCache getCache(String string) {
        return caches.get(string);
    }
    @Override
    public void clearAll() {
        for (ServerCache cache : caches.values()) {
            if (cache instanceof LocalEbeanCache) {
                ((LocalEbeanCache) cache).onClear();
            }
        }
    }
}