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

org.tentackle.web.app.WebSessionKeyCache Maven / Gradle / Ivy

There is a newer version: 21.16.1.0
Show newest version
/*
 * Tentackle - https://tentackle.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


package org.tentackle.web.app;

import org.tentackle.common.TentackleRuntimeException;
import org.tentackle.session.SessionInfo;

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * A cache mapping user session infos to the web container's session keys.
 *
 * @author harald
 * @param  the session key type
 */
public class WebSessionKeyCache {

  // the session info map, key is the session-key
  private final ConcurrentHashMap> sessionInfoMap;
  private CleanupThread cleanupThread;



  // thread to clean up soft references
  private class CleanupThread extends Thread {

    private final long ms;
    private boolean stop;

    private CleanupThread(long ms) {
      this.ms = ms;
      setDaemon(true);
    }

    private void requestToStop()  {
      stop = true;
      interrupt();
    }

    @Override
    public void run() {
      while (!interrupted() && !stop) {
        try {
          sleep(ms);
          sessionInfoMap.values().removeIf(ref -> ref.get() == null);
        }
        catch (InterruptedException ex) {
          if (stop) {
            break;
          }
          // continue
        }
      }
    }
  }




  /**
   * Creates a session key cache.
   */
  public WebSessionKeyCache() {
    sessionInfoMap = new ConcurrentHashMap<>();
  }



  /**
   * Starts the session key cache.

* This will start a thread to clean up crashed sessions. *

* Throws TentackleRuntimeException if the cleanup thread is already running. * * @param cleanupInterval interval in [ms] to run cleanup */ public void startup(long cleanupInterval) { if (cleanupInterval <= 0) { throw new IllegalArgumentException("cleanupInterval must be > 0"); } if (cleanupThread != null && cleanupThread.isAlive()) { throw new TentackleRuntimeException("session cache cleanup thread already running"); } // start the session pool cleanup thread cleanupThread = new CleanupThread(cleanupInterval); // 5 minutes cleanupThread.start(); } /** * Terminates the session cache. *

* Throws TentackleRuntimeException if the cleanup thread is not running at all. */ public void terminate() { if (cleanupThread != null) { boolean isAlive = cleanupThread.isAlive(); if (isAlive) { cleanupThread.requestToStop(); try { cleanupThread.join(); } catch (InterruptedException ex) { // we're already terminating... } } cleanupThread = null; if (!isAlive) { throw new TentackleRuntimeException("session cache cleanup thread died"); } } else { throw new TentackleRuntimeException("session cache cleanup thread hasn't been started"); } } /** * Adds a mapping between a session and a session info.
* This is usually done in the login controller. * If a session with that key already exists, the session info * will be replaced. * * @param sessionKey the (unique) session key * @param sessionInfo the user's session info */ public void addSessionInfo(T sessionKey, SessionInfo sessionInfo) { sessionInfoMap.put(sessionKey, new SoftReference<>(sessionInfo)); } /** * Removes a mapping between a session and a session info.
* This is usually done in the logout controller. * If there is no such session, the method will do nothing. * * @param sessionKey the (unique) session key */ public void removeSessionInfo(T sessionKey) { sessionInfoMap.remove(sessionKey); } /** * Gets the session info. * * @param sessionKey the session key * @return the session info, null if no such session info */ public SessionInfo getSessionInfo(T sessionKey) { SoftReference sessionInfoRef = sessionInfoMap.get(sessionKey); if (sessionInfoRef != null) { SessionInfo sessionInfo = sessionInfoRef.get(); if (sessionInfo != null) { // not cleared so far: we can use it return sessionInfo; } removeSessionInfo(sessionKey); } return null; } /** * Gets the session keys for a user session info. * * @param sessionInfo the session info * @return the session keys, never null */ public Collection getSessionKeys(SessionInfo sessionInfo) { Collection sessionKeys = new ArrayList<>(); for (Iterator>> iter = sessionInfoMap.entrySet().iterator(); iter.hasNext();) { Map.Entry> entry = iter.next(); SessionInfo info = entry.getValue().get(); if (info == null) { iter.remove(); } else if (info.equals(sessionInfo)) { sessionKeys.add(entry.getKey()); } } return sessionKeys; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy