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

com.yahoo.parsec.clients.ParsecAsyncHttpResponseLoadingCache Maven / Gradle / Ivy

There is a newer version: 1.0.24
Show newest version
// Copyright 2016 Yahoo Inc.
// Licensed under the terms of the Apache license. Please see LICENSE.md file distributed with this work for terms.

package com.yahoo.parsec.clients;

import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.core.Response;
import java.util.concurrent.*;


/**
 * An asynchronous loading cache.
 * key: {@link ParsecAsyncHttpRequest}
 * value: CompletableFuture<{@link Response}>.
 *
 * @author sho
 */
@SuppressWarnings("unused")
final class ParsecAsyncHttpResponseLoadingCache {

    /**
     * Logger.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ParsecAsyncHttpResponseLoadingCache.class);

    /**
     * Base async loading cache.
     */
    private AsyncLoadingCache asyncLoadingCache;

    /**
     * Response cache loader.
     */
    private ResponseCacheLoader responseCacheLoader;

    /**
     * Executor for cleaning up cache periodically.
     */
    private ScheduledExecutorService cleanUpExecutorService;

    /**
     * Unused private constructor.
     */
    private ParsecAsyncHttpResponseLoadingCache() {

    }

    /**
     * Private constructor.
     *
     * @param builder builder
     */
    @SuppressWarnings("unchecked")
    private ParsecAsyncHttpResponseLoadingCache(final Builder builder) {
        responseCacheLoader = new ResponseCacheLoader(builder.client);
        asyncLoadingCache = builder.caffeine.buildAsync(responseCacheLoader);

        cleanUpExecutorService = Executors.newSingleThreadScheduledExecutor();
        cleanUpExecutorService.scheduleWithFixedDelay(() -> {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Performing cache cleanup");
                }
                asyncLoadingCache.synchronous().cleanUp();
            },
            builder.cleanUpInterval, builder.cleanUpInterval, builder.cleanUpTimeUnit
        );
    }

    /**
     * Get from cache.
     *
     * @param request Request to lookup in cache
     * @return CompletableFuture<{@link Response}>
     */
    public CompletableFuture get(ParsecAsyncHttpRequest request) {
        return asyncLoadingCache.get(request);
    }

    /**
     * Get from cache.
     *
     * @param request Request to lookup in cache
     * @param executorService Executor service to use for asynchronous load
     * @return CompletableFuture<{@link Response}>
     */
    public CompletableFuture get(ParsecAsyncHttpRequest request, ExecutorService executorService) {
        return asyncLoadingCache.get(request, (k, e) -> responseCacheLoader.asyncLoad(request, executorService));
    }

    /**
     * Get from cache or null if not in cache.
     *
     * @param request Request to lookup in cache
     * @return CompletableFuture<{@link Response}> or null if not in cache
     */
    public CompletableFuture getIfPresent(ParsecAsyncHttpRequest request) {
        return asyncLoadingCache.getIfPresent(request);
    }

    /**
     * Put CompletableFuture<{@link Response}> into cache.
     *
     * @param request Request to use as cache key
     * @param completableFuture CompletableFuture<{@link Response}> to store
     */
    public void put(ParsecAsyncHttpRequest request, CompletableFuture completableFuture) {
        asyncLoadingCache.put(request, completableFuture);
    }

    /**
     * Returns a view of the entries stored in this cache as a synchronous {@link LoadingCache}.
     * A mapping is not present if the value is currently being loaded. Modifications made to the
     * synchronous cache directly affect the asynchronous cache. If a modification is made to a
     * mapping that is currently loading, the operation blocks until the computation completes.
     *
     * @return a thread-safe synchronous view of this cache
     */
    public LoadingCache synchronous() {
        return asyncLoadingCache.synchronous();
    }

    /**
     * Shutdown.
     */
    void shutdownCleanUpExecutorService() {
        if (!cleanUpExecutorService.isShutdown()) {
            cleanUpExecutorService.shutdown();
        }
    }

    /**
     * Static Builder class for {@link ParsecAsyncHttpResponseLoadingCache}.
     *
     * @author sho
     */
    public static class Builder {
        /**
         * Default clean up interval.
         */
        private static final long DEFAULT_CLEANUP_INTERVAL = 60;

        /**
         * Caffeine.
         */
        private Caffeine caffeine;

        /**
         * Client.
         */
        private ParsecAsyncHttpClient client;

        /**
         * Clean up interval.
         */
        private long cleanUpInterval;

        /**
         * Clean up time unit.
         */
        private TimeUnit cleanUpTimeUnit;

        /**
         * Constructor.
         * @param client client
         */
        public Builder(final ParsecAsyncHttpClient client) {
            caffeine = Caffeine.newBuilder();
            this.client = client;
            cleanUpInterval = DEFAULT_CLEANUP_INTERVAL;
            cleanUpTimeUnit = TimeUnit.SECONDS;
        }

        /**
         * Build new {@link ParsecAsyncHttpResponseLoadingCache} instance.
         *
         * @return new {@link ParsecAsyncHttpResponseLoadingCache} instance
         */
        public ParsecAsyncHttpResponseLoadingCache build() {
            return new ParsecAsyncHttpResponseLoadingCache(this);
        }

        /**
         * Set executor {@link Executor}.
         *
         * @param executor Executor for asynchronous cache loading
         * @return {@link ParsecAsyncHttpResponseLoadingCache.Builder}
         */
        public Builder executor(final Executor executor) {
            caffeine.executor(executor);
            return this;
        }

        /**
         * Set cache expire after write duration.
         *
         * @param duration Expire duration
         * @param unit Expire time unit
         * @return {@link ParsecAsyncHttpResponseLoadingCache.Builder}
         */
        public Builder expireAfterWrite(final long duration, final TimeUnit unit) {
            caffeine.expireAfterWrite(duration, unit);
            return this;
        }

        /**
         * Set cache maximum size.
         *
         * @param size Cache maximum size
         * @return {@link ParsecAsyncHttpResponseLoadingCache.Builder}
         */
        public Builder maximumSize(final long size) {
            caffeine.maximumSize(size);
            return this;
        }

        /**
         * Set cache clean up interval.
         *
         * @param interval interval
         * @param unit time unit
         * @return {@link ParsecAsyncHttpResponseLoadingCache.Builder}
         */
        public Builder cleanUpInterval(final long interval, final TimeUnit unit) {
            cleanUpInterval = interval;
            cleanUpTimeUnit = unit;
            return this;
        }
    }

    /**
     * {@link CacheLoader} implementation that loads {@link Response} into {@link ParsecAsyncHttpResponseLoadingCache}.
     */
    private final class ResponseCacheLoader implements CacheLoader {
        /**
         * Client.
         */
        private ParsecAsyncHttpClient client;

        /**
         * Private constructor.
         *
         * @param client client
         */
        private ResponseCacheLoader(final ParsecAsyncHttpClient client) {
            this.client = client;
        }

        /**
         * Load {@link Response} into {@link ParsecAsyncHttpResponseLoadingCache}.
         *
         * @param request {@link ParsecAsyncHttpRequest} as {@link ParsecAsyncHttpResponseLoadingCache} key
         * @return {@link Response}
         * @throws RuntimeException Run time exception
         */
        public Response load(final ParsecAsyncHttpRequest request) throws RuntimeException {
            try {
                return client.criticalExecute(request).get();
            } catch (ExecutionException | InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy