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

io.bdeploy.jersey.JerseySessionManager Maven / Gradle / Ivy

Go to download

Public API including dependencies, ready to be used for integrations and plugins.

There is a newer version: 7.3.6
Show newest version
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 - 2024 Weber Informatics LLC | Privacy Policy