Skip to content

Instantly share code, notes, and snippets.

@jkuipers
Created March 20, 2025 10:19
Show Gist options
  • Save jkuipers/8810b7151ef7a2697ab7e89d2aa5232e to your computer and use it in GitHub Desktop.
Save jkuipers/8810b7151ef7a2697ab7e89d2aa5232e to your computer and use it in GitHub Desktop.
Template class for managing authentication tokens while preventing thundering herd problems on token expiry
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public abstract class AbstractTokenManager<T> {
private static final long MIN_MILLIS_INDICATING_REFRESHED = 10_000L;
protected final Logger logger = LoggerFactory.getLogger(getClass());
/** Used to prevent a thundering herd problem when multiple threads want to refresh an expired token */
private final Lock tokenLock = new ReentrantLock();
private volatile T token;
private long lastRefresh;
private boolean forceRequestOnRefresh;
public T currentToken() {
if (token == null) {
refreshToken();
}
return token;
}
public void refreshToken() {
tokenLock.lock();
try {
if (!forceRequestOnRefresh && alreadyRefreshed()) {
logger.debug("Token already refreshed");
return;
}
T newToken = doFetchToken();
if (newToken != null) {
logTokenReceived(newToken);
this.token = newToken;
this.lastRefresh = System.currentTimeMillis();
} else {
logger.warn("Did not receive new token");
}
} finally {
tokenLock.unlock();
}
}
protected void logTokenReceived(T token) {
logger.info("Got authentication token");
}
protected abstract T doFetchToken();
protected void setForceRequestOnRefresh(boolean forceRequestOnRefresh) {
this.forceRequestOnRefresh = forceRequestOnRefresh;
}
private boolean alreadyRefreshed() {
return token != null && (System.currentTimeMillis() - lastRefresh < MIN_MILLIS_INDICATING_REFRESHED);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment