blob: 257f6ba3d0d7f0346dd710341e15071a29fbeb2d [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 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.
*
* 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).
*
* TODO synchronization
*/
public class InfinispanSessionIdManager extends AbstractSessionIdManager
{
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
protected final static String ID_KEY = "__o.e.j.s.infinispanIdMgr__";
protected BasicCache<String,Object> _cache;
private Server _server;
/**
* @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
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
insert (((AbstractSession)session).getClusterId());
}
/**
* 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));
}
}
}
}
public BasicCache<String,Object> getCache()
{
return _cache;
}
public void setCache(BasicCache<String,Object> cache)
{
this._cache = cache;
}
/**
* Ask the cluster if a particular id exists.
*
* @param id
* @return
*/
protected boolean exists (String id)
{
if (_cache == null)
throw new IllegalStateException ("No cache");
Object key =_cache.get(makeKey(id));
if (key == null)
return false;
return true;
}
/**
* 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);
}
/**
* 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;
}
}