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

com.aliyun.credentials.provider.RefreshCachedSupplier Maven / Gradle / Ivy

Go to download

Alibaba Cloud Credentials for Java Copyright (C) Alibaba Cloud Computing All rights reserved. 版权所有 (C)阿里云计算有限公司 https://www.aliyun.com

There is a newer version: 0.3.12
Show newest version
package com.aliyun.credentials.provider;

import com.aliyun.credentials.exception.CredentialException;
import com.aliyun.tea.utils.Validate;

import java.util.Date;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static java.util.concurrent.TimeUnit.SECONDS;

public class RefreshCachedSupplier implements AutoCloseable {
    /**
     * Maximum time to wait for a blocking refresh lock before calling refresh again. Unit of milliseconds.
     */
    private static final long REFRESH_BLOCKING_MAX_WAIT = 5 * 1000;
    private final Lock refreshLock = new ReentrantLock();
    private final AtomicBoolean asyncRefreshing = new AtomicBoolean(false);
    private final ExecutorService executor = new ThreadPoolExecutor(0, 1, 5, SECONDS,
            new LinkedBlockingQueue(1),
            Executors.defaultThreadFactory());
    private volatile RefreshResult cachedValue = RefreshResult.builder((T) null)
            .staleTime(0)
            .build();

    private final Callable> refreshCallable;
    private final boolean asyncUpdateEnabled;

    private RefreshCachedSupplier(Builder builder) {
        this.refreshCallable = Validate.notNull(builder.refreshCallable, "Refresh Callable is null.");
        this.asyncUpdateEnabled = builder.asyncUpdateEnabled;
    }

    public static  Builder builder(Callable> refreshCallable) {
        return new Builder(refreshCallable);
    }

    public T get() {
        if (cacheIsStale()) {
            if (this.asyncUpdateEnabled) {
                asyncRefresh();
            } else {
                blockingRefresh();
            }
        }
        return null != this.cachedValue ? this.cachedValue.value() : null;
    }

    private void refreshCache() {
        try {
            this.cachedValue = refreshCallable.call();
        } catch (CredentialException ex) {
            throw ex;
        } catch (Exception e) {
            throw new CredentialException("Failed to refresh credentials.", e);
        }
    }

    private void blockingRefresh() {
        try {
            if (refreshLock.tryLock(REFRESH_BLOCKING_MAX_WAIT, TimeUnit.MILLISECONDS)) {
                try {
                    if (cacheIsStale()) {
                        refreshCache();
                    }
                } finally {
                    refreshLock.unlock();
                }
            }
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            throw new IllegalStateException("Interrupted waiting to refresh the value.", ex);
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     * Used to asynchronously refresh the value. Caller is never blocked.
     */
    private void asyncRefresh() {
        // Immediately return if refresh already in progress
        if (asyncRefreshing.compareAndSet(false, true)) {
            try {
                executor.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            refreshCache();
                        } finally {
                            asyncRefreshing.set(false);
                        }
                    }
                });
            } catch (RuntimeException ex) {
                asyncRefreshing.set(false);
                throw ex;
            }
        }
    }

    @Override
    public void close() {
        executor.shutdown();
    }

    private boolean cacheIsStale() {
        return this.cachedValue == null || new Date().getTime() >= this.cachedValue.staleTime();
    }

    public static final class Builder {
        private final Callable> refreshCallable;
        private boolean asyncUpdateEnabled;

        private Builder(Callable> refreshCallable) {
            this.refreshCallable = refreshCallable;
        }

        public Builder asyncUpdateEnabled(Boolean asyncUpdateEnabled) {
            this.asyncUpdateEnabled = asyncUpdateEnabled;
            return this;
        }

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

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy