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

net.moznion.auto_refresh_cache.AutoRefreshCache Maven / Gradle / Ivy

There is a newer version: 0.4.0
Show newest version
package net.moznion.auto_refresh_cache;

import lombok.extern.slf4j.Slf4j;

import java.time.Instant;
import java.util.concurrent.Semaphore;
import java.util.function.Supplier;

/**
 * Cached object that can be refreshed automatically when cache is expired.
 * 

* It holds the same object until cache is expired, * and it refreshes the object by given supplier automatically when cache is expired. * * @param Type of cache object. */ @Slf4j public class AutoRefreshCache { private final long discardIntervalSec; private final Supplier supplier; private final Semaphore semaphore; private final boolean useBeforeCacheOnException; private volatile long expiresAt; private volatile T cached; /** * A constructor. * * @param discardIntervalSec Lifetime of cache object (seconds). If this interval is over, cache object will be refreshed by given supplier. * @param supplier Supplier of cache object. This supplier is used when making a cache object and refreshing one. * @param useBeforeCacheOnException If true, it returns already cached object and suppresses exception when supplier raises some exception. Otherwise, it throws exception as it is. */ public AutoRefreshCache(final long discardIntervalSec, final boolean useBeforeCacheOnException, final Supplier supplier) { this(supplier.get(), discardIntervalSec, useBeforeCacheOnException, supplier); } /** * A constructor with initial cached object. * * @param init the initial cached object. * @param discardIntervalSec Lifetime of cache object (seconds). If this interval is over, cache object will be refreshed by given supplier. * @param supplier Supplier of cache object. This supplier is used when making a cache object and refreshing one. * @param useBeforeCacheOnException If true, it returns already cached object and suppresses exception when supplier raises some exception. Otherwise, it throws exception as it is. */ public AutoRefreshCache(final T init, final long discardIntervalSec, final boolean useBeforeCacheOnException, final Supplier supplier) { this.discardIntervalSec = discardIntervalSec; this.supplier = supplier; this.useBeforeCacheOnException = useBeforeCacheOnException; cached = init; expiresAt = getExpiresAt(discardIntervalSec); semaphore = new Semaphore(1); } /** * Get cached or refreshed object. * * @return When cache is alive, it returns a cached object. Otherwise, it returns an object that is refreshed by supplier. */ public T get() { return get(getCurrentEpoch()); } /** * Get refreshed object. *

* It returns always refreshed object and extends lifetime. * But if other thread is attempting to refresh, this method returns the cached object that is not refreshed. *

* This method runs as exclusive between threads to ensure atomicity of updating cached object and lifetime. * * @return Refreshed object. */ public T forceGet() { if (!semaphore.tryAcquire()) { // If attempt to get cached object while refreshing that by other thread, current thread returns old cache. return cached; } try { cached = supplier.get(); } catch (RuntimeException e) { if (!useBeforeCacheOnException) { throw e; } log.warn("Failed to refresh cache so use old cache", e); } expiresAt = getExpiresAt(discardIntervalSec); semaphore.release(); return cached; } T get(final long currentEpoch) { if (expiresAt < currentEpoch) { // Expired. Fill new instance return forceGet(); } return cached; } private static long getCurrentEpoch() { return Instant.now().getEpochSecond(); } private static long getExpiresAt(final long discardIntervalSec) { return getCurrentEpoch() + discardIntervalSec; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy