
io.bdeploy.jersey.JerseySessionManager Maven / Gradle / Ivy
package io.bdeploy.jersey;
import java.time.Duration;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import io.bdeploy.common.util.NamedDaemonThreadFactory;
import io.bdeploy.common.util.UuidHelper;
public class JerseySessionManager implements SessionManager {
private static final Logger log = LoggerFactory.getLogger(JerseySessionManager.class);
private final Cache sessions;
private final SessionStorage storage;
private Future> storeJob;
private final ScheduledExecutorService saveSched = Executors.newScheduledThreadPool(1,
new NamedDaemonThreadFactory("Session Storage Persistence"));
private final Cache activeInPeriod = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build();
private final Cache otps = CacheBuilder.newBuilder().expireAfterWrite(Duration.ofSeconds(60)).build();
public JerseySessionManager(JerseySessionConfiguration config) {
this.storage = config.storage;
sessions = CacheBuilder.newBuilder().expireAfterWrite(config.sessionTimeout, TimeUnit.HOURS)
.expireAfterAccess(config.sessionActiveTimeout, TimeUnit.HOURS).build();
if (storage != null) {
// In case we are restarting and sessions are restored, we cannot have active session timeout,
// or better: the timeout is reset as if the user was just active. This is acceptable :)
sessions.putAll(storage.load());
}
// save every 5 minutes to have *something* persisted in case the server suffers a stroke :)
// an orderly shutdown will anyhow save the current sessions to disc.
saveSched.scheduleAtFixedRate(this::syncSave, 5, 5, TimeUnit.MINUTES);
}
private synchronized void syncSave() {
if (storage != null) {
log.debug("Persisting session storage");
storage.save(sessions.asMap());
}
}
@Override
public void close() {
if (storeJob != null && !storeJob.isDone()) {
storeJob.cancel(true);
}
syncSave();
saveSched.shutdownNow();
}
@Override
public synchronized String createSession(String token) {
String id = getRandomIdNotInCache(sessions);
sessions.put(id, token);
if (log.isDebugEnabled()) {
log.debug("Created session {}", id);
}
return id;
}
@Override
public String getSessionToken(String session) {
if (session == null) {
return null;
}
String sess = sessions.getIfPresent(session);
if (sess != null) {
try {
activeInPeriod.get(session, () -> session);
} catch (Exception x) {
// cannot happen.
log.error("Huh?", x);
}
}
return sess;
}
@Override
public Set getActiveSessions() {
return new TreeSet<>(activeInPeriod.asMap().keySet());
}
@Override
public synchronized void removeSession(String session) {
sessions.invalidate(session);
if (log.isDebugEnabled()) {
log.debug("Removed session {}", session);
}
}
@Override
public synchronized String createSessionWithOtp(String token) {
String otp = getRandomIdNotInCache(otps);
otps.put(otp, createSession(token));
return otp;
}
@Override
public synchronized String checkSessionOtp(String otp) {
if (otps.asMap().containsKey(otp)) {
String session = otps.getIfPresent(otp);
otps.invalidate(otp);
log.debug("Invalidated otp of session {}", session);
return session;
}
return null;
}
private static String getRandomIdNotInCache(Cache c) {
String id = UuidHelper.randomId();
while (c.asMap().containsKey(id)) {
id = UuidHelper.randomId();
}
return id;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy