diff options
author | Jan Bartel | 2015-04-02 01:20:12 +0000 |
---|---|---|
committer | Jan Bartel | 2015-04-02 01:20:12 +0000 |
commit | 7375ba2cc3b5f156bbba4afff23bb2dd444fe923 (patch) | |
tree | dba3753b27eeae9925531fc7be311bdd978f9e09 | |
parent | abb74087a5b19267af09d9d4314321dbc1d1e301 (diff) | |
download | org.eclipse.jetty.project-7375ba2cc3b5f156bbba4afff23bb2dd444fe923.tar.gz org.eclipse.jetty.project-7375ba2cc3b5f156bbba4afff23bb2dd444fe923.tar.xz org.eclipse.jetty.project-7375ba2cc3b5f156bbba4afff23bb2dd444fe923.zip |
Alpha impl for infinispan session manager.
Conflicts:
tests/test-sessions/pom.xml
Conflicts:
tests/test-sessions/pom.xml
tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java
31 files changed, 2845 insertions, 149 deletions
diff --git a/jetty-infinispan/pom.xml b/jetty-infinispan/pom.xml new file mode 100644 index 0000000000..ed5c2ca9ce --- /dev/null +++ b/jetty-infinispan/pom.xml @@ -0,0 +1,83 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <parent> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-project</artifactId> + <version>9.1.6-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <artifactId>jetty-infinispan</artifactId> + <name>Jetty :: Infinispan Session Managers</name> + <url>http://www.eclipse.org/jetty</url> + <properties> + <bundle-symbolic-name>${project.groupId}.infinispan</bundle-symbolic-name> + <infinispan.version>7.1.1.Final</infinispan.version> + </properties> + <build> + <defaultGoal>install</defaultGoal> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + <configuration> + <descriptorRefs> + <descriptorRef>config</descriptorRef> + </descriptorRefs> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Import-Package>javax.servlet.*;version="[2.6.0,3.2)",org.eclipse.jetty.server.session.jmx;version="9.1";resolution:=optional,,org.eclipse.jetty.*;version="9.1",*</Import-Package> + </instructions> + </configuration> + <extensions>true</extensions> + <executions> + <execution> + <goals> + <goal>manifest</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>artifact-jar</id> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + <configuration> + <archive> + <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> + </archive> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>org.infinispan</groupId> + <artifactId>infinispan-core</artifactId> + <version>${infinispan.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java new file mode 100644 index 0000000000..ceb0d1c760 --- /dev/null +++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java @@ -0,0 +1,316 @@ +// +// ======================================================================== +// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.session.infinispan; + +import java.util.Random; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.SessionManager; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.session.AbstractSession; +import org.eclipse.jetty.server.session.AbstractSessionIdManager; +import org.eclipse.jetty.server.session.SessionHandler; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.infinispan.Cache; + + + +/** + * InfinispanSessionIdManager + * + * Maintain a set of in-use session ids. This session id manager does NOT locally store + * a list of in-use sesssion ids, but rather stores them in the cluster cache. Thus, + * all operations to this session manager involve interaction with a possibly remote + * cache. + * + * For each session id that is in-use, an entry of the following form is put into + * the cluster cache: + * <pre> + * ("__o.e.j.s.infinispanIdMgr__"+[id], [id]) + * </pre> + * where [id] is the id of the session. + * + * Having one entry per in-use session id means that there is no contention on + * cache entries (as would be the case if a single entry was kept containing a + * list of in-use session ids). + * + * TODO synchronization + */ +public class InfinispanSessionIdManager extends AbstractSessionIdManager +{ + private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); + protected final static String ID_KEY = "__o.e.j.s.infinispanIdMgr__"; + protected Cache<String,Object> _cache; + private Server _server; + + + + + + /** + * @param server + */ + public InfinispanSessionIdManager(Server server) + { + super(); + _server = server; + } + + /** + * @param server + * @param random + */ + public InfinispanSessionIdManager(Server server, Random random) + { + super(random); + _server = server; + } + + + + /** + * Start the id manager. + * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStart() + */ + @Override + protected void doStart() throws Exception + { + super.doStart(); + } + + + + /** + * Stop the id manager + * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#doStop() + */ + @Override + protected void doStop() throws Exception + { + super.doStop(); + } + + + + + + /** + * Check to see if the given session id is being + * used by a session in any context. + * + * This method will consult the cluster. + * + * @see org.eclipse.jetty.server.SessionIdManager#idInUse(java.lang.String) + */ + @Override + public boolean idInUse(String id) + { + if (id == null) + return false; + + String clusterId = getClusterId(id); + + //ask the cluster + try + { + return exists(clusterId); + } + catch (Exception e) + { + LOG.warn("Problem checking inUse for id="+clusterId, e); + return false; + } + + } + + /** + * Remember a new in-use session id. + * + * This will save the in-use session id to the cluster. + * + * @see org.eclipse.jetty.server.SessionIdManager#addSession(javax.servlet.http.HttpSession) + */ + @Override + public void addSession(HttpSession session) + { + if (session == null) + return; + + //insert into the cache + insert (((AbstractSession)session).getClusterId()); + } + + /** + * Remove a session id from the list of in-use ids. + * + * This will remvove the corresponding session id from the cluster. + * + * @see org.eclipse.jetty.server.SessionIdManager#removeSession(javax.servlet.http.HttpSession) + */ + @Override + public void removeSession(HttpSession session) + { + if (session == null) + return; + + //delete from the cache + delete (((AbstractSession)session).getClusterId()); + } + + /** + * Remove a session id. This compels all other contexts who have a session + * with the same id to also remove it. + * + * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String) + */ + @Override + public void invalidateAll(String id) + { + //delete the session id from list of in-use sessions + delete (id); + + + //tell all contexts that may have a session object with this id to + //get rid of them + Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class); + for (int i=0; contexts!=null && i<contexts.length; i++) + { + SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class); + if (sessionHandler != null) + { + SessionManager manager = sessionHandler.getSessionManager(); + + if (manager != null && manager instanceof InfinispanSessionManager) + { + ((InfinispanSessionManager)manager).invalidateSession(id); + } + } + } + + } + + /** + * Change a session id. + * + * Typically this occurs when a previously existing session has passed through authentication. + * + * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#renewSessionId(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest) + */ + @Override + public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request) + { + //generate a new id + String newClusterId = newSessionId(request.hashCode()); + + delete(oldClusterId); + insert(newClusterId); + + + //tell all contexts to update the id + Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class); + for (int i=0; contexts!=null && i<contexts.length; i++) + { + SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class); + if (sessionHandler != null) + { + SessionManager manager = sessionHandler.getSessionManager(); + + if (manager != null && manager instanceof InfinispanSessionManager) + { + ((InfinispanSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request)); + } + } + } + + } + + public Cache<String,Object> getCache() + { + return _cache; + } + + public void setCache(Cache<String,Object> cache) + { + this._cache = cache; + } + + /** + * Ask the cluster if a particular id exists. + * + * @param id + * @return + */ + protected boolean exists (String id) + { + if (_cache == null) + throw new IllegalStateException ("No cache"); + + Object key =_cache.get(makeKey(id)); + if (key == null) + return false; + return true; + } + + + /** + * Put a session id into the cluster. + * + * @param id + */ + protected void insert (String id) + { + if (_cache == null) + throw new IllegalStateException ("No cache"); + + _cache.putIfAbsent(makeKey(id), id); + } + + + + /** + * Remove a session id from the cluster. + * + * @param id + */ + protected void delete (String id) + { + if (_cache == null) + throw new IllegalStateException ("No cache"); + + _cache.remove(makeKey(id)); + } + + + + /** + * Generate a unique cache key from the session id. + * + * @param id + * @return + */ + protected String makeKey (String id) + { + return ID_KEY+id; + } +} diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java new file mode 100644 index 0000000000..ca4d80899d --- /dev/null +++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java @@ -0,0 +1,1188 @@ +// +// ======================================================================== +// 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.io.IOException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandler.Context; +import org.eclipse.jetty.server.session.AbstractSession; +import org.eclipse.jetty.server.session.AbstractSessionManager; +import org.eclipse.jetty.server.session.MemSession; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; +import org.eclipse.jetty.util.thread.Scheduler; +import org.infinispan.Cache; + +/** + * InfinispanSessionManager + * + * The data for a session relevant to a particular context is stored in an Infinispan (clustered) cache: + * <pre> + * Key: is the id of the session + the context path + the vhost for the context + * Value: is the data of the session + * </pre> + * + * The key is necessarily complex because the same session id can be in-use by more than one + * context. In this case, the contents of the session will strictly be different for each + * context, although the id will be the same. + * + * Sessions are also kept in local memory when they are used by this session manager. This allows + * multiple different request threads in the same context to call Request.getSession() and + * obtain the same object. + * + * This session manager support scavenging, which is only done over the set of sessions in its + * local memory. This can result in some sessions being "stranded" in the cluster cache if no + * session manager is currently managing it (eg the node managing the session crashed and it + * was never requested on another node). + * + */ +public class InfinispanSessionManager extends AbstractSessionManager +{ + private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); + + /** + * Clustered cache of sessions + */ + private Cache<String, Object> _cache; + + + /** + * Sessions known to this node held in memory + */ + private ConcurrentHashMap<String, InfinispanSessionManager.Session> _sessions; + + + /** + * The length of time a session can be in memory without being checked against + * the cluster. A value of 0 indicates that the session is never checked against + * the cluster - the current node is considered to be the master for the session. + * + */ + private long _staleIntervalSec = 0; + + protected Scheduler.Task _task; //scavenge task + protected Scheduler _scheduler; + protected Scavenger _scavenger; + protected long _scavengeIntervalMs = 1000L * 60 * 10; //10mins + protected boolean _ownScheduler; + + + + /** + * 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); + } + } + } + + + /* + * Every time a Session is put into the cache one of these objects + * is created to copy the data out of the in-memory session, and + * every time an object is read from the cache one of these objects + * a fresh Session object is created based on the data held by this + * object. + */ + public class SerializableSessionData implements Serializable + { + /** + * + */ + private static final long serialVersionUID = -7779120106058533486L; + String clusterId; + String contextPath; + String vhost; + long accessed; + long lastAccessed; + long createTime; + long cookieSetTime; + String lastNode; + long expiry; + long maxInactive; + Map<String, Object> attributes; + + public SerializableSessionData() + { + + } + + + public SerializableSessionData(Session s) + { + clusterId = s.getClusterId(); + contextPath = s.getContextPath(); + vhost = s.getVHost(); + accessed = s.getAccessed(); + lastAccessed = s.getLastAccessedTime(); + createTime = s.getCreationTime(); + cookieSetTime = s.getCookieSetTime(); + lastNode = s.getLastNode(); + expiry = s.getExpiry(); + maxInactive = s.getMaxInactiveInterval(); + attributes = s.getAttributeMap(); // TODO pointer, not a copy + } + + private void writeObject(java.io.ObjectOutputStream out) throws IOException + { + out.writeUTF(clusterId); //session id + out.writeUTF(contextPath); //context path + out.writeUTF(vhost); //first vhost + + out.writeLong(accessed);//accessTime + out.writeLong(lastAccessed); //lastAccessTime + out.writeLong(createTime); //time created + out.writeLong(cookieSetTime);//time cookie was set + out.writeUTF(lastNode); //name of last node managing + + out.writeLong(expiry); + out.writeLong(maxInactive); + out.writeObject(attributes); + } + + private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException + { + clusterId = in.readUTF(); + contextPath = in.readUTF(); + vhost = in.readUTF(); + + accessed = in.readLong();//accessTime + lastAccessed = in.readLong(); //lastAccessTime + createTime = in.readLong(); //time created + cookieSetTime = in.readLong();//time cookie was set + lastNode = in.readUTF(); //last managing node + expiry = in.readLong(); + maxInactive = in.readLong(); + HashMap<String,Object> attributes = (HashMap<String,Object>)in.readObject(); + } + + } + + + + + /** + * SerializableSession + * + * Helper class that is responsible for de/serialization of the non-serializable session object. + */ + public class SerializableSession implements Serializable + { + + /** + * + */ + private static final long serialVersionUID = -7603529353470249059L; + private transient Session _session; + + + public SerializableSession () + { + + } + + public SerializableSession (Session session) + { + setSession(session); + } + + /** + * Existing session + * @param session + */ + public void setSession (Session session) + { + _session = session; + } + + public Session getSession () + { + return _session; + } + + + private void writeObject(java.io.ObjectOutputStream out) throws IOException + { + if (_session == null) + throw new IOException ("No session to serialize"); + + out.writeUTF(_session.getClusterId()); //session id + out.writeUTF(_session.getContextPath()); //context path + out.writeUTF(_session.getVHost()); //first vhost + + out.writeLong(_session.getAccessed());//accessTime + out.writeLong(_session.getLastAccessedTime()); //lastAccessTime + out.writeLong(_session.getCreationTime()); //time created + out.writeLong(_session.getCookieSetTime());//time cookie was set + out.writeUTF(_session.getLastNode()); //name of last node managing + + out.writeLong(_session.getExpiry()); + out.writeLong(_session.getMaxInactiveInterval()); + out.writeObject(_session.getAttributeMap()); + } + + + private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException + { + String clusterId = in.readUTF(); + String context = in.readUTF(); + String vhost = in.readUTF(); + + Long accessed = in.readLong();//accessTime + Long lastAccessed = in.readLong(); //lastAccessTime + Long created = in.readLong(); //time created + Long cookieSet = in.readLong();//time cookie was set + String lastNode = in.readUTF(); //last managing node + Long expiry = in.readLong(); + Long maxIdle = in.readLong(); + HashMap<String,Object> attributes = (HashMap<String,Object>)in.readObject(); + Session session = new Session(clusterId, created, accessed, maxIdle); + session.setCookieSetTime(cookieSet); + session.setLastAccessedTime(lastAccessed); + session.setLastNode(lastNode); + session.setContextPath(context); + session.setVHost(vhost); + session.setExpiry(expiry); + session.addAttributes(attributes); + setSession(session); + } + + + private void readObjectNoData() throws ObjectStreamException + { + setSession(null); + } + + + } + + + /** + * Session + * + * Representation of a session in local memory. + */ + public class Session extends MemSession + { + + private ReentrantLock _lock = new ReentrantLock(); + + /** + * The (canonical) context path for with which this session is associated + */ + private String _contextPath; + + + /** + * The number of currently active request threads in this session + */ + private AtomicInteger _activeThreads = new AtomicInteger(0); + + + /** + * The time in msec since the epoch at which this session should expire + */ + private long _expiryTime; + + + /** + * Time in msec since the epoch at which this session was last read from cluster + */ + private long _lastSyncTime; + + + /** + * The workername of last node known to be managing the session + */ + private String _lastNode; + + + + + /** + * Any virtual hosts for the context with which this session is associated + */ + private String _vhost; + + + + + /** + * A new session. + * + * @param request + */ + protected Session (HttpServletRequest request) + { + super(InfinispanSessionManager.this,request); + long maxInterval = getMaxInactiveInterval(); + _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L)); + _lastNode = getSessionIdManager().getWorkerName(); + setVHost(InfinispanSessionManager.getVirtualHost(_context)); + setContextPath(InfinispanSessionManager.getContextPath(_context)); + } + + + protected Session (SerializableSessionData sd) + { + super(InfinispanSessionManager.this, sd.createTime, sd.accessed, sd.clusterId); + _expiryTime = (sd.maxInactive <= 0 ? 0 : (System.currentTimeMillis() + sd.maxInactive*1000L)); + setLastNode(sd.lastNode); + setContextPath(sd.contextPath); + setVHost(sd.vhost); + addAttributes(sd.attributes); + } + + + /** + * A restored session. + * + * @param sessionId + * @param created + * @param accessed + * @param maxInterval + */ + protected Session (String sessionId, long created, long accessed, long maxInterval) + { + super(InfinispanSessionManager.this, created, accessed, sessionId); + _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L)); + } + + /** + * Called on entry to the session. + * + * @see org.eclipse.jetty.server.session.AbstractSession#access(long) + */ + @Override + protected boolean access(long time) + { + if (LOG.isDebugEnabled()) + LOG.debug("Access session({}) for context {} on worker {}", getId(), getContextPath(), getSessionIdManager().getWorkerName()); + try + { + _lock.lock(); + + //a request thread is entering + if (_activeThreads.incrementAndGet() == 1) + { + long now = System.currentTimeMillis(); + + //if the first thread, check that the session in memory is not stale, if we're checking for stale sessions + if (getStaleIntervalSec() > 0 && (now - getLastSyncTime()) >= (getStaleIntervalSec() * 1000L)) + { + if (LOG.isDebugEnabled()) + LOG.debug("Acess session({}) for context {} on worker {} stale session. Reloading.", getId(), getContextPath(), getSessionIdManager().getWorkerName()); + refresh(); + } + } + } + catch (Exception e) + { + LOG.warn(e); + } + finally + { + _lock.unlock(); + } + + if (super.access(time)) + { + int maxInterval=getMaxInactiveInterval(); + _expiryTime = (maxInterval <= 0 ? 0 : (time + maxInterval*1000L)); + return true; + } + return false; + } + + + /** + * Exit from session + * @see org.eclipse.jetty.server.session.AbstractSession#complete() + */ + @Override + protected void complete() + { + super.complete(); + + //if this is the last request thread to be in the session + if (_activeThreads.decrementAndGet() == 0) + { + try + { + //an invalid session will already have been removed from the + //local session map and deleted from the cluster. If its valid save + //it to the cluster. + //TODO consider doing only periodic saves if only the last access + //time to the session changes + if (isValid()) + { + willPassivate(); + save(this); + didActivate(); + } + } + catch (Exception e) + { + LOG.warn("Problem saving session({})",getId(), e); + } + } + } + + /** + * Expire the session. + * + * @see org.eclipse.jetty.server.session.AbstractSession#timeout() + */ + @Override + protected void timeout() + { + super.timeout(); + } + + /** + * Reload the session from the cluster. If the node that + * last managed the session from the cluster is ourself, + * then the session does not need refreshing. + * NOTE: this method MUST be called with sufficient locks + * in place to prevent 2 or more concurrent threads from + * simultaneously updating the session. + */ + private void refresh () + { + //get fresh copy from the cluster + Session fresh = load(getId()); + + //if the session no longer exists, invalidate + if (fresh == null) + { + invalidate(); + return; + } + + //cluster copy assumed to be the same as we were the last + //node to manage it + if (fresh.getLastNode().equals(getLastNode())) + return; + + setLastNode(getSessionIdManager().getWorkerName()); + + //prepare for refresh + willPassivate(); + + //if fresh has no attributes, remove them + if (fresh.getAttributes() == 0) + this.clearAttributes(); + else + { + //reconcile attributes + for (String key:fresh.getAttributeMap().keySet()) + { + Object freshvalue = fresh.getAttribute(key); + + //session does not already contain this attribute, so bind it + if (getAttribute(key) == null) + { + doPutOrRemove(key,freshvalue); + bindValue(key,freshvalue); + } + else //session already contains this attribute, update its value + { + doPutOrRemove(key,freshvalue); + } + + } + // cleanup, remove values from session, that don't exist in data anymore: + for (String key : getNames()) + { + if (fresh.getAttribute(key) == null) + { + Object oldvalue = getAttribute(key); + doPutOrRemove(key,null); + unbindValue(key,oldvalue); + } + } + } + //finish refresh + didActivate(); + } + + + public void setExpiry (long expiry) + { + _expiryTime = expiry; + } + + + public long getExpiry () + { + return _expiryTime; + } + + public void swapId (String newId, String newNodeId) + { + _lock.lock(); + setClusterId(newId); + setNodeId(newNodeId); + _lock.unlock(); + } + + @Override + public void setAttribute (String name, Object value) + { + Object old = changeAttribute(name, value); + if (value == null && old == null) + return; //if same as remove attribute but attribute was already removed, no change + + //TODO _dirty = true; + } + + + public String getContextPath() + { + return _contextPath; + } + + + public void setContextPath(String contextPath) + { + this._contextPath = contextPath; + } + + + public String getVHost() + { + return _vhost; + } + + + public void setVHost(String vhost) + { + this._vhost = vhost; + } + + public String getLastNode() + { + return _lastNode; + } + + + public void setLastNode(String lastNode) + { + _lastNode = lastNode; + } + + + public long getLastSyncTime() + { + return _lastSyncTime; + } + + + public void setLastSyncTime(long lastSyncTime) + { + _lastSyncTime = lastSyncTime; + } + + } + + + + + /** + * Start the session manager. + * + * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart() + */ + @Override + public void doStart() throws Exception + { + if (_sessionIdManager == null) + throw new IllegalStateException("No session id manager defined"); + + if (_cache == null) + throw new IllegalStateException("No session cache defined"); + + _sessions = new ConcurrentHashMap<String, Session>(); + + //try and use a common scheduler, fallback to own + _scheduler = getSessionHandler().getServer().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(); + } + + + /** + * Stop the session manager. + * + * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop() + */ + @Override + public void doStop() throws Exception + { + super.doStop(); + + if (_task!=null) + _task.cancel(); + _task=null; + if (_ownScheduler && _scheduler !=null) + _scheduler.stop(); + _scheduler = null; + + _sessions.clear(); + _sessions = null; + } + + + + /** + * Look for sessions in local memory that have expired. + */ + /** + * + */ + public void scavenge () + { + Set<String> candidateIds = new HashSet<String>(); + long now = System.currentTimeMillis(); + + LOG.info("SessionManager for context {} scavenging at {} ", getContextPath(getContext()), now); + synchronized (_sessions) + { + for (Map.Entry<String, Session> entry:_sessions.entrySet()) + { + long expiry = entry.getValue().getExpiry(); + if (expiry > 0 && expiry < now) + candidateIds.add(entry.getKey()); + } + } + + for (String candidateId:candidateIds) + { + if (LOG.isDebugEnabled()) + LOG.debug("Session {} expired ", candidateId); + + Session candidateSession = _sessions.get(candidateId); + if (candidateSession != null) + { + //double check the state of the session in the cache, as the + //session may have migrated to another node + Session cachedSession = load(makeKey(candidateId, _context)); + if (cachedSession == null) + { + if (LOG.isDebugEnabled()) LOG.debug("Locally expired session({}) does not exist in cluster ",candidateId); + //the session no longer exists, do a full invalidation + candidateSession.timeout(); + } + else if (getSessionIdManager().getWorkerName().equals(cachedSession.getLastNode())) + { + if (LOG.isDebugEnabled()) LOG.debug("Expiring session({}) local to session manager",candidateId); + //if I am the master of the session then it can be timed out + candidateSession.timeout(); + } + else + { + //some other node is the master of the session, simply remove it from my memory + if (LOG.isDebugEnabled()) LOG.debug("Session({}) not local to this session manager, removing from local memory", candidateId); + candidateSession.willPassivate(); + _sessions.remove(candidateSession.getClusterId()); + } + + } + } + } + + + + public long getScavengeInterval () + { + return _scavengeIntervalMs/1000; + } + + + + /** + * Set the interval between runs of the scavenger. As this will be a costly + * exercise (need to iterate over all cache entries) it should not be run too + * often. + * + * + * @param sec + */ + 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 time 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 (_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); + } + } + } + + + + + /** + * Get the clustered cache instance. + * + * @return + */ + public Cache<String, Object> getCache() + { + return _cache; + } + + + + /** + * Set the clustered cache instance. + * + * @param cache + */ + public void setCache (Cache<String, Object> cache) + { + this._cache = cache; + } + + + + + + public long getStaleIntervalSec() + { + return _staleIntervalSec; + } + + + public void setStaleIntervalSec(long staleIntervalSec) + { + _staleIntervalSec = staleIntervalSec; + } + + + /** + * Add a new session for the context related to this session manager + * + * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession) + */ + @Override + protected void addSession(AbstractSession session) + { + if (session==null) + return; + + if (LOG.isDebugEnabled()) LOG.debug("Adding session({}) to session manager for context {} on worker {}",session.getClusterId(), getContextPath(getContext()),getSessionIdManager().getWorkerName() + " with lastnode="+((Session)session).getLastNode()); + _sessions.put(session.getClusterId(), (Session)session); + + try + { + session.willPassivate(); + save(((InfinispanSessionManager.Session)session)); + session.didActivate(); + + } + catch (Exception e) + { + LOG.warn("Unable to store new session id="+session.getId() , e); + } + } + + /** + * Ask the cluster for the session. + * + * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String) + */ + @Override + public AbstractSession getSession(String idInCluster) + { + Session session = null; + + + //try and find the session in this node's memory + Session memSession = (Session)_sessions.get(idInCluster); + + if (LOG.isDebugEnabled()) + LOG.debug("getSession({}) {} in session map",idInCluster,(memSession==null?"not":"")); + + long now = System.currentTimeMillis(); + try + { + //if the session is not in this node's memory, then load it from the cluster cache + if (memSession == null) + { + if (LOG.isDebugEnabled()) + LOG.debug("getSession({}): loading session data from cluster", idInCluster); + + session = load(makeKey(idInCluster, _context)); + if (session != null) + { + //We retrieved a session with the same key from the database + + //Check that it wasn't expired + if (session.getExpiry() > 0 && session.getExpiry() <= now) + { + if (LOG.isDebugEnabled()) LOG.debug("getSession ({}): Session expired", idInCluster); + //ensure that the session id for the expired session is deleted so that a new session with the + //same id cannot be created (because the idInUse() test would succeed) + ((InfinispanSessionIdManager)getSessionIdManager()).removeSession(session); + return null; + } + + //Update the last worker node to me + session.setLastNode(getSessionIdManager().getWorkerName()); + //TODO consider saving session here if lastNode was not this node + + //Check that another thread hasn't loaded the same session + Session existingSession = _sessions.putIfAbsent(idInCluster, session); + if (existingSession != null) + { + //use the one that the other thread inserted + session = existingSession; + LOG.debug("getSession({}): using session loaded by another request thread ", idInCluster); + } + else + { + //indicate that the session was reinflated + session.didActivate(); + LOG.debug("getSession({}): loaded session from cluster", idInCluster); + } + return session; + } + else + { + //The requested session does not exist anywhere in the cluster + LOG.debug("getSession({}): No session in cluster matching",idInCluster); + return null; + } + } + else + { + //The session exists in this node's memory + LOG.debug("getSession({}): returning session from local memory ", memSession.getClusterId()); + return memSession; + } + } + catch (Exception e) + { + LOG.warn("Unable to load session {}", idInCluster, e); + return null; + } + } + + + + /** + * The session manager is stopping. + * + * @see org.eclipse.jetty.server.session.AbstractSessionManager#shutdownSessions() + */ + @Override + protected void shutdownSessions() throws Exception + { + //TODO if implementing period saves, if we might have un-saved changes, + //then we need to write them back to the clustered cache + } + + + @Override + protected AbstractSession newSession(HttpServletRequest request) + { + return new Session(request); + } + + /** + * Remove a session from local memory, and delete it from + * the cluster cache. + * + * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String) + */ + @Override + protected boolean removeSession(String idInCluster) + { + Session session = (Session)_sessions.remove(idInCluster); + try + { + if (session != null) + delete(session); + } + catch (Exception e) + { + LOG.warn("Problem deleting session id="+idInCluster, e); + } + return session!=null; + } + + + + + @Override + public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId) + { + Session session = null; + try + { + //take the session with that id out of our managed list + session = (Session)_sessions.remove(oldClusterId); + if (session != null) + { + //TODO consider transactionality and ramifications if the session is live on another node + delete(session); //delete the old session from the cluster + session.swapId(newClusterId, newNodeId); //update the session + _sessions.put(newClusterId, session); //put it into managed list under new key + save(session); //put the session under the new id into the cluster + } + } + catch (Exception e) + { + LOG.warn(e); + } + + super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId); + } + + + /** + * Load a session from the clustered cache. + * + * @param key + * @return + */ + protected Session load (String key) + { + if (_cache == null) + throw new IllegalStateException("No cache"); + + if (LOG.isDebugEnabled()) LOG.debug("Loading session {} from cluster", key); + //SerializableSession storableSession = (SerializableSession)_cache.get(key); + SerializableSessionData storableSession = (SerializableSessionData)_cache.get(key); + if (storableSession == null) + { + if (LOG.isDebugEnabled()) LOG.debug("No session {} in cluster ",key); + return null; + } + else + { + //Session session = storableSession.getSession(); + Session session = new Session (storableSession); + session.setLastSyncTime(System.currentTimeMillis()); + return session; + } + } + + + + /** + * Save or update the session to the cluster cache + * + * @param session + * @throws Exception + */ + protected void save (InfinispanSessionManager.Session session) + throws Exception + { + if (_cache == null) + throw new IllegalStateException("No cache"); + + if (LOG.isDebugEnabled()) LOG.debug("Writing session {} to cluster", session.getId()); + + SerializableSessionData storableSession = new SerializableSessionData(session); + _cache.put(makeKey(session, _context), storableSession); + session.setLastSyncTime(System.currentTimeMillis()); + } + + + + /** + * Remove the session from the cluster cache. + * + * @param session + */ + protected void delete (InfinispanSessionManager.Session session) + { + if (_cache == null) + throw new IllegalStateException("No cache"); + if (LOG.isDebugEnabled()) LOG.debug("Removing session {} from cluster", session.getId()); + _cache.remove(makeKey(session, _context)); + } + + + /** + * Invalidate a session for this context with the given id + * + * @param idInCluster + */ + public void invalidateSession (String idInCluster) + { + Session session = (Session)_sessions.get(idInCluster); + + if (session != null) + { + session.invalidate(); + } + } + + + /** + * Make a unique key for this session. + * As the same session id can be used across multiple contexts, to + * make it unique, the key must be composed of: + * <ol> + * <li>the id</li> + * <li>the context path</li> + * <li>the virtual hosts</li> + * </ol> + * + *TODO consider the difference between getClusterId and getId + * @param session + * @return + */ + private String makeKey (Session session, Context context) + { + return makeKey(session.getId(), context); + } + + /** + * Make a unique key for this session. + * As the same session id can be used across multiple contexts, to + * make it unique, the key must be composed of: + * <ol> + * <li>the id</li> + * <li>the context path</li> + * <li>the virtual hosts</li> + * </ol> + * + *TODO consider the difference between getClusterId and getId + * @param session + * @return + */ + private String makeKey (String id, Context context) + { + String key = getContextPath(context); + key = key + "_" + getVirtualHost(context); + key = key+"_"+id; + return key; + } + + /** + * Turn the context path into an acceptable string + * + * @param context + * @return + */ + private static String getContextPath (ContextHandler.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 (ContextHandler.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('\\','_'); + } + +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java index 5c6e8bfe37..c6e4b2b49d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java @@ -155,6 +155,12 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI { return _cookieSet; } + + /* ------------------------------------------------------------- */ + public void setCookieSetTime(long time) + { + _cookieSet = time; + } /* ------------------------------------------------------------- */ @Override diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java index 9aafc00599..be3c55a99c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java @@ -98,10 +98,7 @@ public class JDBCSessionManager extends AbstractSessionManager protected boolean _dirty=false; - /** - * Time in msec since the epoch that a session cookie was set for this session - */ - protected long _cookieSet; + /** @@ -223,16 +220,7 @@ public class JDBCSessionManager extends AbstractSessionManager return _canonicalContext; } - public void setCookieSet (long ms) - { - _cookieSet = ms; - } - - public synchronized long getCookieSet () - { - return _cookieSet; - } - + public synchronized void setLastNode (String node) { _lastNode=node; @@ -261,11 +249,6 @@ public class JDBCSessionManager extends AbstractSessionManager _dirty=true; } - @Override - protected void cookieSet() - { - _cookieSet = getAccessed(); - } /** * Entry to session. @@ -400,7 +383,7 @@ public class JDBCSessionManager extends AbstractSessionManager { return "Session rowId="+_rowId+",id="+getId()+",lastNode="+_lastNode+ ",created="+getCreationTime()+",accessed="+getAccessed()+ - ",lastAccessed="+getLastAccessedTime()+",cookieSet="+_cookieSet+ + ",lastAccessed="+getLastAccessedTime()+",cookieSet="+getCookieSetTime()+ ",maxInterval="+getMaxInactiveInterval()+",lastSaved="+_lastSaved+",expiry="+_expiryTime; } } @@ -925,7 +908,7 @@ public class JDBCSessionManager extends AbstractSessionManager result.getLong(_sessionTableSchema.getCreateTimeColumn()), result.getLong(_sessionTableSchema.getAccessTimeColumn()), maxInterval); - session.setCookieSet(result.getLong(_sessionTableSchema.getCookieTimeColumn())); + session.setCookieSetTime(result.getLong(_sessionTableSchema.getCookieTimeColumn())); session.setLastAccessedTime(result.getLong(_sessionTableSchema.getLastAccessTimeColumn())); session.setLastNode(result.getString(_sessionTableSchema.getLastNodeColumn())); session.setLastSaved(result.getLong(_sessionTableSchema.getLastSavedTimeColumn())); @@ -999,7 +982,7 @@ public class JDBCSessionManager extends AbstractSessionManager statement.setLong(6, session.getAccessed());//accessTime statement.setLong(7, session.getLastAccessedTime()); //lastAccessTime statement.setLong(8, session.getCreationTime()); //time created - statement.setLong(9, session.getCookieSet());//time cookie was set + statement.setLong(9, session.getCookieSetTime());//time cookie was set statement.setLong(10, now); //last saved time statement.setLong(11, session.getExpiryTime()); statement.setLong(12, session.getMaxInactiveInterval()); @@ -224,6 +224,8 @@ <exclude>jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java</exclude> <exclude>jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/PolicyFileScanner.java</exclude> <exclude>jetty-ant/**</exclude> + <exclude>jetty-infinispan/**</exclude> + <exclude>tests/test-sessions/test-infinispan-sessions/**</exclude> </excludes> </configuration> <executions> @@ -484,6 +486,7 @@ <module>jetty-jaspi</module> <module>jetty-rewrite</module> <module>jetty-nosql</module> + <module>jetty-infinispan</module> <module>tests</module> <module>examples</module> <module>jetty-quickstart</module> diff --git a/tests/test-sessions/pom.xml b/tests/test-sessions/pom.xml index dc15b9181d..288ebdf72d 100644 --- a/tests/test-sessions/pom.xml +++ b/tests/test-sessions/pom.xml @@ -33,7 +33,7 @@ <module>test-sessions-common</module> <module>test-hash-sessions</module> <module>test-jdbc-sessions</module> - <!-- Requires mongodb server running --> <module>test-mongodb-sessions</module> + <module>test-infinispan-sessions</module> </modules> </project> diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java index d4d6b283b1..3393d8ea2f 100644 --- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java +++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java @@ -38,7 +38,7 @@ public class HashTestServer extends AbstractTestServer } - public SessionIdManager newSessionIdManager(String config) + public SessionIdManager newSessionIdManager(Object config) { return new HashSessionIdManager(); } diff --git a/tests/test-sessions/test-infinispan-sessions/pom.xml b/tests/test-sessions/test-infinispan-sessions/pom.xml new file mode 100644 index 0000000000..c94a6e18ab --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/pom.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +// ======================================================================== +// Copyright (c) Webtide LLC +// +// 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.apache.org/licenses/LICENSE-2.0.txt +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.eclipse.jetty.tests</groupId> + <artifactId>test-sessions-parent</artifactId> + <version>9.1.6-SNAPSHOT</version> + </parent> + <artifactId>test-infinispan-sessions</artifactId> + <name>Jetty Tests :: Sessions :: Infinispan</name> + <url>http://www.eclipse.org/jetty</url> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <!-- DO NOT DEPLOY (or Release) --> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <skipTests>false</skipTests> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>unpack</id> + <phase>generate-test-resources</phase> + <goals> + <goal>unpack</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.eclipse.jetty.toolchain</groupId> + <artifactId>jetty-test-policy</artifactId> + <version>${jetty-test-policy-version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <includes>**/*.keystore,**/*.pem</includes> + <outputDirectory>${jetty.test.policy.loc}</outputDirectory> + </artifactItem> + </artifactItems> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-webapp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-client</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.tests</groupId> + <artifactId>test-sessions-common</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-infinispan</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-jmx</artifactId> + <version>${project.version}</version> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.toolchain</groupId> + <artifactId>jetty-test-helper</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +<!-- + <profiles> + <profile> + <id>mongodb</id> + <activation> + <property> + <name>mongodb.enabled</name> + <value>true</value> + </property> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <skipTests>false</skipTests> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> +--> +</project> diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java new file mode 100644 index 0000000000..22beec61a8 --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java @@ -0,0 +1,60 @@ +// +// ======================================================================== +// 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.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ClientCrossContextSessionTest extends AbstractClientCrossContextSessionTest +{ + + public static InfinispanTestSupport __testSupport; + + + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + + @Override + public AbstractTestServer createServer(int port) + { + InfinispanTestSessionServer server = new InfinispanTestSessionServer(port, __testSupport.getCache()); + return server; + } + + @Test + public void testCrossContextDispatch() throws Exception + { + super.testCrossContextDispatch(); + } + +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java new file mode 100644 index 0000000000..f021a70d62 --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java @@ -0,0 +1,71 @@ +// +// ======================================================================== +// 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.infinispan.Cache; +import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.manager.DefaultCacheManager; +import org.infinispan.manager.EmbeddedCacheManager; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * ImmortalSessionTest + * + * + */ +public class ImmortalSessionTest extends AbstractImmortalSessionTest +{ + + + public static InfinispanTestSupport __testSupport; + + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + + /** + * @see org.eclipse.jetty.server.session.AbstractImmortalSessionTest#createServer(int, int, int) + */ + @Override + public AbstractTestServer createServer(int port, int maxInactiveMs, int scavengeMs) + { + return new InfinispanTestSessionServer(port, maxInactiveMs, scavengeMs, __testSupport.getCache()); + } + + @Override + public void testImmortalSession() throws Exception + { + super.testImmortalSession(); + } + + +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java new file mode 100644 index 0000000000..2edcbd34c2 --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java @@ -0,0 +1,79 @@ +package org.eclipse.jetty.server.session; + +import org.eclipse.jetty.server.SessionIdManager; +import org.eclipse.jetty.server.SessionManager; +import org.eclipse.jetty.session.infinispan.InfinispanSessionIdManager; +import org.eclipse.jetty.session.infinispan.InfinispanSessionManager; +import org.infinispan.Cache; +import org.infinispan.commons.util.CloseableIteratorSet; + +public class InfinispanTestSessionServer extends AbstractTestServer +{ + static int __workers=0; + + + + + + public InfinispanTestSessionServer(int port, Cache config) + { + this(port, 30, 10, config); + } + + + + public InfinispanTestSessionServer(int port, int maxInactivePeriod, int scavengePeriod, Cache config) + { + super(port, maxInactivePeriod, scavengePeriod, config); + } + + + + @Override + public SessionIdManager newSessionIdManager(Object config) + { + InfinispanSessionIdManager idManager = new InfinispanSessionIdManager(getServer()); + idManager.setWorkerName("w"+(__workers++)); + idManager.setCache((Cache)config); + return idManager; + } + + @Override + public SessionManager newSessionManager() + { + InfinispanSessionManager sessionManager = new InfinispanSessionManager(); + sessionManager.setSessionIdManager((InfinispanSessionIdManager)_sessionIdManager); + sessionManager.setCache(((InfinispanSessionIdManager)_sessionIdManager).getCache()); + sessionManager.setStaleIntervalSec(1); + sessionManager.setScavengeInterval(_scavengePeriod); + + return sessionManager; + } + + @Override + public SessionHandler newSessionHandler(SessionManager sessionManager) + { + return new SessionHandler(sessionManager); + } + + + public void dumpCache () + { + Cache cache = ((InfinispanSessionIdManager)_sessionIdManager).getCache(); + if (cache != null) + { + System.err.println(cache.getName()+" contains "+cache.size()+" entries"); + CloseableIteratorSet<String> keys = cache.keySet(); + for (String key:keys) + System.err.println(key + " "+cache.get(key)); + } + } + + public void clearCache () + { + Cache cache = ((InfinispanSessionIdManager)_sessionIdManager).getCache(); + if (cache != null) + cache.clear(); + } + +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java new file mode 100644 index 0000000000..a3b61bd122 --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java @@ -0,0 +1,114 @@ +// +// ======================================================================== +// 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.io.File; + +import org.eclipse.jetty.util.IO; +import org.infinispan.Cache; +import org.infinispan.configuration.cache.Configuration; +import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.configuration.global.GlobalConfigurationBuilder; +import org.infinispan.manager.DefaultCacheManager; +import org.infinispan.manager.EmbeddedCacheManager; + +/** + * InfinispanTestSupport + * + * + */ +public class InfinispanTestSupport +{ + public static final String DEFAULT_CACHE_NAME = "session_test_cache"; + public Cache _cache; + + public ConfigurationBuilder _builder; + private File _tmpdir; + private boolean _useFileStore; + private String _name; + public static EmbeddedCacheManager _manager; + + static + { + try + { + _manager = new DefaultCacheManager(new GlobalConfigurationBuilder().globalJmxStatistics().allowDuplicateDomains(true).build()); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public InfinispanTestSupport () + { + this (null); + } + + public InfinispanTestSupport(String cacheName) + { + if (cacheName == null) + cacheName = DEFAULT_CACHE_NAME+System.currentTimeMillis(); + + _name = cacheName; + _builder = new ConfigurationBuilder(); + } + + public void setUseFileStore (boolean useFileStore) + { + _useFileStore = useFileStore; + } + + public Cache getCache () + { + return _cache; + } + + public void setup () throws Exception + { + if (_useFileStore) + { + _tmpdir = File.createTempFile("infini", "span"); + _tmpdir.delete(); + _tmpdir.mkdir(); + System.err.println("Temp file: "+_tmpdir); + Configuration config = _builder.persistence().addSingleFileStore().location(_tmpdir.getAbsolutePath()).storeAsBinary().build(); + _manager.defineConfiguration(_name, config); + } + else + { + _manager.defineConfiguration(_name, _builder.build()); + } + _cache = _manager.getCache(_name); + } + + + public void teardown () throws Exception + { + _manager.removeCache(_name); + if (_useFileStore) + { + if (_tmpdir != null) + { + IO.delete(_tmpdir); + } + } + } +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java new file mode 100644 index 0000000000..44123a590d --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java @@ -0,0 +1,116 @@ +// +// ======================================================================== +// 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.io.File; + +import org.eclipse.jetty.server.SessionManager; +import org.eclipse.jetty.session.infinispan.InfinispanSessionIdManager; +import org.eclipse.jetty.session.infinispan.InfinispanSessionManager; +import org.eclipse.jetty.util.IO; +import org.infinispan.Cache; +import org.infinispan.configuration.cache.Configuration; +import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.manager.DefaultCacheManager; +import org.infinispan.manager.EmbeddedCacheManager; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * InvalidationSessionTest + * + * + */ +public class InvalidationSessionTest extends AbstractInvalidationSessionTest +{ + + public static InfinispanTestSupport __testSupport; + public static long __staleSec = 3L; + + + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#createServer(int) + */ + @Override + public AbstractTestServer createServer(int port) + { + return new InfinispanTestSessionServer(port, __testSupport.getCache()); + +// return new InfinispanTestSessionServer(port, __cache) +// { +// +// @Override +// public SessionManager newSessionManager() +// { +// InfinispanSessionManager sessionManager = new InfinispanSessionManager(); +// sessionManager.setSessionIdManager((InfinispanSessionIdManager)_sessionIdManager); +// sessionManager.setStaleIntervalSec(__staleSec); +// sessionManager.setCache(((InfinispanSessionIdManager)_sessionIdManager).getCache()); +// return sessionManager; +// } +// }; + } + + + + + @Override + public void testInvalidation() throws Exception + { + super.testInvalidation(); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#pause() + */ + @Override + public void pause() + { + //This test moves a session from node 1 to node 2, then invalidates the session back on node1. This + //should never happen with a decent load balancer. + //The infinispan session manager on node 2 will hold the session in local memory for a specific (configurable) + //amount of time. We've set the stale session time to 3 sec, so we need to pause for at least this long before making + //another request to node2 + + //that the node will re-load the session from the database and discover that it has gone. + try + { + Thread.sleep(2 * __staleSec * 1000); + } + catch (InterruptedException e) + { + } + } + +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java new file mode 100644 index 0000000000..25c0ad2308 --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java @@ -0,0 +1,65 @@ +// +// ======================================================================== +// 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.io.File; + +import org.eclipse.jetty.util.IO; +import org.infinispan.Cache; +import org.infinispan.configuration.cache.Configuration; +import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.manager.DefaultCacheManager; +import org.infinispan.manager.EmbeddedCacheManager; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +public class LastAccessTimeTest extends AbstractLastAccessTimeTest +{ + public static InfinispanTestSupport __testSupport; + + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setUseFileStore(true); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache()); + } + + @Override + public void testLastAccessTime() throws Exception + { + super.testLastAccessTime(); + } + + +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LocalSessionScavengingTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LocalSessionScavengingTest.java new file mode 100644 index 0000000000..58e17de60c --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LocalSessionScavengingTest.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// 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.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * LocalSessionScavengingTest + * + * + */ +public class LocalSessionScavengingTest extends AbstractLocalSessionScavengingTest +{ + + public static InfinispanTestSupport __testSupport; + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setUseFileStore(true); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + + /** + * @see org.eclipse.jetty.server.session.AbstractLocalSessionScavengingTest#createServer(int, int, int) + */ + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache()); + } + +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java new file mode 100644 index 0000000000..afdfe667a1 --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java @@ -0,0 +1,66 @@ +// +// ======================================================================== +// 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.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * NewSessionTest + * + * + */ +public class NewSessionTest extends AbstractNewSessionTest +{ + + public static InfinispanTestSupport __testSupport; + + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractNewSessionTest#createServer(int, int, int) + */ + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache()); + } + + + @Override + public void testNewSession() throws Exception + { + super.testNewSession(); + } + + +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java new file mode 100644 index 0000000000..3edd429cf5 --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// 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.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * ReentrantRequestSessionTest + * + * + */ +public class ReentrantRequestSessionTest extends AbstractReentrantRequestSessionTest +{ + public static InfinispanTestSupport __testSupport; + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + + /** + * @see org.eclipse.jetty.server.session.AbstractReentrantRequestSessionTest#createServer(int) + */ + @Override + public AbstractTestServer createServer(int port) + { + return new InfinispanTestSessionServer(port, __testSupport.getCache()); + } + + @Override + public void testReentrantRequestSession() throws Exception + { + super.testReentrantRequestSession(); + } + + +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/RemoveSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/RemoveSessionTest.java new file mode 100644 index 0000000000..ebb534c27f --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/RemoveSessionTest.java @@ -0,0 +1,58 @@ +// +// ======================================================================== +// 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.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class RemoveSessionTest extends AbstractRemoveSessionTest +{ + + public static InfinispanTestSupport __testSupport; + + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + InfinispanTestSessionServer s = new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache()); + return s; + } + + @Test + public void testRemoveSession() throws Exception + { + super.testRemoveSession(); + } + +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java new file mode 100644 index 0000000000..56f6fe5ac1 --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// 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.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class SessionExpiryTest extends AbstractSessionExpiryTest +{ + + public static InfinispanTestSupport __testSupport; + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + InfinispanTestSessionServer server = new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache()); + return server; + } + + @Test + @Override + public void testSessionNotExpired() throws Exception + { + super.testSessionNotExpired(); + } + + @Test + @Override + public void testSessionExpiry() throws Exception + { + super.testSessionExpiry(); + } + + @Override + public void verifySessionDestroyed (TestHttpSessionListener listener, String sessionId) + { + //noop - sessions that expired when the InfinispanSessionManager was not running are not reloaded and do not have their listeners called on them. + } +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java new file mode 100644 index 0000000000..766d964d6b --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java @@ -0,0 +1,66 @@ +// +// ======================================================================== +// 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.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * SessionInvalidateAndCreateTest + * + * + */ +public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest +{ + public static InfinispanTestSupport __testSupport; + + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + + /** + * @see org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest#createServer(int, int, int) + */ + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache()); + } + + @Override + public void testSessionScavenge() throws Exception + { + super.testSessionScavenge(); + } + + + +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionMigrationTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionMigrationTest.java new file mode 100644 index 0000000000..b327c4c7e5 --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionMigrationTest.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// 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.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * SessionMigrationTest + * + * + */ +public class SessionMigrationTest extends AbstractSessionMigrationTest +{ + + public static InfinispanTestSupport __testSupport; + + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractSessionMigrationTest#createServer(int) + */ + @Override + public AbstractTestServer createServer(int port) + { + return new InfinispanTestSessionServer(port, __testSupport.getCache()); + } + + @Override + public void testSessionMigration() throws Exception + { + super.testSessionMigration(); + } + +} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java new file mode 100644 index 0000000000..36de28037a --- /dev/null +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// 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.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * SessionRenewTest + * + * + */ +public class SessionRenewTest extends AbstractSessionRenewTest +{ + + public static InfinispanTestSupport __testSupport; + + + @BeforeClass + public static void setup () throws Exception + { + __testSupport = new InfinispanTestSupport(); + __testSupport.setup(); + } + + @AfterClass + public static void teardown () throws Exception + { + __testSupport.teardown(); + } + + + /** + * @see org.eclipse.jetty.server.session.AbstractSessionRenewTest#createServer(int, int, int) + */ + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + return new InfinispanTestSessionServer(port, max, scavenge, __testSupport.getCache()); + } + + @Test + public void testSessionRenewal() throws Exception + { + super.testSessionRenewal(); + } + + +} diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java index ad0a6921fa..5b55861707 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java @@ -76,14 +76,14 @@ public class JdbcTestServer extends AbstractTestServer * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionIdManager(String) */ @Override - public SessionIdManager newSessionIdManager(String config) + public SessionIdManager newSessionIdManager(Object config) { synchronized(JdbcTestServer.class) { JDBCSessionIdManager idManager = new JDBCSessionIdManager(_server); idManager.setScavengeInterval(_scavengePeriod); idManager.setWorkerName("w"+(__workers++)); - idManager.setDriverInfo(DRIVER_CLASS, (config==null?DEFAULT_CONNECTION_URL:config)); + idManager.setDriverInfo(DRIVER_CLASS, (config==null?DEFAULT_CONNECTION_URL:(String)config)); JDBCSessionIdManager.SessionIdTableSchema idTableSchema = new JDBCSessionIdManager.SessionIdTableSchema(); idTableSchema.setTableName("mysessionids"); idTableSchema.setIdColumn("myid"); diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java index 49f4d72ae2..5177b93987 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java @@ -18,23 +18,9 @@ package org.eclipse.jetty.server.session; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.sql.DriverManager; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSessionEvent; -import javax.servlet.http.HttpSessionListener; -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.api.ContentResponse; -import org.eclipse.jetty.client.api.Request; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.junit.After; import org.junit.Test; @@ -47,22 +33,6 @@ import org.junit.Test; public class SessionExpiryTest extends AbstractSessionExpiryTest { - public class TestHttpSessionListener implements HttpSessionListener - { - public List<String> createdSessions = new ArrayList<String>(); - public List<String> destroyedSessions = new ArrayList<String>(); - - public void sessionDestroyed(HttpSessionEvent se) - { - destroyedSessions.add(se.getSession().getId()); - } - - public void sessionCreated(HttpSessionEvent se) - { - createdSessions.add(se.getSession().getId()); - } - }; - /** * @see org.eclipse.jetty.server.session.AbstractSessionExpiryTest#createServer(int, int, int) */ @@ -75,70 +45,9 @@ public class SessionExpiryTest extends AbstractSessionExpiryTest @Test public void testSessionExpiry() throws Exception { - - - String contextPath = ""; - String servletMapping = "/server"; - int inactivePeriod = 2; - int scavengePeriod = 1; - AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod); - TestServlet servlet = new TestServlet(); - ServletHolder holder = new ServletHolder(servlet); - ServletContextHandler context = server1.addContext(contextPath); - context.addServlet(holder, servletMapping); - TestHttpSessionListener listener = new TestHttpSessionListener(); - - context.getSessionHandler().addEventListener(listener); - - server1.start(); - int port1 = server1.getPort(); - - try - { - HttpClient client = new HttpClient(); - client.start(); - String url = "http://localhost:" + port1 + contextPath + servletMapping; - - //make a request to set up a session on the server - ContentResponse response1 = client.GET(url + "?action=init"); - assertEquals(HttpServletResponse.SC_OK,response1.getStatus()); - String sessionCookie = response1.getHeaders().get("Set-Cookie"); - assertTrue(sessionCookie != null); - // Mangle the cookie, replacing Path with $Path, etc. - sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); - - String sessionId = extractSessionId(sessionCookie); - - assertTrue(listener.createdSessions.contains(sessionId)); - //now stop the server - server1.stop(); - - //and wait until the expiry time has passed - pause(inactivePeriod); - - //restart the server - server1.start(); - - port1 = server1.getPort(); - url = "http://localhost:" + port1 + contextPath + servletMapping; - - //make another request, the session should have expired - Request request = client.newRequest(url + "?action=test"); - request.getHeaders().add("Cookie", sessionCookie); - ContentResponse response2 = request.send(); - assertEquals(HttpServletResponse.SC_OK,response2.getStatus()); - - //and wait until the expiry time has passed - pause(inactivePeriod); - - assertTrue(listener.destroyedSessions.contains(sessionId)); - } - finally - { - server1.stop(); - } + super.testSessionExpiry(); } - + @@ -149,24 +58,7 @@ public class SessionExpiryTest extends AbstractSessionExpiryTest super.testSessionNotExpired(); } - - - public String extractSessionId (String sessionCookie) - { - if (sessionCookie == null) - return null; - sessionCookie = sessionCookie.trim(); - int i = sessionCookie.indexOf(';'); - if (i >= 0) - sessionCookie = sessionCookie.substring(0,i); - if (sessionCookie.startsWith("JSESSIONID")) - sessionCookie = sessionCookie.substring("JSESSIONID=".length()); - i = sessionCookie.indexOf('.'); - if (i >=0) - sessionCookie = sessionCookie.substring(0,i); - return sessionCookie; - } - + @After diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java index 4ef587c257..ead992ca73 100644 --- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java +++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java @@ -50,7 +50,7 @@ public class MongoTestServer extends AbstractTestServer _saveAllAttributes = saveAllAttributes; } - public SessionIdManager newSessionIdManager(String config) + public SessionIdManager newSessionIdManager(Object config) { try { diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java index f83a8cc3e3..c344d72530 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java @@ -82,8 +82,7 @@ public abstract class AbstractRemoveSessionTest assertTrue(testListener.isDestroyed()); - // The session is not there anymore, but we present an old cookie - // The server creates a new session, we must ensure we released all locks + // The session is not there anymore, even if we present an old cookie request = client.newRequest("http://localhost:" + port + contextPath + servletMapping + "?action=check"); request.header("Cookie", sessionCookie); response = request.send(); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java index e04e15cef2..b79a0a33ac 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java @@ -39,7 +39,7 @@ import org.junit.Ignore; import org.junit.Test; /** - * AbstractNewSessionTest + * AbstractSessionCookieTest */ public abstract class AbstractSessionCookieTest { diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java index 8f5353e2a1..5c11f8b35c 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java @@ -22,16 +22,21 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionEvent; +import javax.servlet.http.HttpSessionListener; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.junit.Test; @@ -50,6 +55,23 @@ public abstract class AbstractSessionExpiryTest e.printStackTrace(); } } + + public class TestHttpSessionListener implements HttpSessionListener + { + public List<String> createdSessions = new ArrayList<String>(); + public List<String> destroyedSessions = new ArrayList<String>(); + + public void sessionDestroyed(HttpSessionEvent se) + { + destroyedSessions.add(se.getSession().getId()); + } + + public void sessionCreated(HttpSessionEvent se) + { + createdSessions.add(se.getSession().getId()); + } + }; + @Test public void testSessionNotExpired() throws Exception @@ -101,18 +123,26 @@ public abstract class AbstractSessionExpiryTest server1.stop(); } } + @Test public void testSessionExpiry() throws Exception { + + String contextPath = ""; String servletMapping = "/server"; int inactivePeriod = 2; - int scavengePeriod = 10; + int scavengePeriod = 1; AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod); TestServlet servlet = new TestServlet(); ServletHolder holder = new ServletHolder(servlet); - server1.addContext(contextPath).addServlet(holder, servletMapping); + ServletContextHandler context = server1.addContext(contextPath); + context.addServlet(holder, servletMapping); + TestHttpSessionListener listener = new TestHttpSessionListener(); + + context.getSessionHandler().addEventListener(listener); + server1.start(); int port1 = server1.getPort(); @@ -129,7 +159,11 @@ public abstract class AbstractSessionExpiryTest assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); - + + String sessionId = AbstractTestServer.extractSessionId(sessionCookie); + + verifySessionCreated(listener,sessionId); + //now stop the server server1.stop(); @@ -138,6 +172,7 @@ public abstract class AbstractSessionExpiryTest //restart the server server1.start(); + port1 = server1.getPort(); url = "http://localhost:" + port1 + contextPath + servletMapping; @@ -146,12 +181,27 @@ public abstract class AbstractSessionExpiryTest request.getHeaders().add("Cookie", sessionCookie); ContentResponse response2 = request.send(); assertEquals(HttpServletResponse.SC_OK,response2.getStatus()); + + //and wait until the expiry time has passed + pause(inactivePeriod); + + verifySessionDestroyed (listener, sessionId); } finally { server1.stop(); - } + } + } + public void verifySessionCreated (TestHttpSessionListener listener, String sessionId) + { + assertTrue(listener.createdSessions.contains(sessionId)); } + public void verifySessionDestroyed (TestHttpSessionListener listener, String sessionId) + { + assertTrue (listener.destroyedSessions.contains(sessionId)); + } + + public static class TestServlet extends HttpServlet { diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java index d62e3f7e92..8b0851b00c 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java @@ -37,7 +37,7 @@ import org.junit.Test; /** - * AbstractLastAccessTimeTest + * AbstractSessionValueSavingTest */ public abstract class AbstractSessionValueSavingTest { diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java index 329d6b8fa5..789f6b28d1 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java @@ -34,17 +34,38 @@ import org.eclipse.jetty.webapp.WebAppContext; */ public abstract class AbstractTestServer { + public static int DEFAULT_MAX_INACTIVE = 30; + public static int DEFAULT_SCAVENGE = 10; + protected final Server _server; protected final int _maxInactivePeriod; protected final int _scavengePeriod; protected final ContextHandlerCollection _contexts; protected SessionIdManager _sessionIdManager; + + + public static String extractSessionId (String sessionCookie) + { + if (sessionCookie == null) + return null; + sessionCookie = sessionCookie.trim(); + int i = sessionCookie.indexOf(';'); + if (i >= 0) + sessionCookie = sessionCookie.substring(0,i); + if (sessionCookie.startsWith("JSESSIONID")) + sessionCookie = sessionCookie.substring("JSESSIONID=".length()); + i = sessionCookie.indexOf('.'); + if (i >=0) + sessionCookie = sessionCookie.substring(0,i); + return sessionCookie; + } + public AbstractTestServer(int port) { - this(port, 30, 10); + this(port, DEFAULT_MAX_INACTIVE, DEFAULT_SCAVENGE); } public AbstractTestServer(int port, int maxInactivePeriod, int scavengePeriod) @@ -52,7 +73,7 @@ public abstract class AbstractTestServer this (port, maxInactivePeriod, scavengePeriod, null); } - public AbstractTestServer(int port, int maxInactivePeriod, int scavengePeriod, String sessionIdMgrConfig) + public AbstractTestServer(int port, int maxInactivePeriod, int scavengePeriod, Object sessionIdMgrConfig) { _server = new Server(port); _maxInactivePeriod = maxInactivePeriod; @@ -64,7 +85,7 @@ public abstract class AbstractTestServer - public abstract SessionIdManager newSessionIdManager(String config); + public abstract SessionIdManager newSessionIdManager(Object config); public abstract SessionManager newSessionManager(); public abstract SessionHandler newSessionHandler(SessionManager sessionManager); |