Port infinispan sessions to new session model.

This commit is contained in:
Jan Bartel 2015-11-13 14:43:15 +11:00
parent 98558faffd
commit 070284643b
15 changed files with 375 additions and 1267 deletions

View File

@ -0,0 +1,157 @@
//
// ========================================================================
// 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.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.SessionIdManager;
import org.eclipse.jetty.server.session.AbstractSessionDataStore;
import org.eclipse.jetty.server.session.SessionData;
import org.eclipse.jetty.server.session.SessionKey;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.infinispan.commons.api.BasicCache;
/**
* InfinispanSessionDataStore
*
*
*/
public class InfinispanSessionDataStore extends AbstractSessionDataStore
{
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2;
/**
* Clustered cache of sessions
*/
private BasicCache<String, Object> _cache;
private SessionIdManager _idMgr = null;
private int _idleExpiryMultiple = DEFAULT_IDLE_EXPIRY_MULTIPLE;
/**
* Get the clustered cache instance.
*
* @return
*/
public BasicCache<String, Object> getCache()
{
return _cache;
}
/**
* Set the clustered cache instance.
*
* @param cache
*/
public void setCache (BasicCache<String, Object> cache)
{
this._cache = cache;
}
public void setSessionIdManager (SessionIdManager idMgr)
{
_idMgr = idMgr;
}
/**
* @see org.eclipse.jetty.server.session.SessionDataStore#load(org.eclipse.jetty.server.session.SessionKey)
*/
@Override
public SessionData load(SessionKey key) throws Exception
{
return (SessionData)_cache.get(key.toString());
}
/**
* @see org.eclipse.jetty.server.session.SessionDataStore#delete(org.eclipse.jetty.server.session.SessionKey)
*/
@Override
public boolean delete(SessionKey key) throws Exception
{
return (_cache.remove(key.toString()) != null);
}
/**
* @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set)
*/
@Override
public Set<SessionKey> getExpired(Set<SessionKey> candidates)
{
if (candidates == null || candidates.isEmpty())
return candidates;
long now = System.currentTimeMillis();
Set<SessionKey> expired = new HashSet<SessionKey>();
if (LOG.isDebugEnabled())
LOG.debug("Getting expired sessions " + now);
for (SessionKey candidate:candidates)
{
try
{
SessionData sd = load(candidate);
if (sd.isExpiredAt(now))
expired.add(candidate);
}
catch (Exception e)
{
LOG.warn("Error checking if session {} is expired", candidate, e);
}
}
return expired;
}
/**
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(org.eclipse.jetty.server.session.SessionKey, org.eclipse.jetty.server.session.SessionData, boolean)
*/
@Override
public void doStore(SessionKey key, SessionData data, boolean isNew) throws Exception
{
//Put an idle timeout on the cache entry if the session is not immortal -
//if no requests arrive at any node before this timeout occurs, or no node
//scavenges the session before this timeout occurs, the session will be removed.
//NOTE: that no session listeners can be called for this.
if (data.getMaxInactiveMs() > 0)
_cache.put(key.toString(), data, -1, TimeUnit.MILLISECONDS, (data.getMaxInactiveMs() * _idleExpiryMultiple), TimeUnit.MILLISECONDS);
else
_cache.put(key.toString(), data);
//tickle the session id manager to keep the sessionid entry for this session up-to-date
if (_idMgr != null && _idMgr instanceof InfinispanSessionIdManager)
{
((InfinispanSessionIdManager)_idMgr).touch(key.getId());
}
}
}

View File

@ -28,8 +28,8 @@ 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.Session;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -154,29 +154,6 @@ public class InfinispanSessionIdManager extends AbstractSessionIdManager
}
/**
* 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)
{
@ -192,90 +169,8 @@ public class InfinispanSessionIdManager extends AbstractSessionIdManager
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#expireAll(java.lang.String)
*/
@Override
public void expireAll(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, getExtendedId(newClusterId, request));
}
}
}
}
/**
* Get the cache.
@ -377,4 +272,28 @@ public class InfinispanSessionIdManager extends AbstractSessionIdManager
{
return ID_KEY+id;
}
/**
* @see org.eclipse.jetty.server.SessionIdManager#useId(java.lang.String)
*/
@Override
public void useId(Session session)
{
if (session == null)
return;
if (session.getMaxInactiveInterval() == 0)
insert (session.getId());
else
insert (session.getId(), session.getMaxInactiveInterval() * getIdleExpiryMultiple());
}
/**
* @see org.eclipse.jetty.server.SessionIdManager#removeId(java.lang.String)
*/
@Override
public void removeId(String id)
{
delete (id);
}
}

