blob: 7ca6a58b7391d93aadc6a72b603ed6a732f04559 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.session.infinispan;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SessionManager;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.session.AbstractSession;
import org.eclipse.jetty.server.session.AbstractSessionIdManager;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.infinispan.commons.api.BasicCache;
/**
* InfinispanSessionIdManager
*
* Maintain a set of in-use session ids. This session id manager does NOT locally store
* a list of in-use sesssion ids, but rather stores them in the cluster cache. Thus,
* all operations to this session manager involve interaction with a possibly remote
* cache.
*
* For each session id that is in-use, an entry of the following form is put into
* the cluster cache:
* <pre>
* ("__o.e.j.s.infinispanIdMgr__"+[id], [id])
* </pre>
* where [id] is the id of the session.
*
* If the first session to be added is not immortal (ie it has a timeout on it) then
* the corresponding session id is entered into infinispan with an idle expiry timeout
* equivalent to double the session's timeout (the multiplier is configurable).
*
*
* Having one entry per in-use session id means that there is no contention on
* cache entries (as would be the case if a single entry was kept containing a
* list of in-use session ids).
*
*
*/
public class InfinispanSessionIdManager extends AbstractSessionIdManager
{
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
public final static String ID_KEY = "__o.e.j.s.infinispanIdMgr__";
public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2;
protected BasicCache<String,Object> _cache;
private Server _server;
private int _idleExpiryMultiple = DEFAULT_IDLE_EXPIRY_MULTIPLE;
/**
* @param server
*/
public InfinispanSessionIdManager(Server server)
{
super();
_server = server;
}
/**
* @param server
* @param random
*/
public InfinispanSessionIdManager(Server server, Random random)
{
super(random);
_server = server;
}
/**
* Start the id manager.
* @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStart()
*/
@Override
protected void doStart() throws Exception
{
super.doStart();
}
/**
* Stop the id manager
* @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStop()
*/
@Override
protected void doStop() throws Exception
{
super.doStop();
}
/**
* Check to see if the given session id is being
* used by a session in any context.
*
* This method will consult the cluster.
*
* @see org.eclipse.jetty.server.SessionIdManager#idInUse(java.lang.String)
*/
@Override
public boolean idInUse(String id)
{
if (id == null)
return false;
String clusterId = getClusterId(id);
//ask the cluster - this should also tickle the idle expiration timer on the sessionid entry
//keeping it valid
try
{
return exists(clusterId);
}
catch (Exception e)
{
LOG.warn("Problem checking inUse for id="+clusterId, e);
return false;
}
}
/**
* Remember a new in-use session id.
*
* This will save the in-use session id to the cluster.
*
* @see org.eclipse.jetty.server.SessionIdManager#addSession(javax.servlet.http.HttpSession)
*/
@Override
public void addSession(HttpSession session)
{
if (session == null)
return;
//insert into the cache and set an idle expiry on the entry that
//is based off the max idle time configured for the session. If the
//session is immortal, then there is no idle expiry on the corresponding
//session id
if (session.getMaxInactiveInterval() == 0)
insert (((AbstractSession)session).getClusterId());
else
insert (((AbstractSession)session).getClusterId(), session.getMaxInactiveInterval() * getIdleExpiryMultiple());
}
public void setIdleExpiryMultiple (int multiplier)
{
if (multiplier <= 1)
{
LOG.warn("Idle expiry multiple of {} for session ids set to less than minimum. Using value of {} instead.", multiplier, DEFAULT_IDLE_EXPIRY_MULTIPLE);
}
_idleExpiryMultiple = multiplier;
}
public int getIdleExpiryMultiple ()
{
return _idleExpiryMultiple;
}
/**
* Remove a session id from the list of in-use ids.
*
* This will remvove the corresponding session id from the cluster.
*
* @see org.eclipse.jetty.server.SessionIdManager#removeSession(javax.servlet.http.HttpSession)
*/
@Override
public void removeSession(HttpSession session)
{
if (session == null)
return;
//delete from the cache
delete (((AbstractSession)session).getClusterId());
}
/**
* Remove a session id. This compels all other contexts who have a session
* with the same id to also remove it.
*
* @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
*/
@Override
public void invalidateAll(String id)
{
//delete the session id from list of in-use sessions
delete (id);
//tell all contexts that may have a session object with this id to
//get rid of them
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
for (int i=0; contexts!=null && i<contexts.length; i++)
{
SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
if (sessionHandler != null)
{
SessionManager manager = sessionHandler.getSessionManager();
if (manager != null && manager instanceof InfinispanSessionManager)
{
((InfinispanSessionManager)manager).invalidateSession(id);
}
}
}
}
/**
* Change a session id.
*
* Typically this occurs when a previously existing session has passed through authentication.
*
* @see org.eclipse.jetty.server.session.AbstractSessionIdManager#renewSessionId(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest)
*/
@Override
public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request)
{
//generate a new id
String newClusterId = newSessionId(request.hashCode());
delete(oldClusterId);
insert(newClusterId);
//tell all contexts to update the id
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
for (int i=0; contexts!=null && i<contexts.length; i++)
{
SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
if (sessionHandler != null)
{
SessionManager manager = sessionHandler.getSessionManager();
if (manager != null && manager instanceof InfinispanSessionManager)
{
((InfinispanSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
}
}
}
}
/**
* Get the cache.
* @return
*/
public BasicCache<String,Object> getCache()
{
return _cache;
}
/**
* Set the cache.
* @param cache
*/
public void setCache(BasicCache<String,Object> cache)
{
this._cache = cache;
}
/**
* Do any operation to the session id in the cache to
* ensure its idle expiry time moves forward
* @param id
*/
public void touch (String id)
{
exists(id);
}
/**
* Ask the cluster if a particular id exists.
*
* @param id
* @return
*/
protected boolean exists (String id)
{
if (_cache == null)
throw new IllegalStateException ("No cache");
return _cache.containsKey(makeKey(id));
}
/**
* Put a session id into the cluster.
*
* @param id
*/
protected void insert (String id)
{
if (_cache == null)
throw new IllegalStateException ("No cache");
_cache.putIfAbsent(makeKey(id), id);
}
/**
* Put a session id into the cluster with an idle expiry.
*
* @param id
*/
protected void insert (String id, long idleTimeOutSec)
{
if (_cache == null)
throw new IllegalStateException ("No cache");
_cache.putIfAbsent(makeKey(id),id,-1L, TimeUnit.SECONDS, idleTimeOutSec, TimeUnit.SECONDS);
}
/**
* Remove a session id from the cluster.
*
* @param id
*/
protected void delete (String id)
{
if (_cache == null)
throw new IllegalStateException ("No cache");
_cache.remove(makeKey(id));
}
/**
* Generate a unique cache key from the session id.
*
* @param id
* @return
*/
protected String makeKey (String id)
{
return ID_KEY+id;
}
}