Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [jetty-dev] Cannot deserialize session attributes

Your stacktrace shows ...

at com.cloudbees.jetty.session.redis.RedisSessionDataStore.doLoad(RedisSessionDataStore.java:55)

That is part of the jetty-redis module, that was managed by cloudbees.

They stopped maintaining it over 2 years ago and have since archived the project.

https://github.com/cloudbees/jetty-redis

You should change over to the replacement project at ...

https://github.com/cloudbees/jetty-redis-sessions

Joakim Erdfelt / joakim@xxxxxxxxxxx


On Wed, Jan 15, 2020 at 11:04 AM John Ruggentaler <john.ruggentaler@xxxxxxxxx> wrote:
When the scavenger runs and I try to cleanup abandoned sessions from my Redis store I get an exception (IOException("Not ClassLoadingObjectInputStream")) from SessionData.java line 138.

/**
* 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
*/
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();
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
ClassLoader serverLoader = SessionData.class.getClassLoader();
for (int i = 0; i < entries; i++)
{
String name = in.readUTF(); //attribute name
boolean isServerClassLoader = in.readBoolean(); //use server or webapp classloader to load
if (LOG.isDebugEnabled())
LOG.debug("Deserialize {} isServerLoader={} serverLoader={} tccl={}", name, isServerClassLoader, serverLoader, contextLoader);
Object value = ((ClassLoadingObjectInputStream)in).readObject(isServerClassLoader ? serverLoader : contextLoader);
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<>();
data.putAllAttributes((Map<String, Object>)o);
}
}
Below is the log output.
2020-01-15 10:26:44.599:DBUG:ccjsr.RedisSessionDataStore:Session-HouseKeeper-5f8edcc5-1: Checking expiry for candidates
2020-01-15 10:26:44.599:DBUG:ccjsr.RedisSessionDataStore:Session-HouseKeeper-5f8edcc5-1: Checking expiry for NULL candidates
2020-01-15 10:26:44.616:DBUG:ccjsr.RedisSessionDataStore:Session-HouseKeeper-5f8edcc5-1: Loading session node012c4httt216k31ewmcqw42bdql1 from Redis
java.io.IOException: Not ClassLoadingObjectInputStream
	at org.eclipse.jetty.server.session.SessionData.deserializeAttributes(SessionData.java:138)
	at org.eclipse.jetty.server.session.SessionData.readObject(SessionData.java:490)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1170)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2178)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2069)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
	at com.cloudbees.jetty.session.redis.RedisSessionDataStore.doLoad(RedisSessionDataStore.java:55)
	at org.eclipse.jetty.server.session.AbstractSessionDataStore.lambda$load$0(AbstractSessionDataStore.java:93)
	at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1382)
	at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1401)
	at org.eclipse.jetty.server.session.SessionContext.run(SessionContext.java:92)
	at org.eclipse.jetty.server.session.AbstractSessionDataStore.load(AbstractSessionDataStore.java:101)
	at com.cloudbees.jetty.session.redis.RedisSessionDataStore.doGetExpired(RedisSessionDataStore.java:116)
	at org.eclipse.jetty.server.session.AbstractSessionDataStore.getExpired(AbstractSessionDataStore.java:168)
	at org.eclipse.jetty.server.session.AbstractSessionCache.checkExpiration(AbstractSessionCache.java:677)
	at org.eclipse.jetty.server.session.SessionHandler.scavenge(SessionHandler.java:1256)
	at org.eclipse.jetty.server.session.HouseKeeper.scavenge(HouseKeeper.java:256)
	at org.eclipse.jetty.server.session.HouseKeeper$Runner.run(HouseKeeper.java:61)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