View File

@ -20,7 +20,7 @@ package org.eclipse.jetty.server;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.server.session.Session;
import org.eclipse.jetty.util.component.LifeCycle;
/** Session ID Manager.
@ -37,9 +37,9 @@ public interface SessionIdManager extends LifeCycle
/**
* Notify the sessionid manager that a particular session id is in use
* @param id
* @param the session whose id is being used
*/
public void useId (String id);
public void useId (Session session);
/**
* Remove id

View File

@ -93,4 +93,10 @@ public abstract class AbstractSessionDataStore extends AbstractLifeCycle impleme
return new SessionData(key.getId(), key.getCanonicalContextPath(), key.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
}
protected void checkStarted () throws IllegalStateException
{
if (isStarted())
throw new IllegalStateException("Already started");
}
}

View File

@ -374,7 +374,6 @@ public abstract class AbstractSessionIdManager extends AbstractLifeCycle impleme
removeId(oldClusterId);//remove the old one from the list (and database)
useId(newClusterId); //add the new id to list
//tell all contexts to update the id
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);

View File

@ -0,0 +1,143 @@
//
// ========================================================================
// 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.server.session;
import java.util.Set;
/**
* CachingSessionDataStore
*
* A SessionDataStore is a mechanism for (persistently) storing data associated with sessions.
* This implementation delegates to a pluggable SessionDataStore for actually storing the
* session data. It also uses a pluggable JCache implementation in front of the
* delegate SessionDataStore to improve performance: accessing most persistent store
* technology can be expensive time-wise, so introducing a fronting cache
* can increase performance. The cache implementation can either be a local cache,
* a remote cache, or a clustered cache.
*/
public class CachingSessionDataStore extends AbstractSessionDataStore
{
public interface SessionDataCache
{
public SessionData get (SessionKey key); //get mapped value
public boolean putIfAbsent (SessionKey key, SessionData data); //only insert if no mapping for key already
public boolean remove (SessionKey key); //remove the mapping for key, returns false if no mapping
public void put (SessionKey key, SessionData data); //overwrite or add the mapping
}
protected SessionDataStore _delegateDataStore;
protected SessionDataCache _cache;
public void setSessionDataStore (SessionDataStore store)
{
checkStarted();
_delegateDataStore = store;
}
public SessionDataStore getSessionDataStore()
{
return _delegateDataStore;
}
public void setSessionDataCache (SessionDataCache cache)
{
checkStarted();
_cache = cache;
}
public SessionDataCache getSessionDataCache ()
{
return _cache;
}
/**
* @see org.eclipse.jetty.server.session.SessionDataStore#load(org.eclipse.jetty.server.session.SessionKey)
*/
@Override
public SessionData load(SessionKey key) throws Exception
{
//check to see if the session data is already in our cache
SessionData d = _cache.get(key);
if (d == null)
{
//not in the cache, go get it from the store
d = _delegateDataStore.load(key);
//put it into the cache, unless another thread/node has put it into the cache
boolean inserted = _cache.putIfAbsent(key, d);
if (!inserted)
{
//some other thread/node put this data into the cache, so get it from there
SessionData d2 = _cache.get(key);
if (d2 != null)
d = d2;
//else: The cache either timed out the entry, or maybe the session data was being removed, and we're about to resurrect it!
}
}
return d;
}
/**
* @see org.eclipse.jetty.server.session.SessionDataStore#delete(org.eclipse.jetty.server.session.SessionKey)
*/
@Override
public boolean delete(SessionKey key) throws Exception
{
//delete from the store and from the cache
_delegateDataStore.delete(key);
_cache.remove(key);
//TODO need to check removal at each level?
return false;
}
/**
* @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set)
*/
@Override
public Set<SessionKey> getExpired(Set<SessionKey> candidates)
{
// TODO Auto-generated method stub
return null;
}
/**
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(org.eclipse.jetty.server.session.SessionKey, org.eclipse.jetty.server.session.SessionData, boolean)
*/
@Override
public void doStore(SessionKey key, SessionData data, boolean isNew) throws Exception
{
//write to the SessionDataStore first
if (_delegateDataStore instanceof AbstractSessionDataStore)
((AbstractSessionDataStore)_delegateDataStore).doStore(key, data, isNew);
//else??????
//then update the cache with written data
_cache.put(key,data);
}
}

