From 90a72b079826e65e11f638641918d98651103025 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Mon, 2 Aug 2021 10:04:51 +1000 Subject: [PATCH] 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 --- .../session/MemcachedSessionDataMap.java | 46 ++++++++++++++- .../session/AbstractSessionDataStore.java | 59 +++++-------------- .../org/eclipse/jetty/util/FuturePromise.java | 24 ++++++++ 3 files changed, 81 insertions(+), 48 deletions(-) diff --git a/jetty-memcached/jetty-memcached-sessions/src/main/java/org/eclipse/jetty/memcached/session/MemcachedSessionDataMap.java b/jetty-memcached/jetty-memcached-sessions/src/main/java/org/eclipse/jetty/memcached/session/MemcachedSessionDataMap.java index 531da8eaf1b..0d503bc5dba 100644 --- a/jetty-memcached/jetty-memcached-sessions/src/main/java/org/eclipse/jetty/memcached/session/MemcachedSessionDataMap.java +++ b/jetty-memcached/jetty-memcached-sessions/src/main/java/org/eclipse/jetty/memcached/session/MemcachedSessionDataMap.java @@ -25,6 +25,7 @@ import org.eclipse.jetty.server.session.SessionContext; import org.eclipse.jetty.server.session.SessionData; import org.eclipse.jetty.server.session.SessionDataMap; 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.ManagedObject; import org.eclipse.jetty.util.component.AbstractLifeCycle; @@ -43,6 +44,7 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio protected int _expirySec = 0; protected boolean _heartbeats = true; protected XMemcachedClientBuilder _builder; + protected SessionContext _context; /** * SessionDataTranscoder @@ -140,8 +142,12 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio @Override public void initialize(SessionContext context) { + if (isStarted()) + throw new IllegalStateException("Context set after MemcachedSessionDataMap started"); + try { + _context = context; _builder.setTranscoder(new SessionDataTranscoder()); _client = _builder.build(); _client.setEnableHeartBeat(isHeartbeats()); @@ -155,14 +161,48 @@ public class MemcachedSessionDataMap extends AbstractLifeCycle implements Sessio @Override public SessionData load(String id) throws Exception { - SessionData data = _client.get(id); - return data; + if (!isStarted()) + throw new IllegalStateException("Not started"); + + final FuturePromise result = new FuturePromise<>(); + + Runnable r = () -> + { + try + { + result.succeeded(_client.get(id)); + } + catch (Exception e) + { + result.failed(e); + } + }; + + _context.run(r); + return result.getOrThrow(); } @Override public void store(String id, SessionData data) throws Exception { - _client.set(id, _expirySec, data); + if (!isStarted()) + throw new IllegalStateException("Not started"); + + final FuturePromise 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 diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java index 49ee9690f16..524bccb5d88 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java @@ -15,8 +15,10 @@ package org.eclipse.jetty.server.session; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.util.FuturePromise; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; 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 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 the type of the result. - */ - private class Result - { - 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. * @@ -171,21 +138,22 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem if (!isStarted()) throw new IllegalStateException("Not started"); - final Result result = new Result<>(); - + final FuturePromise result = new FuturePromise<>(); + Runnable r = () -> { try { - result.setResult(doLoad(id)); + result.succeeded(doLoad(id)); } catch (Exception e) { - result.setException(e); + result.failed(e); } }; _context.run(r); + return result.getOrThrow(); } @@ -214,7 +182,7 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem //set the last saved time to now data.setLastSaved(System.currentTimeMillis()); - final Result result = new Result<>(); + final FuturePromise result = new FuturePromise<>(); Runnable r = () -> { try @@ -222,32 +190,33 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem //call the specific store method, passing in previous save time doStore(id, data, lastSave); data.clean(); //unset all dirty flags + result.succeeded(null); } catch (Exception e) { //reset last save time if save failed data.setLastSaved(lastSave); - result.setException(e); + result.failed(e); } }; _context.run(r); - result.throwIfException(); + result.getOrThrow(); } } @Override public boolean exists(String id) throws Exception { - Result result = new Result<>(); + FuturePromise result = new FuturePromise<>(); Runnable r = () -> { try { - result.setResult(doExists(id)); + result.succeeded(doExists(id)); } catch (Exception e) { - result.setException(e); + result.failed(e); } }; diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/FuturePromise.java b/jetty-util/src/main/java/org/eclipse/jetty/util/FuturePromise.java index ad8ab65aa20..deae5c669c0 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/FuturePromise.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/FuturePromise.java @@ -118,6 +118,30 @@ public class FuturePromise implements Future, Promise throw (CancellationException)new CancellationException().initCause(_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 public C get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException