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

com.microsoft.azure.documentdb.internal.AsyncCache Maven / Gradle / Ivy

package com.microsoft.azure.documentdb.internal;

import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Cache which supports asynchronous value initialization for the Azure Cosmos DB database service Java SDK.
 * It ensures that for given key only single initialization function is running at any point in time.
 */
public final class AsyncCache {
    private static final Logger LOGGER = LoggerFactory.getLogger(AsyncCache.class);
    private final ConcurrentHashMap> values;
    private final ExecutorService executorService;

    public AsyncCache(ExecutorService executorService) {
        this.values = new ConcurrentHashMap>();
        this.executorService = executorService;
    }

    public Set getKeys() {
        return this.values.keySet();
    }

    public AsyncLazy put(TKey key, final TValue value) {
        return this.values.put(key, new AsyncLazy(new Callable() {
            @Override
            public TValue call() throws Exception {
                return value;
            }
        }, this.executorService));
    }

    public Future get(TKey key, TValue obsoleteValue, Callable callable) {
        AsyncLazy initialAsyncLazy = this.values.get(key);
        if (initialAsyncLazy != null && !initialAsyncLazy.isCancelled()) {
            try {
                if (!initialAsyncLazy.isDone() || !this.areEqual(initialAsyncLazy.getValue().get(), obsoleteValue)) {
                    LOGGER.debug("Use cache[{}], obsoleteValue: {}", key, obsoleteValue);
                    return initialAsyncLazy.getValue();
                }
            } catch (CancellationException | ExecutionException | InterruptedException e) {
                // Nothing to do, proceed with replacing new AsyncLazy
                LOGGER.debug("Cache[{}] encountered an exception during evaluation", key, e);
            }
        }

        AsyncLazy newAsyncLazy = new AsyncLazy(callable, this.executorService);

        // $ISSUE-felixfan-2016-08-03: We should use ConcurrentHashMap.merge when moving to Java 8
        AsyncLazy actualAsyncLazy;
        if (initialAsyncLazy == null) {
            actualAsyncLazy = this.values.putIfAbsent(key, newAsyncLazy);
            if (actualAsyncLazy == null) {
                LOGGER.debug("Add cache [{}], obsoleteValue: {}", key, obsoleteValue);
                actualAsyncLazy = newAsyncLazy;
            }
        } else if (this.values.replace(key, initialAsyncLazy, newAsyncLazy)) {
            LOGGER.debug("Replace cache [{}], obsoleteValue: {}", key, obsoleteValue);
            actualAsyncLazy = newAsyncLazy;
        } else {
            LOGGER.debug("Use cache in the end [{}], obsoleteValue: {}", key, obsoleteValue);
            actualAsyncLazy = this.values.get(key);
        }

        return actualAsyncLazy.getValue();
    }

    public AsyncLazy remove(TKey key) {
        return this.values.remove(key);
    }

    public void clear() {
        this.values.clear();
    }

    public void refresh(TKey key, Callable callable) {
        AsyncLazy initialAsyncLazy = this.values.get(key);
        if (initialAsyncLazy != null && initialAsyncLazy.isDone()) {
            AsyncLazy newAsyncLazy = new AsyncLazy(callable, this.executorService);
            // $ISSUE-felixfan-2016-08-03: We should use ConcurrentHashMap.merge when moving to Java 8
            if (this.values.replace(key, initialAsyncLazy, newAsyncLazy)) {
                newAsyncLazy.getValue();
            }
        }
    }

    private boolean areEqual(TValue value1, TValue value2) {
        if (value1 == value2)
            return true;
        if (value1 == null || value2 == null)
            return false;
        return value1.equals(value2);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy