Does not compile, draft of changes.

This commit is contained in:
Jan Bartel 2015-09-04 12:07:24 +10:00
parent c45cd8ce03
commit 3e3bfb9d6b
11 changed files with 1082 additions and 141 deletions

View File

@ -27,17 +27,17 @@ package org.eclipse.jetty.server.session.x;
public abstract class AbstractSessionDataStore implements SessionDataStore
{
public abstract void doStore() throws Exception;
public abstract void doStore(SessionKey key, SessionData data) throws Exception;
/**
* @see org.eclipse.jetty.server.session.x.SessionDataStore#store(java.lang.String, org.eclipse.jetty.server.session.x.SessionData)
*/
@Override
public void store(String id, SessionData data) throws Exception
public void store(SessionKey key, SessionData data) throws Exception
{
try
{
doStore();
doStore(key, data);
}
finally
{

View File

@ -32,15 +32,13 @@ public abstract class AbstractSessionStore implements SessionStore
public abstract Session newSession (SessionData data);
public abstract Session doGet(String id);
public abstract Session doGet(SessionKey key);
public abstract Session doPutIfAbsent (String id, Session session);
public abstract void doPut (SessionKey key, Session session);
public abstract void doPut (String id, Session session);
public abstract boolean doExists (SessionKey key);
public abstract boolean doExists (String id);
public abstract void doDelete (String id);
public abstract void doDelete (SessionKey key);
public AbstractSessionStore ()
@ -69,17 +67,17 @@ public abstract class AbstractSessionStore implements SessionStore
* @see org.eclipse.jetty.server.session.x.SessionStore#get(java.lang.String)
*/
@Override
public Session get(String id) throws Exception
public Session get(SessionKey key) throws Exception
{
//look locally
Session session = doGet(id);
Session session = doGet(key);
//not in session store, load the data for the session if possible
if (session == null && _sessionDataStore != null)
{
SessionData data = _sessionDataStore.load(id);
SessionData data = _sessionDataStore.load(key);
session = newSession(data);
doPut(id, session);
doPut(key, session);
}
return session;
}
@ -92,16 +90,16 @@ public abstract class AbstractSessionStore implements SessionStore
* @see org.eclipse.jetty.server.session.x.SessionStore#put(java.lang.String, org.eclipse.jetty.server.session.x.Session)
*/
@Override
public void put(String id, Session session) throws Exception
public void put(SessionKey key, Session session) throws Exception
{
//if the session is already in our cache, then we want to write through any changes
if (doExists(id))
if (doExists(key))
{
//if the session data has changed, or the cache is considered stale, write it to any backing store
if ((session.getSessionData().isDirty() || isStale(session)) && _sessionDataStore != null)
{
session.willPassivate();
_sessionDataStore.store(id, session.getSessionData());
_sessionDataStore.store(key, session.getSessionData());
session.didActivate();
}
}
@ -111,10 +109,10 @@ public abstract class AbstractSessionStore implements SessionStore
if (_sessionDataStore != null)
{
session.willPassivate();
_sessionDataStore.store(id, session.getSessionData());
_sessionDataStore.store(SessionKey.getKey(session.getSessionData()), session.getSessionData());
session.didActivate();
}
doPut(id,session);
doPut(key,session);
}
}
@ -127,21 +125,11 @@ public abstract class AbstractSessionStore implements SessionStore
* @see org.eclipse.jetty.server.session.x.SessionStore#exists(java.lang.String)
*/
@Override
public boolean exists(String id)
public boolean exists(SessionKey key)
{
return doExists(id);
return doExists(key);
}
/**
*TODO does this mean absent in the Session cache or the backing store???
*
* @see org.eclipse.jetty.server.session.x.SessionStore#putIfAbsent(java.lang.String, org.eclipse.jetty.server.session.x.Session)
*/
@Override
public Session putIfAbsent(String id, Session session) throws Exception
{
return doPutIfAbsent(id, session);
}
/**
* Remove a session object from this store and from any backing store.
@ -149,13 +137,13 @@ public abstract class AbstractSessionStore implements SessionStore
* @see org.eclipse.jetty.server.session.x.SessionStore#delete(java.lang.String)
*/
@Override
public boolean delete(String id) throws Exception
public boolean delete(SessionKey key) throws Exception
{
boolean deleted = true;
//TODO synchronization???
if (_sessionDataStore != null)
deleted = _sessionDataStore.delete(id);
doDelete(id);
deleted = _sessionDataStore.delete(key);
doDelete(key);
return deleted;
}

View File

@ -19,7 +19,26 @@
package org.eclipse.jetty.server.session.x;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Locale;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* JDBCSessionDataStore
@ -28,9 +47,653 @@ package org.eclipse.jetty.server.session.x;
*/
public class JDBCSessionDataStore extends AbstractSessionDataStore
{
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
protected int _deleteBlockSize = 10; //number of ids to include in where 'in' clause for finding long expired sessions
protected boolean _initialized = false;
private DatabaseAdaptor _dbAdaptor;
private SessionTableSchema _sessionTableSchema;
/**
* DatabaseAdaptor
*
* Handles differences between databases.
*
* Postgres uses the getBytes and setBinaryStream methods to access
* a "bytea" datatype, which can be up to 1Gb of binary data. MySQL
* is happy to use the "blob" type and getBlob() methods instead.
*
* TODO if the differences become more major it would be worthwhile
* refactoring this class.
*/
public static class DatabaseAdaptor
{
String _dbName;
boolean _isLower;
boolean _isUpper;
protected String _blobType; //if not set, is deduced from the type of the database at runtime
protected String _longType; //if not set, is deduced from the type of the database at runtime
private String _driverClassName;
private String _connectionUrl;
private Driver _driver;
private DataSource _datasource;
private String _jndiName;
public DatabaseAdaptor ()
{
}
public void adaptTo(DatabaseMetaData dbMeta)
throws SQLException
{
_dbName = dbMeta.getDatabaseProductName().toLowerCase(Locale.ENGLISH);
if (LOG.isDebugEnabled())
LOG.debug ("Using database {}",_dbName);
_isLower = dbMeta.storesLowerCaseIdentifiers();
_isUpper = dbMeta.storesUpperCaseIdentifiers();
}
public void setBlobType(String blobType)
{
_blobType = blobType;
}
public String getBlobType ()
{
if (_blobType != null)
return _blobType;
if (_dbName.startsWith("postgres"))
return "bytea";
return "blob";
}
public void setLongType(String longType)
{
_longType = longType;
}
public String getLongType ()
{
if (_longType != null)
return _longType;
if (_dbName == null)
throw new IllegalStateException ("DbAdaptor missing metadata");
if (_dbName.startsWith("oracle"))
return "number(20)";
return "bigint";
}
/**
* Convert a camel case identifier into either upper or lower
* depending on the way the db stores identifiers.
*
* @param identifier the raw identifier
* @return the converted identifier
*/
public String convertIdentifier (String identifier)
{
if (_dbName == null)
throw new IllegalStateException ("DbAdaptor missing metadata");
if (_isLower)
return identifier.toLowerCase(Locale.ENGLISH);
if (_isUpper)
return identifier.toUpperCase(Locale.ENGLISH);
return identifier;
}
public String getDBName ()
{
return _dbName;
}
public InputStream getBlobInputStream (ResultSet result, String columnName)
throws SQLException
{
if (_dbName == null)
throw new IllegalStateException ("DbAdaptor missing metadata");
if (_dbName.startsWith("postgres"))
{
byte[] bytes = result.getBytes(columnName);
return new ByteArrayInputStream(bytes);
}
Blob blob = result.getBlob(columnName);
return blob.getBinaryStream();
}
public boolean isEmptyStringNull ()
{
if (_dbName == null)
throw new IllegalStateException ("DbAdaptor missing metadata");
return (_dbName.startsWith("oracle"));
}
/**
* rowId is a reserved word for Oracle, so change the name of this column
* @return true if db in use is oracle
*/
public boolean isRowIdReserved ()
{
if (_dbName == null)
throw new IllegalStateException ("DbAdaptor missing metadata");
return (_dbName != null && _dbName.startsWith("oracle"));
}
/**
* Configure jdbc connection information via a jdbc Driver
*
* @param driverClassName the driver classname
* @param connectionUrl the driver connection url
*/
public void setDriverInfo (String driverClassName, String connectionUrl)
{
_driverClassName=driverClassName;
_connectionUrl=connectionUrl;
}
/**
* Configure jdbc connection information via a jdbc Driver
*
* @param driverClass the driver class
* @param connectionUrl the driver connection url
*/
public void setDriverInfo (Driver driverClass, String connectionUrl)
{
_driver=driverClass;
_connectionUrl=connectionUrl;
}
public void setDatasource (DataSource ds)
{
_datasource = ds;
}
public void setDatasourceName (String jndi)
{
_jndiName=jndi;
}
public void initializeDatabase ()
throws Exception
{
if (_datasource != null)
return; //already set up
if (_jndiName!=null)
{
InitialContext ic = new InitialContext();
_datasource = (DataSource)ic.lookup(_jndiName);
}
else if ( _driver != null && _connectionUrl != null )
{
DriverManager.registerDriver(_driver);
}
else if (_driverClassName != null && _connectionUrl != null)
{
Class.forName(_driverClassName);
}
else
{
try
{
InitialContext ic = new InitialContext();
_datasource = (DataSource)ic.lookup("jdbc/sessions"); //last ditch effort
}
catch (NamingException e)
{
throw new IllegalStateException("No database configured for sessions");
}
}
}
/**
* Get a connection from the driver or datasource.
*
* @return the connection for the datasource
* @throws SQLException if unable to get the connection
*/
protected Connection getConnection ()
throws SQLException
{
if (_datasource != null)
return _datasource.getConnection();
else
return DriverManager.getConnection(_connectionUrl);
}
}
/**
* SessionTableSchema
*
*/
public static class SessionTableSchema
{
public final static int MAX_INTERVAL_NOT_SET = -999;
protected DatabaseAdaptor _dbAdaptor;
protected String _tableName = "JettySessions";
protected String _rowIdColumn = "rowId";
protected String _idColumn = "sessionId";
protected String _contextPathColumn = "contextPath";
protected String _virtualHostColumn = "virtualHost";
protected String _lastNodeColumn = "lastNode";
protected String _accessTimeColumn = "accessTime";
protected String _lastAccessTimeColumn = "lastAccessTime";
protected String _createTimeColumn = "createTime";
protected String _cookieTimeColumn = "cookieTime";
protected String _lastSavedTimeColumn = "lastSavedTime";
protected String _expiryTimeColumn = "expiryTime";
protected String _maxIntervalColumn = "maxInterval";
protected String _mapColumn = "map";
private String _insertSession;
private String _deleteSession;
private String _updateSession;
private String _updateSessionNode;
private String _updateSessionAccessTime;
private String _selectBoundedExpiredSessions;
private String _selectExpiredSessions;
protected void setDatabaseAdaptor(DatabaseAdaptor dbadaptor)
{
_dbAdaptor = dbadaptor;
}
public String getTableName()
{
return _tableName;
}
public void setTableName(String tableName)
{
checkNotNull(tableName);
_tableName = tableName;
}
public String getRowIdColumn()
{
if ("rowId".equals(_rowIdColumn) && _dbAdaptor.isRowIdReserved())
_rowIdColumn = "srowId";
return _rowIdColumn;
}
public void setRowIdColumn(String rowIdColumn)
{
checkNotNull(rowIdColumn);
if (_dbAdaptor == null)
throw new IllegalStateException ("DbAdaptor is null");
if (_dbAdaptor.isRowIdReserved() && "rowId".equals(rowIdColumn))
throw new IllegalArgumentException("rowId is reserved word for Oracle");
_rowIdColumn = rowIdColumn;
}
public String getIdColumn()
{
return _idColumn;
}
public void setIdColumn(String idColumn)
{
checkNotNull(idColumn);
_idColumn = idColumn;
}
public String getContextPathColumn()
{
return _contextPathColumn;
}
public void setContextPathColumn(String contextPathColumn)
{
checkNotNull(contextPathColumn);
_contextPathColumn = contextPathColumn;
}
public String getVirtualHostColumn()
{
return _virtualHostColumn;
}
public void setVirtualHostColumn(String virtualHostColumn)
{
checkNotNull(virtualHostColumn);
_virtualHostColumn = virtualHostColumn;
}
public String getLastNodeColumn()
{
return _lastNodeColumn;
}
public void setLastNodeColumn(String lastNodeColumn)
{
checkNotNull(lastNodeColumn);
_lastNodeColumn = lastNodeColumn;
}
public String getAccessTimeColumn()
{
return _accessTimeColumn;
}
public void setAccessTimeColumn(String accessTimeColumn)
{
checkNotNull(accessTimeColumn);
_accessTimeColumn = accessTimeColumn;
}
public String getLastAccessTimeColumn()
{
return _lastAccessTimeColumn;
}
public void setLastAccessTimeColumn(String lastAccessTimeColumn)
{
checkNotNull(lastAccessTimeColumn);
_lastAccessTimeColumn = lastAccessTimeColumn;
}
public String getCreateTimeColumn()
{
return _createTimeColumn;
}
public void setCreateTimeColumn(String createTimeColumn)
{
checkNotNull(createTimeColumn);
_createTimeColumn = createTimeColumn;
}
public String getCookieTimeColumn()
{
return _cookieTimeColumn;
}
public void setCookieTimeColumn(String cookieTimeColumn)
{
checkNotNull(cookieTimeColumn);
_cookieTimeColumn = cookieTimeColumn;
}
public String getLastSavedTimeColumn()
{
return _lastSavedTimeColumn;
}
public void setLastSavedTimeColumn(String lastSavedTimeColumn)
{
checkNotNull(lastSavedTimeColumn);
_lastSavedTimeColumn = lastSavedTimeColumn;
}
public String getExpiryTimeColumn()
{
return _expiryTimeColumn;
}
public void setExpiryTimeColumn(String expiryTimeColumn)
{
checkNotNull(expiryTimeColumn);
_expiryTimeColumn = expiryTimeColumn;
}
public String getMaxIntervalColumn()
{
return _maxIntervalColumn;
}
public void setMaxIntervalColumn(String maxIntervalColumn)
{
checkNotNull(maxIntervalColumn);
_maxIntervalColumn = maxIntervalColumn;
}
public String getMapColumn()
{
return _mapColumn;
}
public void setMapColumn(String mapColumn)
{
checkNotNull(mapColumn);
_mapColumn = mapColumn;
}
public String getCreateStatementAsString ()
{
if (_dbAdaptor == null)
throw new IllegalStateException ("No DBAdaptor");
String blobType = _dbAdaptor.getBlobType();
String longType = _dbAdaptor.getLongType();
return "create table "+_tableName+" ("+getRowIdColumn()+" varchar(120), "+_idColumn+" varchar(120), "+
_contextPathColumn+" varchar(60), "+_virtualHostColumn+" varchar(60), "+_lastNodeColumn+" varchar(60), "+_accessTimeColumn+" "+longType+", "+
_lastAccessTimeColumn+" "+longType+", "+_createTimeColumn+" "+longType+", "+_cookieTimeColumn+" "+longType+", "+
_lastSavedTimeColumn+" "+longType+", "+_expiryTimeColumn+" "+longType+", "+_maxIntervalColumn+" "+longType+", "+
_mapColumn+" "+blobType+", primary key("+getRowIdColumn()+"))";
}
public String getCreateIndexOverExpiryStatementAsString (String indexName)
{
return "create index "+indexName+" on "+getTableName()+" ("+getExpiryTimeColumn()+")";
}
public String getCreateIndexOverSessionStatementAsString (String indexName)
{
return "create index "+indexName+" on "+getTableName()+" ("+getIdColumn()+", "+getContextPathColumn()+")";
}
public String getAlterTableForMaxIntervalAsString ()
{
if (_dbAdaptor == null)
throw new IllegalStateException ("No DBAdaptor");
String longType = _dbAdaptor.getLongType();
String stem = "alter table "+getTableName()+" add "+getMaxIntervalColumn()+" "+longType;
if (_dbAdaptor.getDBName().contains("oracle"))
return stem + " default "+ MAX_INTERVAL_NOT_SET + " not null";
else
return stem +" not null default "+ MAX_INTERVAL_NOT_SET;
}
private void checkNotNull(String s)
{
if (s == null)
throw new IllegalArgumentException(s);
}
public String getInsertSessionStatementAsString()
{
return "insert into "+getTableName()+
" ("+getRowIdColumn()+", "+getIdColumn()+", "+getContextPathColumn()+", "+getVirtualHostColumn()+", "+getLastNodeColumn()+
", "+getAccessTimeColumn()+", "+getLastAccessTimeColumn()+", "+getCreateTimeColumn()+", "+getCookieTimeColumn()+
", "+getLastSavedTimeColumn()+", "+getExpiryTimeColumn()+", "+getMaxIntervalColumn()+", "+getMapColumn()+") "+
" values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
}
public String getDeleteSessionStatementAsString()
{
return "delete from "+getTableName()+
" where "+getRowIdColumn()+" = ?";
}
public String getUpdateSessionStatementAsString()
{
return "update "+getTableName()+
" set "+getIdColumn()+" = ?, "+getLastNodeColumn()+" = ?, "+getAccessTimeColumn()+" = ?, "+
getLastAccessTimeColumn()+" = ?, "+getLastSavedTimeColumn()+" = ?, "+getExpiryTimeColumn()+" = ?, "+
getMaxIntervalColumn()+" = ?, "+getMapColumn()+" = ? where "+getRowIdColumn()+" = ?";
}
public String getUpdateSessionNodeStatementAsString()
{
return "update "+getTableName()+
" set "+getLastNodeColumn()+" = ? where "+getRowIdColumn()+" = ?";
}
public String getUpdateSessionAccessTimeStatementAsString()
{
return "update "+getTableName()+
" set "+getLastNodeColumn()+" = ?, "+getAccessTimeColumn()+" = ?, "+getLastAccessTimeColumn()+" = ?, "+
getLastSavedTimeColumn()+" = ?, "+getExpiryTimeColumn()+" = ?, "+getMaxIntervalColumn()+" = ? where "+getRowIdColumn()+" = ?";
}
public String getBoundedExpiredSessionsStatementAsString()
{
return "select * from "+getTableName()+" where "+getLastNodeColumn()+" = ? and "+getExpiryTimeColumn()+" >= ? and "+getExpiryTimeColumn()+" <= ?";
}
public String getSelectExpiredSessionsStatementAsString()
{
return "select * from "+getTableName()+" where "+getExpiryTimeColumn()+" >0 and "+getExpiryTimeColumn()+" <= ?";
}
public PreparedStatement getLoadStatement (Connection connection, String rowId, String contextPath, String virtualHosts)
throws SQLException
{
if (_dbAdaptor == null)
throw new IllegalStateException("No DB adaptor");
if (contextPath == null || "".equals(contextPath))
{
if (_dbAdaptor.isEmptyStringNull())
{
PreparedStatement statement = connection.prepareStatement("select * from "+getTableName()+
" where "+getIdColumn()+" = ? and "+
getContextPathColumn()+" is null and "+
getVirtualHostColumn()+" = ?");
statement.setString(1, rowId);
statement.setString(2, virtualHosts);
return statement;
}
}
PreparedStatement statement = connection.prepareStatement("select * from "+getTableName()+
" where "+getIdColumn()+" = ? and "+getContextPathColumn()+
" = ? and "+getVirtualHostColumn()+" = ?");
statement.setString(1, rowId);
statement.setString(2, contextPath);
statement.setString(3, virtualHosts);
return statement;
}
/**
* Set up the tables in the database
* @throws SQLException
*/
/**
* @throws SQLException
*/
public void prepareTables()
throws SQLException
{
try (Connection connection = _dbAdaptor.getConnection();
Statement statement = connection.createStatement())
{
//make the id table
connection.setAutoCommit(true);
DatabaseMetaData metaData = connection.getMetaData();
_dbAdaptor.adaptTo(metaData);
//make the session table if necessary
String tableName = _dbAdaptor.convertIdentifier(getTableName());
try (ResultSet result = metaData.getTables(null, null, tableName, null))
{
if (!result.next())
{
//table does not exist, so create it
statement.executeUpdate(getCreateStatementAsString());
}
else
{
//session table exists, check it has maxinterval column
ResultSet colResult = null;
try
{
colResult = metaData.getColumns(null, null,
_dbAdaptor.convertIdentifier(getTableName()),
_dbAdaptor.convertIdentifier(getMaxIntervalColumn()));
}
catch (SQLException s)
{
LOG.warn("Problem checking if "+getTableName()+
" table contains "+getMaxIntervalColumn()+" column. Ensure table contains column definition: \""
+ getMaxIntervalColumn()+" long not null default -999\"");
throw s;
}
try
{
if (!colResult.next())
{
try
{
//add the maxinterval column
statement.executeUpdate(getAlterTableForMaxIntervalAsString());
}
catch (SQLException s)
{
LOG.warn("Problem adding "+getMaxIntervalColumn()+
" column. Ensure table contains column definition: \""+getMaxIntervalColumn()+
" long not null default -999\"");
throw s;
}
}
}
finally
{
colResult.close();
}
}
}
//make some indexes on the JettySessions table
String index1 = "idx_"+getTableName()+"_expiry";
String index2 = "idx_"+getTableName()+"_session";
boolean index1Exists = false;
boolean index2Exists = false;
try (ResultSet result = metaData.getIndexInfo(null, null, tableName, false, false))
{
while (result.next())
{
String idxName = result.getString("INDEX_NAME");
if (index1.equalsIgnoreCase(idxName))
index1Exists = true;
else if (index2.equalsIgnoreCase(idxName))
index2Exists = true;
}
}
if (!index1Exists)
statement.executeUpdate(getCreateIndexOverExpiryStatementAsString(index1));
if (!index2Exists)
statement.executeUpdate(getCreateIndexOverSessionStatementAsString(index2));
//set up some strings representing the statements for session manipulation
_insertSession = getInsertSessionStatementAsString();
_deleteSession = getDeleteSessionStatementAsString();
_updateSession = getUpdateSessionStatementAsString();
_updateSessionNode = getUpdateSessionNodeStatementAsString();
_updateSessionAccessTime = getUpdateSessionAccessTimeStatementAsString();
_selectBoundedExpiredSessions = getBoundedExpiredSessionsStatementAsString();
_selectExpiredSessions = getSelectExpiredSessionsStatementAsString();
}
}
}
/**
@ -72,8 +735,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
}
/**
@ -85,6 +747,19 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
}
public void setDeleteBlockSize (int bsize)
{
this._deleteBlockSize = bsize;
}
public int getDeleteBlockSize ()
{
return this._deleteBlockSize;
}
@Override
@ -95,19 +770,94 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
public void initialize () throws Exception
{
if (!_initialized)
{
_initialized = true;
if (_dbAdaptor == null)
_dbAdaptor = new DatabaseAdaptor();
if (_sessionTableSchema == null)
_sessionTableSchema = new SessionTableSchema();
_dbAdaptor.initializeDatabase();
_sessionTableSchema.setDatabaseAdaptor(_dbAdaptor);
_sessionTableSchema.prepareTables();
}
}
/**
* @see org.eclipse.jetty.server.session.x.SessionDataStore#load(java.lang.String)
*/
@Override
public SessionData load(String id) throws Exception
public SessionData load(SessionKey key) throws Exception
{
// TODO make jdbc calls to load in the session
long created = 0;
long accessed = 0;
long lastAccessed = 0;
long maxInactiveMs = 0;
JDBCSessionData data = (JDBCSessionData)newSessionData(id, created, accessed, lastAccessed, maxInactiveMs);
try (Connection connection = _dbAdaptor.getConnection();
PreparedStatement statement = _sessionTableSchema.getLoadStatement(connection, key.getId(), key.getCanonicalContextPath(), key.getVhost());
ResultSet result = statement.executeQuery())
{
JDBCSessionData data = null;
if (result.next())
{
long maxInterval = result.getLong(_sessionTableSchema.getMaxIntervalColumn());
if (maxInterval == SessionTableSchema.MAX_INTERVAL_NOT_SET)
{
maxInterval = getMaxInactiveInterval(); //if value not saved for maxInactiveInterval, use current value from sessionmanager
}
data = (Session)newSession(id, ,
,
,
maxInterval);
data = (JDBCSessionData)newSessionData(id,
result.getLong(_sessionTableSchema.getCreateTimeColumn()),
result.getLong(_sessionTableSchema.getAccessTimeColumn()),
result.getLong(_sessionTableSchema.getLastAccessTimeColumn()), maxInactiveMs);
data.setRowId(result.getString(_sessionTableSchema.getRowIdColumn()));
data.setCookieSetTime(result.getLong(_sessionTableSchema.getCookieTimeColumn()));
data.setLastNode(result.getString(_sessionTableSchema.getLastNodeColumn()));
data.setLastSaved(result.getLong(_sessionTableSchema.getLastSavedTimeColumn()));
data.setExpiryTime(result.getLong(_sessionTableSchema.getExpiryTimeColumn()));
data.setCanonicalContext(result.getString(_sessionTableSchema.getContextPathColumn()));
data.setVirtualHost(result.getString(_sessionTableSchema.getVirtualHostColumn()));
try (InputStream is = _dbAdaptor.getBlobInputStream(result, _sessionTableSchema.getMapColumn());
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
{
Object o = ois.readObject();
data.addAttributes((Map<String,Object>)o);
}
if (LOG.isDebugEnabled())
LOG.debug("LOADED session "+data);
}
else
if (LOG.isDebugEnabled())
LOG.debug("Failed to load session "+id);
_reference.set(data);
}
catch (Exception e)
{
_exception.set(e);
}
// set vhost etc
// set row id
// set expiry time
@ -139,6 +889,30 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
}
/**
* @see org.eclipse.jetty.server.session.x.SessionDataStore#scavenge()
*/
@Override
public void scavenge()
{
// TODO Auto-generated method stub
}
public void setDatabaseAdaptor (DatabaseAdaptor dbAdaptor)
{
_dbAdaptor = dbAdaptor;
}
public void setSessionTableSchema (SessionTableSchema schema)
{
_sessionTableSchema = schema;
}
}

View File

@ -81,55 +81,49 @@ public class MemorySessionStore extends AbstractSessionStore
* @see org.eclipse.jetty.server.session.x.AbstractSessionStore#doGet(java.lang.String)
*/
@Override
public Session doGet(String id)
public Session doGet(SessionKey key)
{
Session session = _sessions.get(id);
Session session = _sessions.get(key.getId());
if (isStale(session))
{
//delete from memory
doDelete(id);
doDelete(key);
return null;
}
return session;
}
/**
* @see org.eclipse.jetty.server.session.x.AbstractSessionStore#doPutIfAbsent(java.lang.String, org.eclipse.jetty.server.session.x.Session)
*/
@Override
public Session doPutIfAbsent(String id, Session session)
{
return _sessions.putIfAbsent(id, session);
}
/**
* @see org.eclipse.jetty.server.session.x.AbstractSessionStore#doPut(java.lang.String, org.eclipse.jetty.server.session.x.Session)
*/
@Override
public void doPut(String id, Session session)
public void doPut(SessionKey key, Session session)
{
_sessions.put(id, session);
_sessions.put(key.getId(), session);
}
/**
* @see org.eclipse.jetty.server.session.x.AbstractSessionStore#doExists(java.lang.String)
*/
@Override
public boolean doExists(String id)
public boolean doExists(SessionKey key)
{
return _sessions.containsKey(id);
return _sessions.containsKey(key.getId());
}
/**
* @see org.eclipse.jetty.server.session.x.AbstractSessionStore#doDelete(java.lang.String)
*/
@Override
public void doDelete(String id)
public void doDelete(SessionKey key)
{
_sessions.remove(id);
_sessions.remove(key.getId());
}
@Override
public void shutdown ()
@ -153,14 +147,14 @@ public class MemorySessionStore extends AbstractSessionStore
session.willPassivate();
try
{
_sessionDataStore.store(session.getId(), session.getSessionData());
_sessionDataStore.store(SessionKey.getKey(session.getSessionData()), session.getSessionData());
}
catch (Exception e)
{
LOG.warn(e);
}
}
doDelete (session.getId()); //remove from memory
doDelete (SessionKey.getKey(session.getSessionData())); //remove from memory
}
else
{
@ -194,4 +188,17 @@ public class MemorySessionStore extends AbstractSessionStore
return new MemorySession (data);
}
/**
* @see org.eclipse.jetty.server.session.x.SessionStore#scavenge()
*/
@Override
public void scavenge()
{
// TODO Auto-generated method stub
}
}

