Ensure session loading runs in scope of context classloader.

This commit is contained in:
Jan Bartel 2015-12-11 16:04:39 +11:00
parent 31ea1704a1
commit b1707ef228
18 changed files with 421 additions and 498 deletions

View File

@ -27,7 +27,7 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.server.session.AbstractSessionDataStore;
import org.eclipse.jetty.server.session.ContextId;
import org.eclipse.jetty.server.session.SessionContext;
import org.eclipse.jetty.server.session.SessionData;
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
import org.eclipse.jetty.util.log.Log;
@ -161,7 +161,7 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
{
if (LOG.isDebugEnabled()) LOG.debug("Loading session {} from DataStore", id);
Entity entity = _datastore.get(makeKey(id, _contextId));
Entity entity = _datastore.get(makeKey(id, _context));
if (entity == null)
{
if (LOG.isDebugEnabled()) LOG.debug("No session {} in DataStore ", id);
@ -181,7 +181,7 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
public boolean delete(String id) throws Exception
{
if (LOG.isDebugEnabled()) LOG.debug("Removing session {} from DataStore", id);
_datastore.delete(makeKey(id, _contextId));
_datastore.delete(makeKey(id, _context));
return true;
}
@ -252,7 +252,7 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
{
if (LOG.isDebugEnabled()) LOG.debug("Writing session {} to DataStore", data.getId());
Entity entity = entityFromSession(data, makeKey(id, _contextId));
Entity entity = entityFromSession(data, makeKey(id, _context));
_datastore.put(entity);
}
@ -270,7 +270,7 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
* @param session
* @return
*/
private Key makeKey (String id, ContextId context)
private Key makeKey (String id, SessionContext context)
{
String key = context.getCanonicalContextPath()+"_"+context.getVhost()+"_"+id;
return _keyFactory.newKey(key);
@ -345,8 +345,6 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
long expiry = entity.getLong(EXPIRY);
long maxInactive = entity.getLong(MAXINACTIVE);
Blob blob = (Blob) entity.getBlob(ATTRIBUTES);
System.err.println("Session "+id+" from Entity, expiry="+expiry);
SessionData session = newSessionData (id, createTime, accessed, lastAccessed, maxInactive);
session.setLastNode(lastNode);
@ -369,18 +367,11 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
}
};
load.run();
/* if (_context==null)
load.run();
else
_context.getContextHandler().handle(null,load);*/
//ensure this runs in the context classloader
_context.run(load);
if (exception.get() != null)
{
throw exception.get();
}
return reference.get();
}

View File

