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

ca.ibodrov.mica.server.data.ViewCache Maven / Gradle / Ivy

The newest version!
package ca.ibodrov.mica.server.data;

import ca.ibodrov.mica.api.model.RenderRequest;
import ca.ibodrov.mica.api.model.ViewLike;
import ca.ibodrov.mica.api.model.ViewLike.Caching;
import ca.ibodrov.mica.server.data.ViewRenderer.RenderOverrides;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Expiry;
import org.checkerframework.checker.index.qual.NonNegative;

import java.time.Duration;
import java.util.function.BiFunction;

public interface ViewCache {

    String DEFAULT_CACHE_ENABLED = "false";
    String DEFAULT_CACHE_TTL = "PT10S";

    static ViewCache inMemory() {
        return new InMemoryViewCache();
    }

    static ViewCache noop() {
        return new NoopViewCache();
    }

    RenderedView getOrRender(RenderRequest request,
                             RenderOverrides overrides,
                             ViewLike viewLike,
                             BiFunction renderer);

    class InMemoryViewCache implements ViewCache {

        private final Cache cache;

        public InMemoryViewCache() {
            this.cache = Caffeine.newBuilder()
                    .expireAfter(new RenderedViewExpiry())
                    .build();
        }

        public RenderedView getOrRender(RenderRequest request,
                                        RenderOverrides overrides,
                                        ViewLike viewLike,
                                        BiFunction renderer) {
            if (!isCachingEnabled(viewLike)) {
                return renderer.apply(viewLike, overrides);
            }

            var key = new CacheKey(request, overrides);
            return cache.get(key, _key -> renderer.apply(viewLike, overrides));
        }
    }

    class NoopViewCache implements ViewCache {

        @Override
        public RenderedView getOrRender(RenderRequest request,
                                        RenderOverrides overrides,
                                        ViewLike viewLike,
                                        BiFunction renderer) {
            return renderer.apply(viewLike, overrides);
        }
    }

    class RenderedViewExpiry implements Expiry {

        @Override
        public long expireAfterCreate(CacheKey key, RenderedView value, long currentTime) {
            var ttl = value.view().caching()
                    .map(caching -> caching.ttl().orElse(DEFAULT_CACHE_TTL))
                    .orElse(DEFAULT_CACHE_TTL);
            return Duration.parse(ttl).toNanos();
        }

        @Override
        public long expireAfterUpdate(CacheKey key,
                                      RenderedView value,
                                      long currentTime,
                                      @NonNegative long currentDuration) {
            return 0;
        }

        @Override
        public long expireAfterRead(CacheKey key,
                                    RenderedView value,
                                    long currentTime,
                                    @NonNegative long currentDuration) {
            return 0;
        }
    }

    record CacheKey(RenderRequest request, RenderOverrides overrides) {
    }

    private static boolean isCachingEnabled(ViewLike view) {
        var enabled = view.caching()
                .flatMap(Caching::enabled)
                .orElse(DEFAULT_CACHE_ENABLED);
        return Boolean.parseBoolean(enabled);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy