Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x
This commit is contained in:
commit
5f94f249a7
|
@ -179,3 +179,44 @@ In a clustered environment, there is a risk of the last access time of the sessi
|
||||||
This allows the possibility that a node may prematurely expire the session, even though it is in use by another node.
|
This allows the possibility that a node may prematurely expire the session, even though it is in use by another node.
|
||||||
Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`.
|
Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`.
|
||||||
____
|
____
|
||||||
|
|
||||||
|
==== Converting session format for jetty-9.4.13
|
||||||
|
|
||||||
|
From jetty-9.4.13 onwards, we have changed the format of the serialized session when using a remote cache (ie using hotrod).
|
||||||
|
Prior to release 9.4.13 we used the default Infinispan serialization, however this was not able to store sufficient information to allow jetty to properly deserialize session attributes in all circumstances.
|
||||||
|
See issue https://github.com/eclipse/jetty.project/issues/2919 for more background.
|
||||||
|
|
||||||
|
We have provided a conversion program which will convert any sessions stored in Infinispan to the new format.
|
||||||
|
____
|
||||||
|
[IMPORTANT]
|
||||||
|
We recommend that you backup your stored sessions before running the conversion program.
|
||||||
|
____
|
||||||
|
|
||||||
|
How to use the converter:
|
||||||
|
|
||||||
|
[source, screen, subs="{sub-order}"]
|
||||||
|
----
|
||||||
|
java -cp servlet-api-3.1.jar:jetty-util-9.4.13.jar:jetty-server-9.4.13.jar:infinispan-remote-9.1.0.Final.jar:jetty-infinispan-9.4.13.jar:[other classpath] org.eclipse.jetty.session.infinispan.InfinispanSessionLegacyConverter
|
||||||
|
|
||||||
|
Usage: InfinispanSessionLegacyConverter [-Dhost=127.0.0.1] [-Dverbose=true|false] <cache-name> [check]
|
||||||
|
----
|
||||||
|
|
||||||
|
The classpath::
|
||||||
|
Must contain the servlet-api, jetty-util, jetty-server, jetty-infinispan and infinispan-remote jars. If your sessions contain attributes that use application classes, you will also need to also put those classes onto the classpath. If your session has been authenticated, you may also need to include the jetty-security and jetty-http jars on the classpath.
|
||||||
|
Parameters::
|
||||||
|
When used with no arguments the usage message is printed. When used with the `cache-name` parameter the conversion is performed. When used with both `cache-name` and `check` parameters, sessions are checked for whether or not they are converted.
|
||||||
|
|
||||||
|
-Dhost::: you can optionally provide a system property with the address of your remote Infinispan server. Defaults to the localhost.
|
||||||
|
-Dverbose::: defaults to false. If true, prints more comprehensive stacktrace information about failures. Useful to diagnose why a session is not converted.
|
||||||
|
cache-name::: the name of the remote cache containing your sessions. This is mandatory.
|
||||||
|
check::: the optional check command will verify sessions have been converted. Use it _after_ doing the conversion.
|
||||||
|
|
||||||
|
To perform the conversion, run the InfinispanSessionLegacyConverter with just the `cache-name`, and optionally the `host` system property.
|
||||||
|
The following command will attempt to convert all sessions in the cached named `my-remote-cache` on the machine `myhost`, ensuring that application classes in the `/my/custom/classes` directory are on the classpath:
|
||||||
|
|
||||||
|
[source, screen, subs="{sub-order}"]
|
||||||
|
----
|
||||||
|
java -cp servlet-api-3.1.jar:jetty-util-9.4.13.jar:jetty-server-9.4.13.jar:infinispan-remote-9.1.0.Final.jar:jetty-infinispan-9.4.13.jar:/my/custom/classes org.eclipse.jetty.session.infinispan.InfinispanSessionLegacyConverter -Dhost=myhost my-remote-cache
|
||||||
|
----
|
||||||
|
|
||||||
|
If the converter fails to convert a session, an error message and stacktrace will be printed and the conversion will abort. The failed session should be untouched, however _it is prudent to take a backup of your cache before attempting the conversion_.
|
||||||
|
|
|
@ -22,9 +22,7 @@ package org.eclipse.jetty.gcloud.session;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.server.session.AbstractSessionDataStore;
|
import org.eclipse.jetty.server.session.AbstractSessionDataStore;
|
||||||
import org.eclipse.jetty.server.session.SessionContext;
|
import org.eclipse.jetty.server.session.SessionContext;
|
||||||
|
@ -324,9 +322,7 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
_lastSaved = lastSaved;
|
_lastSaved = lastSaved;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.lang.Object#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
@ -334,8 +330,6 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
_kind,_accessed,_attributes,_contextPath,_cookieSetTime,_createTime,_expiry,_id,_lastAccessed,_lastNode,_maxInactive,_vhost);
|
_kind,_accessed,_attributes,_contextPath,_cookieSetTime,_createTime,_expiry,_id,_lastAccessed,_lastNode,_maxInactive,_vhost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -437,9 +431,7 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
return _maxRetries;
|
return _maxRetries;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStart()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected void doStart() throws Exception
|
protected void doStart() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -466,9 +458,7 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
super.doStart();
|
super.doStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected void doStop() throws Exception
|
protected void doStop() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -501,12 +491,8 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#load(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load(String id) throws Exception
|
public SessionData doLoad(String id) throws Exception
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("Loading session {} from DataStore", id);
|
if (LOG.isDebugEnabled()) LOG.debug("Loading session {} from DataStore", id);
|
||||||
|
|
||||||
|
@ -529,9 +515,7 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#delete(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete(String id) throws Exception
|
public boolean delete(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -540,9 +524,7 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> doGetExpired(Set<String> candidates)
|
public Set<String> doGetExpired(Set<String> candidates)
|
||||||
{
|
{
|
||||||
|
@ -701,9 +683,6 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#exists(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String id) throws Exception
|
public boolean exists(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -766,6 +745,7 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check to see if the given time is in the past.
|
* Check to see if the given time is in the past.
|
||||||
*
|
*
|
||||||
|
@ -780,9 +760,7 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
return timestamp < System.currentTimeMillis();
|
return timestamp < System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(java.lang.String, org.eclipse.jetty.server.session.SessionData, long)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -874,6 +852,8 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a gcloud datastore Entity from SessionData
|
* Generate a gcloud datastore Entity from SessionData
|
||||||
* @param session the session data
|
* @param session the session data
|
||||||
|
@ -889,10 +869,10 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
Entity entity = null;
|
Entity entity = null;
|
||||||
|
|
||||||
//serialize the attribute map
|
//serialize the attribute map
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
ObjectOutputStream oos = new ObjectOutputStream(baos))
|
||||||
oos.writeObject(session.getAllAttributes());
|
{
|
||||||
oos.flush();
|
SessionData.serializeAttributes(session, oos);
|
||||||
|
|
||||||
//turn a session into an entity
|
//turn a session into an entity
|
||||||
entity = Entity.newBuilder(key)
|
entity = Entity.newBuilder(key)
|
||||||
|
@ -908,10 +888,9 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
.set(_model.getMaxInactive(), session.getMaxInactiveMs())
|
.set(_model.getMaxInactive(), session.getMaxInactiveMs())
|
||||||
.set(_model.getLastSaved(), session.getLastSaved())
|
.set(_model.getLastSaved(), session.getLastSaved())
|
||||||
.set(_model.getAttributes(), BlobValue.newBuilder(Blob.copyFrom(baos.toByteArray())).setExcludeFromIndexes(true).build()).build();
|
.set(_model.getAttributes(), BlobValue.newBuilder(Blob.copyFrom(baos.toByteArray())).setExcludeFromIndexes(true).build()).build();
|
||||||
|
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate SessionData from an Entity retrieved from gcloud datastore.
|
* Generate SessionData from an Entity retrieved from gcloud datastore.
|
||||||
|
@ -924,15 +903,6 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
final AtomicReference<SessionData> reference = new AtomicReference<>();
|
|
||||||
final AtomicReference<Exception> exception = new AtomicReference<>();
|
|
||||||
Runnable load = new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//turn an Entity into a Session
|
//turn an Entity into a Session
|
||||||
String id = entity.getString(_model.getId());
|
String id = entity.getString(_model.getId());
|
||||||
String contextPath = entity.getString(_model.getContextPath());
|
String contextPath = entity.getString(_model.getContextPath());
|
||||||
|
@ -967,36 +937,17 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
session.setExpiry(expiry);
|
session.setExpiry(expiry);
|
||||||
try (ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(blob.asInputStream()))
|
try (ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(blob.asInputStream()))
|
||||||
{
|
{
|
||||||
Object o = ois.readObject();
|
SessionData.deserializeAttributes(session, ois);
|
||||||
session.putAllAttributes((Map<String,Object>)o);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
throw new UnreadableSessionDataException (id, _context, e);
|
throw new UnreadableSessionDataException (id, _context, e);
|
||||||
}
|
}
|
||||||
reference.set(session);
|
return session;
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
exception.set(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//ensure this runs in the context classloader
|
|
||||||
_context.run(load);
|
|
||||||
|
|
||||||
if (exception.get() != null)
|
|
||||||
{
|
|
||||||
throw exception.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return reference.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
|
|
||||||
*/
|
|
||||||
@ManagedAttribute(value="does gcloud serialize session data", readonly=true)
|
@ManagedAttribute(value="does gcloud serialize session data", readonly=true)
|
||||||
@Override
|
@Override
|
||||||
public boolean isPassivating()
|
public boolean isPassivating()
|
||||||
|
@ -1005,14 +956,10 @@ public class GCloudSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s[namespace=%s,backoff=%d,maxRetries=%d,maxResults=%d,indexes=%b]",super.toString(), _namespace, _backoff, _maxRetries, _maxResults,_indexesPresent);
|
return String.format("%s[namespace=%s,backoff=%d,maxRetries=%d,maxResults=%d,indexes=%b]",super.toString(), _namespace, _backoff, _maxRetries, _maxResults,_indexesPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,10 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.hazelcast.session;
|
package org.eclipse.jetty.hazelcast.session;
|
||||||
|
|
||||||
import com.hazelcast.core.IMap;
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.session.AbstractSessionDataStore;
|
import org.eclipse.jetty.server.session.AbstractSessionDataStore;
|
||||||
import org.eclipse.jetty.server.session.SessionContext;
|
import org.eclipse.jetty.server.session.SessionContext;
|
||||||
import org.eclipse.jetty.server.session.SessionData;
|
import org.eclipse.jetty.server.session.SessionData;
|
||||||
|
@ -28,10 +31,7 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
import java.util.Collections;
|
import com.hazelcast.core.IMap;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Session data stored in Hazelcast
|
* Session data stored in Hazelcast
|
||||||
|
@ -52,35 +52,23 @@ public class HazelcastSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load( String id )
|
public SessionData doLoad( String id )
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
final AtomicReference<SessionData> reference = new AtomicReference<>();
|
|
||||||
final AtomicReference<Exception> exception = new AtomicReference<>();
|
|
||||||
|
|
||||||
//ensure the load runs in the context classloader scope
|
|
||||||
_context.run( () -> {
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug( "Loading session {} from hazelcast", id );
|
LOG.debug( "Loading session {} from hazelcast", id );
|
||||||
|
|
||||||
SessionData sd = sessionDataMap.get( getCacheKey( id ) );
|
SessionData sd = sessionDataMap.get( getCacheKey( id ) );
|
||||||
reference.set(sd);
|
return sd;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
exception.set(new UnreadableSessionDataException(id, _context, e));
|
throw new UnreadableSessionDataException(id, _context, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} );
|
|
||||||
|
|
||||||
if (exception.get() != null)
|
|
||||||
{
|
|
||||||
throw exception.get();
|
|
||||||
}
|
|
||||||
return reference.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete( String id )
|
public boolean delete( String id )
|
||||||
|
|
|
@ -23,10 +23,12 @@ import com.hazelcast.client.config.ClientConfig;
|
||||||
import com.hazelcast.client.config.XmlClientConfigBuilder;
|
import com.hazelcast.client.config.XmlClientConfigBuilder;
|
||||||
import com.hazelcast.config.Config;
|
import com.hazelcast.config.Config;
|
||||||
import com.hazelcast.config.MapConfig;
|
import com.hazelcast.config.MapConfig;
|
||||||
|
import com.hazelcast.config.SerializerConfig;
|
||||||
import com.hazelcast.config.XmlConfigBuilder;
|
import com.hazelcast.config.XmlConfigBuilder;
|
||||||
import com.hazelcast.core.Hazelcast;
|
import com.hazelcast.core.Hazelcast;
|
||||||
import com.hazelcast.core.HazelcastInstance;
|
import com.hazelcast.core.HazelcastInstance;
|
||||||
import org.eclipse.jetty.server.session.AbstractSessionDataStoreFactory;
|
import org.eclipse.jetty.server.session.AbstractSessionDataStoreFactory;
|
||||||
|
import org.eclipse.jetty.server.session.SessionData;
|
||||||
import org.eclipse.jetty.server.session.SessionDataStore;
|
import org.eclipse.jetty.server.session.SessionDataStore;
|
||||||
import org.eclipse.jetty.server.session.SessionDataStoreFactory;
|
import org.eclipse.jetty.server.session.SessionDataStoreFactory;
|
||||||
import org.eclipse.jetty.server.session.SessionHandler;
|
import org.eclipse.jetty.server.session.SessionHandler;
|
||||||
|
@ -68,7 +70,12 @@ public class HazelcastSessionDataStoreFactory
|
||||||
{
|
{
|
||||||
if ( configurationLocation == null )
|
if ( configurationLocation == null )
|
||||||
{
|
{
|
||||||
hazelcastInstance = HazelcastClient.newHazelcastClient( new ClientConfig() );
|
ClientConfig config = new ClientConfig();
|
||||||
|
SerializerConfig sc = new SerializerConfig().
|
||||||
|
setImplementation(new SessionDataSerializer()).
|
||||||
|
setTypeClass(SessionData.class);
|
||||||
|
config.getSerializationConfig().addSerializerConfig(sc);
|
||||||
|
hazelcastInstance = HazelcastClient.newHazelcastClient(config);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -82,7 +89,12 @@ public class HazelcastSessionDataStoreFactory
|
||||||
Config config;
|
Config config;
|
||||||
if ( configurationLocation == null )
|
if ( configurationLocation == null )
|
||||||
{
|
{
|
||||||
|
|
||||||
|
SerializerConfig sc = new SerializerConfig().
|
||||||
|
setImplementation(new SessionDataSerializer()).
|
||||||
|
setTypeClass(SessionData.class);
|
||||||
config = new Config();
|
config = new Config();
|
||||||
|
config.getSerializationConfig().addSerializerConfig(sc);
|
||||||
// configure a default Map if null
|
// configure a default Map if null
|
||||||
if ( mapConfig == null )
|
if ( mapConfig == null )
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2018 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.hazelcast.session;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.session.SessionData;
|
||||||
|
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
||||||
|
|
||||||
|
import com.hazelcast.nio.ObjectDataInput;
|
||||||
|
import com.hazelcast.nio.ObjectDataOutput;
|
||||||
|
import com.hazelcast.nio.serialization.StreamSerializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SessionDataSerializer
|
||||||
|
*
|
||||||
|
* Handles serialization on behalf of the SessionData object, and
|
||||||
|
* ensures that we use jetty's classloading knowledge.
|
||||||
|
*/
|
||||||
|
public class SessionDataSerializer implements StreamSerializer<SessionData>
|
||||||
|
{
|
||||||
|
public static final int __TYPEID = 99;
|
||||||
|
@Override
|
||||||
|
public int getTypeId()
|
||||||
|
{
|
||||||
|
return __TYPEID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ObjectDataOutput out, SessionData data) throws IOException
|
||||||
|
{
|
||||||
|
out.writeUTF(data.getId());
|
||||||
|
out.writeUTF(data.getContextPath());
|
||||||
|
out.writeUTF(data.getVhost());
|
||||||
|
|
||||||
|
out.writeLong(data.getAccessed());
|
||||||
|
out.writeLong(data.getLastAccessed());
|
||||||
|
out.writeLong(data.getCreated());
|
||||||
|
out.writeLong(data.getCookieSet());
|
||||||
|
out.writeUTF(data.getLastNode());
|
||||||
|
|
||||||
|
out.writeLong(data.getExpiry());
|
||||||
|
out.writeLong(data.getMaxInactiveMs());
|
||||||
|
|
||||||
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(baos))
|
||||||
|
{
|
||||||
|
SessionData.serializeAttributes(data, oos);
|
||||||
|
out.writeByteArray(baos.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionData read(ObjectDataInput in) throws IOException
|
||||||
|
{
|
||||||
|
String id = in.readUTF();
|
||||||
|
String contextPath = in.readUTF();
|
||||||
|
String vhost = in.readUTF();
|
||||||
|
|
||||||
|
long accessed = in.readLong();
|
||||||
|
long lastAccessed = in.readLong();
|
||||||
|
long created = in.readLong();
|
||||||
|
long cookieSet = in.readLong();
|
||||||
|
String lastNode = in.readUTF();
|
||||||
|
long expiry = in.readLong();
|
||||||
|
long maxInactiveMs = in.readLong();
|
||||||
|
|
||||||
|
SessionData sd = new SessionData(id, contextPath, vhost, created, accessed, lastAccessed, maxInactiveMs);
|
||||||
|
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(in.readByteArray());
|
||||||
|
try (ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(bais))
|
||||||
|
{
|
||||||
|
SessionData.deserializeAttributes(sd, ois);
|
||||||
|
}
|
||||||
|
catch(ClassNotFoundException e)
|
||||||
|
{
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
sd.setCookieSet(cookieSet);
|
||||||
|
sd.setLastNode(lastNode);
|
||||||
|
sd.setExpiry(expiry);
|
||||||
|
return sd;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -350,23 +350,16 @@ public class Huffman
|
||||||
return decode(buffer,buffer.remaining());
|
return decode(buffer,buffer.remaining());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String decode(ByteBuffer buffer,int length) throws HpackException.CompressionException
|
public static String decode(ByteBuffer buffer, int length) throws HpackException.CompressionException
|
||||||
{
|
{
|
||||||
StringBuilder out = new StringBuilder(length*2);
|
StringBuilder out = new StringBuilder(length*2);
|
||||||
int node = 0;
|
int node = 0;
|
||||||
int current = 0;
|
int current = 0;
|
||||||
int bits = 0;
|
int bits = 0;
|
||||||
|
|
||||||
byte[] array = buffer.array();
|
for (int i=0; i<length; i++)
|
||||||
int position=buffer.position();
|
|
||||||
int start=buffer.arrayOffset()+position;
|
|
||||||
int end=start+length;
|
|
||||||
buffer.position(position+length);
|
|
||||||
|
|
||||||
|
|
||||||
for (int i=start; i<end; i++)
|
|
||||||
{
|
{
|
||||||
int b = array[i]&0xFF;
|
int b = buffer.get()&0xFF;
|
||||||
current = (current << 8) | b;
|
current = (current << 8) | b;
|
||||||
bits += 8;
|
bits += 8;
|
||||||
while (bits >= 8)
|
while (bits >= 8)
|
||||||
|
@ -460,10 +453,6 @@ public class Huffman
|
||||||
{
|
{
|
||||||
long current = 0;
|
long current = 0;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
byte[] array = buffer.array();
|
|
||||||
int p=buffer.arrayOffset()+buffer.position();
|
|
||||||
|
|
||||||
int len = s.length();
|
int len = s.length();
|
||||||
for (int i=0;i<len;i++)
|
for (int i=0;i<len;i++)
|
||||||
{
|
{
|
||||||
|
@ -480,7 +469,7 @@ public class Huffman
|
||||||
while (n >= 8)
|
while (n >= 8)
|
||||||
{
|
{
|
||||||
n -= 8;
|
n -= 8;
|
||||||
array[p++]=(byte)(current >> n);
|
buffer.put((byte)(current >> n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,10 +477,9 @@ public class Huffman
|
||||||
{
|
{
|
||||||
current <<= (8 - n);
|
current <<= (8 - n);
|
||||||
current |= (0xFF >>> n);
|
current |= (0xFF >>> n);
|
||||||
array[p++]=(byte)current;
|
buffer.put((byte)(current));
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.position(p-buffer.arrayOffset());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,9 @@
|
||||||
package org.eclipse.jetty.http2.hpack;
|
package org.eclipse.jetty.http2.hpack;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -51,7 +50,7 @@ public class HpackTest
|
||||||
{
|
{
|
||||||
HpackEncoder encoder = new HpackEncoder();
|
HpackEncoder encoder = new HpackEncoder();
|
||||||
HpackDecoder decoder = new HpackDecoder(4096,8192);
|
HpackDecoder decoder = new HpackDecoder(4096,8192);
|
||||||
ByteBuffer buffer = BufferUtil.allocate(16*1024);
|
ByteBuffer buffer = BufferUtil.allocateDirect(16*1024);
|
||||||
|
|
||||||
HttpFields fields0 = new HttpFields();
|
HttpFields fields0 = new HttpFields();
|
||||||
fields0.add(HttpHeader.CONTENT_TYPE,"text/html");
|
fields0.add(HttpHeader.CONTENT_TYPE,"text/html");
|
||||||
|
@ -104,7 +103,7 @@ public class HpackTest
|
||||||
{
|
{
|
||||||
HpackEncoder encoder = new HpackEncoder();
|
HpackEncoder encoder = new HpackEncoder();
|
||||||
HpackDecoder decoder = new HpackDecoder(4096,164);
|
HpackDecoder decoder = new HpackDecoder(4096,164);
|
||||||
ByteBuffer buffer = BufferUtil.allocate(16*1024);
|
ByteBuffer buffer = BufferUtil.allocateDirect(16*1024);
|
||||||
|
|
||||||
HttpFields fields0 = new HttpFields();
|
HttpFields fields0 = new HttpFields();
|
||||||
fields0.add("1234567890","1234567890123456789012345678901234567890");
|
fields0.add("1234567890","1234567890123456789012345678901234567890");
|
||||||
|
@ -144,7 +143,7 @@ public class HpackTest
|
||||||
{
|
{
|
||||||
HpackEncoder encoder = new HpackEncoder(200,200);
|
HpackEncoder encoder = new HpackEncoder(200,200);
|
||||||
HpackDecoder decoder = new HpackDecoder(200,1024);
|
HpackDecoder decoder = new HpackDecoder(200,1024);
|
||||||
ByteBuffer buffer = BufferUtil.allocate(16*1024);
|
ByteBuffer buffer = BufferUtil.allocateDirect(16*1024);
|
||||||
|
|
||||||
HttpFields fields0 = new HttpFields();
|
HttpFields fields0 = new HttpFields();
|
||||||
fields0.add("123456789012345678901234567890123456788901234567890","value");
|
fields0.add("123456789012345678901234567890123456788901234567890","value");
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.http2.hpack;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import java.nio.BufferOverflowException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
@ -79,7 +80,7 @@ public class HuffmanTest
|
||||||
assertThrows(IllegalArgumentException.class,
|
assertThrows(IllegalArgumentException.class,
|
||||||
() -> Huffman.octetsNeeded(s));
|
() -> Huffman.octetsNeeded(s));
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class,
|
assertThrows(BufferOverflowException.class,
|
||||||
() -> Huffman.encode(BufferUtil.allocate(32), s));
|
() -> Huffman.encode(BufferUtil.allocate(32), s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,10 +40,27 @@
|
||||||
<artifactId>infinispan-core</artifactId>
|
<artifactId>infinispan-core</artifactId>
|
||||||
<version>${infinispan.version}</version>
|
<version>${infinispan.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.infinispan.protostream</groupId>
|
||||||
|
<artifactId>protostream</artifactId>
|
||||||
|
<version>4.1.0.Final</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-server</artifactId>
|
<artifactId>jetty-server</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.infinispan</groupId>
|
||||||
|
<artifactId>infinispan-client-hotrod</artifactId>
|
||||||
|
<version>9.1.0.Final</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.infinispan</groupId>
|
||||||
|
<artifactId>infinispan-remote-query-client</artifactId>
|
||||||
|
<version>9.1.0.Final</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -4,15 +4,72 @@
|
||||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ===================================================================== -->
|
||||||
|
<!-- Set up infinispan to use protobuf marshalling instead of default. -->
|
||||||
|
<!-- Avoids classloading issues when marshalling/demarshalling webapps -->
|
||||||
|
<!-- that use jetty server classes in their attributes. -->
|
||||||
|
<!-- ===================================================================== -->
|
||||||
|
|
||||||
|
<New id="properties" class="java.util.Properties">
|
||||||
|
<Call name="load">
|
||||||
|
<Arg>
|
||||||
|
<New class="java.io.FileInputStream">
|
||||||
|
<Arg><Property name="jetty.base" default="."/>/resources/hotrod-client.properties</Arg>
|
||||||
|
</New>
|
||||||
|
</Arg>
|
||||||
|
</Call>
|
||||||
|
</New>
|
||||||
|
|
||||||
|
<New class="org.infinispan.client.hotrod.configuration.ConfigurationBuilder">
|
||||||
|
<Call name="withProperties">
|
||||||
|
<Arg><Ref refid="properties"/></Arg>
|
||||||
|
</Call>
|
||||||
|
<Call name="marshaller">
|
||||||
|
<Arg>
|
||||||
|
<New class="org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller"/>
|
||||||
|
</Arg>
|
||||||
|
</Call>
|
||||||
|
<Call id="config" name="build"/>
|
||||||
|
</New>
|
||||||
|
|
||||||
|
|
||||||
|
<New id="remoteCacheManager" class="org.infinispan.client.hotrod.RemoteCacheManager">
|
||||||
|
<Arg><Ref refid="config"/></Arg>
|
||||||
|
</New>
|
||||||
|
|
||||||
|
<Call id="serial_context" class="org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller" name="getSerializationContext">
|
||||||
|
<Arg>
|
||||||
|
<Ref refid="remoteCacheManager"/>
|
||||||
|
</Arg>
|
||||||
|
<Call name="registerProtoFiles">
|
||||||
|
<Arg>
|
||||||
|
<New class="org.infinispan.protostream.FileDescriptorSource">
|
||||||
|
<Call name="addProtoFiles">
|
||||||
|
<Arg>
|
||||||
|
<Array type="java.lang.String">
|
||||||
|
<Item>/session.proto</Item>
|
||||||
|
</Array>
|
||||||
|
</Arg>
|
||||||
|
</Call>
|
||||||
|
</New>
|
||||||
|
</Arg>
|
||||||
|
</Call>
|
||||||
|
<Call name="registerMarshaller">
|
||||||
|
<Arg>
|
||||||
|
<New class="org.eclipse.jetty.session.infinispan.SessionDataMarshaller"/>
|
||||||
|
</Arg>
|
||||||
|
</Call>
|
||||||
|
</Call>
|
||||||
|
|
||||||
<!-- ===================================================================== -->
|
<!-- ===================================================================== -->
|
||||||
<!-- Get a reference to the remote cache. -->
|
<!-- Get a reference to the remote cache. -->
|
||||||
<!-- ===================================================================== -->
|
<!-- ===================================================================== -->
|
||||||
<New id="hotrodMgr" class="org.infinispan.client.hotrod.RemoteCacheManager">
|
<Ref refid="remoteCacheManager">
|
||||||
<Call id="remoteCache" name="getCache">
|
<Call id="remoteCache" name="getCache">
|
||||||
<Arg><Property name="jetty.session.infinispan.remoteCacheName" default="sessions"/></Arg>
|
<Arg><Property name="jetty.session.infinispan.remoteCacheName" default="sessions"/></Arg>
|
||||||
</Call>
|
</Call>
|
||||||
</New>
|
</Ref>
|
||||||
|
|
||||||
|
|
||||||
<!-- ===================================================================== -->
|
<!-- ===================================================================== -->
|
||||||
<!-- Configure a factory for InfinispanSessionDataStore using an -->
|
<!-- Configure a factory for InfinispanSessionDataStore using an -->
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package org.eclipse.jetty.session.infinispan;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.session.SessionData;
|
||||||
|
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InfinispanSessionData
|
||||||
|
*
|
||||||
|
* Specialization of SessionData to hold the attributes as a serialized byte
|
||||||
|
* array. This is necessary because to deserialize the attributes correctly, we
|
||||||
|
* need to know which classloader to use, which is normally provided as the
|
||||||
|
* thread context classloader. However, infinispan marshalling uses a thread
|
||||||
|
* pool and thus these threads have no knowledge of the correct classloader to
|
||||||
|
* use.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class InfinispanSessionData extends SessionData
|
||||||
|
{
|
||||||
|
protected byte[] _serializedAttributes;
|
||||||
|
|
||||||
|
public InfinispanSessionData(String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs)
|
||||||
|
{
|
||||||
|
super(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfinispanSessionData(String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs,
|
||||||
|
Map<String, Object> attributes)
|
||||||
|
{
|
||||||
|
super(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSerializedAttributes()
|
||||||
|
{
|
||||||
|
return _serializedAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSerializedAttributes(byte[] serializedAttributes)
|
||||||
|
{
|
||||||
|
_serializedAttributes = serializedAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deserializeAttributes() throws ClassNotFoundException, IOException
|
||||||
|
{
|
||||||
|
if (_serializedAttributes == null) return;
|
||||||
|
|
||||||
|
try (ByteArrayInputStream bais = new ByteArrayInputStream(_serializedAttributes);
|
||||||
|
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(bais))
|
||||||
|
{
|
||||||
|
SessionData.deserializeAttributes(this, ois);
|
||||||
|
_serializedAttributes = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serializeAttributes() throws IOException
|
||||||
|
{
|
||||||
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(baos))
|
||||||
|
{
|
||||||
|
SessionData.serializeAttributes(this, oos);
|
||||||
|
_serializedAttributes = baos.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,7 +45,6 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
||||||
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
|
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clustered cache of sessions
|
* Clustered cache of sessions
|
||||||
*/
|
*/
|
||||||
|
@ -54,6 +53,8 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
||||||
|
|
||||||
private int _infinispanIdleTimeoutSec;
|
private int _infinispanIdleTimeoutSec;
|
||||||
|
|
||||||
|
private boolean _passivating;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the clustered cache instance.
|
* Get the clustered cache instance.
|
||||||
|
@ -79,48 +80,53 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#load(String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load(String id) throws Exception
|
protected void doStart() throws Exception
|
||||||
{
|
{
|
||||||
final AtomicReference<SessionData> reference = new AtomicReference<>();
|
super.doStart();
|
||||||
final AtomicReference<Exception> exception = new AtomicReference<>();
|
if (_cache == null)
|
||||||
|
throw new IllegalStateException ("No cache");
|
||||||
|
|
||||||
Runnable load = new Runnable()
|
try
|
||||||
{
|
{
|
||||||
|
_passivating = false;
|
||||||
|
Class<?> remoteClass = Thread.currentThread().getContextClassLoader().loadClass("org.infinispan.client.hotrod.RemoteCache");
|
||||||
|
if (remoteClass.isAssignableFrom(_cache.getClass()))
|
||||||
|
_passivating = true;
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException e)
|
||||||
|
{
|
||||||
|
//expected if not running with remote cache
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run ()
|
public SessionData doLoad(String id) throws Exception
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Loading session {} from infinispan", id);
|
LOG.debug("Loading session {} from infinispan", id);
|
||||||
|
|
||||||
SessionData sd = (SessionData)_cache.get(getCacheKey(id));
|
InfinispanSessionData sd = (InfinispanSessionData)_cache.get(getCacheKey(id));
|
||||||
reference.set(sd);
|
if (isPassivating() && sd != null)
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Deserializing session attributes for {}", id);
|
||||||
|
sd.deserializeAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
exception.set(new UnreadableSessionDataException(id, _context, e));
|
throw new UnreadableSessionDataException(id, _context, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
//ensure the load runs in the context classloader scope
|
|
||||||
_context.run(load);
|
|
||||||
|
|
||||||
if (exception.get() != null)
|
|
||||||
throw exception.get();
|
|
||||||
|
|
||||||
return reference.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#delete(String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete(String id) throws Exception
|
public boolean delete(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -129,9 +135,7 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
||||||
return (_cache.remove(getCacheKey(id)) != null);
|
return (_cache.remove(getCacheKey(id)) != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> doGetExpired(Set<String> candidates)
|
public Set<String> doGetExpired(Set<String> candidates)
|
||||||
{
|
{
|
||||||
|
@ -199,9 +203,7 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
||||||
return expired;
|
return expired;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(String, SessionData, long)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -210,9 +212,9 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
||||||
//scavenges the session before this timeout occurs, the session will be removed.
|
//scavenges the session before this timeout occurs, the session will be removed.
|
||||||
//NOTE: that no session listeners can be called for this.
|
//NOTE: that no session listeners can be called for this.
|
||||||
if (data.getMaxInactiveMs() > 0 && getInfinispanIdleTimeoutSec() > 0)
|
if (data.getMaxInactiveMs() > 0 && getInfinispanIdleTimeoutSec() > 0)
|
||||||
_cache.put(getCacheKey(id), data, -1, TimeUnit.MILLISECONDS, getInfinispanIdleTimeoutSec(), TimeUnit.SECONDS);
|
_cache.put(getCacheKey(id), (InfinispanSessionData)data, -1, TimeUnit.MILLISECONDS, getInfinispanIdleTimeoutSec(), TimeUnit.SECONDS);
|
||||||
else
|
else
|
||||||
_cache.put(getCacheKey(id), data);
|
_cache.put(getCacheKey(id), (InfinispanSessionData)data);
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Session {} saved to infinispan, expires {} ", id, data.getExpiry());
|
LOG.debug("Session {} saved to infinispan, expires {} ", id, data.getExpiry());
|
||||||
|
@ -225,35 +227,15 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
|
|
||||||
*/
|
|
||||||
@ManagedAttribute(value="does store serialize sessions", readonly=true)
|
@ManagedAttribute(value="does store serialize sessions", readonly=true)
|
||||||
@Override
|
@Override
|
||||||
public boolean isPassivating()
|
public boolean isPassivating()
|
||||||
{
|
{
|
||||||
//TODO run in the _context to ensure classloader is set
|
return _passivating;
|
||||||
try
|
|
||||||
{
|
|
||||||
Class<?> remoteClass = Thread.currentThread().getContextClassLoader().loadClass("org.infinispan.client.hotrod.RemoteCache");
|
|
||||||
if (remoteClass.isAssignableFrom(_cache.getClass()))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#exists(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String id) throws Exception
|
public boolean exists(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -299,6 +281,13 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionData newSessionData(String id, long created, long accessed, long lastAccessed, long maxInactiveMs)
|
||||||
|
{
|
||||||
|
return new InfinispanSessionData(id, _context.getCanonicalContextPath(), _context.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param sec the infinispan-specific idle timeout in sec or 0 if not set
|
* @param sec the infinispan-specific idle timeout in sec or 0 if not set
|
||||||
*/
|
*/
|
||||||
|
@ -315,15 +304,9 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s[cache=%s,idleTimeoutSec=%d]",super.toString(), (_cache==null?"":_cache.getName()),_infinispanIdleTimeoutSec);
|
return String.format("%s[cache=%s,idleTimeoutSec=%d]",super.toString(), (_cache==null?"":_cache.getName()),_infinispanIdleTimeoutSec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,224 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package org.eclipse.jetty.session.infinispan;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.session.SessionData;
|
||||||
|
import org.infinispan.client.hotrod.RemoteCache;
|
||||||
|
import org.infinispan.client.hotrod.RemoteCacheManager;
|
||||||
|
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
|
||||||
|
import org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller;
|
||||||
|
import org.infinispan.protostream.FileDescriptorSource;
|
||||||
|
import org.infinispan.protostream.SerializationContext;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InfinispanSessionLegacyConverter
|
||||||
|
*
|
||||||
|
* Converts sessions saved in the old serialization
|
||||||
|
* format into the new protobuf-based serialization.
|
||||||
|
*
|
||||||
|
* Use the -Dverbose=true system property to
|
||||||
|
* print out more information about conversion failures.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class InfinispanSessionLegacyConverter
|
||||||
|
{
|
||||||
|
RemoteCacheManager _protoManager;
|
||||||
|
RemoteCache<String,InfinispanSessionData> _protoCache;
|
||||||
|
RemoteCacheManager _legacyManager;
|
||||||
|
RemoteCache<String,SessionData> _legacyCache;
|
||||||
|
boolean _verbose = false;
|
||||||
|
|
||||||
|
|
||||||
|
public InfinispanSessionLegacyConverter (String cacheName)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
//legacy serialization
|
||||||
|
_legacyManager = new RemoteCacheManager();
|
||||||
|
_legacyCache = _legacyManager.getCache(cacheName);
|
||||||
|
|
||||||
|
//new protobuf based
|
||||||
|
String host = System.getProperty("host", "127.0.0.1");
|
||||||
|
_verbose = Boolean.getBoolean("verbose");
|
||||||
|
|
||||||
|
Properties properties = new Properties();
|
||||||
|
ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
|
||||||
|
clientBuilder.withProperties(properties).addServer().host(host).marshaller(new ProtoStreamMarshaller());
|
||||||
|
_protoManager = new RemoteCacheManager(clientBuilder.build());
|
||||||
|
FileDescriptorSource fds = new FileDescriptorSource();
|
||||||
|
fds.addProtoFiles("/session.proto");
|
||||||
|
SerializationContext serCtx = ProtoStreamMarshaller.getSerializationContext(_protoManager);
|
||||||
|
serCtx.registerProtoFiles(fds);
|
||||||
|
serCtx.registerMarshaller(new SessionDataMarshaller());
|
||||||
|
_protoCache = _protoManager.getCache(cacheName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert all sessions to protobuf format sessions.
|
||||||
|
*/
|
||||||
|
public void convert ()
|
||||||
|
{
|
||||||
|
long conversions = 0;
|
||||||
|
List<String> keys = null;
|
||||||
|
|
||||||
|
//Get all sessions stored in the legacy format
|
||||||
|
try
|
||||||
|
{
|
||||||
|
keys = _legacyCache.keySet().stream().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
System.err.println("Error listing legacy sessions, assuming previously converted. Run again using 'check' argument to verify conversion");
|
||||||
|
if (_verbose) e.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String s:keys)
|
||||||
|
{
|
||||||
|
SessionData data = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data = (SessionData)_legacyCache.get(s);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
System.err.println("Read of session "+s+" failed. Assuming session already converted and skipping.");
|
||||||
|
if (_verbose) e.printStackTrace();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_legacyCache.remove(s);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
System.err.println("Remove legacy session failed for "+s+" skipping conversion.");
|
||||||
|
if (_verbose) e.printStackTrace();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InfinispanSessionData isd = new InfinispanSessionData(data.getId(), data.getContextPath(), data.getVhost(), data.getCreated(),
|
||||||
|
data.getAccessed(), data.getLastAccessed(), data.getMaxInactiveMs());
|
||||||
|
isd.putAllAttributes(data.getAllAttributes());
|
||||||
|
isd.setExpiry(data.getExpiry());
|
||||||
|
isd.setCookieSet(data.getCookieSet());
|
||||||
|
isd.setLastSaved(data.getLastSaved());
|
||||||
|
isd.setLastNode(data.getLastNode());
|
||||||
|
// now write it out to the protobuf format
|
||||||
|
_protoCache.put(s, isd);
|
||||||
|
System.err.println("Converted " + s);
|
||||||
|
conversions++;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (_verbose) e.printStackTrace();
|
||||||
|
System.err.println("Conversion failed for " + s + " re-instating legacy session.");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_legacyCache.put(s, data);
|
||||||
|
}
|
||||||
|
catch (Exception x)
|
||||||
|
{
|
||||||
|
System.err.println("FAILED REINSTATING SESSION " + s + ". ABORTING.");
|
||||||
|
x.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
System.err.println("Unreadable legacy session "+s);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println("Total sessions converted: "+conversions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the sessions using protobuf format and print them out to
|
||||||
|
* confirm they're ok.
|
||||||
|
*/
|
||||||
|
public void checkConverted ()
|
||||||
|
{
|
||||||
|
List<String> keys = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
keys = _protoCache.keySet().stream().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
System.err.println("Unable to read converted sessions, assuming still in legacy format. Run again without 'check' option to convert.");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String s:keys)
|
||||||
|
{
|
||||||
|
InfinispanSessionData converted = (InfinispanSessionData)_protoCache.get(s);
|
||||||
|
if (converted != null)
|
||||||
|
{
|
||||||
|
System.err.println("OK: "+converted);
|
||||||
|
converted.getKeys().stream().forEach((ss)->{System.err.println(ss+":"+converted.getAttribute(ss));});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
System.err.println("Failed: "+s);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println("Total converted sessions: "+keys.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final void usage ()
|
||||||
|
{
|
||||||
|
System.err.println("Usage: InfinispanSessionLegacyConverter [-Dhost=127.0.0.1] [-Dverbose=true] <cache-name> [check]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final void main (String... args)
|
||||||
|
{
|
||||||
|
if (args == null || args.length < 1)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InfinispanSessionLegacyConverter converter = new InfinispanSessionLegacyConverter(args[0]);
|
||||||
|
|
||||||
|
if (args.length == 1)
|
||||||
|
converter.convert();
|
||||||
|
else if (args[1].equals("check"))
|
||||||
|
converter.checkConverted();
|
||||||
|
else
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
System.err.println("Conversion failure");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.session.infinispan;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.infinispan.protostream.MessageMarshaller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SessionDataMarshaller
|
||||||
|
*
|
||||||
|
* A marshaller for converting a SessionData object into protobuf format which
|
||||||
|
* gives greater control over serialization/deserialization. We use that extra
|
||||||
|
* control to ensure that session attributes can be deserialized using either
|
||||||
|
* the container class loader or the webapp classloader, as appropriate.
|
||||||
|
*/
|
||||||
|
public class SessionDataMarshaller implements MessageMarshaller<InfinispanSessionData>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The version of the serializer.
|
||||||
|
*/
|
||||||
|
private static final int VERSION = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends InfinispanSessionData> getJavaClass()
|
||||||
|
{
|
||||||
|
return InfinispanSessionData.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTypeName()
|
||||||
|
{
|
||||||
|
return "org_eclipse_jetty_session_infinispan.InfinispanSessionData";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InfinispanSessionData readFrom(ProtoStreamReader in) throws IOException
|
||||||
|
{
|
||||||
|
int version = in.readInt("version");// version of serialized session
|
||||||
|
String id = in.readString("id"); // session id
|
||||||
|
String cpath = in.readString("contextPath"); // context path
|
||||||
|
String vhost = in.readString("vhost"); // first vhost
|
||||||
|
|
||||||
|
long accessed = in.readLong("accessed");// accessTime
|
||||||
|
long lastAccessed = in.readLong("lastAccessed"); // lastAccessTime
|
||||||
|
long created = in.readLong("created"); // time created
|
||||||
|
long cookieSet = in.readLong("cookieSet");// time cookie was set
|
||||||
|
String lastNode = in.readString("lastNode"); // name of last node
|
||||||
|
// managing
|
||||||
|
|
||||||
|
long expiry = in.readLong("expiry");
|
||||||
|
long maxInactiveMs = in.readLong("maxInactiveMs");
|
||||||
|
|
||||||
|
InfinispanSessionData sd = new InfinispanSessionData(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs);
|
||||||
|
sd.setCookieSet(cookieSet);
|
||||||
|
sd.setLastNode(lastNode);
|
||||||
|
sd.setExpiry(expiry);
|
||||||
|
|
||||||
|
if (version == 0)
|
||||||
|
{
|
||||||
|
byte[] attributeArray = in.readBytes("attributes");
|
||||||
|
sd.setSerializedAttributes(attributeArray);
|
||||||
|
return sd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new IOException("Unrecognized infinispan session version " + version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(ProtoStreamWriter out, InfinispanSessionData sdata) throws IOException
|
||||||
|
{
|
||||||
|
out.writeInt("version", VERSION);
|
||||||
|
out.writeString("id", sdata.getId()); // session id
|
||||||
|
out.writeString("contextPath", sdata.getContextPath()); // context path
|
||||||
|
out.writeString("vhost", sdata.getVhost()); // first vhost
|
||||||
|
|
||||||
|
out.writeLong("accessed", sdata.getAccessed());// accessTime
|
||||||
|
out.writeLong("lastAccessed", sdata.getLastAccessed()); // lastAccessTime
|
||||||
|
out.writeLong("created", sdata.getCreated()); // time created
|
||||||
|
out.writeLong("cookieSet", sdata.getCookieSet());// time cookie was set
|
||||||
|
out.writeString("lastNode", sdata.getLastNode()); // name of last node
|
||||||
|
// managing
|
||||||
|
|
||||||
|
out.writeLong("expiry", sdata.getExpiry());
|
||||||
|
out.writeLong("maxInactiveMs", sdata.getMaxInactiveMs());
|
||||||
|
|
||||||
|
sdata.serializeAttributes();
|
||||||
|
out.writeBytes("attributes", sdata.getSerializedAttributes());
|
||||||
|
}
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ import org.jboss.marshalling.ContextClassResolver;
|
||||||
* </Call>
|
* </Call>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class WebAppMarshaller extends AbstractJBossMarshaller
|
public class WebAppMarshaller extends AbstractJBossMarshaller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package org_eclipse_jetty_session_infinispan;
|
||||||
|
|
||||||
|
message InfinispanSessionData
|
||||||
|
{
|
||||||
|
required int32 version = 1;
|
||||||
|
required string id = 2;
|
||||||
|
required string contextPath = 3;
|
||||||
|
required string vhost = 4;
|
||||||
|
|
||||||
|
required sint64 accessed = 5;
|
||||||
|
required sint64 lastAccessed = 6;
|
||||||
|
required sint64 created = 7;
|
||||||
|
required sint64 cookieSet = 8;
|
||||||
|
required string lastNode = 9;
|
||||||
|
|
||||||
|
required sint64 expiry = 10;
|
||||||
|
required sint64 maxInactiveMs = 11;
|
||||||
|
required bytes attributes = 12;
|
||||||
|
}
|
|
@ -294,7 +294,7 @@ abstract public class WriteFlusher
|
||||||
else
|
else
|
||||||
fail(callback);
|
fail(callback);
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
LOG.debug("write exception", e);
|
LOG.debug("write exception", e);
|
||||||
|
@ -366,7 +366,7 @@ abstract public class WriteFlusher
|
||||||
else
|
else
|
||||||
fail(callback);
|
fail(callback);
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
LOG.debug("completeWrite exception", e);
|
LOG.debug("completeWrite exception", e);
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2018 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.io.jmh;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
|
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||||
|
import org.openjdk.jmh.annotations.Measurement;
|
||||||
|
import org.openjdk.jmh.annotations.Mode;
|
||||||
|
import org.openjdk.jmh.annotations.Scope;
|
||||||
|
import org.openjdk.jmh.annotations.State;
|
||||||
|
import org.openjdk.jmh.annotations.Threads;
|
||||||
|
import org.openjdk.jmh.annotations.Warmup;
|
||||||
|
import org.openjdk.jmh.runner.Runner;
|
||||||
|
import org.openjdk.jmh.runner.RunnerException;
|
||||||
|
import org.openjdk.jmh.runner.options.Options;
|
||||||
|
import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||||
|
|
||||||
|
|
||||||
|
@State(Scope.Benchmark)
|
||||||
|
@Threads(4)
|
||||||
|
@Warmup(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||||
|
@Measurement(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||||
|
public class ByteBufferBenchmark
|
||||||
|
{
|
||||||
|
public long test(ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.clear();
|
||||||
|
while(buffer.hasRemaining())
|
||||||
|
{
|
||||||
|
int size = ThreadLocalRandom.current().nextInt(1024);
|
||||||
|
byte[] bytes = new byte[size];
|
||||||
|
ThreadLocalRandom.current().nextBytes(bytes);
|
||||||
|
buffer.put(bytes,0,Math.min(bytes.length,buffer.remaining()));
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.flip();
|
||||||
|
|
||||||
|
long sum = 0;
|
||||||
|
while(buffer.hasRemaining())
|
||||||
|
sum += buffer.get();
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public long testArray(ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.clear();
|
||||||
|
byte[] array = buffer.array();
|
||||||
|
int offset = buffer.arrayOffset();
|
||||||
|
int end = offset + buffer.remaining();
|
||||||
|
while(offset<end)
|
||||||
|
{
|
||||||
|
int size = ThreadLocalRandom.current().nextInt(1024);
|
||||||
|
byte[] bytes = new byte[size];
|
||||||
|
ThreadLocalRandom.current().nextBytes(bytes);
|
||||||
|
System.arraycopy(bytes,0,array,offset,Math.min(bytes.length,end-offset));
|
||||||
|
offset += bytes.length;
|
||||||
|
}
|
||||||
|
buffer.position(buffer.limit());
|
||||||
|
buffer.flip();
|
||||||
|
|
||||||
|
long sum = 0;
|
||||||
|
array = buffer.array();
|
||||||
|
offset = buffer.arrayOffset();
|
||||||
|
end = offset + buffer.remaining();
|
||||||
|
|
||||||
|
while(offset<end)
|
||||||
|
sum += array[offset++];
|
||||||
|
buffer.position(buffer.limit());
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
@BenchmarkMode({ Mode.Throughput})
|
||||||
|
public long testDirect()
|
||||||
|
{
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocateDirect(32768);
|
||||||
|
long sum = 0;
|
||||||
|
sum ^= test(buffer);
|
||||||
|
sum ^= test(buffer);
|
||||||
|
sum ^= test(buffer);
|
||||||
|
sum ^= test(buffer);
|
||||||
|
sum ^= test(buffer);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
@BenchmarkMode({ Mode.Throughput})
|
||||||
|
public long testInDirect()
|
||||||
|
{
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(32768);
|
||||||
|
long sum = 0;
|
||||||
|
sum ^= test(buffer);
|
||||||
|
sum ^= test(buffer);
|
||||||
|
sum ^= test(buffer);
|
||||||
|
sum ^= test(buffer);
|
||||||
|
sum ^= test(buffer);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
@BenchmarkMode({ Mode.Throughput})
|
||||||
|
public long testInDirectArray()
|
||||||
|
{
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(32768);
|
||||||
|
long sum = 0;
|
||||||
|
sum ^= testArray(buffer);
|
||||||
|
sum ^= testArray(buffer);
|
||||||
|
sum ^= testArray(buffer);
|
||||||
|
sum ^= testArray(buffer);
|
||||||
|
sum ^= testArray(buffer);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) throws RunnerException
|
||||||
|
{
|
||||||
|
Options opt = new OptionsBuilder()
|
||||||
|
.include(ByteBufferBenchmark.class.getSimpleName())
|
||||||
|
.warmupIterations(20)
|
||||||
|
.measurementIterations(10)
|
||||||
|
// .addProfiler(GCProfiler.class)
|
||||||
|
.forks(1)
|
||||||
|
.threads(10)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
new Runner(opt).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,7 +19,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.googlecode.xmemcached</groupId>
|
<groupId>com.googlecode.xmemcached</groupId>
|
||||||
<artifactId>xmemcached</artifactId>
|
<artifactId>xmemcached</artifactId>
|
||||||
<version>2.0.0</version>
|
<version>2.4.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
|
|
@ -11,7 +11,7 @@ session-store
|
||||||
slf4j-api
|
slf4j-api
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
maven://com.googlecode.xmemcached/xmemcached/2.0.0|lib/xmemcached/xmemcached-2.0.0.jar
|
maven://com.googlecode.xmemcached/xmemcached/2.4.5|lib/xmemcached/xmemcached-2.4.5.jar
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
lib/jetty-memcached-sessions-${jetty.version}.jar
|
lib/jetty-memcached-sessions-${jetty.version}.jar
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.memcached.session;
|
package org.eclipse.jetty.memcached.session;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -25,12 +26,14 @@ import java.util.List;
|
||||||
import org.eclipse.jetty.server.session.SessionContext;
|
import org.eclipse.jetty.server.session.SessionContext;
|
||||||
import org.eclipse.jetty.server.session.SessionData;
|
import org.eclipse.jetty.server.session.SessionData;
|
||||||
import org.eclipse.jetty.server.session.SessionDataMap;
|
import org.eclipse.jetty.server.session.SessionDataMap;
|
||||||
|
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||||
|
|
||||||
import net.rubyeye.xmemcached.MemcachedClient;
|
import net.rubyeye.xmemcached.MemcachedClient;
|
||||||
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
|
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
|
||||||
|
import net.rubyeye.xmemcached.transcoders.SerializingTranscoder;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,6 +53,39 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio
|
||||||
protected XMemcachedClientBuilder _builder;
|
protected XMemcachedClientBuilder _builder;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SessionDataTranscoder
|
||||||
|
*
|
||||||
|
* We override memcached deserialization to use our classloader-aware
|
||||||
|
* ObjectInputStream.
|
||||||
|
*/
|
||||||
|
public static class SessionDataTranscoder extends SerializingTranscoder
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object deserialize(byte[] in)
|
||||||
|
{
|
||||||
|
Object rv = null;
|
||||||
|
|
||||||
|
if (in != null)
|
||||||
|
{
|
||||||
|
try (ByteArrayInputStream bis = new ByteArrayInputStream(in);
|
||||||
|
ClassLoadingObjectInputStream is = new ClassLoadingObjectInputStream(bis))
|
||||||
|
{
|
||||||
|
rv = is.readObject();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
log.error("Caught IOException decoding " + in.length + " bytes of data", e);
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException e)
|
||||||
|
{
|
||||||
|
log.error("Caught CNFE decoding " + in.length + " bytes of data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,14 +153,12 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataMap#initialize(org.eclipse.jetty.server.session.SessionContext)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(SessionContext context)
|
public void initialize(SessionContext context)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_builder.setTranscoder(new SessionDataTranscoder ());
|
||||||
_client = _builder.build();
|
_client = _builder.build();
|
||||||
_client.setEnableHeartBeat(isHeartbeats());
|
_client.setEnableHeartBeat(isHeartbeats());
|
||||||
}
|
}
|
||||||
|
@ -134,9 +168,7 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataMap#load(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load(String id) throws Exception
|
public SessionData load(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -145,9 +177,6 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataMap#store(java.lang.String, org.eclipse.jetty.server.session.SessionData)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void store(String id, SessionData data) throws Exception
|
public void store(String id, SessionData data) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -155,9 +184,6 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataMap#delete(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete(String id) throws Exception
|
public boolean delete(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -178,5 +204,4 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,15 +19,19 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.nosql.mongodb;
|
package org.eclipse.jetty.nosql.mongodb;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.nosql.NoSqlSessionDataStore;
|
import org.eclipse.jetty.nosql.NoSqlSessionDataStore;
|
||||||
import org.eclipse.jetty.server.session.SessionContext;
|
import org.eclipse.jetty.server.session.SessionContext;
|
||||||
import org.eclipse.jetty.server.session.SessionData;
|
import org.eclipse.jetty.server.session.SessionData;
|
||||||
import org.eclipse.jetty.server.session.UnreadableSessionDataException;
|
import org.eclipse.jetty.server.session.UnreadableSessionDataException;
|
||||||
|
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
@ -86,9 +90,9 @@ import com.mongodb.WriteResult;
|
||||||
* </pre>
|
* </pre>
|
||||||
* <p>
|
* <p>
|
||||||
* In MongoDB, the nesting level is indicated by "." separators for the key name. Thus to
|
* In MongoDB, the nesting level is indicated by "." separators for the key name. Thus to
|
||||||
* interact with a session attribute, the key is composed of:
|
* interact with session fields, the key is composed of:
|
||||||
* <code>"context".unique_context_name.attribute_name</code>
|
* <code>"context".unique_context_name.field_name</code>
|
||||||
* Eg <code>"context"."0_0_0_0:_testA"."A"</code>
|
* Eg <code>"context"."0_0_0_0:_testA"."lastSaved"</code>
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -128,6 +132,8 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
|
|
||||||
public final static String __LAST_ACCESSED = "lastAccessed";
|
public final static String __LAST_ACCESSED = "lastAccessed";
|
||||||
|
|
||||||
|
public final static String __ATTRIBUTES = "attributes";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time this session will expire, based on last access time and maxIdle
|
* Time this session will expire, based on last access time and maxIdle
|
||||||
*/
|
*/
|
||||||
|
@ -178,40 +184,30 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#load(String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load(String id) throws Exception
|
public SessionData doLoad(String id) throws Exception
|
||||||
{
|
|
||||||
final AtomicReference<SessionData> reference = new AtomicReference<>();
|
|
||||||
final AtomicReference<Exception> exception = new AtomicReference<>();
|
|
||||||
Runnable r = new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
DBObject sessionDocument = _dbSessions.findOne(new BasicDBObject(__ID, id));
|
DBObject sessionDocument = _dbSessions.findOne(new BasicDBObject(__ID, id));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("id={} loaded={}", id, sessionDocument);
|
LOG.debug("id={} loaded={}", id, sessionDocument);
|
||||||
|
|
||||||
if (sessionDocument == null)
|
if (sessionDocument == null)
|
||||||
return;
|
return null;
|
||||||
|
|
||||||
Boolean valid = (Boolean)sessionDocument.get(__VALID);
|
Boolean valid = (Boolean)sessionDocument.get(__VALID);
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("id={} valid={}", id, valid);
|
LOG.debug("id={} valid={}", id, valid);
|
||||||
if (valid == null || !valid)
|
if (valid == null || !valid)
|
||||||
return;
|
return null;
|
||||||
|
|
||||||
Object version = MongoUtils.getNestedValue(sessionDocument, getContextSubfield(__VERSION));
|
Object version = MongoUtils.getNestedValue(sessionDocument, getContextSubfield(__VERSION));
|
||||||
Long lastSaved = (Long)MongoUtils.getNestedValue(sessionDocument, getContextSubfield(__LASTSAVED));
|
Long lastSaved = (Long)MongoUtils.getNestedValue(sessionDocument, getContextSubfield(__LASTSAVED));
|
||||||
String lastNode = (String)MongoUtils.getNestedValue(sessionDocument, getContextSubfield(__LASTNODE));
|
String lastNode = (String)MongoUtils.getNestedValue(sessionDocument, getContextSubfield(__LASTNODE));
|
||||||
|
byte[] attributes = (byte[])MongoUtils.getNestedValue(sessionDocument, getContextSubfield(__ATTRIBUTES));
|
||||||
|
|
||||||
Long created = (Long)sessionDocument.get(__CREATED);
|
Long created = (Long)sessionDocument.get(__CREATED);
|
||||||
Long accessed = (Long)sessionDocument.get(__ACCESSED);
|
Long accessed = (Long)sessionDocument.get(__ACCESSED);
|
||||||
|
@ -240,7 +236,10 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
data.setLastSaved(lastSaved);
|
data.setLastSaved(lastSaved);
|
||||||
data.setLastNode(lastNode);
|
data.setLastNode(lastNode);
|
||||||
|
|
||||||
HashMap<String, Object> attributes = new HashMap<>();
|
if (attributes == null)
|
||||||
|
{
|
||||||
|
//legacy attribute storage format: the attributes are all fields in the document
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
for (String name : sessionSubDocumentForContext.keySet())
|
for (String name : sessionSubDocumentForContext.keySet())
|
||||||
{
|
{
|
||||||
//skip special metadata attribute which is not one of the actual session attributes
|
//skip special metadata attribute which is not one of the actual session attributes
|
||||||
|
@ -248,10 +247,19 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
continue;
|
continue;
|
||||||
String attr = MongoUtils.decodeName(name);
|
String attr = MongoUtils.decodeName(name);
|
||||||
Object value = MongoUtils.decodeValue(sessionSubDocumentForContext.get(name));
|
Object value = MongoUtils.decodeValue(sessionSubDocumentForContext.get(name));
|
||||||
attributes.put(attr,value);
|
map.put(attr,value);
|
||||||
|
}
|
||||||
|
data.putAllAttributes(map);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//attributes have special serialized format
|
||||||
|
try (ByteArrayInputStream bais = new ByteArrayInputStream(attributes);
|
||||||
|
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(bais);)
|
||||||
|
{
|
||||||
|
SessionData.deserializeAttributes(data, ois);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.putAllAttributes(attributes);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -259,26 +267,15 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
LOG.debug("Session {} not present for context {}", id, _context);
|
LOG.debug("Session {} not present for context {}", id, _context);
|
||||||
}
|
}
|
||||||
|
|
||||||
reference.set(data);
|
return data;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
exception.set(new UnreadableSessionDataException(id, _context, e));
|
throw (new UnreadableSessionDataException(id, _context, e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
_context.run(r);
|
|
||||||
|
|
||||||
if (exception.get() != null)
|
|
||||||
throw exception.get();
|
|
||||||
|
|
||||||
return reference.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#delete(String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete(String id) throws Exception
|
public boolean delete(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -336,9 +333,6 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#exists(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String id) throws Exception
|
public boolean exists(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -371,9 +365,6 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> doGetExpired(Set<String> candidates)
|
public Set<String> doGetExpired(Set<String> candidates)
|
||||||
{
|
{
|
||||||
|
@ -459,9 +450,7 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
return expiredSessions;
|
return expiredSessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#initialize(org.eclipse.jetty.server.session.SessionContext)
|
|
||||||
*/
|
|
||||||
public void initialize (SessionContext context) throws Exception
|
public void initialize (SessionContext context) throws Exception
|
||||||
{
|
{
|
||||||
if (isStarted())
|
if (isStarted())
|
||||||
|
@ -470,9 +459,7 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
ensureIndexes();
|
ensureIndexes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(String, SessionData, long)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -483,7 +470,7 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
BasicDBObject update = new BasicDBObject();
|
BasicDBObject update = new BasicDBObject();
|
||||||
boolean upsert = false;
|
boolean upsert = false;
|
||||||
BasicDBObject sets = new BasicDBObject();
|
BasicDBObject sets = new BasicDBObject();
|
||||||
BasicDBObject unsets = new BasicDBObject();
|
|
||||||
|
|
||||||
Object version = ((NoSqlSessionData)data).getVersion();
|
Object version = ((NoSqlSessionData)data).getVersion();
|
||||||
|
|
||||||
|
@ -533,28 +520,16 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
sets.put(__ACCESSED, data.getAccessed());
|
sets.put(__ACCESSED, data.getAccessed());
|
||||||
sets.put(__LAST_ACCESSED, data.getLastAccessed());
|
sets.put(__LAST_ACCESSED, data.getLastAccessed());
|
||||||
|
|
||||||
Set<String> names = ((NoSqlSessionData)data).takeDirtyAttributes();
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(baos);)
|
||||||
if (lastSaveTime <= 0)
|
|
||||||
{
|
{
|
||||||
names.addAll(((NoSqlSessionData)data).getAllAttributeNames()); // note dirty may include removed names
|
SessionData.serializeAttributes(data, oos);
|
||||||
}
|
sets.put(getContextSubfield(__ATTRIBUTES), baos.toByteArray());
|
||||||
|
|
||||||
|
|
||||||
for (String name : names)
|
|
||||||
{
|
|
||||||
Object value = data.getAttribute(name);
|
|
||||||
if (value == null)
|
|
||||||
unsets.put(getContextField() + "." + MongoUtils.encodeName(name),1);
|
|
||||||
else
|
|
||||||
sets.put(getContextField() + "." + MongoUtils.encodeName(name), MongoUtils.encodeName(value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the upsert
|
// Do the upsert
|
||||||
if (!sets.isEmpty())
|
if (!sets.isEmpty())
|
||||||
update.put("$set",sets);
|
update.put("$set",sets);
|
||||||
if (!unsets.isEmpty())
|
|
||||||
update.put("$unset",unsets);
|
|
||||||
|
|
||||||
WriteResult res = _dbSessions.update(key,update,upsert,false,WriteConcern.SAFE);
|
WriteResult res = _dbSessions.update(key,update,upsert,false,WriteConcern.SAFE);
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
|
@ -584,9 +559,6 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
//TODO perhaps index on expiry time?
|
//TODO perhaps index on expiry time?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------ */
|
|
||||||
private String getContextField ()
|
private String getContextField ()
|
||||||
{
|
{
|
||||||
return __CONTEXT + "." + getCanonicalContextId();
|
return __CONTEXT + "." + getCanonicalContextId();
|
||||||
|
@ -613,9 +585,6 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
|
|
||||||
*/
|
|
||||||
@ManagedAttribute(value="does store serialize sessions", readonly=true)
|
@ManagedAttribute(value="does store serialize sessions", readonly=true)
|
||||||
@Override
|
@Override
|
||||||
public boolean isPassivating()
|
public boolean isPassivating()
|
||||||
|
@ -624,14 +593,11 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s[collection=%s]", super.toString(),getDBCollection());
|
return String.format("%s[collection=%s]", super.toString(),getDBCollection());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,15 @@ public class SessionAuthentication extends AbstractUserAuthentication implements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserIdentity getUserIdentity()
|
||||||
|
{
|
||||||
|
if (_userIdentity == null)
|
||||||
|
throw new IllegalStateException("!UserIdentity");
|
||||||
|
return super.getUserIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void readObject(ObjectInputStream stream)
|
private void readObject(ObjectInputStream stream)
|
||||||
throws IOException, ClassNotFoundException
|
throws IOException, ClassNotFoundException
|
||||||
{
|
{
|
||||||
|
@ -66,10 +75,17 @@ public class SessionAuthentication extends AbstractUserAuthentication implements
|
||||||
|
|
||||||
SecurityHandler security=SecurityHandler.getCurrentSecurityHandler();
|
SecurityHandler security=SecurityHandler.getCurrentSecurityHandler();
|
||||||
if (security==null)
|
if (security==null)
|
||||||
throw new IllegalStateException("!SecurityHandler");
|
{
|
||||||
|
if (LOG.isDebugEnabled()) LOG.debug("!SecurityHandler");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LoginService login_service=security.getLoginService();
|
LoginService login_service=security.getLoginService();
|
||||||
if (login_service==null)
|
if (login_service==null)
|
||||||
throw new IllegalStateException("!LoginService");
|
{
|
||||||
|
if (LOG.isDebugEnabled()) LOG.debug("!LoginService");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_userIdentity=login_service.login(_name,_credentials, null);
|
_userIdentity=login_service.login(_name,_credentials, null);
|
||||||
LOG.debug("Deserialized and relogged in {}",this);
|
LOG.debug("Deserialized and relogged in {}",this);
|
||||||
|
|
|
@ -22,6 +22,7 @@ package org.eclipse.jetty.server.session;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
|
@ -54,6 +55,16 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem
|
||||||
*/
|
*/
|
||||||
public abstract void doStore(String id, SessionData data, long lastSaveTime) throws Exception;
|
public abstract void doStore(String id, SessionData data, long lastSaveTime) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the session from persistent store.
|
||||||
|
*
|
||||||
|
* @param id the id of the session to load
|
||||||
|
* @return the re-inflated session
|
||||||
|
*
|
||||||
|
* @throws Exception if unable to load the session
|
||||||
|
*/
|
||||||
|
public abstract SessionData doLoad (String id) throws Exception;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implemented by subclasses to resolve which sessions this node
|
* Implemented by subclasses to resolve which sessions this node
|
||||||
|
@ -64,10 +75,6 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem
|
||||||
*/
|
*/
|
||||||
public abstract Set<String> doGetExpired (Set<String> candidates);
|
public abstract Set<String> doGetExpired (Set<String> candidates);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#initialize(org.eclipse.jetty.server.session.SessionContext)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize (SessionContext context) throws Exception
|
public void initialize (SessionContext context) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -76,16 +83,51 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem
|
||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#store(java.lang.String, org.eclipse.jetty.server.session.SessionData)
|
|
||||||
*/
|
@Override
|
||||||
|
public SessionData load(String id) throws Exception
|
||||||
|
{
|
||||||
|
final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
|
||||||
|
final AtomicReference<Exception> exception = new AtomicReference<Exception>();
|
||||||
|
|
||||||
|
Runnable r = new Runnable()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run ()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reference.set(doLoad(id));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
exception.set(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.run(r);
|
||||||
|
if (exception.get() != null)
|
||||||
|
throw exception.get();
|
||||||
|
|
||||||
|
return reference.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void store(String id, SessionData data) throws Exception
|
public void store(String id, SessionData data) throws Exception
|
||||||
{
|
{
|
||||||
if (data == null)
|
if (data == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
final AtomicReference<Exception> exception = new AtomicReference<Exception>();
|
||||||
|
|
||||||
|
Runnable r = new Runnable()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run ()
|
||||||
|
{
|
||||||
long lastSave = data.getLastSaved();
|
long lastSave = data.getLastSaved();
|
||||||
long savePeriodMs = (_savePeriodSec <=0? 0: TimeUnit.SECONDS.toMillis(_savePeriodSec));
|
long savePeriodMs = (_savePeriodSec <=0? 0: TimeUnit.SECONDS.toMillis(_savePeriodSec));
|
||||||
|
|
||||||
|
@ -107,16 +149,18 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem
|
||||||
{
|
{
|
||||||
//reset last save time if save failed
|
//reset last save time if save failed
|
||||||
data.setLastSaved(lastSave);
|
data.setLastSaved(lastSave);
|
||||||
throw e;
|
exception.set(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.run(r);
|
||||||
|
if (exception.get() != null)
|
||||||
|
throw exception.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getExpired(Set<String> candidates)
|
public Set<String> getExpired(Set<String> candidates)
|
||||||
{
|
{
|
||||||
|
@ -131,11 +175,6 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#newSessionData(java.lang.String, long, long, long, long)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData newSessionData(String id, long created, long accessed, long lastAccessed, long maxInactiveMs)
|
public SessionData newSessionData(String id, long created, long accessed, long lastAccessed, long maxInactiveMs)
|
||||||
{
|
{
|
||||||
|
@ -201,10 +240,6 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem
|
||||||
_savePeriodSec = savePeriodSec;
|
_savePeriodSec = savePeriodSec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.lang.Object#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,15 +31,11 @@ import java.io.OutputStream;
|
||||||
import java.nio.file.FileVisitOption;
|
import java.nio.file.FileVisitOption;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
||||||
import org.eclipse.jetty.util.MultiException;
|
import org.eclipse.jetty.util.MultiException;
|
||||||
|
@ -277,18 +273,9 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#load(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load(String id) throws Exception
|
public SessionData doLoad(String id) throws Exception
|
||||||
{
|
|
||||||
final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
|
|
||||||
final AtomicReference<Exception> exception = new AtomicReference<Exception>();
|
|
||||||
Runnable r = new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run ()
|
|
||||||
{
|
{
|
||||||
//load session info from its file
|
//load session info from its file
|
||||||
String idWithContext = getIdWithContext(id);
|
String idWithContext = getIdWithContext(id);
|
||||||
|
@ -297,21 +284,21 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Unknown file {}",idWithContext);
|
LOG.debug("Unknown file {}",idWithContext);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
File file = new File (_storeDir, filename);
|
File file = new File (_storeDir, filename);
|
||||||
if (!file.exists())
|
if (!file.exists())
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("No such file {}",filename);
|
LOG.debug("No such file {}",filename);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (FileInputStream in = new FileInputStream(file))
|
try (FileInputStream in = new FileInputStream(file))
|
||||||
{
|
{
|
||||||
SessionData data = load(in, id);
|
SessionData data = load(in, id);
|
||||||
data.setLastSaved(file.lastModified());
|
data.setLastSaved(file.lastModified());
|
||||||
reference.set(data);
|
return data;
|
||||||
}
|
}
|
||||||
catch (UnreadableSessionDataException e)
|
catch (UnreadableSessionDataException e)
|
||||||
{
|
{
|
||||||
|
@ -327,30 +314,12 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
LOG.warn("Unable to delete unrestorable file {} for session {}", filename, id, x);
|
LOG.warn("Unable to delete unrestorable file {} for session {}", filename, id, x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
throw e;
|
||||||
exception.set(e);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
exception.set(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
//ensure this runs with the context classloader set
|
|
||||||
_context.run(r);
|
|
||||||
|
|
||||||
if (exception.get() != null)
|
|
||||||
throw exception.get();
|
|
||||||
|
|
||||||
return reference.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(java.lang.String, org.eclipse.jetty.server.session.SessionData, long)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -473,10 +442,6 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
@ManagedAttribute(value="are sessions serialized by this store", readonly=true)
|
@ManagedAttribute(value="are sessions serialized by this store", readonly=true)
|
||||||
public boolean isPassivating()
|
public boolean isPassivating()
|
||||||
|
@ -486,10 +451,6 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#exists(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String id) throws Exception
|
public boolean exists(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -507,8 +468,10 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
return (expiry > System.currentTimeMillis()); //hasn't yet expired
|
return (expiry > System.currentTimeMillis()); //hasn't yet expired
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
/**
|
||||||
|
* Save the session data.
|
||||||
|
*
|
||||||
* @param os the output stream to save to
|
* @param os the output stream to save to
|
||||||
* @param id identity of the session
|
* @param id identity of the session
|
||||||
* @param data the info of the session
|
* @param data the info of the session
|
||||||
|
@ -528,14 +491,8 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
out.writeLong(data.getExpiry());
|
out.writeLong(data.getExpiry());
|
||||||
out.writeLong(data.getMaxInactiveMs());
|
out.writeLong(data.getMaxInactiveMs());
|
||||||
|
|
||||||
List<String> keys = new ArrayList<String>(data.getKeys());
|
|
||||||
out.writeInt(keys.size());
|
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(out);
|
ObjectOutputStream oos = new ObjectOutputStream(out);
|
||||||
for (String name:keys)
|
SessionData.serializeAttributes(data, oos);
|
||||||
{
|
|
||||||
oos.writeUTF(name);
|
|
||||||
oos.writeObject(data.getAttribute(name));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -650,7 +607,9 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param is inputstream containing session data
|
* Load the session data from a file.
|
||||||
|
*
|
||||||
|
* @param is file input stream containing session data
|
||||||
* @param expectedId the id we've been told to load
|
* @param expectedId the id we've been told to load
|
||||||
* @return the session data
|
* @return the session data
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
|
@ -685,8 +644,8 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
data.setMaxInactiveMs(maxIdle);
|
data.setMaxInactiveMs(maxIdle);
|
||||||
|
|
||||||
// Attributes
|
// Attributes
|
||||||
restoreAttributes(di, di.readInt(), data);
|
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is);
|
||||||
|
SessionData.deserializeAttributes(data, ois);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -695,39 +654,10 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param is inputstream containing session data
|
|
||||||
* @param size number of attributes
|
|
||||||
* @param data the data to restore to
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
protected void restoreAttributes (InputStream is, int size, SessionData data)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
if (size>0)
|
|
||||||
{
|
|
||||||
// input stream should not be closed here
|
|
||||||
Map<String,Object> attributes = new HashMap<String,Object>();
|
|
||||||
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is);
|
|
||||||
for (int i=0; i<size;i++)
|
|
||||||
{
|
|
||||||
String key = ois.readUTF();
|
|
||||||
Object value = ois.readObject();
|
|
||||||
attributes.put(key,value);
|
|
||||||
}
|
|
||||||
data.putAllAttributes(attributes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s[dir=%s,deleteUnrestorableFiles=%b]",super.toString(),_storeDir,_deleteUnrestorableFiles);
|
return String.format("%s[dir=%s,deleteUnrestorableFiles=%b]",super.toString(),_storeDir,_deleteUnrestorableFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,9 +30,7 @@ import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
|
@ -549,9 +547,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
statement.executeUpdate(getCreateIndexOverSessionStatementAsString(index2));
|
statement.executeUpdate(getCreateIndexOverSessionStatementAsString(index2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* @see java.lang.Object#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
@ -621,20 +617,8 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#load(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load(String id) throws Exception
|
public SessionData doLoad(String id) throws Exception
|
||||||
{
|
|
||||||
final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
|
|
||||||
final AtomicReference<Exception> exception = new AtomicReference<Exception>();
|
|
||||||
|
|
||||||
Runnable r = new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run ()
|
|
||||||
{
|
{
|
||||||
try (Connection connection = _dbAdaptor.getConnection();
|
try (Connection connection = _dbAdaptor.getConnection();
|
||||||
PreparedStatement statement = _sessionTableSchema.getLoadStatement(connection, id, _context);
|
PreparedStatement statement = _sessionTableSchema.getLoadStatement(connection, id, _context);
|
||||||
|
@ -658,8 +642,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
try (InputStream is = _dbAdaptor.getBlobInputStream(result, _sessionTableSchema.getMapColumn());
|
try (InputStream is = _dbAdaptor.getBlobInputStream(result, _sessionTableSchema.getMapColumn());
|
||||||
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
|
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
|
||||||
{
|
{
|
||||||
Object o = ois.readObject();
|
SessionData.deserializeAttributes(data, ois);
|
||||||
data.putAllAttributes((Map<String,Object>)o);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -673,28 +656,12 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("No session {}", id);
|
LOG.debug("No session {}", id);
|
||||||
|
|
||||||
reference.set(data);
|
return data;
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
exception.set(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
//ensure this runs with context classloader set
|
|
||||||
_context.run(r);
|
|
||||||
if (exception.get() != null)
|
|
||||||
throw exception.get();
|
|
||||||
|
|
||||||
return reference.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#delete(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete(String id) throws Exception
|
public boolean delete(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -712,10 +679,6 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(String, SessionData, long)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -762,12 +725,11 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
statement.setLong(10, data.getExpiry());
|
statement.setLong(10, data.getExpiry());
|
||||||
statement.setLong(11, data.getMaxInactiveMs());
|
statement.setLong(11, data.getMaxInactiveMs());
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
ObjectOutputStream oos = new ObjectOutputStream(baos))
|
||||||
oos.writeObject(data.getAllAttributes());
|
{
|
||||||
oos.flush();
|
SessionData.serializeAttributes(data, oos);
|
||||||
byte[] bytes = baos.toByteArray();
|
byte[] bytes = baos.toByteArray();
|
||||||
|
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||||
statement.setBinaryStream(12, bais, bytes.length);//attribute map as blob
|
statement.setBinaryStream(12, bais, bytes.length);//attribute map as blob
|
||||||
statement.executeUpdate();
|
statement.executeUpdate();
|
||||||
|
@ -776,6 +738,7 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void doUpdate (String id, SessionData data)
|
protected void doUpdate (String id, SessionData data)
|
||||||
|
@ -793,12 +756,13 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
statement.setLong(5, data.getExpiry());
|
statement.setLong(5, data.getExpiry());
|
||||||
statement.setLong(6, data.getMaxInactiveMs());
|
statement.setLong(6, data.getMaxInactiveMs());
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
try(ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
ObjectOutputStream oos = new ObjectOutputStream(baos))
|
||||||
oos.writeObject(data.getAllAttributes());
|
{
|
||||||
oos.flush();
|
SessionData.serializeAttributes(data, oos);
|
||||||
byte[] bytes = baos.toByteArray();
|
byte[] bytes = baos.toByteArray();
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes))
|
||||||
|
{
|
||||||
statement.setBinaryStream(7, bais, bytes.length);//attribute map as blob
|
statement.setBinaryStream(7, bais, bytes.length);//attribute map as blob
|
||||||
statement.executeUpdate();
|
statement.executeUpdate();
|
||||||
|
|
||||||
|
@ -807,12 +771,10 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> doGetExpired(Set<String> candidates)
|
public Set<String> doGetExpired(Set<String> candidates)
|
||||||
{
|
{
|
||||||
|
@ -940,10 +902,6 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
@ManagedAttribute(value="does this store serialize sessions", readonly=true)
|
@ManagedAttribute(value="does this store serialize sessions", readonly=true)
|
||||||
public boolean isPassivating()
|
public boolean isPassivating()
|
||||||
|
@ -954,9 +912,6 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#exists(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String id)
|
public boolean exists(String id)
|
||||||
throws Exception
|
throws Exception
|
||||||
|
@ -988,11 +943,3 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,28 +33,20 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
public class NullSessionDataStore extends AbstractSessionDataStore
|
public class NullSessionDataStore extends AbstractSessionDataStore
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#load(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load(String id) throws Exception
|
public SessionData doLoad(String id) throws Exception
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#newSessionData(java.lang.String, long, long, long, long)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData newSessionData(String id, long created, long accessed, long lastAccessed, long maxInactiveMs)
|
public SessionData newSessionData(String id, long created, long accessed, long lastAccessed, long maxInactiveMs)
|
||||||
{
|
{
|
||||||
return new SessionData(id, _context.getCanonicalContextPath(), _context.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
|
return new SessionData(id, _context.getCanonicalContextPath(), _context.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#delete(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete(String id) throws Exception
|
public boolean delete(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -62,9 +54,7 @@ public class NullSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(java.lang.String, org.eclipse.jetty.server.session.SessionData, long)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -72,9 +62,7 @@ public class NullSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> doGetExpired(Set<String> candidates)
|
public Set<String> doGetExpired(Set<String> candidates)
|
||||||
{
|
{
|
||||||
|
@ -82,9 +70,7 @@ public class NullSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
|
|
||||||
*/
|
|
||||||
@ManagedAttribute(value="does this store serialize sessions", readonly=true)
|
@ManagedAttribute(value="does this store serialize sessions", readonly=true)
|
||||||
@Override
|
@Override
|
||||||
public boolean isPassivating()
|
public boolean isPassivating()
|
||||||
|
@ -93,9 +79,7 @@ public class NullSessionDataStore extends AbstractSessionDataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#exists(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String id)
|
public boolean exists(String id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,9 +23,11 @@ import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
@ -57,6 +59,85 @@ public class SessionData implements Serializable
|
||||||
protected boolean _dirty;
|
protected boolean _dirty;
|
||||||
protected long _lastSaved; //time in msec since last save
|
protected long _lastSaved; //time in msec since last save
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize the attribute map of the session.
|
||||||
|
*
|
||||||
|
* This special handling allows us to record which classloader should be used to load the value of the
|
||||||
|
* attribute: either the container classloader (which could be the application loader ie null, or jetty's
|
||||||
|
* startjar loader) or the webapp's classloader.
|
||||||
|
*
|
||||||
|
* @param data the SessionData for which to serialize the attributes
|
||||||
|
* @param out the stream to which to serialize
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void serializeAttributes (SessionData data, java.io.ObjectOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
int entries = data._attributes.size();
|
||||||
|
out.writeObject(entries);
|
||||||
|
for (Entry<String,Object> entry: data._attributes.entrySet())
|
||||||
|
{
|
||||||
|
out.writeUTF(entry.getKey());
|
||||||
|
ClassLoader loader = entry.getValue().getClass().getClassLoader();
|
||||||
|
boolean isServerLoader = false;
|
||||||
|
|
||||||
|
if (loader == Thread.currentThread().getContextClassLoader()) //is it the webapp classloader?
|
||||||
|
isServerLoader = false;
|
||||||
|
else if (loader == Thread.currentThread().getContextClassLoader().getParent() || loader == SessionData.class.getClassLoader() || loader == null) // is it the container loader?
|
||||||
|
isServerLoader = true;
|
||||||
|
else
|
||||||
|
throw new IOException ("Unknown loader"); // we don't know what loader to use
|
||||||
|
|
||||||
|
out.writeBoolean(isServerLoader);
|
||||||
|
out.writeObject(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* De-serialize the attribute map of a session.
|
||||||
|
*
|
||||||
|
* When the session was serialized, we will have recorded which classloader should be used to
|
||||||
|
* recover the attribute value. The classloader could be the container classloader, or the
|
||||||
|
* webapp classloader.
|
||||||
|
*
|
||||||
|
* @param data the SessionData for which to deserialize the attribute map
|
||||||
|
* @param in the serialized stream
|
||||||
|
* @throws IOException
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
*/
|
||||||
|
public static void deserializeAttributes (SessionData data, java.io.ObjectInputStream in)
|
||||||
|
throws IOException, ClassNotFoundException
|
||||||
|
{
|
||||||
|
Object o = in.readObject();
|
||||||
|
if (o instanceof Integer)
|
||||||
|
{
|
||||||
|
//new serialization was used
|
||||||
|
if (!(ClassLoadingObjectInputStream.class.isAssignableFrom(in.getClass())))
|
||||||
|
throw new IOException ("Not ClassLoadingObjectInputStream");
|
||||||
|
|
||||||
|
data._attributes = new ConcurrentHashMap<>();
|
||||||
|
int entries = ((Integer)o).intValue();
|
||||||
|
for (int i=0; i < entries; i++)
|
||||||
|
{
|
||||||
|
String name = in.readUTF(); //attribute name
|
||||||
|
boolean isServerClassLoader = in.readBoolean(); //use server or webapp classloader to load
|
||||||
|
|
||||||
|
Object value = ((ClassLoadingObjectInputStream)in).readObject(isServerClassLoader?SessionData.class.getClassLoader():Thread.currentThread().getContextClassLoader());
|
||||||
|
data._attributes.put(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.info("Legacy serialization detected for {}", data.getId());
|
||||||
|
//legacy serialization was used, we have just deserialized the
|
||||||
|
//entire attribute map
|
||||||
|
data._attributes = new ConcurrentHashMap<String, Object>();
|
||||||
|
data.putAllAttributes((Map<String,Object>)o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public SessionData (String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs)
|
public SessionData (String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs)
|
||||||
{
|
{
|
||||||
this(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs, new ConcurrentHashMap<String, Object>());
|
this(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs, new ConcurrentHashMap<String, Object>());
|
||||||
|
@ -335,16 +416,14 @@ public class SessionData implements Serializable
|
||||||
out.writeUTF(_id); //session id
|
out.writeUTF(_id); //session id
|
||||||
out.writeUTF(_contextPath); //context path
|
out.writeUTF(_contextPath); //context path
|
||||||
out.writeUTF(_vhost); //first vhost
|
out.writeUTF(_vhost); //first vhost
|
||||||
|
|
||||||
out.writeLong(_accessed);//accessTime
|
out.writeLong(_accessed);//accessTime
|
||||||
out.writeLong(_lastAccessed); //lastAccessTime
|
out.writeLong(_lastAccessed); //lastAccessTime
|
||||||
out.writeLong(_created); //time created
|
out.writeLong(_created); //time created
|
||||||
out.writeLong(_cookieSet);//time cookie was set
|
out.writeLong(_cookieSet);//time cookie was set
|
||||||
out.writeUTF(_lastNode); //name of last node managing
|
out.writeUTF(_lastNode); //name of last node managing
|
||||||
|
|
||||||
out.writeLong(_expiry);
|
out.writeLong(_expiry);
|
||||||
out.writeLong(_maxInactiveMs);
|
out.writeLong(_maxInactiveMs);
|
||||||
out.writeObject(_attributes);
|
serializeAttributes(this, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
|
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
|
||||||
|
@ -352,7 +431,6 @@ public class SessionData implements Serializable
|
||||||
_id = in.readUTF();
|
_id = in.readUTF();
|
||||||
_contextPath = in.readUTF();
|
_contextPath = in.readUTF();
|
||||||
_vhost = in.readUTF();
|
_vhost = in.readUTF();
|
||||||
|
|
||||||
_accessed = in.readLong();//accessTime
|
_accessed = in.readLong();//accessTime
|
||||||
_lastAccessed = in.readLong(); //lastAccessTime
|
_lastAccessed = in.readLong(); //lastAccessTime
|
||||||
_created = in.readLong(); //time created
|
_created = in.readLong(); //time created
|
||||||
|
@ -360,7 +438,7 @@ public class SessionData implements Serializable
|
||||||
_lastNode = in.readUTF(); //last managing node
|
_lastNode = in.readUTF(); //last managing node
|
||||||
_expiry = in.readLong();
|
_expiry = in.readLong();
|
||||||
_maxInactiveMs = in.readLong();
|
_maxInactiveMs = in.readLong();
|
||||||
_attributes = (Map<String,Object>)in.readObject();
|
deserializeAttributes(this, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isExpiredAt (long time)
|
public boolean isExpiredAt (long time)
|
||||||
|
|
|
@ -34,6 +34,20 @@ import java.lang.reflect.Proxy;
|
||||||
*/
|
*/
|
||||||
public class ClassLoadingObjectInputStream extends ObjectInputStream
|
public class ClassLoadingObjectInputStream extends ObjectInputStream
|
||||||
{
|
{
|
||||||
|
|
||||||
|
protected static class ClassLoaderThreadLocal extends ThreadLocal<ClassLoader>
|
||||||
|
{
|
||||||
|
protected static final ClassLoader UNSET = new ClassLoader() {};
|
||||||
|
@Override
|
||||||
|
protected ClassLoader initialValue()
|
||||||
|
{
|
||||||
|
return UNSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private ThreadLocal<ClassLoader> _classloader = new ClassLoaderThreadLocal();
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
|
public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
|
||||||
{
|
{
|
||||||
|
@ -46,13 +60,33 @@ public class ClassLoadingObjectInputStream extends ObjectInputStream
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Object readObject (ClassLoader loader)
|
||||||
|
throws IOException, ClassNotFoundException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_classloader.set(loader);
|
||||||
|
return readObject();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_classloader.set(ClassLoaderThreadLocal.UNSET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@Override
|
@Override
|
||||||
public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
|
public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
|
ClassLoader loader = _classloader.get();
|
||||||
|
if (ClassLoaderThreadLocal.UNSET == loader)
|
||||||
|
loader = Thread.currentThread().getContextClassLoader();
|
||||||
|
|
||||||
|
return Class.forName(cl.getName(), false, loader);
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException e)
|
catch (ClassNotFoundException e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,9 +31,6 @@ import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
||||||
|
@ -161,9 +158,8 @@ public class FileTestHelper
|
||||||
{
|
{
|
||||||
String filename = ""+expiry+"_"+contextPath+"_"+vhost+"_"+id;
|
String filename = ""+expiry+"_"+contextPath+"_"+vhost+"_"+id;
|
||||||
File file = new File(_tmpDir, filename);
|
File file = new File(_tmpDir, filename);
|
||||||
try(FileOutputStream fos = new FileOutputStream(file,false))
|
try (FileOutputStream fos = new FileOutputStream(file,false); DataOutputStream out = new DataOutputStream(fos))
|
||||||
{
|
{
|
||||||
DataOutputStream out = new DataOutputStream(fos);
|
|
||||||
out.writeUTF(id);
|
out.writeUTF(id);
|
||||||
out.writeUTF(contextPath);
|
out.writeUTF(contextPath);
|
||||||
out.writeUTF(vhost);
|
out.writeUTF(vhost);
|
||||||
|
@ -177,14 +173,9 @@ public class FileTestHelper
|
||||||
|
|
||||||
if (attributes != null)
|
if (attributes != null)
|
||||||
{
|
{
|
||||||
List<String> keys = new ArrayList<String>(attributes.keySet());
|
SessionData tmp = new SessionData(id,contextPath, vhost, created, accessed, lastAccessed, maxIdle);
|
||||||
out.writeInt(keys.size());
|
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(out);
|
ObjectOutputStream oos = new ObjectOutputStream(out);
|
||||||
for (String name:keys)
|
SessionData.serializeAttributes(tmp, oos);
|
||||||
{
|
|
||||||
oos.writeUTF(name);
|
|
||||||
oos.writeObject(attributes.get(name));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,10 +188,8 @@ public class FileTestHelper
|
||||||
File file = new File(_tmpDir, filename);
|
File file = new File(_tmpDir, filename);
|
||||||
assertTrue(file.exists());
|
assertTrue(file.exists());
|
||||||
|
|
||||||
try (FileInputStream in = new FileInputStream(file))
|
try (FileInputStream in = new FileInputStream(file); DataInputStream di = new DataInputStream(in))
|
||||||
{
|
{
|
||||||
DataInputStream di = new DataInputStream(in);
|
|
||||||
|
|
||||||
String id = di.readUTF();
|
String id = di.readUTF();
|
||||||
String contextPath = di.readUTF();
|
String contextPath = di.readUTF();
|
||||||
String vhost = di.readUTF();
|
String vhost = di.readUTF();
|
||||||
|
@ -223,28 +212,18 @@ public class FileTestHelper
|
||||||
assertEquals(data.getExpiry(), expiry);
|
assertEquals(data.getExpiry(), expiry);
|
||||||
assertEquals(data.getMaxInactiveMs(), maxIdle);
|
assertEquals(data.getMaxInactiveMs(), maxIdle);
|
||||||
|
|
||||||
Map<String,Object> attributes = new HashMap<>();
|
SessionData tmp = new SessionData(id, contextPath, vhost, created, accessed, lastAccessed, maxIdle);
|
||||||
|
|
||||||
int size = di.readInt();
|
|
||||||
if (size > 0)
|
|
||||||
{
|
|
||||||
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(di);
|
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(di);
|
||||||
for (int i=0; i<size;i++)
|
SessionData.deserializeAttributes(tmp, ois);
|
||||||
{
|
|
||||||
String key = ois.readUTF();
|
|
||||||
Object value = ois.readObject();
|
|
||||||
attributes.put(key,value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//same number of attributes
|
//same number of attributes
|
||||||
assertEquals(data.getAllAttributes().size(), attributes.size());
|
assertEquals(data.getAllAttributes().size(), tmp.getAllAttributes().size());
|
||||||
//same keys
|
//same keys
|
||||||
assertTrue(data.getKeys().equals(attributes.keySet()));
|
assertTrue(data.getKeys().equals(tmp.getAllAttributes().keySet()));
|
||||||
//same values
|
//same values
|
||||||
for (String name:data.getKeys())
|
for (String name:data.getKeys())
|
||||||
{
|
{
|
||||||
assertTrue(data.getAttribute(name).equals(attributes.get(name)));
|
assertTrue(data.getAttribute(name).equals(tmp.getAttribute(name)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<configuration>
|
<configuration>
|
||||||
<skipTests>true</skipTests>
|
<skipTests>true</skipTests>
|
||||||
<test>GCloudTestSuite</test>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
|
|
@ -45,16 +45,10 @@ public class ClusteredOrphanedSessionTest extends AbstractClusteredOrphanedSessi
|
||||||
@AfterAll
|
@AfterAll
|
||||||
public static void tearDown () throws Exception
|
public static void tearDown () throws Exception
|
||||||
{
|
{
|
||||||
|
__testSupport.deleteSessions();
|
||||||
__testSupport.tearDown();
|
__testSupport.tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterAll
|
|
||||||
public static void teardown () throws Exception
|
|
||||||
{
|
|
||||||
__testSupport.deleteSessions();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see org.eclipse.jetty.server.session.AbstractTestBase#createSessionDataStoreFactory()
|
* @see org.eclipse.jetty.server.session.AbstractTestBase#createSessionDataStoreFactory()
|
||||||
|
|
|
@ -43,14 +43,9 @@ public class ClusteredSessionScavengingTest extends AbstractClusteredSessionScav
|
||||||
|
|
||||||
@AfterAll
|
@AfterAll
|
||||||
public static void tearDown () throws Exception
|
public static void tearDown () throws Exception
|
||||||
{
|
|
||||||
__testSupport.tearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterAll
|
|
||||||
public static void teardown () throws Exception
|
|
||||||
{
|
{
|
||||||
__testSupport.deleteSessions();
|
__testSupport.deleteSessions();
|
||||||
|
__testSupport.tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -133,12 +133,13 @@ public class GCloudSessionTestSupport
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
//serialize the attribute map
|
//serialize the attribute map
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream())
|
||||||
|
{
|
||||||
if (attributes != null)
|
if (attributes != null)
|
||||||
{
|
{
|
||||||
|
SessionData tmp = new SessionData(id, contextPath, vhost, created, accessed, lastAccessed, maxIdle);
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||||
oos.writeObject(attributes);
|
SessionData.serializeAttributes(tmp, oos);
|
||||||
oos.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//turn a session into an entity
|
//turn a session into an entity
|
||||||
|
@ -156,11 +157,11 @@ public class GCloudSessionTestSupport
|
||||||
.set(EntityDataModel.LASTSAVED, lastSaved);
|
.set(EntityDataModel.LASTSAVED, lastSaved);
|
||||||
if (attributes != null)
|
if (attributes != null)
|
||||||
builder.set(EntityDataModel.ATTRIBUTES, BlobValue.newBuilder(Blob.copyFrom(baos.toByteArray())).setExcludeFromIndexes(true).build());
|
builder.set(EntityDataModel.ATTRIBUTES, BlobValue.newBuilder(Blob.copyFrom(baos.toByteArray())).setExcludeFromIndexes(true).build());
|
||||||
|
|
||||||
Entity entity = builder.build();
|
Entity entity = builder.build();
|
||||||
|
|
||||||
_ds.put(entity);
|
_ds.put(entity);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean checkSessionPersisted (SessionData data)
|
public boolean checkSessionPersisted (SessionData data)
|
||||||
throws Exception
|
throws Exception
|
||||||
|
@ -183,21 +184,27 @@ public class GCloudSessionTestSupport
|
||||||
assertEquals(data.getMaxInactiveMs(), entity.getLong(EntityDataModel.MAXINACTIVE));
|
assertEquals(data.getMaxInactiveMs(), entity.getLong(EntityDataModel.MAXINACTIVE));
|
||||||
Blob blob = (Blob) entity.getBlob(EntityDataModel.ATTRIBUTES);
|
Blob blob = (Blob) entity.getBlob(EntityDataModel.ATTRIBUTES);
|
||||||
|
|
||||||
Map<String,Object> attributes = new HashMap<>();
|
SessionData tmp = new SessionData(data.getId(),entity.getString(EntityDataModel.CONTEXTPATH),
|
||||||
|
entity.getString(EntityDataModel.VHOST),
|
||||||
|
entity.getLong(EntityDataModel.CREATETIME),
|
||||||
|
entity.getLong(EntityDataModel.ACCESSED),
|
||||||
|
entity.getLong(EntityDataModel.LASTACCESSED),
|
||||||
|
entity.getLong(EntityDataModel.MAXINACTIVE));
|
||||||
|
|
||||||
try (ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(blob.asInputStream()))
|
try (ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(blob.asInputStream()))
|
||||||
{
|
{
|
||||||
Object o = ois.readObject();
|
SessionData.deserializeAttributes(tmp, ois);
|
||||||
attributes.putAll((Map<String,Object>)o);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//same number of attributes
|
//same number of attributes
|
||||||
assertEquals(data.getAllAttributes().size(), attributes.size());
|
assertEquals(data.getAllAttributes().size(), tmp.getAllAttributes().size());
|
||||||
//same keys
|
//same keys
|
||||||
assertTrue(data.getKeys().equals(attributes.keySet()));
|
assertTrue(data.getKeys().equals(tmp.getAllAttributes().keySet()));
|
||||||
//same values
|
//same values
|
||||||
for (String name:data.getKeys())
|
for (String name:data.getKeys())
|
||||||
{
|
{
|
||||||
assertTrue(data.getAttribute(name).equals(attributes.get(name)));
|
assertTrue(data.getAttribute(name).equals(tmp.getAttribute(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -45,14 +45,10 @@ public class InvalidationSessionTest extends AbstractClusteredInvalidationSessio
|
||||||
@AfterAll
|
@AfterAll
|
||||||
public static void tearDown () throws Exception
|
public static void tearDown () throws Exception
|
||||||
{
|
{
|
||||||
|
__testSupport.deleteSessions();
|
||||||
__testSupport.tearDown();
|
__testSupport.tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterAll
|
|
||||||
public static void teardown () throws Exception
|
|
||||||
{
|
|
||||||
__testSupport.deleteSessions();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see org.eclipse.jetty.server.session.AbstractTestBase#createSessionDataStoreFactory()
|
* @see org.eclipse.jetty.server.session.AbstractTestBase#createSessionDataStoreFactory()
|
||||||
|
|
|
@ -27,6 +27,8 @@ import java.util.concurrent.TimeUnit;
|
||||||
import com.hazelcast.config.JoinConfig;
|
import com.hazelcast.config.JoinConfig;
|
||||||
import com.hazelcast.config.MulticastConfig;
|
import com.hazelcast.config.MulticastConfig;
|
||||||
import com.hazelcast.config.NetworkConfig;
|
import com.hazelcast.config.NetworkConfig;
|
||||||
|
import com.hazelcast.config.SerializerConfig;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.session.SessionData;
|
import org.eclipse.jetty.server.session.SessionData;
|
||||||
import org.eclipse.jetty.server.session.SessionDataStoreFactory;
|
import org.eclipse.jetty.server.session.SessionDataStoreFactory;
|
||||||
|
|
||||||
|
@ -45,12 +47,21 @@ public class HazelcastTestHelper
|
||||||
static final String _hazelcastInstanceName = "SESSION_TEST_"+Long.toString( TimeUnit.NANOSECONDS.toMillis(System.nanoTime()));
|
static final String _hazelcastInstanceName = "SESSION_TEST_"+Long.toString( TimeUnit.NANOSECONDS.toMillis(System.nanoTime()));
|
||||||
|
|
||||||
static final String _name = Long.toString( TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) );
|
static final String _name = Long.toString( TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) );
|
||||||
static final HazelcastInstance _instance = Hazelcast.getOrCreateHazelcastInstance( new Config() //
|
|
||||||
.setInstanceName(_hazelcastInstanceName ) //
|
static SerializerConfig _serializerConfig;
|
||||||
.setNetworkConfig( new NetworkConfig().setJoin( //
|
|
||||||
new JoinConfig().setMulticastConfig( //
|
static HazelcastInstance _instance;
|
||||||
new MulticastConfig().setEnabled( false ) ) ) ) //
|
|
||||||
.addMapConfig( new MapConfig().setName(_name) ) );
|
static
|
||||||
|
{
|
||||||
|
_serializerConfig = new SerializerConfig().setImplementation(new SessionDataSerializer()).setTypeClass(SessionData.class);
|
||||||
|
Config config = new Config();
|
||||||
|
config.setInstanceName(_hazelcastInstanceName );
|
||||||
|
config.setNetworkConfig( new NetworkConfig().setJoin(new JoinConfig().setMulticastConfig(new MulticastConfig().setEnabled(false))));
|
||||||
|
config.addMapConfig(new MapConfig().setName(_name));
|
||||||
|
config.getSerializationConfig().addSerializerConfig(_serializerConfig);
|
||||||
|
_instance = Hazelcast.getOrCreateHazelcastInstance(config);
|
||||||
|
}
|
||||||
|
|
||||||
public HazelcastTestHelper ()
|
public HazelcastTestHelper ()
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
<url>http://www.eclipse.org/jetty</url>
|
<url>http://www.eclipse.org/jetty</url>
|
||||||
<properties>
|
<properties>
|
||||||
<bundle-symbolic-name>${project.groupId}.sessions.infinispan</bundle-symbolic-name>
|
<bundle-symbolic-name>${project.groupId}.sessions.infinispan</bundle-symbolic-name>
|
||||||
|
<hotrod.host>127.0.0.1</hotrod.host>
|
||||||
</properties>
|
</properties>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
@ -114,6 +115,12 @@
|
||||||
<version>9.1.0.Final</version>
|
<version>9.1.0.Final</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.infinispan</groupId>
|
||||||
|
<artifactId>infinispan-remote-query-client</artifactId>
|
||||||
|
<version>9.1.0.Final</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<profiles>
|
<profiles>
|
||||||
<!-- to test hotrod, configure a cache called "remote-session-test" -->
|
<!-- to test hotrod, configure a cache called "remote-session-test" -->
|
||||||
|
|
|
@ -21,10 +21,21 @@ package org.eclipse.jetty.server.session.remote;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.session.SessionData;
|
import org.eclipse.jetty.server.session.SessionData;
|
||||||
|
import org.eclipse.jetty.session.infinispan.InfinispanSessionData;
|
||||||
|
import org.eclipse.jetty.session.infinispan.SessionDataMarshaller;
|
||||||
|
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
|
||||||
import org.infinispan.client.hotrod.RemoteCache;
|
import org.infinispan.client.hotrod.RemoteCache;
|
||||||
import org.infinispan.client.hotrod.RemoteCacheManager;
|
import org.infinispan.client.hotrod.RemoteCacheManager;
|
||||||
|
import org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller;
|
||||||
|
import org.infinispan.protostream.FileDescriptorSource;
|
||||||
|
import org.infinispan.protostream.SerializationContext;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RemoteInfinispanTestSupport
|
* RemoteInfinispanTestSupport
|
||||||
|
@ -34,7 +45,7 @@ import org.infinispan.client.hotrod.RemoteCacheManager;
|
||||||
public class RemoteInfinispanTestSupport
|
public class RemoteInfinispanTestSupport
|
||||||
{
|
{
|
||||||
public static final String DEFAULT_CACHE_NAME = "session_test_cache";
|
public static final String DEFAULT_CACHE_NAME = "session_test_cache";
|
||||||
public RemoteCache _cache;
|
public RemoteCache<String,Object> _cache;
|
||||||
private String _name;
|
private String _name;
|
||||||
public static RemoteCacheManager _manager;
|
public static RemoteCacheManager _manager;
|
||||||
|
|
||||||
|
@ -42,11 +53,24 @@ public class RemoteInfinispanTestSupport
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_manager = new RemoteCacheManager();
|
String host = System.getProperty("hotrod.host","127.0.0.1");
|
||||||
|
Properties properties = new Properties();
|
||||||
|
|
||||||
|
ConfigurationBuilder clientBuilder = new ConfigurationBuilder();
|
||||||
|
clientBuilder.withProperties(properties).addServer().host(host).marshaller(new ProtoStreamMarshaller());
|
||||||
|
|
||||||
|
_manager = new RemoteCacheManager(clientBuilder.build());
|
||||||
|
|
||||||
|
FileDescriptorSource fds = new FileDescriptorSource();
|
||||||
|
fds.addProtoFiles("/session.proto");
|
||||||
|
|
||||||
|
SerializationContext serCtx = ProtoStreamMarshaller.getSerializationContext(_manager);
|
||||||
|
serCtx.registerProtoFiles(fds);
|
||||||
|
serCtx.registerMarshaller(new SessionDataMarshaller());
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
fail(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +89,7 @@ public class RemoteInfinispanTestSupport
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public RemoteCache getCache ()
|
public RemoteCache<String,Object> getCache ()
|
||||||
{
|
{
|
||||||
return _cache;
|
return _cache;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +107,6 @@ public class RemoteInfinispanTestSupport
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void createSession (SessionData data)
|
public void createSession (SessionData data)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
|
@ -111,7 +134,8 @@ public class RemoteInfinispanTestSupport
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SessionData saved = (SessionData)obj;
|
InfinispanSessionData saved = (InfinispanSessionData)obj;
|
||||||
|
saved.deserializeAttributes();
|
||||||
|
|
||||||
assertEquals(data.getId(), saved.getId());
|
assertEquals(data.getId(), saved.getId());
|
||||||
assertEquals(data.getContextPath(), saved.getContextPath());
|
assertEquals(data.getContextPath(), saved.getContextPath());
|
||||||
|
|
|
@ -210,24 +210,26 @@ public class JdbcTestHelper
|
||||||
assertEquals(data.getContextPath(), result.getString(CONTEXT_COL));
|
assertEquals(data.getContextPath(), result.getString(CONTEXT_COL));
|
||||||
assertEquals(data.getVhost(), result.getString("virtualHost"));
|
assertEquals(data.getVhost(), result.getString("virtualHost"));
|
||||||
|
|
||||||
Map<String,Object> attributes = new HashMap<>();
|
|
||||||
Blob blob = result.getBlob(MAP_COL);
|
Blob blob = result.getBlob(MAP_COL);
|
||||||
|
|
||||||
|
SessionData tmp = new SessionData(data.getId(), data.getContextPath(), data.getVhost(), result.getLong(CREATE_COL),
|
||||||
|
result.getLong(ACCESS_COL), result.getLong(LAST_ACCESS_COL),result.getLong(MAX_IDLE_COL));
|
||||||
|
|
||||||
try (InputStream is = blob.getBinaryStream();
|
try (InputStream is = blob.getBinaryStream();
|
||||||
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
|
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
|
||||||
{
|
{
|
||||||
Object o = ois.readObject();
|
|
||||||
attributes.putAll((Map<String,Object>)o);
|
SessionData.deserializeAttributes(tmp, ois);
|
||||||
}
|
}
|
||||||
|
|
||||||
//same number of attributes
|
//same number of attributes
|
||||||
assertEquals(data.getAllAttributes().size(), attributes.size());
|
assertEquals(data.getAllAttributes().size(), tmp.getAllAttributes().size());
|
||||||
//same keys
|
//same keys
|
||||||
assertTrue(data.getKeys().equals(attributes.keySet()));
|
assertTrue(data.getKeys().equals(tmp.getAllAttributes().keySet()));
|
||||||
//same values
|
//same values
|
||||||
for (String name:data.getKeys())
|
for (String name:data.getKeys())
|
||||||
{
|
{
|
||||||
assertTrue(data.getAttribute(name).equals(attributes.get(name)));
|
assertTrue(data.getAttribute(name).equals(tmp.getAttribute(name)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -303,15 +305,18 @@ public class JdbcTestHelper
|
||||||
|
|
||||||
if (attributes != null)
|
if (attributes != null)
|
||||||
{
|
{
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
SessionData tmp = new SessionData (id, contextPath, vhost, created, accessed, lastAccessed, maxIdle);
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
Map<String,Object> emptyMap = Collections.emptyMap();
|
ObjectOutputStream oos = new ObjectOutputStream(baos);)
|
||||||
oos.writeObject(emptyMap);
|
{
|
||||||
oos.flush();
|
SessionData.serializeAttributes(tmp, oos);
|
||||||
byte[] bytes = baos.toByteArray();
|
byte[] bytes = baos.toByteArray();
|
||||||
|
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);)
|
||||||
statement.setBinaryStream(12, bais, bytes.length);//attribute map as blob
|
{
|
||||||
|
statement.setBinaryStream(12, bais, bytes.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
statement.setBinaryStream(12, new ByteArrayInputStream("".getBytes()), 0);
|
statement.setBinaryStream(12, new ByteArrayInputStream("".getBytes()), 0);
|
||||||
|
|
|
@ -62,6 +62,12 @@
|
||||||
<artifactId>jetty-test-helper</artifactId>
|
<artifactId>jetty-test-helper</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<version>1.7.9</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
|
|
|
@ -48,29 +48,22 @@ public class MemcachedTestHelper
|
||||||
private int _loadCount = 0;
|
private int _loadCount = 0;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPassivating()
|
public boolean isPassivating()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#exists(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String id) throws Exception
|
public boolean exists(String id) throws Exception
|
||||||
{
|
{
|
||||||
return _store.get(id) != null;
|
return _store.get(id) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataMap#load(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load(String id) throws Exception
|
public SessionData doLoad(String id) throws Exception
|
||||||
{
|
{
|
||||||
_loadCount++;
|
_loadCount++;
|
||||||
return _store.get(id);
|
return _store.get(id);
|
||||||
|
@ -86,18 +79,12 @@ public class MemcachedTestHelper
|
||||||
return _loadCount;
|
return _loadCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataMap#delete(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete(String id) throws Exception
|
public boolean delete(String id) throws Exception
|
||||||
{
|
{
|
||||||
return (_store.remove(id) != null);
|
return (_store.remove(id) != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(java.lang.String, org.eclipse.jetty.server.session.SessionData, long)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -105,9 +92,6 @@ public class MemcachedTestHelper
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doGetExpired(java.util.Set)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> doGetExpired(Set<String> candidates)
|
public Set<String> doGetExpired(Set<String> candidates)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,11 +19,24 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.nosql.mongodb;
|
package org.eclipse.jetty.nosql.mongodb;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.session.AbstractSessionDataStoreFactory;
|
||||||
import org.eclipse.jetty.server.session.AbstractSessionDataStoreTest;
|
import org.eclipse.jetty.server.session.AbstractSessionDataStoreTest;
|
||||||
|
import org.eclipse.jetty.server.session.SessionContext;
|
||||||
import org.eclipse.jetty.server.session.SessionData;
|
import org.eclipse.jetty.server.session.SessionData;
|
||||||
|
import org.eclipse.jetty.server.session.SessionDataStore;
|
||||||
import org.eclipse.jetty.server.session.SessionDataStoreFactory;
|
import org.eclipse.jetty.server.session.SessionDataStoreFactory;
|
||||||
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MongoSessionDataStoreTest
|
* MongoSessionDataStoreTest
|
||||||
|
@ -81,4 +94,55 @@ public class MongoSessionDataStoreTest extends AbstractSessionDataStoreTest
|
||||||
{
|
{
|
||||||
return MongoTestHelper.checkSessionPersisted(data);
|
return MongoTestHelper.checkSessionPersisted(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that a session stored in the legacy attribute
|
||||||
|
* format can be read.
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testReadLegacySession() throws Exception
|
||||||
|
{
|
||||||
|
//create the SessionDataStore
|
||||||
|
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||||
|
context.setContextPath("/legacy");
|
||||||
|
SessionDataStoreFactory factory = createSessionDataStoreFactory();
|
||||||
|
((AbstractSessionDataStoreFactory)factory).setGracePeriodSec(GRACE_PERIOD_SEC);
|
||||||
|
SessionDataStore store = factory.getSessionDataStore(context.getSessionHandler());
|
||||||
|
SessionContext sessionContext = new SessionContext("foo", context.getServletContext());
|
||||||
|
store.initialize(sessionContext);
|
||||||
|
|
||||||
|
//persist an old-style session
|
||||||
|
|
||||||
|
Map<String,Object> attributes = new HashMap<>();
|
||||||
|
attributes.put("attribute1", "attribute1value");
|
||||||
|
attributes.put("attribute2", new ArrayList<String>(Arrays.asList("1", "2", "3")));
|
||||||
|
MongoTestHelper.createLegacySession("1234",
|
||||||
|
sessionContext.getCanonicalContextPath(), sessionContext.getVhost(),
|
||||||
|
"foo",
|
||||||
|
1000L, System.currentTimeMillis()-1000L, System.currentTimeMillis()-2000L,
|
||||||
|
-1, -1,
|
||||||
|
attributes );
|
||||||
|
|
||||||
|
store.start();
|
||||||
|
|
||||||
|
//test that we can retrieve it
|
||||||
|
SessionData loaded = store.load("1234");
|
||||||
|
assertNotNull(loaded);
|
||||||
|
assertEquals("1234", loaded.getId());
|
||||||
|
assertEquals(1000L, loaded.getCreated());
|
||||||
|
|
||||||
|
assertEquals("attribute1value", loaded.getAttribute("attribute1"));
|
||||||
|
assertNotNull(loaded.getAttribute("attribute2"));
|
||||||
|
|
||||||
|
|
||||||
|
//test that we can write it
|
||||||
|
store.store("1234", loaded);
|
||||||
|
|
||||||
|
//and that it has now been written out with the new format
|
||||||
|
MongoTestHelper.checkSessionPersisted(loaded);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,26 +22,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.sql.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.mongodb.MongoClient;
|
|
||||||
import org.eclipse.jetty.nosql.NoSqlSessionDataStore.NoSqlSessionData;
|
|
||||||
import org.eclipse.jetty.server.session.SessionData;
|
import org.eclipse.jetty.server.session.SessionData;
|
||||||
|
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
import com.mongodb.BasicDBObject;
|
import com.mongodb.BasicDBObject;
|
||||||
import com.mongodb.DBCollection;
|
import com.mongodb.DBCollection;
|
||||||
import com.mongodb.DBObject;
|
import com.mongodb.DBObject;
|
||||||
import com.mongodb.Mongo;
|
import com.mongodb.MongoClient;
|
||||||
import com.mongodb.MongoException;
|
import com.mongodb.MongoException;
|
||||||
import com.mongodb.WriteConcern;
|
import com.mongodb.WriteConcern;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -148,10 +145,9 @@ public class MongoTestHelper
|
||||||
MongoSessionDataStore.__CONTEXT + "." + data.getVhost().replace('.', '_') + ":" + data.getContextPath() +"."+MongoSessionDataStore.__LASTSAVED);
|
MongoSessionDataStore.__CONTEXT + "." + data.getVhost().replace('.', '_') + ":" + data.getContextPath() +"."+MongoSessionDataStore.__LASTSAVED);
|
||||||
String lastNode = (String)MongoUtils.getNestedValue(sessionDocument,
|
String lastNode = (String)MongoUtils.getNestedValue(sessionDocument,
|
||||||
MongoSessionDataStore.__CONTEXT + "." + data.getVhost().replace('.', '_') + ":" + data.getContextPath() +"."+MongoSessionDataStore.__LASTNODE);
|
MongoSessionDataStore.__CONTEXT + "." + data.getVhost().replace('.', '_') + ":" + data.getContextPath() +"."+MongoSessionDataStore.__LASTNODE);
|
||||||
|
byte[] attributes = (byte[])MongoUtils.getNestedValue(sessionDocument,
|
||||||
|
MongoSessionDataStore.__CONTEXT + "." + data.getVhost().replace('.', '_') + ":" + data.getContextPath() +"."+MongoSessionDataStore.__ATTRIBUTES);
|
||||||
|
|
||||||
|
|
||||||
LOG.info("DA:{} MA:{}", data.getAccessed(), accessed);
|
|
||||||
LOG.info("DLA:{} DLA:{}",data.getLastAccessed(),lastAccessed);
|
|
||||||
assertEquals(data.getCreated(), created.longValue());
|
assertEquals(data.getCreated(), created.longValue());
|
||||||
assertEquals(data.getAccessed(), accessed.longValue());
|
assertEquals(data.getAccessed(), accessed.longValue());
|
||||||
assertEquals(data.getLastAccessed(), lastAccessed.longValue());
|
assertEquals(data.getLastAccessed(), lastAccessed.longValue());
|
||||||
|
@ -168,25 +164,23 @@ public class MongoTestHelper
|
||||||
|
|
||||||
assertNotNull(sessionSubDocumentForContext);
|
assertNotNull(sessionSubDocumentForContext);
|
||||||
|
|
||||||
Map<String,Object> attributes = new HashMap<>();
|
if (! data.getAllAttributes().isEmpty())
|
||||||
for (String name : sessionSubDocumentForContext.keySet())
|
|
||||||
{
|
{
|
||||||
//skip special metadata attribute which is not one of the actual session attributes
|
assertNotNull(attributes);
|
||||||
if (MongoSessionDataStore.__METADATA.equals(name) )
|
SessionData tmp = new SessionData(data.getId(), data.getContextPath(), data.getVhost(), created.longValue(), accessed.longValue(), lastAccessed.longValue(), maxInactive.longValue());
|
||||||
continue;
|
try (ByteArrayInputStream bais = new ByteArrayInputStream(attributes);ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(bais))
|
||||||
String attr = MongoUtils.decodeName(name);
|
{
|
||||||
Object value = MongoUtils.decodeValue(sessionSubDocumentForContext.get(name));
|
SessionData.deserializeAttributes(tmp, ois);
|
||||||
attributes.put(attr, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//same keys
|
//same keys
|
||||||
assertTrue(data.getKeys().equals(attributes.keySet()));
|
assertTrue(data.getKeys().equals(tmp.getKeys()));
|
||||||
//same values
|
//same values
|
||||||
for (String name:data.getKeys())
|
for (String name:data.getKeys())
|
||||||
{
|
{
|
||||||
assertTrue(data.getAttribute(name).equals(attributes.get(name)));
|
assertTrue(data.getAttribute(name).equals(tmp.getAttribute(name)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -226,13 +220,14 @@ public class MongoTestHelper
|
||||||
|
|
||||||
if (attributes != null)
|
if (attributes != null)
|
||||||
{
|
{
|
||||||
for (String name : attributes.keySet())
|
SessionData tmp = new SessionData (id, contextPath, vhost, created, accessed, lastAccessed, maxIdle, attributes);
|
||||||
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos))
|
||||||
{
|
{
|
||||||
Object value = attributes.get(name);
|
SessionData.serializeAttributes(tmp, oos);
|
||||||
sets.put(MongoSessionDataStore.__CONTEXT + "." + vhost.replace('.', '_') + ":" + contextPath+ "." + MongoUtils.encodeName(name),
|
sets.put(MongoSessionDataStore.__CONTEXT + "." + vhost.replace('.', '_') + ":" + contextPath +"."+MongoSessionDataStore.__ATTRIBUTES, baos.toByteArray());
|
||||||
MongoUtils.encodeName(value));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update.put("$set",sets);
|
update.put("$set",sets);
|
||||||
collection.update(key,update,upsert,false,WriteConcern.SAFE);
|
collection.update(key,update,upsert,false,WriteConcern.SAFE);
|
||||||
}
|
}
|
||||||
|
@ -269,6 +264,53 @@ public class MongoTestHelper
|
||||||
sets.put(MongoSessionDataStore.__ACCESSED, accessed);
|
sets.put(MongoSessionDataStore.__ACCESSED, accessed);
|
||||||
sets.put(MongoSessionDataStore.__LAST_ACCESSED, lastAccessed);
|
sets.put(MongoSessionDataStore.__LAST_ACCESSED, lastAccessed);
|
||||||
|
|
||||||
|
if (attributes != null)
|
||||||
|
{
|
||||||
|
SessionData tmp = new SessionData (id, contextPath, vhost, created, accessed, lastAccessed, maxIdle, attributes);
|
||||||
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(baos);)
|
||||||
|
{
|
||||||
|
SessionData.serializeAttributes(tmp, oos);
|
||||||
|
sets.put(MongoSessionDataStore.__CONTEXT + "." + vhost.replace('.', '_') + ":" + contextPath +"."+MongoSessionDataStore.__ATTRIBUTES, baos.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update.put("$set",sets);
|
||||||
|
collection.update(key,update,upsert,false,WriteConcern.SAFE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void createLegacySession (String id, String contextPath, String vhost,
|
||||||
|
String lastNode, long created, long accessed,
|
||||||
|
long lastAccessed, long maxIdle, long expiry,
|
||||||
|
Map<String,Object> attributes)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
//make old-style session to test if we can retrieve it
|
||||||
|
DBCollection collection = _mongoClient.getDB(DB_NAME).getCollection(COLLECTION_NAME);
|
||||||
|
|
||||||
|
// Form query for upsert
|
||||||
|
BasicDBObject key = new BasicDBObject(MongoSessionDataStore.__ID, id);
|
||||||
|
|
||||||
|
// Form updates
|
||||||
|
BasicDBObject update = new BasicDBObject();
|
||||||
|
boolean upsert = false;
|
||||||
|
BasicDBObject sets = new BasicDBObject();
|
||||||
|
|
||||||
|
Object version = new Long(1);
|
||||||
|
|
||||||
|
// New session
|
||||||
|
upsert = true;
|
||||||
|
sets.put(MongoSessionDataStore.__CREATED,created);
|
||||||
|
sets.put(MongoSessionDataStore.__VALID,true);
|
||||||
|
sets.put(MongoSessionDataStore.__CONTEXT + "." + vhost.replace('.', '_') + ":" + contextPath +"."+MongoSessionDataStore.__VERSION,version);
|
||||||
|
sets.put(MongoSessionDataStore.__CONTEXT + "." + vhost.replace('.', '_') + ":" + contextPath +"."+MongoSessionDataStore.__LASTSAVED, System.currentTimeMillis());
|
||||||
|
sets.put(MongoSessionDataStore.__CONTEXT + "." + vhost.replace('.', '_') + ":" + contextPath +"."+MongoSessionDataStore.__LASTNODE, lastNode);
|
||||||
|
sets.put(MongoSessionDataStore.__MAX_IDLE, maxIdle);
|
||||||
|
sets.put(MongoSessionDataStore.__EXPIRY, expiry);
|
||||||
|
sets.put(MongoSessionDataStore.__ACCESSED, accessed);
|
||||||
|
sets.put(MongoSessionDataStore.__LAST_ACCESSED, lastAccessed);
|
||||||
|
|
||||||
if (attributes != null)
|
if (attributes != null)
|
||||||
{
|
{
|
||||||
for (String name : attributes.keySet())
|
for (String name : attributes.keySet())
|
||||||
|
@ -281,9 +323,4 @@ public class MongoTestHelper
|
||||||
update.put("$set",sets);
|
update.put("$set",sets);
|
||||||
collection.update(key,update,upsert,false,WriteConcern.SAFE);
|
collection.update(key,update,upsert,false,WriteConcern.SAFE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class TestSessionDataStore extends AbstractSessionDataStore
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load(String id) throws Exception
|
public SessionData doLoad(String id) throws Exception
|
||||||
{
|
{
|
||||||
SessionData sd = _map.get(id);
|
SessionData sd = _map.get(id);
|
||||||
if (sd == null)
|
if (sd == null)
|
||||||
|
|
|
@ -65,37 +65,29 @@ public class DeleteUnloadableSessionTest
|
||||||
|
|
||||||
String unloadableId = null;
|
String unloadableId = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPassivating()
|
public boolean isPassivating()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#exists(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String id) throws Exception
|
public boolean exists(String id) throws Exception
|
||||||
{
|
{
|
||||||
return o != null;
|
return o != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataMap#load(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load(String id) throws Exception
|
public SessionData doLoad(String id) throws Exception
|
||||||
{
|
{
|
||||||
unloadableId = id;
|
unloadableId = id;
|
||||||
throw new UnreadableSessionDataException(id, null, new Exception("fake"));
|
throw new UnreadableSessionDataException(id, null, new Exception("fake"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataMap#delete(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete(String id) throws Exception
|
public boolean delete(String id) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -107,18 +99,14 @@ public class DeleteUnloadableSessionTest
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(java.lang.String, org.eclipse.jetty.server.session.SessionData, long)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
||||||
{
|
{
|
||||||
//pretend it was saved
|
//pretend it was saved
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doGetExpired(java.util.Set)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> doGetExpired(Set<String> candidates)
|
public Set<String> doGetExpired(Set<String> candidates)
|
||||||
{
|
{
|
||||||
|
|
|
@ -67,45 +67,33 @@ public class SessionEvictionFailureTest
|
||||||
_nextStoreResult = results;
|
_nextStoreResult = results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPassivating()
|
public boolean isPassivating()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#exists(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String id) throws Exception
|
public boolean exists(String id) throws Exception
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataMap#load(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SessionData load(String id) throws Exception
|
public SessionData doLoad(String id) throws Exception
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataMap#delete(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete(String id) throws Exception
|
public boolean delete(String id) throws Exception
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(java.lang.String, org.eclipse.jetty.server.session.SessionData, long)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception
|
||||||
{
|
{
|
||||||
|
@ -115,9 +103,6 @@ public class SessionEvictionFailureTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doGetExpired(java.util.Set)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> doGetExpired(Set<String> candidates)
|
public Set<String> doGetExpired(Set<String> candidates)
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,6 +31,8 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.servlet.http.HttpSession;
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.MultiMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test Servlet Sessions.
|
* Test Servlet Sessions.
|
||||||
*/
|
*/
|
||||||
|
@ -83,6 +85,7 @@ public class SessionDump extends HttpServlet
|
||||||
session = request.getSession(true);
|
session = request.getSession(true);
|
||||||
session.setAttribute("test","value");
|
session.setAttribute("test","value");
|
||||||
session.setAttribute("obj", new ObjectAttributeValue(System.currentTimeMillis()));
|
session.setAttribute("obj", new ObjectAttributeValue(System.currentTimeMillis()));
|
||||||
|
session.setAttribute("WEBCL", new MultiMap());
|
||||||
}
|
}
|
||||||
else if (session!=null)
|
else if (session!=null)
|
||||||
{
|
{
|
||||||
|
@ -140,6 +143,8 @@ public class SessionDump extends HttpServlet
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (session.getAttribute("WEBCL") == null)
|
||||||
|
session.setAttribute("WEBCL", new MultiMap());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
out.println("<b>ID:</b> "+session.getId()+"<br/>");
|
out.println("<b>ID:</b> "+session.getId()+"<br/>");
|
||||||
|
|
Loading…
Reference in New Issue