View File

@ -151,12 +151,6 @@ public class FileSessionDataStore extends AbstractSessionDataStore
}
}
private void checkStarted()
throws IllegalStateException
{
if (isStarted())
throw new IllegalStateException("Already started");
}
private SessionData load (SessionKey key, InputStream is)

View File

@ -56,14 +56,15 @@ public class HashSessionIdManager extends AbstractSessionIdManager
/**
* @see org.eclipse.jetty.server.SessionIdManager#useId(java.lang.String)
* @param session the session whose id to use
*/
@Override
public void useId(String id)
public void useId(Session session)
{
if (id == null)
if (session == null)
return;
_ids.add(id);
_ids.add(session.getId());
}
/**

View File

@ -1003,12 +1003,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
count.incrementAndGet();
}
protected void checkStarted () throws IllegalStateException
{
if (isStarted())
throw new IllegalStateException("Already started");
}
public int getLoadAttempts (SessionKey key)
{

View File

@ -159,12 +159,6 @@ public class JDBCSessionIdManager extends org.eclipse.jetty.server.session.Abstr
return _sessionIdTableSchema;
}
@Override
public String newSessionId(long seedTerm)
{
String id = super.newSessionId(seedTerm);
return id;
}
@ -173,13 +167,15 @@ public class JDBCSessionIdManager extends org.eclipse.jetty.server.session.Abstr
*
* @see org.eclipse.jetty.server.SessionIdManager#useId(java.lang.String)
*/
public void useId (String id)
@Override
public void useId (Session session)
{
if (id == null)
if (session == null)
return;
synchronized (_sessionIds)
{
String id = session.getId();
try
{
insert(id);
@ -391,31 +387,6 @@ public class JDBCSessionIdManager extends org.eclipse.jetty.server.session.Abstr
_sessionIds.clear();
super.doStop();
}
/**
*
* @param sql
* @param atoms
* @throws Exception
*/
private String fillInClause (String sql, String[] literals, int start, int end)
throws Exception
{
StringBuffer buff = new StringBuffer();
buff.append(sql);
buff.append("(");
for (int i=start; i<end; i++)
{
buff.append("'"+(literals[i])+"'");
if (i+1<end)
buff.append(",");
}
buff.append(")");
return buff.toString();
}
}
}

View File

@ -26,13 +26,15 @@ package org.eclipse.jetty.server.session;
public class JDBCSessionManager extends SessionManager
{
protected DatabaseAdaptor _db = new DatabaseAdaptor();
protected JDBCSessionDataStore _sessionDataStore = new JDBCSessionDataStore();
protected DatabaseAdaptor _db;
protected JDBCSessionDataStore _sessionDataStore;
public JDBCSessionManager()
{
_db = new DatabaseAdaptor();
_sessionStore = new MemorySessionStore();
_sessionDataStore = new JDBCSessionDataStore();
}
@Override

View File

@ -580,7 +580,7 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
_sessionsCreatedStats.increment();
_sessionIdManager.useId(id);
_sessionIdManager.useId(session);
if (_sessionListeners!=null)
{
@ -946,6 +946,9 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
session.getSessionData().setLastSaved(0); //forces an insert
_sessionStore.put(newKey, session);
//tell session id manager the id is in use
_sessionIdManager.useId(session);
//remove session with old id
_sessionStore.delete(oldKey);

View File

@ -156,7 +156,7 @@ public class SessionCookieTest
* @see org.eclipse.jetty.server.SessionIdManager#useId(java.lang.String)
*/
@Override
public void useId(String id)
public void useId(Session session)
{
// TODO Auto-generated method stub

View File

@ -63,10 +63,10 @@ public class InfinispanTestSessionServer extends AbstractTestServer
{
InfinispanSessionManager sessionManager = new InfinispanSessionManager();
sessionManager.setSessionIdManager((InfinispanSessionIdManager)_sessionIdManager);
sessionManager.setCache(((InfinispanSessionIdManager)_sessionIdManager).getCache());
sessionManager.setStaleIntervalSec(1);
sessionManager.setScavengeInterval(_scavengePeriod);
sessionManager.getSessionDataStore().setCache(((InfinispanSessionIdManager)_sessionIdManager).getCache());
StalePeriodStrategy staleStrategy = new StalePeriodStrategy();
staleStrategy.setStaleSec(1);
((AbstractSessionStore)sessionManager.getSessionStore()).setStaleStrategy(staleStrategy);
return sessionManager;
}