2020-01-15 10:26:44.621:WARN:ccjsr.RedisSessionDataStore:Session-HouseKeeper-5f8edcc5-1: Error checking if candidate node012c4httt216k31ewmcqw42bdql1 is expired java.io.IOException: Not ClassLoadingObjectInputStream
2020-01-15 10:36:43.329:DBUG:oejs.session:Session-Scheduler-68267da0-1: Timer expired for session node012c4httt216k31ewmcqw42bdql1
2020-01-15 10:36:43.330:DBUG:oejs.session:Session-Scheduler-68267da0-1: Inspecting session node012c4httt216k31ewmcqw42bdql1, valid=true
2020-01-15 10:36:43.330:DBUG:oejs.session:Session-Scheduler-68267da0-1: Testing expiry on session node012c4httt216k31ewmcqw42bdql1: expires at 1579106203302 now 1579106203330 maxIdle 900000
2020-01-15 10:36:43.331:DBUG:oejs.session:Session-Scheduler-68267da0-1: Session node012c4httt216k31ewmcqw42bdql1 is candidate for expiry
2020-01-15 10:36:43.331:DBUG:oejs.session:Session-Scheduler-68267da0-1: Testing expiry on session node012c4httt216k31ewmcqw42bdql1: expires at 1579106203302 now 1579106203330 maxIdle 900000
2020-01-15 10:36:44.640:DBUG:oejs.session:Session-HouseKeeper-5f8edcc5-1: node0 scavenging sessions
2020-01-15 10:36:44.640:DBUG:oejs.session:Session-HouseKeeper-5f8edcc5-1: org.eclipse.jetty.server.session.SessionHandler1747352992==dftMaxIdleSec=1800 scavenging sessions
2020-01-15 10:36:44.640:DBUG:oejs.session:Session-HouseKeeper-5f8edcc5-1: org.eclipse.jetty.server.session.SessionHandler1747352992==dftMaxIdleSec=1800 scavenging session ids [node012c4httt216k31ewmcqw42bdql1]
2020-01-15 10:36:44.640:DBUG:oejs.session:Session-HouseKeeper-5f8edcc5-1: org.eclipse.jetty.server.session.DefaultSessionCache@369f73a2[evict=-1,removeUnloadable=false,saveOnCreate=false,saveOnInactiveEvict=false] checking expiration on [node012c4httt216k31ewmcqw42bdql1]
Googling seems to indicate a problem with class loaders. Do I need to implement custom serialization?
Below is my RedisSessionDataStore doLoad(...), doStore(...) and doGetExpired(...) methods.
@Override
public SessionData doLoad(String id) throws Exception {
LOGGER.debug("Loading session {} from Redis", id);
final byte[] session = jedis.get(getCacheKey(id).getBytes());
if (session != null) {
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(session);
ObjectInputStream objectInputStream = new ObjectInputStreamWithLoader(byteArrayInputStream, _context.getContext().getClassLoader())) {
SessionData sd = (SessionData) objectInputStream.readObject();
return sd;
}
}
return null;
}
@Override
public void doStore(String id, SessionData data, long lastSaveTime) throws Exception {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
objectOutputStream.writeObject(data);
jedis.set(getCacheKey(id).getBytes(Charset.defaultCharset()), byteArrayOutputStream.toByteArray());
LOGGER.debug("Session {} saved to Redis, expires {} ", id, data.getExpiry());
}
}
@Override
public Set<String> doGetExpired(Set<String> candidates) {

LOGGER.debug("Checking expiry for candidates");
long now = System.currentTimeMillis();

if (candidates == null || candidates.isEmpty()) {
LOGGER.debug("Checking expiry for NULL candidates");
String keyPrefix = sessionKeyPrefix();
Set<String> sessionKeys = jedis.keys(keyPrefix + "*");
Set<String> expired = new HashSet<>();

if (null != sessionKeys) {
Iterator<String> iterator = sessionKeys.iterator();
while (iterator.hasNext()) {
String k = getKey(iterator.next());

try {
SessionData sd = load(k);

if (null == sd) {
continue;
}
if ((sd.getExpiry() > 0) && sd.getExpiry() <= now) {
expired.add(k);
LOGGER.debug("Session {} managed by {} is expired", k, _context.getWorkerName());
}
}
catch (Exception e) {
e.printStackTrace();
LOGGER.warn("Error checking if candidate {} is expired", k, e);
}
}
}

return expired;
}

Set<String> expired = new HashSet<>();

for (String candidate : candidates) {
LOGGER.debug("Checking expiry for candidate {}", candidate);
try {
SessionData sd = load(candidate);

//if the session no longer exists
if (sd == null) {
expired.add(candidate);
LOGGER.debug("Session {} does not exist in Redis", candidate);
} else {
if (_context.getWorkerName().equals(sd.getLastNode())) {
//we are its manager, add it to the expired set if it is expired now
if ((sd.getExpiry() > 0) && sd.getExpiry() <= now) {
expired.add(candidate);
LOGGER.debug("Session {} managed by {} is expired", candidate, _context.getWorkerName());
}
} else {
//if we are not the session's manager, only expire it iff:
// this is our first expiryCheck and the session expired a long time ago
//or
//the session expired at least one graceperiod ago
if (_lastExpiryCheckTime <= 0) {
if ((sd.getExpiry() > 0) && sd.getExpiry() < (now - (1000L * (3 * _gracePeriodSec))))
expired.add(candidate);
} else {
if ((sd.getExpiry() > 0) && sd.getExpiry() < (now - (1000L * _gracePeriodSec)))
expired.add(candidate);
}
}
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.warn("Error checking if candidate {} is expired", candidate, e);
}
}

return expired;
}
_______________________________________________
jetty-dev mailing list
jetty-dev@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://www.eclipse.org/mailman/listinfo/jetty-dev

Back to the top