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

com.aliyun.auth.credentials.utils.RefreshCachedSupplier Maven / Gradle / Ivy

The newest version!
package com.aliyun.auth.credentials.utils;

import com.aliyun.core.utils.SdkAutoCloseable;
import com.aliyun.core.utils.Validate;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;

public final class RefreshCachedSupplier implements Supplier, SdkAutoCloseable {
    private static final Duration REFRESH_BLOCKING_MAX_WAIT = Duration.ofSeconds(5);
    private final Lock refreshLock = new ReentrantLock();
    private final PrefetchStrategy prefetchStrategy;
    private volatile RefreshResult cachedValue = RefreshResult.builder((T) null)
                                                                 .staleTime(Instant.MIN)
                                                                 .prefetchTime(Instant.MIN)
                                                                 .build();

    private final Supplier> valueSupplier;

    private RefreshCachedSupplier(Builder builder) {
        this.valueSupplier = Validate.notNull(builder.supplier, "CachedSupplier.supplier is null.");
        this.prefetchStrategy = Validate.notNull(builder.prefetchStrategy, "CachedSupplier.prefetchStrategy is null.");
    }

    public static  Builder builder(Supplier> valueSupplier) {
        return new Builder<>(valueSupplier);
    }

    @Override
    public T get() {
        if (cacheIsStale()) {
            refreshCache();
        } else if (shouldInitiateCachePrefetch()) {
            prefetchCache();
        }

        return this.cachedValue.value();
    }

    private boolean cacheIsStale() {
        return Instant.now().isAfter(cachedValue.staleTime());
    }

    private boolean shouldInitiateCachePrefetch() {
        return Instant.now().isAfter(cachedValue.prefetchTime());
    }

    private void prefetchCache() {
        prefetchStrategy.prefetch(this::refreshCache);
    }

    private void refreshCache() {
        try {
            boolean lockAcquired = refreshLock.tryLock(REFRESH_BLOCKING_MAX_WAIT.getSeconds(), TimeUnit.SECONDS);

            try {
                if (cacheIsStale() || shouldInitiateCachePrefetch()) {
                    cachedValue = valueSupplier.get();
                }
            } finally {
                if (lockAcquired) {
                    refreshLock.unlock();
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IllegalStateException("Interrupted waiting to refresh the value.", e);
        }
    }

    @Override
    public void close() {
        prefetchStrategy.close();
    }

    public static final class Builder {
        private final Supplier> supplier;
        private PrefetchStrategy prefetchStrategy = new OneCallerBlocks();

        private Builder(Supplier> supplier) {
            this.supplier = supplier;
        }

        public Builder prefetchStrategy(PrefetchStrategy prefetchStrategy) {
            this.prefetchStrategy = prefetchStrategy;
            return this;
        }

        public RefreshCachedSupplier build() {
            return new RefreshCachedSupplier<>(this);
        }
    }

    @FunctionalInterface
    public interface PrefetchStrategy extends SdkAutoCloseable {
        void prefetch(Runnable valueUpdater);

        default void close() {
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy