diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index 71f25a46774..c472bd42463 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -778,6 +778,11 @@ jetty-infinispan ${project.version} + + org.eclipse.jetty.gcloud + gcloud-session-manager + ${project.version} + org.eclipse.jetty jetty-nosql diff --git a/jetty-gcloud/gcloud-session-manager/pom.xml b/jetty-gcloud/gcloud-session-manager/pom.xml new file mode 100644 index 00000000000..f83e49d5a0e --- /dev/null +++ b/jetty-gcloud/gcloud-session-manager/pom.xml @@ -0,0 +1,75 @@ + + + + org.eclipse.jetty.gcloud + gcloud-parent + 9.3.4-SNAPSHOT + + + 4.0.0 + gcloud-session-manager + Jetty :: GCloud :: Session Manager + + + + org.eclipse.jetty + jetty-server + ${project.version} + + + com.google.gcloud + gcloud-java-datastore + ${gcloud.version} + + + org.eclipse.jetty + jetty-webapp + ${project.version} + test + + + org.eclipse.jetty.websocket + websocket-servlet + ${project.version} + test + + + org.eclipse.jetty.websocket + websocket-server + ${project.version} + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + + ${project.groupId}.session + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + manifest + + + + org.eclipse.jetty.gcloud.session.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"; + + + + + + + + + diff --git a/jetty-gcloud/gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml b/jetty-gcloud/gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml new file mode 100644 index 00000000000..75caeb1f9ef --- /dev/null +++ b/jetty-gcloud/gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + / + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-gcloud/gcloud-session-manager/src/main/config/modules/gcloud-sessions.mod b/jetty-gcloud/gcloud-session-manager/src/main/config/modules/gcloud-sessions.mod new file mode 100644 index 00000000000..df1a13e6552 --- /dev/null +++ b/jetty-gcloud/gcloud-session-manager/src/main/config/modules/gcloud-sessions.mod @@ -0,0 +1,75 @@ +# +# Jetty GCloudDatastore Session Manager module +# + +[depend] +annotations +webapp + +[files] + +maven://com.google.gcloud/gcloud-java-datastore/0.0.7|lib/gcloud/gcloud-java-datastore-0.0.7.jar +maven://com.google.gcloud/gcloud-java-core/0.0.7|lib/gcloud/gcloud-java-core-0.0.7.jar +maven://com.google.auth/google-auth-library-credentials/0.1.0|lib/gcloud/google-auth-library-credentials-0.1.0.jar +maven://com.google.auth/google-auth-library-oauth2-http/0.1.0|lib/gcloud/google-auth-library-oauth2-http-0.1.0.jar +maven://com.google.http-client/google-http-client-jackson2/1.19.0|lib/gcloud/google-http-client-jackson2-1.19.0.jar +maven://com.fasterxml.jackson.core/jackson-core/2.1.3|lib/gcloud/jackson-core-2.1.3.jar +maven://com.google.http-client/google-http-client/1.20.0|lib/gcloud/google-http-client-1.20.0.jar +maven://com.google.code.findbugs/jsr305/1.3.9|lib/gcloud/jsr305-1.3.9.jar +maven://org.apache.httpcomponents/httpclient/4.0.1|lib/gcloud/httpclient-4.0.1.jar +maven://org.apache.httpcomponents/httpcore/4.0.1|lib/gcloud/httpcore-4.0.1.jar +maven://commons-logging/commons-logging/1.1.1|lib/gcloud/commons-logging-1.1.1.jar +maven://commons-codec/commons-codec/1.3|lib/gcloud/commons-codec-1.3.jar +maven://com.google.oauth-client/google-oauth-client/1.20.0|lib/gcloud//google-oauth-client-1.20.0.jar +maven://com.google.guava/guava/18.0|lib/gcloud/guava-18.0.jar +maven://com.google.api-client/google-api-client-appengine/1.20.0|lib/gcloud/google-api-client-appengine-1.20.0.jar +maven://com.google.oauth-client/google-oauth-client-appengine/1.20.0|lib/gcloud/google-oauth-client-appengine-1.20.0.jar +maven://com.google.oauth-client/google-oauth-client-servlet/1.20.0|lib/gcloud/google-oauth-client-servlet-1.20.0.jar +maven://com.google.http-client/google-http-client-jdo/1.20.0|lib/gcloud/google-http-client-jdo-1.20.0.jar +maven://com.google.api-client/google-api-client-servlet/1.20.0|lib/gcloud/google-api-client-servlet-1.20.0.jar +maven://javax.jdo/jdo2-api/2.3-eb|lib/gcloud/jdo2-api-2.3-eb.jar +maven://javax.transaction/transaction-api/1.1|lib/gcloud/transaction-api-1.1.jar +maven://com.google.http-client/google-http-client-appengine/1.20.0|lib/gcloud/google-http-client-appengine-1.20.0.jar +maven://com.google.http-client/google-http-client-jackson/1.20.0|lib/gcloud/google-http-client-jackson-1.20.0.jar +maven://org.codehaus.jackson/jackson-core-asl/1.9.11|lib/gcloud/jackson-core-asl-1.9.11.jar +maven://joda-time/joda-time/2.8.2|lib/gcloud/joda-time-2.8.2.jar +maven://org.json/json/20090211|lib/gcloud/json-20090211.jar +maven://com.google.apis/google-api-services-datastore-protobuf/v1beta2-rev1-2.1.2|lib/gcloud/google-api-services-datastore-protobuf-v1beta2-rev1-2.1.2.jar +maven://com.google.protobuf/protobuf-java/2.5.0|lib/gcloud/protobuf-java-2.5.0.jar +maven://com.google.http-client/google-http-client-protobuf/1.15.0-rc|lib/gcloud/google-http-client-protobuf-1.15.0-rc.jar +maven://com.google.api-client/google-api-client/1.15.0-rc|lib/gcloud/google-api-client-1.15.0-rc.jar +maven://com.google.apis/google-api-services-datastore/v1beta2-rev23-1.19.0|lib/gcloud/google-api-services-datastore-v1beta2-rev23-1.19.0.jar + +[lib] +lib/gcloud-session-manager-${jetty.version}.jar +lib/gcloud/*.jar + +[xml] +etc/jetty-gcloud-sessions.xml + +[license] +GCloudDatastore is an open source project hosted on Github and released under the Apache 2.0 license. +https://github.com/GoogleCloudPlatform/gcloud-java +http://www.apache.org/licenses/LICENSE-2.0.html + +[ini-template] +## GCloudDatastore Session config + +## Unique identifier for this node in the cluster +# jetty.gcloudSession.workerName=node1 + +## Name of properties files containing gcloud config +#jetty.gcloudSession.configFilet=etc/gcloud.props + +##Alternative to properties file, individual properties +## the gcloud projectId +#jetty.gcloudSession.projectId= + +## the p12 file associated with the project +#jetty.gcloudSession.p12File= + +## the serviceAccount for the Datastore +#jetty.gcloudSession.serviceAccount= + +## the password (can be obfuscated) +#jetty.gcloudSession.password= diff --git a/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudConfiguration.java b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudConfiguration.java new file mode 100644 index 00000000000..c1a95961d47 --- /dev/null +++ b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudConfiguration.java @@ -0,0 +1,184 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.util.Properties; + +import org.eclipse.jetty.util.security.Password; + +import com.google.gcloud.AuthCredentials; +import com.google.gcloud.datastore.DatastoreOptions; + + + +/** + * GCloudConfiguration + * + * + */ +public class GCloudConfiguration +{ + public static final String PROJECT_ID = "projectId"; + public static final String P12 = "p12"; + public static final String PASSWORD = "password"; + public static final String SERVICE_ACCOUNT = "serviceAccount"; + + private String _projectId; + private File _p12File; + private String _serviceAccount; + private String _password; + private AuthCredentials _authCredentials; + private DatastoreOptions _options; + + /** + * Generate a configuration from a properties file + * + * @param propsFile + * @return + * @throws IOException + */ + public static GCloudConfiguration fromFile(String propsFile) + throws IOException + { + if (propsFile == null) + throw new IllegalArgumentException ("Null properties file"); + + File f = new File(propsFile); + if (!f.exists()) + throw new IllegalArgumentException("No such file "+f.getAbsolutePath()); + Properties props = new Properties(); + try (FileInputStream is=new FileInputStream(f)) + { + props.load(is); + } + + GCloudConfiguration config = new GCloudConfiguration(); + config.setProjectId(props.getProperty(PROJECT_ID)); + config.setP12File(props.getProperty(P12)); + config.setPassword(props.getProperty(PASSWORD)); + config.setServiceAccount(props.getProperty(SERVICE_ACCOUNT)); + return config; + } + + + + public String getProjectId() + { + return _projectId; + } + + public File getP12File() + { + return _p12File; + } + + public String getServiceAccount() + { + return _serviceAccount; + } + + + public void setProjectId(String projectId) + { + checkForModification(); + _projectId = projectId; + } + + public void setP12File (String file) + { + checkForModification(); + _p12File = new File(file); + } + + + public void setServiceAccount (String serviceAccount) + { + checkForModification(); + _serviceAccount = serviceAccount; + } + + + public void setPassword (String pwd) + { + checkForModification(); + Password p = new Password(pwd); + _password = p.toString(); + } + + + public DatastoreOptions getDatastoreOptions () + throws Exception + { + if (_options == null) + { + _options = DatastoreOptions.builder() + .projectId(_projectId) + .authCredentials(getAuthCredentials()) + .build(); + } + return _options; + } + + /** + * @return + * @throws Exception + */ + public AuthCredentials getAuthCredentials() + throws Exception + { + if (_authCredentials == null) + { + if (_password == null) + throw new IllegalStateException("No password"); + if (_projectId == null) + throw new IllegalStateException("No project id"); + + if (_projectId == null) + throw new IllegalStateException("No project id"); + + if (_p12File == null || !_p12File.exists()) + throw new IllegalStateException("No p12 file: "+(_p12File==null?"null":_p12File.getAbsolutePath())); + + if (_serviceAccount == null) + throw new IllegalStateException("No service account"); + + char[] pwdChars = _password.toCharArray(); + KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load(new FileInputStream(getP12File()), pwdChars); + PrivateKey privateKey = (PrivateKey) keystore.getKey("privatekey", pwdChars); + _authCredentials = AuthCredentials.createFor(getServiceAccount(), privateKey); + } + return _authCredentials; + } + + /** + * @throws IllegalStateException + */ + protected void checkForModification () throws IllegalStateException + { + if (_authCredentials != null || _options != null) + throw new IllegalStateException("Cannot modify auth configuration after datastore initialized"); + } +} diff --git a/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java new file mode 100644 index 00000000000..e8a32a5e3b7 --- /dev/null +++ b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java @@ -0,0 +1,323 @@ +// +// ======================================================================== +// 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.gcloud.session; + +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 com.google.gcloud.datastore.Datastore; +import com.google.gcloud.datastore.DatastoreFactory; +import com.google.gcloud.datastore.Entity; +import com.google.gcloud.datastore.Key; +import com.google.gcloud.datastore.KeyFactory; + + + +/** + * GCloudSessionIdManager + * + * + * + */ +public class GCloudSessionIdManager extends AbstractSessionIdManager +{ + private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); + public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2; + public static final String KIND = "GCloudSessionId"; + private Server _server; + private Datastore _datastore; + private KeyFactory _keyFactory; + private GCloudConfiguration _config; + + + + + /** + * @param server + */ + public GCloudSessionIdManager(Server server) + { + super(); + _server = server; + } + + /** + * @param server + * @param random + */ + public GCloudSessionIdManager(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 + { + if (_config == null) + throw new IllegalStateException("No gcloud configuration specified"); + + + _datastore = DatastoreFactory.instance().get(_config.getDatastoreOptions()); + _keyFactory = _datastore.newKeyFactory().kind(KIND); + + 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 - this should also tickle the idle expiration timer on the sessionid entry + //keeping it valid + 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 store + insert (((AbstractSession)session).getClusterId()); + } + + + + + public GCloudConfiguration getConfig() + { + return _config; + } + + public void setConfig(GCloudConfiguration config) + { + _config = config; + } + + + + /** + * 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 _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 * DEFAULT_SCAVENGE_SEC; //10mins + protected boolean _ownScheduler; + + private Datastore _datastore; + private KeyFactory _keyFactory; + + + private SessionEntityConverter _converter; + + + private int _maxResults = DEFAULT_MAX_QUERY_RESULTS; + + + /** + * 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); + } + } + } + + /** + * SessionEntityConverter + * + * + */ + public class SessionEntityConverter + { + public final String CLUSTERID = "clusterId"; + public final String CONTEXTPATH = "contextPath"; + public final String VHOST = "vhost"; + public final String ACCESSED = "accessed"; + public final String LASTACCESSED = "lastAccessed"; + public final String CREATETIME = "createTime"; + public final String COOKIESETTIME = "cookieSetTime"; + public final String LASTNODE = "lastNode"; + public final String EXPIRY = "expiry"; + public final String MAXINACTIVE = "maxInactive"; + public final String ATTRIBUTES = "attributes"; + + + + public Entity entityFromSession (Session session, Key key) throws Exception + { + if (session == null) + return null; + + Entity entity = null; + + //serialize the attribute map + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(session.getAttributeMap()); + oos.flush(); + + //turn a session into an entity + entity = Entity.builder(key) + .set(CLUSTERID, session.getId()) + .set(CONTEXTPATH, session.getContextPath()) + .set(VHOST, session.getVHost()) + .set(ACCESSED, session.getAccessed()) + .set(LASTACCESSED, session.getLastAccessedTime()) + .set(CREATETIME, session.getCreationTime()) + .set(COOKIESETTIME, session.getCookieSetTime()) + .set(LASTNODE,session.getLastNode()) + .set(EXPIRY, session.getExpiry()) + .set(MAXINACTIVE, session.getMaxInactiveInterval()) + .set(ATTRIBUTES, Blob.copyFrom(baos.toByteArray())).build(); + + return entity; + } + + public Session sessionFromEntity (Entity entity) throws Exception + { + if (entity == null) + return null; + + final AtomicReference reference = new AtomicReference(); + final AtomicReference exception = new AtomicReference(); + Runnable load = new Runnable() + { + public void run () + { + try + { + //turn an entity into a Session + String clusterId = entity.getString(CLUSTERID); + String contextPath = entity.getString(CONTEXTPATH); + String vhost = entity.getString(VHOST); + long accessed = entity.getLong(ACCESSED); + long lastAccessed = entity.getLong(LASTACCESSED); + long createTime = entity.getLong(CREATETIME); + long cookieSetTime = entity.getLong(COOKIESETTIME); + String lastNode = entity.getString(LASTNODE); + long expiry = entity.getLong(EXPIRY); + long maxInactive = entity.getLong(MAXINACTIVE); + Blob blob = (Blob) entity.getBlob(ATTRIBUTES); + + Session session = new Session (clusterId, createTime, accessed, maxInactive); + session.setLastNode(lastNode); + session.setContextPath(contextPath); + session.setVHost(vhost); + session.setCookieSetTime(cookieSetTime); + session.setLastAccessedTime(lastAccessed); + session.setLastNode(lastNode); + session.setExpiry(expiry); + try (ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(blob.asInputStream())) + { + Object o = ois.readObject(); + session.addAttributes((Map)o); + } + reference.set(session); + } + catch (Exception e) + { + exception.set(e); + } + } + }; + + if (_context==null) + load.run(); + else + _context.getContextHandler().handle(null,load); + + + if (exception.get() != null) + { + exception.get().printStackTrace(); + throw exception.get(); + } + + return reference.get(); + } + } + + /* + * 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 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(); + attributes = (HashMap)in.readObject(); + } + + } + + + + /** + * 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 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; + + + /** + * If dirty, session needs to be (re)sent to cluster + */ + protected boolean _dirty=false; + + + + + /** + * Any virtual hosts for the context with which this session is associated + */ + private String _vhost; + + + /** + * Count of how many threads are active in this session + */ + private AtomicInteger _activeThreads = new AtomicInteger(0); + + + + + /** + * A new session. + * + * @param request + */ + protected Session (HttpServletRequest request) + { + super(GCloudSessionManager.this,request); + long maxInterval = getMaxInactiveInterval(); + _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L)); + _lastNode = getSessionIdManager().getWorkerName(); + setVHost(GCloudSessionManager.getVirtualHost(_context)); + setContextPath(GCloudSessionManager.getContextPath(_context)); + _activeThreads.incrementAndGet(); //access will not be called on a freshly created session so increment here + } + + + + + /** + * A restored session. + * + * @param sessionId + * @param created + * @param accessed + * @param maxInterval + */ + protected Session (String sessionId, long created, long accessed, long maxInterval) + { + super(GCloudSessionManager.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 + { + + long now = System.currentTimeMillis(); + //lock so that no other thread can call access or complete until the first one has refreshed the session object if necessary + _lock.lock(); + //a request thread is entering + if (_activeThreads.incrementAndGet() == 1) + { + //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(); + + //lock so that no other thread that might be calling access can proceed until this complete is done + _lock.lock(); + + try + { + //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()) + { + //if session still valid && its dirty or stale or never been synced, write it to the cluster + //otherwise, we just keep the updated last access time in memory + if (_dirty || getLastSyncTime() == 0 || isStale(System.currentTimeMillis())) + { + willPassivate(); + save(this); + didActivate(); + } + } + } + catch (Exception e) + { + LOG.warn("Problem saving session({})",getId(), e); + } + finally + { + _dirty = false; + } + } + } + finally + { + _lock.unlock(); + } + } + + /** Test if the session is stale + * @param atTime + * @return + */ + protected boolean isStale (long atTime) + { + return (getStaleIntervalSec() > 0) && (atTime - getLastSyncTime() >= (getStaleIntervalSec()*1000L)); + } + + + /** Test if the session is dirty + * @return + */ + protected boolean isDirty () + { + return _dirty; + } + + /** + * Expire the session. + * + * @see org.eclipse.jetty.server.session.AbstractSession#timeout() + */ + @Override + protected void timeout() + { + if (LOG.isDebugEnabled()) LOG.debug("Timing out session {}", getId()); + 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 () + throws Exception + { + //get fresh copy from the cluster + Session fresh = load(makeKey(getClusterId(), _context)); + + //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 boolean isExpiredAt (long time) + { + if (_expiryTime <= 0) + return false; //never expires + + return (_expiryTime <= time); + } + + public void swapId (String newId, String newNodeId) + { + //TODO probably synchronize rather than use the access/complete lock? + _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 + + _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"); + + GCloudConfiguration config = ((GCloudSessionIdManager)_sessionIdManager).getConfig(); + if (config == null) + throw new IllegalStateException("No gcloud configuration"); + + + _datastore = DatastoreFactory.instance().get(config.getDatastoreOptions()); + _keyFactory = _datastore.newKeyFactory().kind(KIND); + _converter = new SessionEntityConverter(); + _sessions = new ConcurrentHashMap(); + + //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"); + + setScavengeIntervalSec(getScavengeIntervalSec()); + + 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 () + { + try + { + //scavenge in the database every so often + scavengeGCloudDataStore(); + } + catch (Exception e) + { + LOG.warn("Problem scavenging", e); + } + } + + + + protected void scavengeGCloudDataStore() + throws Exception + { + + //query the datastore for sessions that have expired + long now = System.currentTimeMillis(); + + //give a bit of leeway so we don't immediately something that has only just expired a nanosecond ago + now = now - (_scavengeIntervalMs/2); + + if (LOG.isDebugEnabled()) + LOG.debug("Scavenging for sessions expired before "+now); + + + GqlQuery.Builder builder = Query.gqlQueryBuilder(ResultType.ENTITY, "select * from "+KIND+" where expiry < @1 limit "+_maxResults); + builder.allowLiteral(true); + builder.addBinding(now); + Query query = builder.build(); + QueryResults results = _datastore.run(query); + + while (results.hasNext()) + { + Entity sessionEntity = results.next(); + scavengeSession(sessionEntity); + } + + } + + /** + * Scavenge a session that has expired + * @param e + * @throws Exception + */ + protected void scavengeSession (Entity e) + throws Exception + { + long now = System.currentTimeMillis(); + Session session = _converter.sessionFromEntity(e); + if (session == null) + return; + + if (LOG.isDebugEnabled()) + LOG.debug("Scavenging session: {}",session.getId()); + //if the session isn't in memory already, put it there so we can do a normal timeout call + Session memSession = _sessions.putIfAbsent(session.getId(), session); + if (memSession == null) + { + memSession = session; + } + + //final check + if (memSession.isExpiredAt(now)) + { + if (LOG.isDebugEnabled()) LOG.debug("Session {} is definitely expired", memSession.getId()); + memSession.timeout(); + } + } + + public long getScavengeIntervalSec () + { + return _scavengeIntervalMs/1000; + } + + + + /** + * Set the interval between runs of the scavenger. It should not be run too + * often. + * + * + * @param sec + */ + public void setScavengeIntervalSec (long sec) + { + + long old_period=_scavengeIntervalMs; + long period=sec*1000L; + + _scavengeIntervalMs=period; + + if (_scavengeIntervalMs > 0) + { + //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"); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("Scavenging disabled"); + } + + + + synchronized (this) + { + if (_scheduler != null && (period!=old_period || _task==null)) + { + //clean up any previously scheduled scavenger + if (_task!=null) + _task.cancel(); + + //start a new one + if (_scavengeIntervalMs > 0) + { + if (_scavenger == null) + _scavenger = new Scavenger(); + + _task = _scheduler.schedule(_scavenger,_scavengeIntervalMs,TimeUnit.MILLISECONDS); + } + } + } + } + + + public long getStaleIntervalSec() + { + return _staleIntervalSec; + } + + + public void setStaleIntervalSec(long staleIntervalSec) + { + _staleIntervalSec = staleIntervalSec; + } + + + public int getMaxResults() + { + return _maxResults; + } + + + public void setMaxResults(int maxResults) + { + if (_maxResults <= 0) + _maxResults = DEFAULT_MAX_QUERY_RESULTS; + else + _maxResults = maxResults; + } + + + /** + * 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(((GCloudSessionManager.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 datastore + if (memSession == null) + { + if (LOG.isDebugEnabled()) + LOG.debug("getSession({}): loading session data from cluster", idInCluster); + + session = load(makeKey(idInCluster, _context)); + if (session != null) + { + //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) + ((GCloudSessionIdManager)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 + { + Set keys = new HashSet(_sessions.keySet()); + for (String key:keys) + { + Session session = _sessions.remove(key); //take the session out of the session list + //If the session is dirty, then write it to the cluster. + //If the session is simply stale do NOT write it to the cluster, as some other node + //may have started managing that session - this means that the last accessed/expiry time + //will not be updated, meaning it may look like it can expire sooner than it should. + try + { + if (session.isDirty()) + { + if (LOG.isDebugEnabled()) + LOG.debug("Saving dirty session {} before exiting ", session.getId()); + save(session); + } + } + catch (Exception e) + { + LOG.warn(e); + } + } + } + + + @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 (Key key) + throws Exception + { + if (_datastore == null) + throw new IllegalStateException("No DataStore"); + + if (LOG.isDebugEnabled()) LOG.debug("Loading session {} from DataStore", key); + + Entity entity = _datastore.get(key); + if (entity == null) + { + if (LOG.isDebugEnabled()) LOG.debug("No session {} in DataStore ",key); + return null; + } + else + { + Session session = _converter.sessionFromEntity(entity); + session.setLastSyncTime(System.currentTimeMillis()); + return session; + } + } + + + + /** + * Save or update the session to the cluster cache + * + * @param session + * @throws Exception + */ + protected void save (GCloudSessionManager.Session session) + throws Exception + { + if (_datastore == null) + throw new IllegalStateException("No DataStore"); + + if (LOG.isDebugEnabled()) LOG.debug("Writing session {} to DataStore", session.getId()); + + Entity entity = _converter.entityFromSession(session, makeKey(session, _context)); + _datastore.put(entity); + session.setLastSyncTime(System.currentTimeMillis()); + } + + + + /** + * Remove the session from the cluster cache. + * + * @param session + */ + protected void delete (GCloudSessionManager.Session session) + { + if (_datastore == null) + throw new IllegalStateException("No DataStore"); + if (LOG.isDebugEnabled()) LOG.debug("Removing session {} from DataStore", session.getId()); + _datastore.delete(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: + *
    + *
  1. the id
  2. + *
  3. the context path
  4. + *
  5. the virtual hosts
  6. + *
