From 3dd0172abda2e1dfed5c99d7a4c146c983b105bc Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Wed, 17 Apr 2019 15:04:28 +1000 Subject: [PATCH] Issue #3395 Add tests for serializing dynamic proxies in sessions. (#3499) * Issue #3395 Add tests for serializing dynamic proxies in sessions. --- .../session/AbstractSessionDataStoreTest.java | 127 ++++++++++++++---- .../src/main/resources/Proxyable.clazz | Bin 0 -> 119 bytes .../src/main/resources/Proxyable.java | 22 +++ .../src/main/resources/ProxyableFactory.clazz | Bin 0 -> 692 bytes .../src/main/resources/ProxyableFactory.java | 34 +++++ .../ProxyableInvocationHandler.clazz | Bin 0 -> 910 bytes .../resources/ProxyableInvocationHandler.java | 46 +++++++ 7 files changed, 202 insertions(+), 27 deletions(-) create mode 100644 tests/test-sessions/test-sessions-common/src/main/resources/Proxyable.clazz create mode 100644 tests/test-sessions/test-sessions-common/src/main/resources/Proxyable.java create mode 100644 tests/test-sessions/test-sessions-common/src/main/resources/ProxyableFactory.clazz create mode 100644 tests/test-sessions/test-sessions-common/src/main/resources/ProxyableFactory.java create mode 100644 tests/test-sessions/test-sessions-common/src/main/resources/ProxyableInvocationHandler.clazz create mode 100644 tests/test-sessions/test-sessions-common/src/main/resources/ProxyableInvocationHandler.java diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStoreTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStoreTest.java index 64740eb790c..ce6d4c0f1c7 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStoreTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStoreTest.java @@ -31,6 +31,7 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; +import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.URL; import java.net.URLClassLoader; @@ -61,7 +62,7 @@ public abstract class AbstractSessionDataStoreTest public static final long ANCIENT_TIMESTAMP = 100L; public static final long RECENT_TIMESTAMP = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(3*GRACE_PERIOD_SEC); - protected URLClassLoader _contextClassLoader; + protected URLClassLoader _contextClassLoader = new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()); @@ -205,37 +206,109 @@ public abstract class AbstractSessionDataStoreTest } + + /** * Test that the store can persist a session that contains - * serializable objects in the attributes. + * serializable Proxy objects in the attributes. * * @throws Exception */ - /* - * @Test public void testStoreObjectAttributes() throws Exception { //create - * the SessionDataStore ServletContextHandler context = new - * ServletContextHandler(ServletContextHandler.SESSIONS); - * context.setContextPath("/test"); 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); - * - * store.start(); - * - * //create a session SessionData data = store.newSessionData("1234", 100, - * 200, 199, -1);//never expires TestFoo testFoo = new TestFoo(); - * testFoo.setInt(33); FooInvocationHandler handler = new - * FooInvocationHandler(testFoo); Foo foo = - * (Foo)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader( - * ), new Class[] {Foo.class}, handler); data.setAttribute("foo", foo); - * data.setLastNode(sessionContext.getWorkerName()); - * - * //test that it can be persisted store.store("1234", data); - * checkSessionPersisted(data); } - */ + @Test + public void testStoreObjectAttributes() throws Exception + { + + //Use classes that would only be known to the webapp classloader + InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("Proxyable.clazz"); + File proxyabledir = new File (MavenTestingUtils.getTargetDir(), "proxyable"); + proxyabledir.mkdirs(); + File proxyableClass = new File (proxyabledir, "Proxyable.class"); + IO.copy(is, new FileOutputStream(proxyableClass)); + is.close(); + + assertTrue(proxyableClass.exists()); + assertTrue(proxyableClass.length() != 0); + + is = Thread.currentThread().getContextClassLoader().getResourceAsStream("ProxyableInvocationHandler.clazz"); + File pihClass = new File (proxyabledir, "ProxyableInvocationHandler.class"); + IO.copy(is, new FileOutputStream(pihClass)); + is.close(); + + is = Thread.currentThread().getContextClassLoader().getResourceAsStream("ProxyableFactory.clazz"); + File factoryClass = new File (proxyabledir, "ProxyableFactory.class"); + IO.copy(is, new FileOutputStream(factoryClass)); + is.close(); + + URL[] proxyabledirUrls = new URL[]{proxyabledir.toURI().toURL()}; + _contextClassLoader = new URLClassLoader(proxyabledirUrls, Thread.currentThread().getContextClassLoader()); + + //create the SessionDataStore + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + //use the classloader with the special class in it + context.setClassLoader(_contextClassLoader); + + 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); + + store.start(); + + ClassLoader old = Thread.currentThread().getContextClassLoader(); + SessionData data = null; + try + { + //Set the classloader and make a session containing a proxy as an attribute + Thread.currentThread().setContextClassLoader(_contextClassLoader); + Class factoryclazz = Class.forName("ProxyableFactory", true, _contextClassLoader); + //create a session + long now = System.currentTimeMillis(); + data = store.newSessionData("1234", 100, now, now-1, -1);//never expires + data.setLastNode(sessionContext.getWorkerName()); + Method m = factoryclazz.getMethod("newProxyable", ClassLoader.class); + Object proxy = m.invoke(null, _contextClassLoader); + + //Make an attribute that uses the proxy only known to the webapp classloader + data.setAttribute("a", proxy); + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + + + //store the session, using a different thread to ensure + //that the thread is adorned with the webapp classloader + //before serialization + final SessionData finalData = data; + + Runnable r = new Runnable() + { + + @Override + public void run() + { + try + { + store.store("1234", finalData); + } + catch (Exception e) + { + fail(e); + } + } + }; + + Thread t = new Thread(r, "saver"); + t.start(); + t.join(TimeUnit.SECONDS.toMillis(10)); + + //check that the store contains all of the session data + assertTrue(checkSessionPersisted(data)); + } + /** * Test that we can load a persisted session. diff --git a/tests/test-sessions/test-sessions-common/src/main/resources/Proxyable.clazz b/tests/test-sessions/test-sessions-common/src/main/resources/Proxyable.clazz new file mode 100644 index 0000000000000000000000000000000000000000..22edfdd509587c8d241b6f36d7d63cd82b04964a GIT binary patch literal 119 zcmX^0Z`VEs1_l!bPId-%b_Nbc2IlnC5+JFe>B-2z6`WsMl$`38nUl)Mz!y-IUs0Ku zl#{BLl~|U@$iN8~U}O*g3F+q~=B4ZVCuOB3m#{H1FfuR!jb>nAWC5DTzye}1umVXY G1~vfRIvD)` literal 0 HcmV?d00001 diff --git a/tests/test-sessions/test-sessions-common/src/main/resources/Proxyable.java b/tests/test-sessions/test-sessions-common/src/main/resources/Proxyable.java new file mode 100644 index 00000000000..1953911c468 --- /dev/null +++ b/tests/test-sessions/test-sessions-common/src/main/resources/Proxyable.java @@ -0,0 +1,22 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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. +// ======================================================================== +// + +public interface Proxyable +{ + public int get(); +} diff --git a/tests/test-sessions/test-sessions-common/src/main/resources/ProxyableFactory.clazz b/tests/test-sessions/test-sessions-common/src/main/resources/ProxyableFactory.clazz new file mode 100644 index 0000000000000000000000000000000000000000..0a3529b10b99419a917996d432fc489866167166 GIT binary patch literal 692 zcmZ{iT}uK%6o%jNYjxYw%(U!7125DfyXek{Adm$603(R5#&wdFZ5Gzm>R;7WQ50SG zqoT8>yPSMvnjpg1L^+s!_B-JEJ{ZegS)? z1H(M6k)fC-WC3wHyQzgC3TWkb1g>8Yd`0MKg2)m| W3z5?upL8E1kV6uA;*xX@VCffM530Wa literal 0 HcmV?d00001 diff --git a/tests/test-sessions/test-sessions-common/src/main/resources/ProxyableFactory.java b/tests/test-sessions/test-sessions-common/src/main/resources/ProxyableFactory.java new file mode 100644 index 00000000000..df19483b528 --- /dev/null +++ b/tests/test-sessions/test-sessions-common/src/main/resources/ProxyableFactory.java @@ -0,0 +1,34 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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. +// ======================================================================== +// + +import java.lang.ClassLoader; +import java.lang.reflect.Proxy; + +public class ProxyableFactory +{ + public static Proxyable newProxyable (ClassLoader cl) + { + return getProxy(cl, new ProxyableInvocationHandler()); + } + + public static Proxyable getProxy(ClassLoader classloader, ProxyableInvocationHandler pih) + { + Proxyable p = (Proxyable)Proxy.newProxyInstance(classloader, new Class[] {Proxyable.class}, pih); + return p; + } +} diff --git a/tests/test-sessions/test-sessions-common/src/main/resources/ProxyableInvocationHandler.clazz b/tests/test-sessions/test-sessions-common/src/main/resources/ProxyableInvocationHandler.clazz new file mode 100644 index 0000000000000000000000000000000000000000..1e4eab6e4e7125462eb02972cd1973cd91634a09 GIT binary patch literal 910 zcmZuwO;Zy=5Pg$uvSAHE0!Bmu0|*;Thyr?vdENcGr{~Y#?>_;o;HiZH+>2qt!X&0F+(+8Pw1pYW zT6lmt6Z0l)6B!c=48u*~IovG^ujwfHzW9=Xy=53&RkG=G>6f|N5)lf#{NpJ6g(3RP zk&geIA#7*L492Re3x=UJM~aQsepPr|yy_AXb!1!Z(WYdt?eaFyyIeN%o7G)W^9upQ z6WcC9`E}v%sCwZ;;HXE=T!lzq$>+7bb$-xGXT3VCi36WXXqtG)V3kzMtBKc+)-v_Z zQ-`0mt|E!5aZP)}W!)9tqSh8i0!flAKDM|^{@FNkSTvE3V*-yD1}^+fJdR@t%O+N| zeZnw)adss(HTQDm3^RS`y!2}vM>{M>r-& zL<3!$LBmhAAVvS0ULk9cwUeRjDZ-~PjxnD5jYKZI^b3(=#EdgUPr`bMoxwaIZm1J6 k1wH<=BxVkGFi&wa^p>)?O{js;q=B){j?t+Wt^Mx)0}KV;TmS$7 literal 0 HcmV?d00001 diff --git a/tests/test-sessions/test-sessions-common/src/main/resources/ProxyableInvocationHandler.java b/tests/test-sessions/test-sessions-common/src/main/resources/ProxyableInvocationHandler.java new file mode 100644 index 00000000000..4f2cdc0da4d --- /dev/null +++ b/tests/test-sessions/test-sessions-common/src/main/resources/ProxyableInvocationHandler.java @@ -0,0 +1,46 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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. +// ======================================================================== +// + +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +public class ProxyableInvocationHandler implements InvocationHandler,Serializable +{ + private static final long serialVersionUID = 222222222222L; + + public ProxyableInvocationHandler () + { + super(); + } + + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + if ("equals".equals(method.getName())) + { + if (args != null && (args[0] instanceof Proxyable)) + return true; + else + return false; + } + + return 5; + } +}