View File

@ -31,7 +31,7 @@ public class NullSessionDataStore extends AbstractSessionDataStore
* @see org.eclipse.jetty.server.session.x.SessionDataStore#load(java.lang.String)
*/
@Override
public SessionData load(String id) throws Exception
public SessionData load(SessionKey key) throws Exception
{
return null;
}
@ -49,7 +49,7 @@ public class NullSessionDataStore extends AbstractSessionDataStore
* @see org.eclipse.jetty.server.session.x.SessionDataStore#delete(java.lang.String)
*/
@Override
public boolean delete(String id) throws Exception
public boolean delete(SessionKey key) throws Exception
{
return true;
}
@ -58,9 +58,18 @@ public class NullSessionDataStore extends AbstractSessionDataStore
* @see org.eclipse.jetty.server.session.x.AbstractSessionDataStore#doStore()
*/
@Override
public void doStore() throws Exception
public void doStore(SessionKey key, SessionData data) throws Exception
{
//noop
}
/**
* @see org.eclipse.jetty.server.session.x.SessionDataStore#scavenge()
*/
@Override
public void scavenge()
{
//noop
}
}

View File

@ -1,33 +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.x;
import javax.servlet.http.HttpServletRequest;
/**
* SessionDataFactory
*
*
*/
public interface SessionDataFactory
{
public SessionData newSessionData (String id, long created, long accessed, long lastAccessed, long maxInactiveMs);
public SessionData newSessionData (HttpServletRequest request);
}

