From e1a8588bc954239a2d9948bf0977bf71dbf34b6a Mon Sep 17 00:00:00 2001 From: Jonathan Gallimore Date: Mon, 13 May 2024 21:26:34 +0100 Subject: [PATCH] TOMEE-4333 proxy refactor to prevent NotSerializableException --- boms/tomee-plume/pom.xml | 11 +++ boms/tomee-plus/pom.xml | 11 +++ .../catalina/HttpServletRequestProxy.java | 59 +++++++++++++ .../tomee/catalina/HttpSessionProxy.java | 62 ++++++++++++++ .../tomee/catalina/ServletContextProxy.java | 83 +++++++++++++++++++ .../tomee/catalina/TomcatWebAppBuilder.java | 24 +++--- .../catalina/cdi/ServletContextHandler.java | 64 -------------- .../catalina/HttpServletRequestProxyTest.java | 48 +++++++++++ .../tomee/catalina/HttpSessionProxyTest.java | 48 +++++++++++ .../catalina/ServletContextProxyTest.java | 48 +++++++++++ 10 files changed, 382 insertions(+), 76 deletions(-) create mode 100644 tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/HttpServletRequestProxy.java create mode 100644 tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/HttpSessionProxy.java create mode 100644 tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/ServletContextProxy.java delete mode 100644 tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/cdi/ServletContextHandler.java create mode 100644 tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/HttpServletRequestProxyTest.java create mode 100644 tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/HttpSessionProxyTest.java create mode 100644 tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/ServletContextProxyTest.java diff --git a/boms/tomee-plume/pom.xml b/boms/tomee-plume/pom.xml index 7c461f30aad..85e06889dd8 100644 --- a/boms/tomee-plume/pom.xml +++ b/boms/tomee-plume/pom.xml @@ -111,6 +111,17 @@ + + com.sun.xml.bind + jaxb-impl + 2.3.4 + + + * + * + + + com.sun.xml.messaging.saaj saaj-impl diff --git a/boms/tomee-plus/pom.xml b/boms/tomee-plus/pom.xml index a34522c1de6..653060e8fc7 100644 --- a/boms/tomee-plus/pom.xml +++ b/boms/tomee-plus/pom.xml @@ -111,6 +111,17 @@ + + com.sun.xml.bind + jaxb-impl + 2.3.4 + + + * + * + + + com.sun.xml.messaging.saaj saaj-impl diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/HttpServletRequestProxy.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/HttpServletRequestProxy.java new file mode 100644 index 00000000000..541d546a91f --- /dev/null +++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/HttpServletRequestProxy.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomee.catalina; + +import jakarta.servlet.http.HttpServletRequest; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +public interface HttpServletRequestProxy extends HttpServletRequest, Serializable { + + public Object writeReplace() throws ObjectStreamException; + + public static HttpServletRequest get() { + return (HttpServletRequest) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), + new Class[] { HttpServletRequestProxy.class, HttpServletRequest.class, Serializable.class }, + new Handler()); + } + + public static class Handler implements InvocationHandler { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("writeReplace") && method.getParameterTypes().length == 0) { + return new Serialized(); + } + + try { + HttpServletRequest obj = OpenEJBSecurityListener.requests.get(); + return method.invoke(obj, args); + } catch (final InvocationTargetException ite) { + throw ite.getCause(); + } + } + } + + public static class Serialized implements Serializable { + public Object readResolve() throws ObjectStreamException { + return HttpServletRequestProxy.get(); + } + } +} diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/HttpSessionProxy.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/HttpSessionProxy.java new file mode 100644 index 00000000000..9ec26e17034 --- /dev/null +++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/HttpSessionProxy.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomee.catalina; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +public interface HttpSessionProxy extends HttpSession, Serializable { + + public Object writeReplace() throws ObjectStreamException; + + public static HttpSession get() { + return (HttpSession) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), + new Class[] { HttpSessionProxy.class, HttpSession.class, Serializable.class }, + new Handler()); + } + + public static class Handler implements InvocationHandler { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("writeReplace") && method.getParameterTypes().length == 0) { + return new Serialized(); + } + + try { + final HttpServletRequest request = OpenEJBSecurityListener.requests.get(); + final HttpSession session = request == null ? null : request.getSession(); + + return method.invoke(session, args); + } catch (final InvocationTargetException ite) { + throw ite.getCause(); + } + } + } + + public static class Serialized implements Serializable { + public Object readResolve() throws ObjectStreamException { + return HttpSessionProxy.get(); + } + } +} diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/ServletContextProxy.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/ServletContextProxy.java new file mode 100644 index 00000000000..eb437c9ed26 --- /dev/null +++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/ServletContextProxy.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomee.catalina; + +import jakarta.servlet.ServletContext; +import org.apache.catalina.connector.Request; +import org.apache.openejb.AppContext; +import org.apache.openejb.cdi.CdiAppContextsService; +import org.apache.openejb.core.WebContext; +import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.spi.ContainerSystem; + +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import static org.apache.tomee.catalina.TomcatWebAppBuilder.CONTEXTS; + +public interface ServletContextProxy extends ServletContext, Serializable { + + public Object writeReplace() throws ObjectStreamException; + + public static ServletContext get() { + return (ServletContext) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), + new Class[] { ServletContextProxy.class, ServletContext.class, CdiAppContextsService.FiredManually.class, Serializable.class }, + new Handler()); + } + + public static class Handler implements InvocationHandler { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("writeReplace") && method.getParameterTypes().length == 0) { + return new Serialized(); + } + + // ITE are handled by Proxys + final Request request = OpenEJBSecurityListener.requests.get(); + if (request != null) { + return method.invoke(request.getServletContext(), args); + } + + final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + final ServletContext c = CONTEXTS.get(contextClassLoader); + if (c != null) { + return method.invoke(c, args); + } + + OpenEJBSecurityListener.requests.remove(); // can be a not container thread so clean it up + for (final AppContext a : SystemInstance.get().getComponent(ContainerSystem.class).getAppContexts()) { + for (final WebContext w : a.getWebContexts()) { + if (w.getClassLoader() == contextClassLoader) { // not in CXF so == should be fine + return method.invoke(w.getServletContext(), args); + } + } + } + + throw new IllegalStateException("Didnt find a web context for " + contextClassLoader); + } + } + + public static class Serialized implements Serializable { + public Object readResolve() throws ObjectStreamException { + return ServletContextProxy.get(); + } + } +} diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java index a885a8a0423..6595eaab26b 100644 --- a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java +++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatWebAppBuilder.java @@ -126,7 +126,6 @@ import org.apache.tomcat.util.descriptor.web.ResourceBase; import org.apache.tomcat.util.http.CookieProcessor; import org.apache.tomcat.util.scan.StandardJarScanFilter; -import org.apache.tomee.catalina.cdi.ServletContextHandler; import org.apache.tomee.catalina.cdi.WebBeansThreadBindingListener; import org.apache.tomee.catalina.cluster.ClusterObserver; import org.apache.tomee.catalina.cluster.TomEEClusterListener; @@ -174,6 +173,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReference; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -223,6 +223,8 @@ public class TomcatWebAppBuilder implements WebAppBuilder, ContextListener, Pare private final Map instanceManagers = new ConcurrentHashMap<>(); + public static final Map CONTEXTS = new ConcurrentHashMap<>(); + /** * Context information for web applications */ @@ -269,7 +271,6 @@ public class TomcatWebAppBuilder implements WebAppBuilder, ContextListener, Pare private ClassLoader parentClassLoader; private boolean initJEEInfo = true; - private final ServletContextHandler servletContextHandler; private final boolean noHostCheck; /** @@ -341,7 +342,6 @@ public TomcatWebAppBuilder() { this.configurationFactory = configurationFactory; deploymentLoader = new DeploymentLoader(); - servletContextHandler = new ServletContextHandler(); setComponentsUsedByCDI(); try { // before tomcat was using ServiceLoader or manually instantiation, now it uses SL for itself so we can be in conflict @@ -356,13 +356,13 @@ public TomcatWebAppBuilder() { private void setComponentsUsedByCDI() { final SystemInstance systemInstance = SystemInstance.get(); if (systemInstance.getComponent(HttpServletRequest.class) == null) { - systemInstance.setComponent(HttpServletRequest.class, Proxys.threadLocalProxy(HttpServletRequest.class, OpenEJBSecurityListener.requests, null)); + systemInstance.setComponent(HttpServletRequest.class, HttpServletRequestProxy.get()); } if (systemInstance.getComponent(HttpSession.class) == null) { - systemInstance.setComponent(jakarta.servlet.http.HttpSession.class, Proxys.threadLocalRequestSessionProxy(OpenEJBSecurityListener.requests, null)); + systemInstance.setComponent(jakarta.servlet.http.HttpSession.class, HttpSessionProxy.get()); } if (systemInstance.getComponent(ServletContext.class) == null) { - systemInstance.setComponent(ServletContext.class, Proxys.handlerProxy(servletContextHandler, ServletContext.class, CdiAppContextsService.FiredManually.class)); + systemInstance.setComponent(ServletContext.class, ServletContextProxy.get()); } } @@ -1335,11 +1335,11 @@ private void startInternal(final StandardContext standardContext) { setFinderOnContextConfig(standardContext, appModule); - servletContextHandler.getContexts().put(classLoader, standardContext.getServletContext()); + CONTEXTS.put(classLoader, standardContext.getServletContext()); try { appContext = a.createApplication(contextInfo.appInfo, classLoader); } finally { - servletContextHandler.getContexts().remove(classLoader); + CONTEXTS.remove(classLoader); } // todo add watched resources to context @@ -1460,7 +1460,7 @@ private void startInternal(final StandardContext standardContext) { if (!contextInfo.appInfo.webAppAlone) { final List beanContexts = assembler.initEjbs(classLoader, contextInfo.appInfo, appContext, injections, new ArrayList(), webAppInfo.moduleId); OpenEJBLifecycle.CURRENT_APP_INFO.set(contextInfo.appInfo); - servletContextHandler.getContexts().put(classLoader, standardContext.getServletContext()); + CONTEXTS.put(classLoader, standardContext.getServletContext()); try { new CdiBuilder().build(contextInfo.appInfo, appContext, beanContexts, webContext); } catch (final Exception e) { @@ -1470,7 +1470,7 @@ private void startInternal(final StandardContext standardContext) { } throw e; } finally { - servletContextHandler.getContexts().remove(classLoader); + CONTEXTS.remove(classLoader); OpenEJBLifecycle.CURRENT_APP_INFO.remove(); } assembler.startEjbs(true, beanContexts); @@ -1807,7 +1807,7 @@ public void afterStart(final StandardContext standardContext) { // the CoreContainerSystem does not contain the WebContext // see also the start method getContainerSystem().addWebDeployment(webContext); try { - servletContextHandler.getContexts().put(classLoader, standardContext.getServletContext()); + CONTEXTS.put(classLoader, standardContext.getServletContext()); for (final WebAppInfo webAppInfo : contextInfo.appInfo.webApps) { final String wId = getId(webAppInfo.host, webAppInfo.contextRoot, contextInfo.version); @@ -1828,7 +1828,7 @@ public void afterStart(final StandardContext standardContext) { } } } finally { - servletContextHandler.getContexts().remove(classLoader); + CONTEXTS.remove(classLoader); } thread.setContextClassLoader(originalLoader); diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/cdi/ServletContextHandler.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/cdi/ServletContextHandler.java deleted file mode 100644 index 2d697ffcbb8..00000000000 --- a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/cdi/ServletContextHandler.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.tomee.catalina.cdi; - -import org.apache.catalina.connector.Request; -import org.apache.openejb.AppContext; -import org.apache.openejb.core.WebContext; -import org.apache.openejb.loader.SystemInstance; -import org.apache.openejb.spi.ContainerSystem; -import org.apache.tomee.catalina.OpenEJBSecurityListener; - -import jakarta.servlet.ServletContext; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -public class ServletContextHandler implements InvocationHandler { - private final ConcurrentMap contexts = new ConcurrentHashMap<>(); - - @Override - public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { - // ITE are handled by Proxys - final Request request = OpenEJBSecurityListener.requests.get(); - if (request != null) { - return method.invoke(request.getServletContext(), args); - } - - final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - final ServletContext c = contexts.get(contextClassLoader); - if (c != null) { - return method.invoke(c, args); - } - - OpenEJBSecurityListener.requests.remove(); // can be a not container thread so clean it up - for (final AppContext a : SystemInstance.get().getComponent(ContainerSystem.class).getAppContexts()) { - for (final WebContext w : a.getWebContexts()) { - if (w.getClassLoader() == contextClassLoader) { // not in CXF so == should be fine - return method.invoke(w.getServletContext(), args); - } - } - } - - throw new IllegalStateException("Didnt find a web context for " + contextClassLoader); - } - - public ConcurrentMap getContexts() { - return contexts; - } -} diff --git a/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/HttpServletRequestProxyTest.java b/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/HttpServletRequestProxyTest.java new file mode 100644 index 00000000000..0e4777b601b --- /dev/null +++ b/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/HttpServletRequestProxyTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomee.catalina; + +import org.junit.Assert; +import org.junit.Test; + +import jakarta.servlet.http.HttpServletRequest; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class HttpServletRequestProxyTest { + + @Test + public void test() throws Exception { + final HttpServletRequest request = HttpServletRequestProxy.get(); + + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + final ObjectOutputStream so = new ObjectOutputStream(outputStream); + so.writeObject(request); + so.flush(); + + final byte[] bytes = outputStream.toByteArray(); + + final ByteArrayInputStream is = new ByteArrayInputStream(bytes); + final ObjectInputStream ois = new ObjectInputStream(is); + final HttpServletRequest readRequest = (HttpServletRequest) ois.readObject(); + + Assert.assertNotNull(readRequest); + } +} diff --git a/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/HttpSessionProxyTest.java b/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/HttpSessionProxyTest.java new file mode 100644 index 00000000000..8064910177b --- /dev/null +++ b/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/HttpSessionProxyTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomee.catalina; + +import org.junit.Assert; +import org.junit.Test; + +import jakarta.servlet.http.HttpSession; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class HttpSessionProxyTest { + + @Test + public void test() throws Exception { + final HttpSession session = HttpSessionProxy.get(); + + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + final ObjectOutputStream so = new ObjectOutputStream(outputStream); + so.writeObject(session); + so.flush(); + + final byte[] bytes = outputStream.toByteArray(); + + final ByteArrayInputStream is = new ByteArrayInputStream(bytes); + final ObjectInputStream ois = new ObjectInputStream(is); + final HttpSession readSession = (HttpSession) ois.readObject(); + + Assert.assertNotNull(readSession); + } +} diff --git a/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/ServletContextProxyTest.java b/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/ServletContextProxyTest.java new file mode 100644 index 00000000000..520f0ce6456 --- /dev/null +++ b/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/ServletContextProxyTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.tomee.catalina; + +import org.junit.Assert; +import org.junit.Test; + +import jakarta.servlet.ServletContext; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class ServletContextProxyTest { + + @Test + public void test() throws Exception { + final ServletContext sc = ServletContextProxy.get(); + + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + final ObjectOutputStream so = new ObjectOutputStream(outputStream); + so.writeObject(sc); + so.flush(); + + final byte[] bytes = outputStream.toByteArray(); + + final ByteArrayInputStream is = new ByteArrayInputStream(bytes); + final ObjectInputStream ois = new ObjectInputStream(is); + final ServletContext readSc = (ServletContext) ois.readObject(); + + Assert.assertNotNull(readSc); + } +}