+ * + *TODO consider the difference between getClusterId and getId + * @param session + * @return + */ + private Key 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: + *
    + *
  1. the id
  2. + *
  3. the context path
  4. + *
  5. the virtual hosts
  6. + *
+ * + *TODO consider the difference between getClusterId and getId + * @param session + * @return + */ + private Key makeKey (String id, Context context) + { + String key = getContextPath(context); + key = key + "_" + getVirtualHost(context); + key = key+"_"+id; + return _keyFactory.newKey(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-gcloud/gcloud-session-manager/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTester.java b/jetty-gcloud/gcloud-session-manager/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTester.java new file mode 100644 index 00000000000..fe596e24ba4 --- /dev/null +++ b/jetty-gcloud/gcloud-session-manager/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTester.java @@ -0,0 +1,75 @@ +// +// ======================================================================== +// 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.gcloud.session; + + + + +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker; +import org.eclipse.jetty.server.session.SessionHandler; +import org.eclipse.jetty.webapp.WebAppContext; + +public class GCloudSessionTester +{ + public static void main( String[] args ) throws Exception + { + if (args.length < 4) + System.err.println("Usage: GCloudSessionTester projectid p12file password serviceaccount"); + + System.setProperty("org.eclipse.jetty.server.session.LEVEL", "DEBUG"); + + Server server = new Server(8080); + HashLoginService loginService = new HashLoginService(); + loginService.setName( "Test Realm" ); + loginService.setConfig( "../../jetty-distribution/target/distribution/demo-base/resources/realm.properties" ); + server.addBean( loginService ); + + GCloudConfiguration config = new GCloudConfiguration(); + config.setProjectId(args[0]); + config.setP12File(args[1]); + config.setPassword(args[2]); + config.setServiceAccount(args[3]); + + GCloudSessionIdManager idmgr = new GCloudSessionIdManager(server); + idmgr.setConfig(config); + idmgr.setWorkerName("w1"); + server.setSessionIdManager(idmgr); + + + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/"); + webapp.setWar("../../jetty-distribution/target/distribution/demo-base/webapps/test.war"); + webapp.addAliasCheck(new AllowSymLinkAliasChecker()); + GCloudSessionManager mgr = new GCloudSessionManager(); + mgr.setSessionIdManager(idmgr); + webapp.setSessionHandler(new SessionHandler(mgr)); + + // A WebAppContext is a ContextHandler as well so it needs to be set to + // the server so it is aware of where to send the appropriate requests. + server.setHandler(webapp); + + // Start things up! + server.start(); + + + server.join(); + } +} diff --git a/jetty-gcloud/pom.xml b/jetty-gcloud/pom.xml new file mode 100644 index 00000000000..af97322fe73 --- /dev/null +++ b/jetty-gcloud/pom.xml @@ -0,0 +1,23 @@ + + + + jetty-project + org.eclipse.jetty + 9.3.4-SNAPSHOT + + + 4.0.0 + org.eclipse.jetty.gcloud + gcloud-parent + pom + Jetty :: GCloud + + + 0.0.8 + + + + gcloud-session-manager + + + diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java index 42b7086d4cc..1ae25d8a668 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java @@ -73,6 +73,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope private static final Logger LOG = Log.getLogger(ServletHolder.class); private int _initOrder = -1; private boolean _initOnStartup=false; + private boolean _initialized = false; private Map _roleMap; private String _forcedPath; private String _runAsRole; @@ -81,7 +82,6 @@ public class ServletHolder extends Holder implements UserIdentity.Scope private ServletRegistration.Dynamic _registration; private JspContainer _jspContainer; - private transient Servlet _servlet; private transient Config _config; private transient long _unavailable; @@ -396,21 +396,24 @@ public class ServletHolder extends Holder implements UserIdentity.Scope public void initialize () throws Exception { - super.initialize(); - if (_extInstance || _initOnStartup) - { - try + if(!_initialized){ + super.initialize(); + if (_extInstance || _initOnStartup) { - initServlet(); - } - catch(Exception e) - { - if (_servletHandler.isStartWithUnavailable()) - LOG.ignore(e); - else - throw e; + try + { + initServlet(); + } + catch(Exception e) + { + if (_servletHandler.isStartWithUnavailable()) + LOG.ignore(e); + else + throw e; + } } } + _initialized = true; } @@ -443,6 +446,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope _servlet=null; _config=null; + _initialized = false; } /* ------------------------------------------------------------ */ diff --git a/pom.xml b/pom.xml index 6fbae2cb702..6ca00ac28e2 100644 --- a/pom.xml +++ b/pom.xml @@ -530,6 +530,7 @@ jetty-rewrite jetty-nosql jetty-infinispan + jetty-gcloud tests examples jetty-quickstart diff --git a/tests/test-sessions/pom.xml b/tests/test-sessions/pom.xml index b2e8e22767c..23fed9a0b4b 100644 --- a/tests/test-sessions/pom.xml +++ b/tests/test-sessions/pom.xml @@ -35,5 +35,6 @@ test-jdbc-sessions test-mongodb-sessions test-infinispan-sessions + test-gcloud-sessions diff --git a/tests/test-sessions/test-gcloud-sessions/pom.xml b/tests/test-sessions/test-gcloud-sessions/pom.xml new file mode 100644 index 00000000000..1c487b7f3bc --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/pom.xml @@ -0,0 +1,110 @@ + + + + 4.0.0 + + org.eclipse.jetty.tests + test-sessions-parent + 9.3.4-SNAPSHOT + + test-gcloud-sessions + Jetty Tests :: Sessions :: GCloud + http://www.eclipse.org/jetty + + ${project.groupId}.sessions.gcloud + + + + + org.apache.maven.plugins + maven-deploy-plugin + + + true + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + + org.eclipse.jetty + jetty-server + ${project.version} + + + org.eclipse.jetty + jetty-webapp + ${project.version} + + + org.eclipse.jetty + jetty-client + ${project.version} + + + org.eclipse.jetty.tests + test-sessions-common + ${project.version} + + + org.eclipse.jetty.gcloud + gcloud-session-manager + ${project.version} + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + + gcloud + + + gcloud.enabled + true + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + jetty9-work + 8088 + + + + + + + + + diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ClientCrossContextSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ClientCrossContextSessionTest.java new file mode 100644 index 00000000000..5d05a3f698e --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ClientCrossContextSessionTest.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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractClientCrossContextSessionTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * ClientCrossContextSessionTest + * + * + */ +public class ClientCrossContextSessionTest extends AbstractClientCrossContextSessionTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _testSupport.setUp(); + } + + @AfterClass + public static void teardown () throws Exception + { + _testSupport.tearDown(); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractClientCrossContextSessionTest#createServer(int) + */ + @Override + public AbstractTestServer createServer(int port) + { + return new GCloudTestServer(port, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testCrossContextDispatch() throws Exception + { + super.testCrossContextDispatch(); + } + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ForwardedSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ForwardedSessionTest.java new file mode 100644 index 00000000000..20109ae51fe --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ForwardedSessionTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractForwardedSessionTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * ForwardedSessionTest + * + * + */ +public class ForwardedSessionTest extends AbstractForwardedSessionTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _testSupport.setUp(); + } + + @AfterClass + public static void teardown () throws Exception + { + _testSupport.tearDown(); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractForwardedSessionTest#createServer(int) + */ + @Override + public AbstractTestServer createServer(int port) + { + return new GCloudTestServer(port, _testSupport.getConfiguration()); + } + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTestSupport.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTestSupport.java new file mode 100644 index 00000000000..adfdf9b7a59 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTestSupport.java @@ -0,0 +1,372 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + + +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.resource.JarResource; +import org.eclipse.jetty.util.resource.Resource; + +import com.google.api.client.util.Strings; +import com.google.gcloud.datastore.Key; +import com.google.gcloud.datastore.Datastore; +import com.google.gcloud.datastore.DatastoreFactory; +import com.google.gcloud.datastore.DatastoreOptions; +import com.google.gcloud.datastore.Entity; +import com.google.gcloud.datastore.GqlQuery; +import com.google.gcloud.datastore.ProjectionEntity; +import com.google.gcloud.datastore.Query; +import com.google.gcloud.datastore.Query.ResultType; +import com.google.gcloud.datastore.QueryResults; +import com.google.gcloud.datastore.StructuredQuery; +import com.google.gcloud.datastore.StructuredQuery.Projection; + +/** + * GCloudSessionTestSupport + * + * + */ +public class GCloudSessionTestSupport +{ + + /** + * GCloudTestConfiguration + * + * Specialization of GCloudConfiguration for gcd test environment + * + */ + public class GCloudTestConfiguration extends GCloudConfiguration + { + int _port; + + public GCloudTestConfiguration(String projectId, int port) + { + setProjectId(projectId); + _port = port; + } + + + @Override + public DatastoreOptions getDatastoreOptions() throws Exception + { + return DatastoreOptions.builder() + .projectId(_projectId) + .host("http://localhost:" + _port) + .build(); + } + } + + + private static class ProcessOutputReader implements Runnable + { + private InputStream _is; + private String _startupSentinel; + private BufferedReader _reader; + + public ProcessOutputReader (InputStream is, String startupSentinel) + throws Exception + { + _is = is; + _startupSentinel = startupSentinel; + _reader = new BufferedReader(new InputStreamReader(_is)); + if (!Strings.isNullOrEmpty(_startupSentinel)) + { + String line; + while ((line = _reader.readLine()) != (null) && !line.contains(_startupSentinel)) + { + //System.err.println(line); + } + } + } + + + public void run() + { + String line; + try + { + while ((line = _reader.readLine()) != (null)) + { + } + } + catch (IOException ignore) + { + /* ignore */ + } + finally + { + IO.close(_reader); + } + } + } + + + public static String DEFAULT_PROJECTID = "jetty9-work"; + public static int DEFAULT_PORT = 8088; + public static String DEFAULT_GCD_ZIP = "gcd-v1beta2-rev1-2.1.2b.zip"; + public static String DEFAULT_GCD_UNPACKED = "gcd-v1beta2-rev1-2.1.2b"; + public static String DEFAULT_DOWNLOAD_URL = "http://storage.googleapis.com/gcd/tools/"; + + String _projectId; + int _port; + File _datastoreDir; + File _gcdInstallDir; + File _gcdUnpackedDir; + Datastore _ds; + + public GCloudSessionTestSupport (String projectId, int port, File gcdInstallDir) + { + _projectId = projectId; + if (_projectId == null) + _projectId = DEFAULT_PROJECTID; + _port = port; + if (_port <= 0) + _port = DEFAULT_PORT; + + _gcdInstallDir = gcdInstallDir; + if (_gcdInstallDir == null) + _gcdInstallDir = new File (System.getProperty("java.io.tmpdir")); + } + + public GCloudSessionTestSupport () + { + this(null,0, null); + } + + public GCloudConfiguration getConfiguration () + { + return new GCloudTestConfiguration(_projectId, _port); + } + + + public void setUp() + throws Exception + { + downloadGCD(); + createDatastore(); + startDatastore(); + } + + + public void downloadGCD() + throws Exception + { + File zipFile = new File (_gcdInstallDir, DEFAULT_GCD_ZIP); + _gcdUnpackedDir = new File (_gcdInstallDir, DEFAULT_GCD_UNPACKED); + File gcdSh = new File (_gcdUnpackedDir, "gcd.sh"); + if (gcdSh.exists()) + return; + + + if (_gcdInstallDir.exists() && !zipFile.exists()) + { + //download it + ReadableByteChannel rbc = Channels.newChannel(new URL(DEFAULT_DOWNLOAD_URL+DEFAULT_GCD_ZIP).openStream()); + try (FileOutputStream fos = new FileOutputStream(zipFile)) + { + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + } + } + + if (zipFile.exists()) + { + //unpack it + Resource zipResource = JarResource.newJarResource(Resource.newResource(zipFile)); + zipResource.copyTo(_gcdInstallDir); + } + + System.err.println("GCD downloaded and unpacked"); + } + + + + public void createDatastore () + throws Exception + { + + _datastoreDir = Files.createTempDirectory("gcloud-sessions").toFile(); + _datastoreDir.deleteOnExit(); + + ProcessBuilder processBuilder = new ProcessBuilder(); + processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT); + processBuilder.directory(_datastoreDir); + if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows")) + { + processBuilder.command("cmd", "/C", new File(_gcdUnpackedDir, "gcd.cmd").getAbsolutePath(), "create", "-p", _projectId, _projectId); + processBuilder.redirectOutput(new File("NULL:")); + } + else + { + processBuilder.redirectOutput(new File("/tmp/run.out")); + processBuilder.command("bash", new File(_gcdUnpackedDir, "gcd.sh").getAbsolutePath(), "create", "-p",_projectId, _projectId); + } + + Process temp = processBuilder.start(); + System.err.println("Create outcome: "+temp.waitFor()); + } + + + public void startDatastore() + throws Exception + { + //start the datastore for the test + ProcessBuilder processBuilder = new ProcessBuilder(); + processBuilder.directory(_datastoreDir); + processBuilder.redirectErrorStream(true); + if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows")) + { + processBuilder.command("cmd", "/C", new File(_gcdUnpackedDir, "gcd.cmd").getAbsolutePath(), "start", "--testing", "--allow_remote_shutdown","--port="+String.valueOf(_port), _projectId); + } + else + { + processBuilder.command("bash", new File(_gcdUnpackedDir, "gcd.sh").getAbsolutePath(), "start", "--testing", "--allow_remote_shutdown", "--port="+String.valueOf(_port), _projectId); + } + + System.err.println("Starting datastore"); + Process temp = processBuilder.start(); + ProcessOutputReader reader = new ProcessOutputReader(temp.getInputStream(), "Dev App Server is now running"); + Thread readerThread = new Thread(reader, "GCD reader"); + readerThread.setDaemon(true); + readerThread.start(); + } + + public void stopDatastore() + throws Exception + { + //Send request to terminate test datastore + URL url = new URL("http", "localhost", _port, "/_ah/admin/quit"); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod("POST"); + con.setDoOutput(true); + con.setDoInput(true); + OutputStream out = con.getOutputStream(); + out.write("".getBytes()); + out.flush(); + InputStream in = con.getInputStream(); + while (in.read() != -1) + { + // consume input + + } + + System.err.println("Stop issued"); + } + + + public void clearDatastore() + { + org.eclipse.jetty.util.IO.delete(_datastoreDir); + } + + public void tearDown() + throws Exception + { + stopDatastore(); + clearDatastore(); + } + + public void ensureDatastore() + throws Exception + { + if (_ds == null) + _ds = DatastoreFactory.instance().get(getConfiguration().getDatastoreOptions()); + } + public void listSessions () throws Exception + { + ensureDatastore(); + GqlQuery.Builder builder = Query.gqlQueryBuilder(ResultType.ENTITY, "select * from "+GCloudSessionManager.KIND); + + Query query = builder.build(); + + QueryResults results = _ds.run(query); + assertNotNull(results); + System.err.println("SESSIONS::::::::"); + while (results.hasNext()) + { + + Entity e = results.next(); + System.err.println(e.getString("clusterId")+" expires at "+e.getLong("expiry")); + } + System.err.println("END OF SESSIONS::::::::"); + } + + public void assertSessions(int count) throws Exception + { + ensureDatastore(); + StructuredQuery keyOnlyProjectionQuery = Query.projectionEntityQueryBuilder() + .kind(GCloudSessionManager.KIND) + .projection(Projection.property("__key__")) + .limit(100) + .build(); + QueryResults results = _ds.run(keyOnlyProjectionQuery); + assertNotNull(results); + int actual = 0; + while (results.hasNext()) + { + results.next(); + ++actual; + } + assertEquals(count, actual); + } + + public void deleteSessions () throws Exception + { + ensureDatastore(); + StructuredQuery keyOnlyProjectionQuery = Query.projectionEntityQueryBuilder() + .kind(GCloudSessionManager.KIND) + .projection(Projection.property("__key__")) + .limit(100) + .build(); + QueryResults results = _ds.run(keyOnlyProjectionQuery); + if (results != null) + { + List keys = new ArrayList(); + + while (results.hasNext()) + { + ProjectionEntity pe = results.next(); + keys.add(pe.key()); + } + + _ds.delete(keys.toArray(new Key[keys.size()])); + } + + assertSessions(0); + } +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java new file mode 100644 index 00000000000..11f2125f680 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import org.eclipse.jetty.server.SessionIdManager; +import org.eclipse.jetty.server.SessionManager; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.eclipse.jetty.server.session.SessionHandler; + +import com.google.gcloud.datastore.Datastore; +import com.google.gcloud.datastore.DatastoreFactory; + +/** + * GCloudTestServer + * + * + */ +public class GCloudTestServer extends AbstractTestServer +{ + static int __workers=0; + public static int STALE_INTERVAL_SEC = 1; + + + + /** + * @param port + * @param maxInactivePeriod + * @param scavengePeriod + * @param sessionIdMgrConfig + */ + public GCloudTestServer(int port, int maxInactivePeriod, int scavengePeriod, GCloudConfiguration config) + { + super(port, maxInactivePeriod, scavengePeriod, config); + } + + /** + * @param port + * @param configuration + */ + public GCloudTestServer(int port, GCloudConfiguration configuration) + { + super(port, 30,10, configuration); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionIdManager(java.lang.Object) + */ + @Override + public SessionIdManager newSessionIdManager(Object config) + { + GCloudSessionIdManager idManager = new GCloudSessionIdManager(getServer()); + idManager.setWorkerName("w"+(__workers++)); + idManager.setConfig((GCloudConfiguration)config); + return idManager; + } + + /** + * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionManager() + */ + @Override + public SessionManager newSessionManager() + { + GCloudSessionManager sessionManager = new GCloudSessionManager(); + sessionManager.setSessionIdManager((GCloudSessionIdManager)_sessionIdManager); + sessionManager.setStaleIntervalSec(STALE_INTERVAL_SEC); + sessionManager.setScavengeIntervalSec(_scavengePeriod); + return sessionManager; + + } + + /** + * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionHandler(org.eclipse.jetty.server.SessionManager) + */ + @Override + public SessionHandler newSessionHandler(SessionManager sessionManager) + { + return new SessionHandler(sessionManager); + } + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ImmortalSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ImmortalSessionTest.java new file mode 100644 index 00000000000..84c3840d21e --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ImmortalSessionTest.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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractImmortalSessionTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * ImmortalSessionTest + * + * + */ +public class ImmortalSessionTest extends AbstractImmortalSessionTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _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 GCloudTestServer(port, port, scavengeMs, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testImmortalSession() throws Exception + { + super.testImmortalSession(); + } + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/InvalidationSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/InvalidationSessionTest.java new file mode 100644 index 00000000000..6cc875cb71f --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/InvalidationSessionTest.java @@ -0,0 +1,82 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractInvalidationSessionTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * InvalidationSessionTest + * + * + */ +public class InvalidationSessionTest extends AbstractInvalidationSessionTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _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 GCloudTestServer(port, _testSupport.getConfiguration()); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#pause() + */ + @Override + public void pause() + { + //This test moves around a session between 2 nodes. After it is invalidated on the 1st node, + //it will still be in the memory of the 2nd node. We need to wait until after the stale time + //has expired on node2 for it to reload the session and discover it has been deleted. + try + { + Thread.currentThread().sleep((2*GCloudTestServer.STALE_INTERVAL_SEC)*1000); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LastAccessTimeTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LastAccessTimeTest.java new file mode 100644 index 00000000000..d4f42fe78de --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LastAccessTimeTest.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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractLastAccessTimeTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * LastAccessTimeTest + * + * + */ +public class LastAccessTimeTest extends AbstractLastAccessTimeTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _testSupport.setUp(); + } + + @AfterClass + public static void teardown () throws Exception + { + _testSupport.tearDown(); + } + /** + * @see org.eclipse.jetty.server.session.AbstractLastAccessTimeTest#createServer(int, int, int) + */ + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + return new GCloudTestServer(port, max, scavenge, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testLastAccessTime() throws Exception + { + super.testLastAccessTime(); + } + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LocalSessionScavengingTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LocalSessionScavengingTest.java new file mode 100644 index 00000000000..ca39661a309 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/LocalSessionScavengingTest.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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractLocalSessionScavengingTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * LocalSessionScavengingTest + * + * + */ +public class LocalSessionScavengingTest extends AbstractLocalSessionScavengingTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _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 GCloudTestServer(port, max, scavenge, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testLocalSessionsScavenging() throws Exception + { + super.testLocalSessionsScavenging(); + } + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/NewSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/NewSessionTest.java new file mode 100644 index 00000000000..1dc256b74b1 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/NewSessionTest.java @@ -0,0 +1,74 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import java.io.File; + +import org.eclipse.jetty.server.session.AbstractNewSessionTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +/** + * NewSessionTest + * + * + */ +public class NewSessionTest extends AbstractNewSessionTest +{ + GCloudSessionTestSupport _testSupport; + + @Before + public void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _testSupport.setUp(); + } + + @After + public 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 GCloudTestServer(port, max, scavenge, _testSupport.getConfiguration()); + } + + @Test + public void testNewSession() throws Exception + { + super.testNewSession(); + } +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/OrphanedSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/OrphanedSessionTest.java new file mode 100644 index 00000000000..ffaaaec0da3 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/OrphanedSessionTest.java @@ -0,0 +1,75 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractOrphanedSessionTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * OrphanedSessionTest + * + * + */ +public class OrphanedSessionTest extends AbstractOrphanedSessionTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _testSupport.setUp(); + } + + @AfterClass + public static void teardown () throws Exception + { + _testSupport.tearDown(); + } + + + /** + * @see org.eclipse.jetty.server.session.AbstractOrphanedSessionTest#createServer(int, int, int) + */ + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + return new GCloudTestServer(port, max, scavenge, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testOrphanedSession() throws Exception + { + super.testOrphanedSession(); + _testSupport.assertSessions(0); + } + + + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ReentrantRequestSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ReentrantRequestSessionTest.java new file mode 100644 index 00000000000..e8216187a57 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ReentrantRequestSessionTest.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractReentrantRequestSessionTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * ReentrantRequestSessionTest + * + * + */ +public class ReentrantRequestSessionTest extends AbstractReentrantRequestSessionTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _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 GCloudTestServer(port, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testReentrantRequestSession() throws Exception + { + super.testReentrantRequestSession(); + } + + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/RemoveSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/RemoveSessionTest.java new file mode 100644 index 00000000000..5b7f99b3d93 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/RemoveSessionTest.java @@ -0,0 +1,75 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractRemoveSessionTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * RemoveSessionTest + * + * + */ +public class RemoveSessionTest extends AbstractRemoveSessionTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _testSupport.setUp(); + } + + @AfterClass + public static void teardown () throws Exception + { + _testSupport.tearDown(); + } + + + /** + * @see org.eclipse.jetty.server.session.AbstractRemoveSessionTest#createServer(int, int, int) + */ + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + return new GCloudTestServer(port, max, scavenge, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testRemoveSession() throws Exception + { + super.testRemoveSession(); + } + + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SameNodeLoadTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SameNodeLoadTest.java new file mode 100644 index 00000000000..e230f43d67a --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SameNodeLoadTest.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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractSameNodeLoadTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * SameNodeLoadTest + * + * + */ +public class SameNodeLoadTest extends AbstractSameNodeLoadTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _testSupport.setUp(); + } + + @AfterClass + public static void teardown () throws Exception + { + _testSupport.tearDown(); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractSameNodeLoadTest#createServer(int) + */ + @Override + public AbstractTestServer createServer(int port) + { + return new GCloudTestServer(port, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testLoad() throws Exception + { + super.testLoad(); + } + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ServerCrossContextSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ServerCrossContextSessionTest.java new file mode 100644 index 00000000000..59f366b6c73 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ServerCrossContextSessionTest.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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractServerCrossContextSessionTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * ServerCrossContextSessionTest + * + * + */ +public class ServerCrossContextSessionTest extends AbstractServerCrossContextSessionTest +{ + + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _testSupport.setUp(); + } + + @AfterClass + public static void teardown () throws Exception + { + _testSupport.tearDown(); + } + /** + * @see org.eclipse.jetty.server.session.AbstractServerCrossContextSessionTest#createServer(int) + */ + @Override + public AbstractTestServer createServer(int port) + { + return new GCloudTestServer(port, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testCrossContextDispatch() throws Exception + { + super.testCrossContextDispatch(); + } + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionExpiryTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionExpiryTest.java new file mode 100644 index 00000000000..785a2a19ec7 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionExpiryTest.java @@ -0,0 +1,102 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractSessionExpiryTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * SessionExpiryTest + * + * + */ +public class SessionExpiryTest extends AbstractSessionExpiryTest +{ + + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _testSupport.setUp(); + } + + @AfterClass + public static void teardown () throws Exception + { + _testSupport.tearDown(); + } + + + /** + * @see org.eclipse.jetty.server.session.AbstractSessionExpiryTest#createServer(int, int, int) + */ + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + return new GCloudTestServer(port, max, scavenge, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testSessionNotExpired() throws Exception + { + super.testSessionNotExpired(); + _testSupport.deleteSessions(); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractSessionExpiryTest#testSessionExpiry() + */ + @Test + @Override + public void testSessionExpiry() throws Exception + { + super.testSessionExpiry(); + _testSupport.assertSessions(0); + } + + @Override + public void verifySessionCreated(TestHttpSessionListener listener, String sessionId) + { + super.verifySessionCreated(listener, sessionId); + try{ _testSupport.listSessions(); _testSupport.assertSessions(1);}catch(Exception e) {e.printStackTrace();} + } + + @Override + public void verifySessionDestroyed(TestHttpSessionListener listener, String sessionId) + { + super.verifySessionDestroyed(listener, sessionId); + try{ _testSupport.listSessions(); _testSupport.assertSessions(0);}catch(Exception e) {e.printStackTrace();} + } + + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionInvalidateAndCreateTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionInvalidateAndCreateTest.java new file mode 100644 index 00000000000..b56a13ea547 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionInvalidateAndCreateTest.java @@ -0,0 +1,73 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * SessionInvalidateAndCreateTest + * + * + */ +public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest +{ + + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _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 GCloudTestServer(port, max, scavenge, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testSessionScavenge() throws Exception + { + super.testSessionScavenge(); + } + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionMigrationTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionMigrationTest.java new file mode 100644 index 00000000000..5ed5740746b --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionMigrationTest.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractSessionMigrationTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * SessionMigrationTest + * + * + */ +public class SessionMigrationTest extends AbstractSessionMigrationTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _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 GCloudTestServer(port, _testSupport.getConfiguration()); + } + + + @Test + @Override + public void testSessionMigration() throws Exception + { + super.testSessionMigration(); + } + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionRenewTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionRenewTest.java new file mode 100644 index 00000000000..c7b0c878bd5 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionRenewTest.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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractSessionRenewTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * SessionRenewTest + * + * + */ +public class SessionRenewTest extends AbstractSessionRenewTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _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 GCloudTestServer(port,max, scavenge, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testSessionRenewal() throws Exception + { + super.testSessionRenewal(); + } + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionValueSavingTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionValueSavingTest.java new file mode 100644 index 00000000000..54bf8ffac4d --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionValueSavingTest.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import org.eclipse.jetty.server.session.AbstractSessionValueSavingTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * SessionValueSavingTest + * + * + */ +public class SessionValueSavingTest extends AbstractSessionValueSavingTest +{ + + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _testSupport.setUp(); + } + + @AfterClass + public static void teardown () throws Exception + { + _testSupport.tearDown(); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractSessionValueSavingTest#createServer(int, int, int) + */ + @Override + public AbstractTestServer createServer(int port, int max, int scavenge) + { + return new GCloudTestServer(port, max, scavenge, _testSupport.getConfiguration()); + } + + @Test + @Override + public void testSessionValueSaving() throws Exception + { + super.testSessionValueSaving(); + } + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/StopSessionManagerPreserveSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/StopSessionManagerPreserveSessionTest.java new file mode 100644 index 00000000000..97b656fbd20 --- /dev/null +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/StopSessionManagerPreserveSessionTest.java @@ -0,0 +1,99 @@ +// +// ======================================================================== +// 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.gcloud.session; + +import static org.junit.Assert.fail; +import org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * StopSessionManagerPreserveSessionTest + * + * + */ +public class StopSessionManagerPreserveSessionTest extends AbstractStopSessionManagerPreserveSessionTest +{ + static GCloudSessionTestSupport _testSupport; + + @BeforeClass + public static void setup () throws Exception + { + String projectId = System.getProperty("test.projectId", null); + String port = System.getProperty("test.port","0"); + _testSupport = new GCloudSessionTestSupport(projectId, + Integer.parseInt(port), + null); + _testSupport.setUp(); + } + + @AfterClass + public static void teardown () throws Exception + { + _testSupport.tearDown(); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest#checkSessionPersisted(boolean) + */ + @Override + public void checkSessionPersisted(boolean expected) + { + try + { + _testSupport.assertSessions(1); + } + catch (Exception e) + { + fail(e.getMessage()); + } + + } + + /** + * @see org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest#createServer(int) + */ + @Override + public AbstractTestServer createServer(int port) + { + return new GCloudTestServer(port, _testSupport.getConfiguration()); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest#configureSessionManagement(org.eclipse.jetty.servlet.ServletContextHandler) + */ + @Override + public void configureSessionManagement(ServletContextHandler context) + { + + } + + @Test + @Override + public void testStopSessionManagerPreserveSession() throws Exception + { + super.testStopSessionManagerPreserveSession(); + } + + +} diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java index 0d1ac5ba923..8d2d660404c 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java @@ -83,19 +83,22 @@ public abstract class AbstractInvalidationSessionTest assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); - + + // Be sure the session is also present in node2 Request request2 = client.newRequest(urls[1] + "?action=increment"); request2.header("Cookie", sessionCookie); ContentResponse response2 = request2.send(); assertEquals(HttpServletResponse.SC_OK,response2.getStatus()); + // Invalidate on node1 Request request1 = client.newRequest(urls[0] + "?action=invalidate"); request1.header("Cookie", sessionCookie); response1 = request1.send(); assertEquals(HttpServletResponse.SC_OK, response1.getStatus()); + pause();