View File

@ -33,7 +33,7 @@ public interface SessionDataStore
* @return
* @throws Exception
*/
public SessionData load (String id) throws Exception;
public SessionData load (SessionKey key) throws Exception;
/**
@ -51,7 +51,7 @@ public interface SessionDataStore
* @param data
* @throws Exception
*/
public void store (String id, SessionData data) throws Exception;
public void store (SessionKey key, SessionData data) throws Exception;
@ -61,8 +61,17 @@ public interface SessionDataStore
* @return
* @throws Exception
*/
public boolean delete (String id) throws Exception;
public boolean delete (SessionKey key) throws Exception;
/**
* Called periodically, this method should search the data store
* for sessions that have been expired for a 'reasonable' amount
* of time.
*/
public void scavenge ();
}

View File

@ -1,33 +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.x;
import javax.servlet.http.HttpServletRequest;
/**
* SessionFactory
*
*
*/
public interface SessionFactory
{
public SessionManager.SessionIf newSession (HttpServletRequest request);
public SessionManager.SessionIf newSession (SessionData data);
}

View File

@ -0,0 +1,119 @@
//
// ========================================================================
// 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.x;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
/**
* SessionKey
*
*
*/
public class SessionKey
{
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 = data.getContextPath();
String vhost = data.getVhost();
String id = data.getId();
return new SessionKey(id, cpath, vhost);
}
private SessionKey (String id, String path, String vhost)
{
_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;
}
private static String getContextPath (Context context)
{
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
*/
private static String getVirtualHost (Context context)
{
String vhost = "0.0.0.0";
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

@ -29,6 +29,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
@ -54,6 +55,8 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.statistic.CounterStatistic;
import org.eclipse.jetty.util.statistic.SampleStatistic;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler;
/**
* AbstractSessionManager
@ -62,8 +65,7 @@ import org.eclipse.jetty.util.statistic.SampleStatistic;
*/
public class SessionManager extends ContainerLifeCycle implements org.eclipse.jetty.server.SessionManager
{
final static Logger __log = Log.getLogger(SessionManager.class);// TODO SessionHandler.LOG
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
public Set<SessionTrackingMode> __defaultSessionTrackingModes =
Collections.unmodifiableSet(
@ -130,6 +132,37 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
protected final CounterStatistic _sessionsStats = new CounterStatistic();
protected final SampleStatistic _sessionTimeStats = new SampleStatistic();
protected Scheduler _scheduler; //scheduler for scavenging
protected boolean _ownScheduler; //did we create our own scheduler or reuse common one
protected Scheduler.Task _task; //scavenge task
/**
* Scavenger
*
*/
protected class Scavenger implements Runnable
{
@Override
public void run()
{
try
{
scavenge();
}
finally
{
if (_scheduler != null && _scheduler.isRunning())
_task = _scheduler.schedule(this, _scavengeIntervalMs, TimeUnit.MILLISECONDS);
}
}
}
/* ------------------------------------------------------------ */
public SessionManager()
@ -224,7 +257,7 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
}
catch (Exception e)
{
__log.warn(e);
LOG.warn(e);
}
}
@ -232,6 +265,11 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
@Override
public void doStart() throws Exception
{
if (_sessionStore == null)
throw new IllegalStateException("No session store configured");
_context=ContextHandler.getCurrentContext();
_loader=Thread.currentThread().getContextClassLoader();
@ -300,8 +338,21 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
_checkingRemoteSessionIdEncoding=Boolean.parseBoolean(tmp);
}
if (_sessionStore == null)
throw new IllegalStateException("No session store configured");
//try and use a common scheduler, fallback to own
_scheduler = server.getBean(Scheduler.class);
if (_scheduler == null)
{
_scheduler = new ScheduledExecutorScheduler();
_ownScheduler = true;
_scheduler.start();
}
else if (!_scheduler.isStarted())
throw new IllegalStateException("Shared scheduler not started");
setScavengeInterval(getScavengeInterval());
super.doStart();
}
@ -590,7 +641,7 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
}
catch (Exception e)
{
__log.warn(e);
LOG.warn(e);
}
_sessionsStats.increment();
@ -716,7 +767,7 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
}
catch (Exception e)
{
__log.warn(e);
LOG.warn(e);
return null;
}
}
@ -796,7 +847,7 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
}
catch (Exception e)
{
__log.warn(e);
LOG.warn(e);
return false;
}
}
@ -853,6 +904,10 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
new CookieConfig();
private long _scavengeIntervalMs;
private Scavenger _scavenger;
/* ------------------------------------------------------------ */
/**
* @return total amount of time all sessions remained valid
@ -926,6 +981,52 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
}
}
public void scavenge ()
{
//TODO call scavenge on the cache, which calls through to scavenge on the backing store
}
public void setScavengeInterval (long sec)
{
if (sec<=0)
sec=60;
long old_period=_scavengeIntervalMs;
long period=sec*1000L;
_scavengeIntervalMs=period;
//add a bit of variability into the scavenge time so that not all
//nodes with the same scavenge interval sync up
long tenPercent = _scavengeIntervalMs/10;
if ((System.currentTimeMillis()%2) == 0)
_scavengeIntervalMs += tenPercent;
if (LOG.isDebugEnabled())
LOG.debug("Scavenging every "+_scavengeIntervalMs+" ms");
synchronized (this)
{
//if (_timer!=null && (period!=old_period || _task==null))
if (_scheduler != null && (period!=old_period || _task==null))
{
if (_task!=null)
_task.cancel();
if (_scavenger == null)
_scavenger = new Scavenger();
_task = _scheduler.schedule(_scavenger,_scavengeIntervalMs,TimeUnit.MILLISECONDS);
}
}
}
public long getScavengeInterval ()
{
return _scavengeIntervalMs/1000;
}
/**
* CookieConfig

View File

@ -28,11 +28,11 @@ package org.eclipse.jetty.server.session.x;
public interface SessionStore
{
Session newSession (String id, long created, long accessed, long lastAccessed, long maxInactiveMs);
Session get(String id) throws Exception;
void put(String id, Session session) throws Exception;
boolean exists (String id) throws Exception;
Session putIfAbsent(String id, Session session) throws Exception;
boolean delete (String id) throws Exception;
Session get(SessionKey key) throws Exception;
void put(SessionKey key, Session session) throws Exception;
boolean exists (SessionKey key) throws Exception;
boolean delete (SessionKey key) throws Exception;
void shutdown ();
void scavenge ();
}