@ -22,12 +22,12 @@ package org.eclipse.jetty.session.infinispan;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.server.SessionIdManager;
import org.eclipse.jetty.server.session.AbstractSessionDataStore;
import org.eclipse.jetty.server.session.ContextId;
import org.eclipse.jetty.server.session.SessionContext;
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;
@ -88,8 +88,34 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
*/
@Override
public SessionData load(String id) throws Exception
{
return (SessionData)_cache.get(getCacheKey(id, _contextId));
{
final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
final AtomicReference<Exception> exception = new AtomicReference<Exception>();
Runnable load = new Runnable()
{
public void run ()
{
try
{
SessionData sd = (SessionData)_cache.get(getCacheKey(id, _context));
reference.set(sd);
}
catch (Exception e)
{
exception.set(e);
}
}
};
//ensure the load runs in the context classloader scope
_context.run(load);
if (exception.get() != null)
throw exception.get();
return reference.get();
}
/**
@ -98,7 +124,7 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
@Override
public boolean delete(String id) throws Exception
{
return (_cache.remove(getCacheKey(id, _contextId)) != null);
return (_cache.remove(getCacheKey(id, _context)) != null);
}
/**
@ -145,9 +171,9 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
//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(getCacheKey(id, _contextId), data, -1, TimeUnit.MILLISECONDS, (data.getMaxInactiveMs() * _idleExpiryMultiple), TimeUnit.MILLISECONDS);
_cache.put(getCacheKey(id, _context), data, -1, TimeUnit.MILLISECONDS, (data.getMaxInactiveMs() * _idleExpiryMultiple), TimeUnit.MILLISECONDS);
else
_cache.put(getCacheKey(id, _contextId), data);
_cache.put(getCacheKey(id, _context), data);
//tickle the session id manager to keep the sessionid entry for this session up-to-date
if (_idMgr != null && _idMgr instanceof InfinispanSessionIdManager)
@ -157,9 +183,9 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
}
public static String getCacheKey (String id, ContextId contextId)
public static String getCacheKey (String id, SessionContext context)
{
return contextId.getCanonicalContextPath()+"_"+contextId.getVhost()+"_"+id;
return context.getCanonicalContextPath()+"_"+context.getVhost()+"_"+id;
}

View File

@ -90,7 +90,7 @@ public abstract class NoSqlSessionDataStore extends AbstractSessionDataStore
@Override
public SessionData newSessionData(String id, long created, long accessed, long lastAccessed, long maxInactiveMs)
{
return new NoSqlSessionData(id, _contextId.getCanonicalContextPath(), _contextId.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
return new NoSqlSessionData(id, _context.getCanonicalContextPath(), _context.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
}

View File

@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.nosql.NoSqlSessionDataStore;
import org.eclipse.jetty.server.session.SessionData;
@ -193,76 +194,90 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
@Override
public SessionData load(String id) throws Exception
{
DBObject sessionDocument = _dbSessions.findOne(new BasicDBObject(__ID, id));
if (LOG.isDebugEnabled())
LOG.debug("id={} loaded={}", id, sessionDocument);
if (sessionDocument == null)
return null;
Boolean valid = (Boolean)sessionDocument.get(__VALID);
if (LOG.isDebugEnabled())
LOG.debug("id={} valid={}", id, valid);
if (valid == null || !valid)
return null;
try
final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
final AtomicReference<Exception> exception = new AtomicReference<Exception>();
Runnable r = new Runnable()
{
Object version = getNestedValue(sessionDocument, getContextSubfield(__VERSION));
Long created = (Long)sessionDocument.get(__CREATED);
Long accessed = (Long)sessionDocument.get(__ACCESSED);
Long maxInactive = (Long)sessionDocument.get(__MAX_IDLE);
Long expiry = (Long)sessionDocument.get(__EXPIRY);
NoSqlSessionData data = null;
// get the session for the context
DBObject sessionSubDocumentForContext = (DBObject)getNestedValue(sessionDocument,getContextField());
if (LOG.isDebugEnabled()) LOG.debug("attrs {}", sessionSubDocumentForContext);
if (sessionSubDocumentForContext != null)
public void run ()
{
if (LOG.isDebugEnabled())
LOG.debug("Session {} present for context {}", id, _contextId);
//only load a session if it exists for this context
data = (NoSqlSessionData)newSessionData(id, created, accessed, accessed, maxInactive);
data.setVersion(version);
data.setExpiry(expiry);
data.setContextPath(_contextId.getCanonicalContextPath());
data.setVhost(_contextId.getVhost());
HashMap<String, Object> attributes = new HashMap<>();
for (String name : sessionSubDocumentForContext.keySet())
try
{
//skip special metadata attribute which is not one of the actual session attributes
if ( __METADATA.equals(name) )
continue;
String attr = decodeName(name);
Object value = decodeValue(sessionSubDocumentForContext.get(name));
attributes.put(attr,value);
}
data.putAllAttributes(attributes);
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("Session {} not present for context {}", id, _contextId);
}
DBObject sessionDocument = _dbSessions.findOne(new BasicDBObject(__ID, id));
return data;
}
catch (Exception e)
{
LOG.warn(e);
}
if (LOG.isDebugEnabled())
LOG.debug("id={} loaded={}", id, sessionDocument);
if (sessionDocument == null)
return;
Boolean valid = (Boolean)sessionDocument.get(__VALID);
if (LOG.isDebugEnabled())
LOG.debug("id={} valid={}", id, valid);
if (valid == null || !valid)
return;
Object version = getNestedValue(sessionDocument, getContextSubfield(__VERSION));
Long created = (Long)sessionDocument.get(__CREATED);
Long accessed = (Long)sessionDocument.get(__ACCESSED);
Long maxInactive = (Long)sessionDocument.get(__MAX_IDLE);
Long expiry = (Long)sessionDocument.get(__EXPIRY);
NoSqlSessionData data = null;
// get the session for the context
DBObject sessionSubDocumentForContext = (DBObject)getNestedValue(sessionDocument,getContextField());
if (LOG.isDebugEnabled()) LOG.debug("attrs {}", sessionSubDocumentForContext);
if (sessionSubDocumentForContext != null)
{
if (LOG.isDebugEnabled())
LOG.debug("Session {} present for context {}", id, _context);
//only load a session if it exists for this context
data = (NoSqlSessionData)newSessionData(id, created, accessed, accessed, maxInactive);
data.setVersion(version);
data.setExpiry(expiry);
data.setContextPath(_context.getCanonicalContextPath());
data.setVhost(_context.getVhost());
HashMap<String, Object> attributes = new HashMap<>();
for (String name : sessionSubDocumentForContext.keySet())
{
//skip special metadata attribute which is not one of the actual session attributes
if ( __METADATA.equals(name) )
continue;
String attr = decodeName(name);
Object value = decodeValue(sessionSubDocumentForContext.get(name));
attributes.put(attr,value);
}
data.putAllAttributes(attributes);
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("Session {} not present for context {}", id, _context);
}
reference.set(data);
}
catch (Exception e)
{
exception.set(e);
}
}
};
return null;
_context.run(r);
if (exception.get() != null)
throw exception.get();
return reference.get();
}
/**
@ -272,7 +287,7 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
public boolean delete(String id) throws Exception
{
if (LOG.isDebugEnabled())
LOG.debug("Remove:session {} for context ",id, _contextId);
LOG.debug("Remove:session {} for context ",id, _context);
/*
* Check if the session exists and if it does remove the context
@ -345,7 +360,7 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
for ( DBObject session : verifiedExpiredSessions )
{
String id = (String)session.get(__ID);
if (LOG.isDebugEnabled()) LOG.debug("{} Mongo confirmed expired session {}", _contextId,id);
if (LOG.isDebugEnabled()) LOG.debug("{} Mongo confirmed expired session {}", _context,id);
expiredSessions.add(id);
}
}
@ -368,7 +383,7 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
for (DBObject session : oldExpiredSessions)
{
String id = (String)session.get(__ID);
if (LOG.isDebugEnabled()) LOG.debug("{} Mongo found old expired session {}", _contextId, id);
if (LOG.isDebugEnabled()) LOG.debug("{} Mongo found old expired session {}", _context, id);
expiredSessions.add(id);
}
@ -499,7 +514,7 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
private String getCanonicalContextId ()
{
return canonicalizeVHost(_contextId.getVhost()) + ":" + _contextId.getCanonicalContextPath();
return canonicalizeVHost(_context.getVhost()) + ":" + _context.getCanonicalContextPath();
}
private String canonicalizeVHost (String vhost)

View File

@ -29,7 +29,7 @@ import org.eclipse.jetty.util.component.AbstractLifeCycle;
*/
public abstract class AbstractSessionDataStore extends AbstractLifeCycle implements SessionDataStore
{
protected ContextId _contextId; //context associated with this session data store
protected SessionContext _context; //context associated with this session data store
public abstract void doStore(String id, SessionData data, boolean isNew) throws Exception;
@ -37,11 +37,11 @@ public abstract class AbstractSessionDataStore extends AbstractLifeCycle impleme
public void initialize (ContextId id)
public void initialize (SessionContext context)
{
if (isStarted())
throw new IllegalStateException("Context set after SessionDataStore started");
_contextId = id;
_context = context;
}
/**
@ -75,7 +75,7 @@ public abstract class AbstractSessionDataStore extends AbstractLifeCycle impleme
@Override
public SessionData newSessionData(String id, long created, long accessed, long lastAccessed, long maxInactiveMs)
{
return new SessionData(id, _contextId.getCanonicalContextPath(), _contextId.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
return new SessionData(id, _context.getCanonicalContextPath(), _context.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
}
protected void checkStarted () throws IllegalStateException
@ -90,8 +90,8 @@ public abstract class AbstractSessionDataStore extends AbstractLifeCycle impleme
@Override
protected void doStart() throws Exception
{
if (_contextId == null)
throw new IllegalStateException ("No ContextId");
if (_context == null)
throw new IllegalStateException ("No SessionContext");
super.doStart();
}

View File

@ -41,7 +41,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
protected SessionDataStore _sessionDataStore;
protected StalenessStrategy _staleStrategy;
protected SessionManager _manager;
protected ContextId _contextId;
protected SessionContext _context;
@ -123,11 +123,11 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
public void initialize (ContextId contextId)
public void initialize (SessionContext context)
{
if (isStarted())
throw new IllegalStateException("Context set after session store started");
_contextId = contextId;
_context = context;
}
@Override
@ -139,10 +139,10 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
if (_manager == null)
throw new IllegalStateException ("No session manager");
if (_contextId == null)
if (_context == null)
throw new IllegalStateException ("No ContextId");
_sessionDataStore.initialize(_contextId);
_sessionDataStore.initialize(_context);
_sessionDataStore.start();
super.doStart();

View File

@ -41,7 +41,7 @@ public class CachingSessionDataStore extends AbstractSessionDataStore
public boolean putIfAbsent (String id, SessionData data); //only insert if no mapping for key already
public boolean remove (String id); //remove the mapping for key, returns false if no mapping
public void put (String id, SessionData data); //overwrite or add the mapping
public void initialize(ContextId contextId);
public void initialize(SessionContext context);
}
@ -144,8 +144,8 @@ public class CachingSessionDataStore extends AbstractSessionDataStore
@Override
protected void doStart() throws Exception
{
_cache.initialize(_contextId);
_delegateDataStore.initialize(_contextId);
_cache.initialize(_context);
_delegateDataStore.initialize(_context);
super.doStart();
}

View File

@ -33,6 +33,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
import org.eclipse.jetty.util.log.Log;
@ -96,7 +97,7 @@ public class FileSessionDataStore extends AbstractSessionDataStore
File file = null;
if (_storeDir != null)
{
file = new File(_storeDir, _contextId.toString()+"_"+id);
file = new File(_storeDir, getFileName(id));
if (file.exists() && file.getParentFile().equals(_storeDir))
{
file.delete();
@ -124,40 +125,135 @@ public class FileSessionDataStore extends AbstractSessionDataStore
@Override
public SessionData load(String id) throws Exception
{
File file = new File(_storeDir, _contextId.toString()+"_"+id);
if (!file.exists())
final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
final AtomicReference<Exception> exception = new AtomicReference<Exception>();
Runnable r = new Runnable()
{
if (LOG.isDebugEnabled())
LOG.debug("No file: {}",file);
return null;
}
try (FileInputStream in = new FileInputStream(file))
{
SessionData data = load(in);
//delete restored file
file.delete();
return data;
}
catch (UnreadableSessionDataException e)
{
if (isDeleteUnrestorableFiles() && file.exists() && file.getParentFile().equals(_storeDir));
public void run ()
{
file.delete();
LOG.warn("Deleted unrestorable file for session {}", id);
File file = new File(_storeDir,getFileName(id));
if (!file.exists())
{
if (LOG.isDebugEnabled())
LOG.debug("No file: {}",file);
return;
}
try (FileInputStream in = new FileInputStream(file))
{
SessionData data = load(in);
//delete restored file
file.delete();
reference.set(data);
}
catch (UnreadableSessionDataException e)
{
if (isDeleteUnrestorableFiles() && file.exists() && file.getParentFile().equals(_storeDir));
{
file.delete();
LOG.warn("Deleted unrestorable file for session {}", id);
}
exception.set(e);
}
catch (Exception e)
{
exception.set(e);
}
}
throw e;
}
};
//ensure this runs with the context classloader set
_context.run(r);
if (exception.get() != null)
throw exception.get();
return reference.get();
}
/**
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(org.eclipse.jetty.server.session.SessionKey, org.eclipse.jetty.server.session.SessionData)
*/
@Override
public void doStore(String id, SessionData data, boolean isNew) throws Exception
{
File file = null;
if (_storeDir != null)
{
file = new File(_storeDir, getFileName(id));
if (file.exists())
file.delete();
try(FileOutputStream fos = new FileOutputStream(file,false))
{
save(fos, id, data);
}
catch (Exception e)
{
if (file != null)
file.delete(); // No point keeping the file if we didn't save the whole session
throw new UnwriteableSessionDataException(id, _context,e);
}
}
}
public void initializeStore ()
{
if (_storeDir == null)
throw new IllegalStateException("No file store specified");
if (!_storeDir.exists())
_storeDir.mkdirs();
}
/**
* @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
*/
@Override
public boolean isPassivating()
{
return true;
}
/* ------------------------------------------------------------ */
private void save(OutputStream os, String id, SessionData data) throws IOException
{
DataOutputStream out = new DataOutputStream(os);
out.writeUTF(id);
out.writeUTF(_context.getCanonicalContextPath());
out.writeUTF(_context.getVhost());
out.writeUTF(data.getLastNode());
out.writeLong(data.getCreated());
out.writeLong(data.getAccessed());
out.writeLong(data.getLastAccessed());
out.writeLong(data.getCookieSet());
out.writeLong(data.getExpiry());
out.writeLong(data.getMaxInactiveMs());
List<String> keys = new ArrayList<String>(data.getKeys());
out.writeInt(keys.size());
ObjectOutputStream oos = new ObjectOutputStream(out);
for (String name:keys)
{
oos.writeUTF(name);
oos.writeObject(data.getAttribute(name));
}
}
private String getFileName (String id)
{
return _context.getCanonicalContextPath()+"_"+_context.getVhost()+"_"+id;
}
private SessionData load (InputStream is)
throws Exception
throws Exception
{
String id = null;
try
{
SessionData data = null;
@ -189,12 +285,12 @@ public class FileSessionDataStore extends AbstractSessionDataStore
}
catch (Exception e)
{
throw new UnreadableSessionDataException(id, _contextId, e);
throw new UnreadableSessionDataException(id, _context, e);
}
}
private void restoreAttributes (InputStream is, int size, SessionData data)
throws Exception
throws Exception
{
if (size>0)
{
@ -211,78 +307,6 @@ public class FileSessionDataStore extends AbstractSessionDataStore
}
}
/**
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(org.eclipse.jetty.server.session.SessionKey, org.eclipse.jetty.server.session.SessionData)
*/
@Override
public void doStore(String id, SessionData data, boolean isNew) throws Exception
{
File file = null;
if (_storeDir != null)
{
file = new File(_storeDir, _contextId.toString()+"_"+id);
if (file.exists())
file.delete();
try(FileOutputStream fos = new FileOutputStream(file,false))
{
save(fos, id, data);
}
catch (Exception e)
{
if (file != null)
file.delete(); // No point keeping the file if we didn't save the whole session
throw new UnwriteableSessionDataException(id, _contextId,e);
}
}
}
/* ------------------------------------------------------------ */
private void save(OutputStream os, String id, SessionData data) throws IOException
{
DataOutputStream out = new DataOutputStream(os);
out.writeUTF(id);
out.writeUTF(_contextId.getCanonicalContextPath());
out.writeUTF(_contextId.getVhost());
out.writeUTF(data.getLastNode());
out.writeLong(data.getCreated());
out.writeLong(data.getAccessed());
out.writeLong(data.getLastAccessed());
out.writeLong(data.getCookieSet());
out.writeLong(data.getExpiry());
out.writeLong(data.getMaxInactiveMs());
List<String> keys = new ArrayList<String>(data.getKeys());
out.writeInt(keys.size());
ObjectOutputStream oos = new ObjectOutputStream(out);
for (String name:keys)
{
oos.writeUTF(name);
oos.writeObject(data.getAttribute(name));
}
}
public void initializeStore ()
{
if (_storeDir == null)
throw new IllegalStateException("No file store specified");
if (!_storeDir.exists())
_storeDir.mkdirs();
}
/**
* @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
*/
@Override
public boolean isPassivating()
{
return true;
}
}

View File

@ -35,6 +35,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
import org.eclipse.jetty.util.log.Log;
@ -359,7 +360,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
return statement;
}
public void fillCheckSessionExistsStatement (PreparedStatement statement, String id, ContextId contextId)
public void fillCheckSessionExistsStatement (PreparedStatement statement, String id, SessionContext contextId)
throws SQLException
{
statement.clearParameters();
@ -378,7 +379,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
}
public PreparedStatement getLoadStatement (Connection connection, String id, ContextId contextId)
public PreparedStatement getLoadStatement (Connection connection, String id, SessionContext contextId)
throws SQLException
{
if (_dbAdaptor == null)
@ -412,7 +413,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
public PreparedStatement getUpdateStatement (Connection connection, String id, ContextId contextId)
public PreparedStatement getUpdateStatement (Connection connection, String id, SessionContext contextId)
throws SQLException
{
if (_dbAdaptor == null)
@ -447,7 +448,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
public PreparedStatement getDeleteStatement (Connection connection, String id, ContextId contextId)
public PreparedStatement getDeleteStatement (Connection connection, String id, SessionContext contextId)
throws Exception
{
if (_dbAdaptor == null)
@ -635,70 +636,92 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
public SessionData load(String id) throws Exception
{
if (getLoadAttempts() > 0 && loadAttemptsExhausted(id))
throw new UnreadableSessionDataException(id, _contextId, true);
throw new UnreadableSessionDataException(id, _context, true);
try (Connection connection = _dbAdaptor.getConnection();
PreparedStatement statement = _sessionTableSchema.getLoadStatement(connection, id, _contextId);
ResultSet result = statement.executeQuery())
final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
final AtomicReference<Exception> exception = new AtomicReference<Exception>();
Runnable r = new Runnable()
{
SessionData data = null;
if (result.next())
{
data = newSessionData(id,
result.getLong(_sessionTableSchema.getCreateTimeColumn()),
result.getLong(_sessionTableSchema.getAccessTimeColumn()),
result.getLong(_sessionTableSchema.getLastAccessTimeColumn()),
result.getLong(_sessionTableSchema.getMaxIntervalColumn()));
data.setCookieSet(result.getLong(_sessionTableSchema.getCookieTimeColumn()));
data.setLastNode(result.getString(_sessionTableSchema.getLastNodeColumn()));
data.setLastSaved(result.getLong(_sessionTableSchema.getLastSavedTimeColumn()));
data.setExpiry(result.getLong(_sessionTableSchema.getExpiryTimeColumn()));
data.setContextPath(result.getString(_sessionTableSchema.getContextPathColumn())); //TODO needed? this is part of the key now
data.setVhost(result.getString(_sessionTableSchema.getVirtualHostColumn())); //TODO needed??? this is part of the key now
try (InputStream is = _dbAdaptor.getBlobInputStream(result, _sessionTableSchema.getMapColumn());
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
public void run ()
{
try (Connection connection = _dbAdaptor.getConnection();
PreparedStatement statement = _sessionTableSchema.getLoadStatement(connection, id, _context);
ResultSet result = statement.executeQuery())
{
Object o = ois.readObject();
data.putAllAttributes((Map<String,Object>)o);
SessionData data = null;
if (result.next())
{
data = newSessionData(id,
result.getLong(_sessionTableSchema.getCreateTimeColumn()),
result.getLong(_sessionTableSchema.getAccessTimeColumn()),
result.getLong(_sessionTableSchema.getLastAccessTimeColumn()),
result.getLong(_sessionTableSchema.getMaxIntervalColumn()));
data.setCookieSet(result.getLong(_sessionTableSchema.getCookieTimeColumn()));
data.setLastNode(result.getString(_sessionTableSchema.getLastNodeColumn()));
data.setLastSaved(result.getLong(_sessionTableSchema.getLastSavedTimeColumn()));
data.setExpiry(result.getLong(_sessionTableSchema.getExpiryTimeColumn()));
data.setContextPath(result.getString(_sessionTableSchema.getContextPathColumn())); //TODO needed? this is part of the key now
data.setVhost(result.getString(_sessionTableSchema.getVirtualHostColumn())); //TODO needed??? this is part of the key now
try (InputStream is = _dbAdaptor.getBlobInputStream(result, _sessionTableSchema.getMapColumn());
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
{
Object o = ois.readObject();
data.putAllAttributes((Map<String,Object>)o);
}
catch (Exception e)
{
if (getLoadAttempts() > 0)
{
incLoadAttempt (id);
}
throw new UnreadableSessionDataException (id, _context, e);
}
//if the session successfully loaded, remove failed attempts
_unloadables.remove(id);
if (LOG.isDebugEnabled())
LOG.debug("LOADED session {}", data);
}
else
if (LOG.isDebugEnabled())
LOG.debug("No session {}", id);
reference.set(data);
}
catch (UnreadableSessionDataException e)
{
if (getLoadAttempts() > 0 && loadAttemptsExhausted(id) && isDeleteUnloadableSessions())
{
try
{
delete (id);
_unloadables.remove(id);
}
catch (Exception x)
{
LOG.warn("Problem deleting unloadable session {}", id);
}
}
exception.set(e);
}
catch (Exception e)
{
if (getLoadAttempts() > 0)
{
incLoadAttempt (id);
}
throw new UnreadableSessionDataException (id, _contextId, e);
exception.set(e);
}
//if the session successfully loaded, remove failed attempts
_unloadables.remove(id);
if (LOG.isDebugEnabled())
LOG.debug("LOADED session {}", data);
}
else
if (LOG.isDebugEnabled())
LOG.debug("No session {}", id);
return data;
}
catch (UnreadableSessionDataException e)
{
if (getLoadAttempts() > 0 && loadAttemptsExhausted(id) && isDeleteUnloadableSessions())
{
try
{
delete (id);
_unloadables.remove(id);
}
catch (Exception x)
{
LOG.warn("Problem deleting unloadable session {}", id);
}
};
}
throw e;
}
//ensure this runs with context classloader set
_context.run(r);
if (exception.get() != null)
throw exception.get();
return reference.get();
}
@ -710,7 +733,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
public boolean delete(String id) throws Exception
{
try (Connection connection = _dbAdaptor.getConnection();
PreparedStatement statement = _sessionTableSchema.getDeleteStatement(connection, id, _contextId))
PreparedStatement statement = _sessionTableSchema.getDeleteStatement(connection, id, _context))
{
connection.setAutoCommit(true);
int rows = statement.executeUpdate();
@ -756,8 +779,8 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
try (PreparedStatement statement = connection.prepareStatement(s))
{
statement.setString(1, id); //session id
statement.setString(2, _contextId.getCanonicalContextPath()); //context path
statement.setString(3, _contextId.getVhost()); //first vhost
statement.setString(2, _context.getCanonicalContextPath()); //context path
statement.setString(3, _context.getVhost()); //first vhost
statement.setString(4, data.getLastNode());//my node id
statement.setLong(5, data.getAccessed());//accessTime
statement.setLong(6, data.getLastAccessed()); //lastAccessTime
@ -788,7 +811,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
try (Connection connection = _dbAdaptor.getConnection())
{
connection.setAutoCommit(true);
try (PreparedStatement statement = _sessionTableSchema.getUpdateSessionStatement(connection, _contextId.getCanonicalContextPath()))
try (PreparedStatement statement = _sessionTableSchema.getUpdateSessionStatement(connection, _context.getCanonicalContextPath()))
{
statement.setString(1, data.getLastNode());//should be my node id
statement.setLong(2, data.getAccessed());//accessTime
@ -805,16 +828,16 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
statement.setBinaryStream(7, bais, bytes.length);//attribute map as blob
if ((_contextId.getCanonicalContextPath() == null || "".equals(_contextId.getCanonicalContextPath())) && _dbAdaptor.isEmptyStringNull())
if ((_context.getCanonicalContextPath() == null || "".equals(_context.getCanonicalContextPath())) && _dbAdaptor.isEmptyStringNull())
{
statement.setString(8, id);
statement.setString(9, _contextId.getVhost());
statement.setString(9, _context.getVhost());
}
else
{
statement.setString(8, id);
statement.setString(9, _contextId.getCanonicalContextPath());
statement.setString(10, _contextId.getVhost());
statement.setString(9, _context.getCanonicalContextPath());
statement.setString(10, _context.getVhost());
}
statement.executeUpdate();
@ -848,9 +871,9 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
*/
long upperBound = now;
if (LOG.isDebugEnabled())
LOG.debug ("{}- Pass 1: Searching for sessions for node {} and context {} expired before {}", _contextId.getNode(), _contextId.getCanonicalContextPath(), upperBound);
LOG.debug ("{}- Pass 1: Searching for sessions for node {} and context {} expired before {}", _context.getWorkerName(), _context.getCanonicalContextPath(), upperBound);
try (PreparedStatement statement = _sessionTableSchema.getMyExpiredSessionsStatement(connection, _contextId.getCanonicalContextPath(), _contextId.getVhost(), upperBound))
try (PreparedStatement statement = _sessionTableSchema.getMyExpiredSessionsStatement(connection, _context.getCanonicalContextPath(), _context.getVhost(), upperBound))
{
try (ResultSet result = statement.executeQuery())
{
@ -859,7 +882,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
String sessionId = result.getString(_sessionTableSchema.getIdColumn());
long exp = result.getLong(_sessionTableSchema.getExpiryTimeColumn());
expiredSessionKeys.add(sessionId);
if (LOG.isDebugEnabled()) LOG.debug (_contextId.getCanonicalContextPath()+"- Found expired sessionId="+sessionId);
if (LOG.isDebugEnabled()) LOG.debug (_context.getCanonicalContextPath()+"- Found expired sessionId="+sessionId);
}
}
}
@ -872,7 +895,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
upperBound = now - (3 * _gracePeriodMs);
if (upperBound > 0)
{
if (LOG.isDebugEnabled()) LOG.debug("{}- Pass 2: Searching for sessions expired before {}",_contextId.getNode(), upperBound);
if (LOG.isDebugEnabled()) LOG.debug("{}- Pass 2: Searching for sessions expired before {}",_context.getWorkerName(), upperBound);
selectExpiredSessions.setLong(1, upperBound);
try (ResultSet result = selectExpiredSessions.executeQuery())
@ -883,7 +906,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
String ctxtpth = result.getString(_sessionTableSchema.getContextPathColumn());
String vh = result.getString(_sessionTableSchema.getVirtualHostColumn());
expiredSessionKeys.add(sessionId);
if (LOG.isDebugEnabled()) LOG.debug ("{}- Found expired sessionId=",_contextId.getNode(), sessionId);
if (LOG.isDebugEnabled()) LOG.debug ("{}- Found expired sessionId=",_context.getWorkerName(), sessionId);
}
}
}
@ -904,11 +927,11 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
if (!notExpiredInDB.isEmpty())
{
//we have some sessions to check
try (PreparedStatement checkSessionExists = _sessionTableSchema.getCheckSessionExistsStatement(connection, _contextId.getCanonicalContextPath()))
try (PreparedStatement checkSessionExists = _sessionTableSchema.getCheckSessionExistsStatement(connection, _context.getCanonicalContextPath()))
{
for (String k: notExpiredInDB)
{
_sessionTableSchema.fillCheckSessionExistsStatement (checkSessionExists, k, _contextId);
_sessionTableSchema.fillCheckSessionExistsStatement (checkSessionExists, k, _context);
try (ResultSet result = checkSessionExists.executeQuery())
{
if (!result.next())

View File

@ -45,7 +45,7 @@ public class NullSessionDataStore extends AbstractSessionDataStore
@Override
public SessionData newSessionData(String id, long created, long accessed, long lastAccessed, long maxInactiveMs)
{
return new SessionData(id, _contextId.getCanonicalContextPath(), _contextId.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
return new SessionData(id, _context.getCanonicalContextPath(), _context.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
}
/**

View File

@ -19,41 +19,44 @@
package org.eclipse.jetty.server.session;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
/**
* ContextId
*
* SessionContext
*
* The worker name which identifies this server instance, and the particular
* Context.
*
* A SessionManager is 1:1 with a SessionContext.
*/
public class ContextId
public class SessionContext
{
public final static String NULL_VHOST = "0.0.0.0";
private String _node;
private ContextHandler.Context _context;
private String _workerName;
private String _canonicalContextPath;
private String _vhost;
public static ContextId getContextId (String node, Context context)
public String getWorkerName()
{
return new ContextId((node==null?"":node), getContextPath(context), getVirtualHost(context));
return _workerName;
}
public SessionContext (String workerName, ContextHandler.Context context)
{
_workerName = workerName;
_context = context;
_canonicalContextPath = canonicalizeContextPath(_context);
_vhost = canonicalizeVHost(_context);
}
private ContextId (String node, String path, String vhost)
public Context getContext ()
{
if (node == null || path == null || vhost == null)
throw new IllegalArgumentException ("Bad values for ContextId ["+node+","+path+","+vhost+"]");
_node = node;
_canonicalContextPath = path;
_vhost = vhost;
}
public String getNode()
{
return _node;
return _context;
}
public String getCanonicalContextPath()
@ -68,28 +71,24 @@ public class ContextId
public String toString ()
{
return _node+"_"+_canonicalContextPath +"_"+_vhost;
return _workerName+"_"+_canonicalContextPath +"_"+_vhost;
}
@Override
public boolean equals (Object o)
/**
* Run a runnable in the context (with context classloader set) if
* there is one, otherwise just run it.
* @param r
*/
public void run (Runnable r)
{
if (o == null)
return false;
ContextId id = (ContextId)o;
if (id.getNode().equals(getNode()) && id.getCanonicalContextPath().equals(getCanonicalContextPath()) && id.getVhost().equals(getVhost()))
return true;
return false;
if (_context != null)
_context.getContextHandler().handle(r);
else
r.run();
}
@Override
public int hashCode()
{
return java.util.Objects.hash(getNode(), getCanonicalContextPath(), getVhost());
}
public static String getContextPath (Context context)
private String canonicalizeContextPath (Context context)
{
if (context == null)
return "";
@ -104,7 +103,7 @@ public class ContextId
*
* @return 0.0.0.0 if no virtual host is defined
*/
public static String getVirtualHost (Context context)
private String canonicalizeVHost (Context context)
{
String vhost = NULL_VHOST;
@ -124,12 +123,12 @@ public class ContextId
* @param path
* @return
*/
private static String canonicalize (String path)
private String canonicalize (String path)
{
if (path==null)
return "";
return path.replace('/', '_').replace('.','_').replace('\\','_');
}
}
}

View File

@ -36,9 +36,9 @@ public interface SessionDataStore extends LifeCycle
* given context. A SessionDataStore can only
* be used by one context(/session manager).
*
* @param contextId
* @param context
*/
void initialize(ContextId contextId);
void initialize(SessionContext context);

View File

@ -1,154 +0,0 @@
//
// ========================================================================
// 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 org.eclipse.jetty.server.handler.ContextHandler.Context;
/**
* SessionKey
*
*
*/
public class SessionKey
{
public final static String NULL_VHOST = "0.0.0.0";
private String _id;
private String _canonicalContextPath;
private String _vhost;
public static SessionKey getKey (String id, Context context)
{
String cpath = getContextPath(context);
String vhosts = getVirtualHost(context);
return new SessionKey (id, cpath, vhosts);
}
public static SessionKey getKey (SessionData data)
{
String cpath = canonicalize(data.getContextPath());
String vhost = data.getVhost();
if (vhost == null)
vhost = NULL_VHOST;
String id = data.getId();
return new SessionKey(id, cpath, vhost);
}
public static SessionKey getKey (String id, String path, String virtualHost)
{
String cpath = canonicalize(path);
String vhost = NULL_VHOST;
if (virtualHost != null && !("".equals(virtualHost)))
vhost = virtualHost;
return new SessionKey(id, cpath, vhost);
}
private SessionKey (String id, String path, String vhost)
{
if (id == null || path == null || vhost == null)
throw new IllegalArgumentException ("Bad values for key");
_id = id;
_canonicalContextPath = path;
_vhost = vhost;
}
public String getId()
{
return _id;
}
public String getCanonicalContextPath()
{
return _canonicalContextPath;
}
public String getVhost()
{
return _vhost;
}
public String toString ()
{
return _canonicalContextPath +"_"+_vhost+"_"+_id;
}
@Override
public boolean equals (Object o)
{
if (o == null)
return false;
SessionKey k = ((SessionKey)o);
if (k.getId().equals(getId()) && k.getCanonicalContextPath().equals(getCanonicalContextPath()) && k.getVhost().equals(getVhost()))
return true;
return false;
}
@Override
public int hashCode()
{
return java.util.Objects.hash(getId(), getCanonicalContextPath(), getVhost());
}
public static String getContextPath (Context context)
{
if (context == null)
return "";
return canonicalize (context.getContextPath());
}
/**
* Get the first virtual host for the context.
*
* Used to help identify the exact session/contextPath.
*
* @return 0.0.0.0 if no virtual host is defined
*/
public static String getVirtualHost (Context context)
{
String vhost = NULL_VHOST;
if (context==null)
return vhost;
String [] vhosts = context.getContextHandler().getVirtualHosts();
if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
return vhost;
return vhosts[0];
}
/**
* Make an acceptable name from a context path.
*
* @param path
* @return
*/
private static String canonicalize (String path)
{
if (path==null)
return "";
return path.replace('/', '_').replace('.','_').replace('\\','_');
}
}

View File

@ -107,7 +107,7 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
protected ClassLoader _loader;
protected ContextHandler.Context _context;
protected ContextId _contextId;
protected SessionContext _sessionContext;
protected String _sessionCookie=__DefaultSessionCookie;
protected String _sessionIdPathParameterName = __DefaultSessionIdPathParameterName;
protected String _sessionIdPathParameterNamePrefix =";"+ _sessionIdPathParameterName +"=";
@ -302,13 +302,13 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
_checkingRemoteSessionIdEncoding=Boolean.parseBoolean(tmp);
}
_contextId = ContextId.getContextId(_sessionIdManager.getWorkerName(), _context);
_sessionContext = new SessionContext(_sessionIdManager.getWorkerName(), _context);
if (_sessionStore instanceof AbstractSessionStore)
((AbstractSessionStore)_sessionStore).setSessionManager(this);
_sessionStore.initialize(_contextId);
_sessionStore.initialize(_sessionContext);
_sessionStore.start();
super.doStart();

View File

@ -36,7 +36,7 @@ import org.eclipse.jetty.util.component.LifeCycle;
*/
public interface SessionStore extends LifeCycle
{
void initialize(ContextId contextId);
void initialize(SessionContext context);
Session newSession (HttpServletRequest request, String id, long time, long maxInactiveMs);
Session get(String id, boolean staleCheck) throws Exception;
void put(String id, Session session) throws Exception;

View File

@ -27,7 +27,7 @@ package org.eclipse.jetty.server.session;
public class UnreadableSessionDataException extends Exception
{
private String _id;
private ContextId _contextId;
private SessionContext _sessionContext;
public String getId()
@ -35,23 +35,23 @@ public class UnreadableSessionDataException extends Exception
return _id;
}
public ContextId getContextId()
public SessionContext getSessionContext()
{
return _contextId;
return _sessionContext;
}
public UnreadableSessionDataException (String id, ContextId contextId, Throwable t)
public UnreadableSessionDataException (String id, SessionContext contextId, Throwable t)
{
super ("Unreadable session "+id+" for "+contextId, t);
_contextId = contextId;
_sessionContext = contextId;
_id = id;
}
public UnreadableSessionDataException (String id, ContextId contextId, boolean loadAttemptsExhausted)
public UnreadableSessionDataException (String id, SessionContext contextId, boolean loadAttemptsExhausted)
{
super("Unreadable session "+id+" for "+contextId+(loadAttemptsExhausted?" max load attempts":""));
_contextId = contextId;
_sessionContext = contextId;
_id = id;
}

View File

@ -27,11 +27,11 @@ package org.eclipse.jetty.server.session;
public class UnwriteableSessionDataException extends Exception
{
private String _id;
private ContextId _contextId;
private SessionContext _sessionContext;
public UnwriteableSessionDataException (String id, ContextId contextId, Throwable t)
public UnwriteableSessionDataException (String id, SessionContext contextId, Throwable t)
{
super ("Unwriteable session "+id+" for "+contextId, t);
_id = id;
@ -42,8 +42,8 @@ public class UnwriteableSessionDataException extends Exception
return _id;
}
public ContextId getContextId()
public SessionContext getSessionContext()
{
return _contextId;
return _sessionContext;
}
}

View File

@ -105,8 +105,7 @@ public class FileSessionManagerTest
manager.getSessionDataStore().setStoreDir(testDir);
manager.start();
//See SessionKey.getKey()
String expectedFilename = "__0.0.0.0_validFile123";
String expectedFilename = "_0.0.0.0_validFile123";
Assert.assertTrue(new File(testDir, expectedFilename).createNewFile());
@ -153,7 +152,7 @@ public class FileSessionManagerTest
manager.setMaxInactiveInterval(30); // change max inactive interval for *new* sessions
manager.stop();
String expectedFilename = "foo__0.0.0.0_"+session.getId();
String expectedFilename = "_0.0.0.0_"+session.getId();
Assert.assertTrue("File should exist!", new File(testDir, expectedFilename).exists());