Issue #6556 Ensure context classloader set when operating on memcache. (#6557)

* Issue #6556 Ensure context classloader set when operating on memcache.

Signed-off-by: Jan Bartel <janb@webtide.com>
This commit is contained in:
Jan Bartel 2021-08-02 10:04:51 +10:00 committed by GitHub
parent 409a2fc9ff
commit 90a72b0798
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 48 deletions

View File

@ -25,6 +25,7 @@ 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.ClassLoadingObjectInputStream;
import org.eclipse.jetty.util.FuturePromise;
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;
@ -43,6 +44,7 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio
protected int _expirySec = 0; protected int _expirySec = 0;
protected boolean _heartbeats = true; protected boolean _heartbeats = true;
protected XMemcachedClientBuilder _builder; protected XMemcachedClientBuilder _builder;
protected SessionContext _context;
/** /**
* SessionDataTranscoder * SessionDataTranscoder
@ -140,8 +142,12 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio
@Override @Override
public void initialize(SessionContext context) public void initialize(SessionContext context)
{ {
if (isStarted())
throw new IllegalStateException("Context set after MemcachedSessionDataMap started");
try try
{ {
_context = context;
_builder.setTranscoder(new SessionDataTranscoder()); _builder.setTranscoder(new SessionDataTranscoder());
_client = _builder.build(); _client = _builder.build();
_client.setEnableHeartBeat(isHeartbeats()); _client.setEnableHeartBeat(isHeartbeats());
@ -155,14 +161,48 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio
@Override @Override
public SessionData load(String id) throws Exception public SessionData load(String id) throws Exception
{ {
SessionData data = _client.get(id); if (!isStarted())
return data; throw new IllegalStateException("Not started");
final FuturePromise<SessionData> result = new FuturePromise<>();
Runnable r = () ->
{
try
{
result.succeeded(_client.get(id));
}
catch (Exception e)
{
result.failed(e);
}
};
_context.run(r);
return result.getOrThrow();
} }
@Override @Override
public void store(String id, SessionData data) throws Exception public void store(String id, SessionData data) throws Exception
{ {
_client.set(id, _expirySec, data); if (!isStarted())
throw new IllegalStateException("Not started");
final FuturePromise<Void> result = new FuturePromise<>();
Runnable r = () ->
{
try
{
_client.set(id, _expirySec, data);
result.succeeded(null);
}
catch (Exception e)
{
result.failed(e);
}
};
_context.run(r);
result.getOrThrow();
} }
@Override @Override

View File

@ -15,8 +15,10 @@ package org.eclipse.jetty.server.session;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.FuturePromise;
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.ContainerLifeCycle; import org.eclipse.jetty.util.component.ContainerLifeCycle;
@ -40,41 +42,6 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem
protected long _lastOrphanSweepTime = 0; //last time in ms that we deleted orphaned sessions protected long _lastOrphanSweepTime = 0; //last time in ms that we deleted orphaned sessions
protected int _savePeriodSec = DEFAULT_SAVE_PERIOD_SEC; //time in sec between saves protected int _savePeriodSec = DEFAULT_SAVE_PERIOD_SEC; //time in sec between saves
/**
* Small utility class to allow us to
* return a result and an Exception
* from invocation of Runnables.
*
* @param <V> the type of the result.
*/
private class Result<V>
{
private V _result;
private Exception _exception;
public void setResult(V result)
{
_result = result;
}
public void setException(Exception exception)
{
_exception = exception;
}
private void throwIfException() throws Exception
{
if (_exception != null)
throw _exception;
}
public V getOrThrow() throws Exception
{
throwIfException();
return _result;
}
}
/** /**
* Check if a session for the given id exists. * Check if a session for the given id exists.
* *
@ -171,21 +138,22 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem
if (!isStarted()) if (!isStarted())
throw new IllegalStateException("Not started"); throw new IllegalStateException("Not started");
final Result<SessionData> result = new Result<>(); final FuturePromise<SessionData> result = new FuturePromise<>();
Runnable r = () -> Runnable r = () ->
{ {
try try
{ {
result.setResult(doLoad(id)); result.succeeded(doLoad(id));
} }
catch (Exception e) catch (Exception e)
{ {
result.setException(e); result.failed(e);
} }
}; };
_context.run(r); _context.run(r);
return result.getOrThrow(); return result.getOrThrow();
} }
@ -214,7 +182,7 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem
//set the last saved time to now //set the last saved time to now
data.setLastSaved(System.currentTimeMillis()); data.setLastSaved(System.currentTimeMillis());
final Result<Object> result = new Result<>(); final FuturePromise<Void> result = new FuturePromise<>();
Runnable r = () -> Runnable r = () ->
{ {
try try
@ -222,32 +190,33 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem
//call the specific store method, passing in previous save time //call the specific store method, passing in previous save time
doStore(id, data, lastSave); doStore(id, data, lastSave);
data.clean(); //unset all dirty flags data.clean(); //unset all dirty flags
result.succeeded(null);
} }
catch (Exception e) catch (Exception e)
{ {
//reset last save time if save failed //reset last save time if save failed
data.setLastSaved(lastSave); data.setLastSaved(lastSave);
result.setException(e); result.failed(e);
} }
}; };
_context.run(r); _context.run(r);
result.throwIfException(); result.getOrThrow();
} }
} }
@Override @Override
public boolean exists(String id) throws Exception public boolean exists(String id) throws Exception
{ {
Result<Boolean> result = new Result<>(); FuturePromise<Boolean> result = new FuturePromise<>();
Runnable r = () -> Runnable r = () ->
{ {
try try
{ {
result.setResult(doExists(id)); result.succeeded(doExists(id));
} }
catch (Exception e) catch (Exception e)
{ {
result.setException(e); result.failed(e);
} }
}; };

View File

@ -118,6 +118,30 @@ public class FuturePromise<C> implements Future<C>, Promise<C>
throw (CancellationException)new CancellationException().initCause(_cause); throw (CancellationException)new CancellationException().initCause(_cause);
throw new ExecutionException(_cause); throw new ExecutionException(_cause);
} }
/**
* Return the result if completed successfully
* or in the case of failure, throw the
* Exception/Error, or an ExecutionException wrapping
* the cause if it is neither an Exception or Error.
*
* @return the computed result
* @throws Exception if the cause is an Exception or Error,
* otherwise an ExecutionException wrapping the cause
*/
public C getOrThrow() throws Exception
{
_latch.await();
if (_cause == COMPLETED)
return _result;
if (_cause instanceof Exception)
throw (Exception)_cause;
if (_cause instanceof Error)
throw (Error)_cause;
throw new ExecutionException(_cause);
}
@Override @Override
public C get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException public C get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException