Skip to content

Commit

Permalink
TOMEE-4333 proxy refactor to prevent NotSerializableException
Browse files Browse the repository at this point in the history
  • Loading branch information
jgallimore committed May 16, 2024
1 parent 77cb279 commit 7d55f9b
Show file tree
Hide file tree
Showing 10 changed files with 385 additions and 76 deletions.
11 changes: 11 additions & 0 deletions boms/tomee-plume/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.4</version>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
Expand Down
11 changes: 11 additions & 0 deletions boms/tomee-plus/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.4</version>
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 javax.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();
}
}
}
Original file line number Diff line number Diff line change
@@ -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 javax.servlet.http.HttpServletRequest;
import javax.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();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* 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.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 javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -223,6 +223,8 @@ public class TomcatWebAppBuilder implements WebAppBuilder, ContextListener, Pare

private final Map<ClassLoader, InstanceManager> instanceManagers = new ConcurrentHashMap<>();

public static final Map<ClassLoader, ServletContext> CONTEXTS = new ConcurrentHashMap<>();

/**
* Context information for web applications
*/
Expand Down Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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
Expand All @@ -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(javax.servlet.http.HttpSession.class, Proxys.threadLocalRequestSessionProxy(OpenEJBSecurityListener.requests, null));
systemInstance.setComponent(javax.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());
}
}

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -1460,7 +1460,7 @@ private void startInternal(final StandardContext standardContext) {
if (!contextInfo.appInfo.webAppAlone) {
final List<BeanContext> beanContexts = assembler.initEjbs(classLoader, contextInfo.appInfo, appContext, injections, new ArrayList<BeanContext>(), 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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -1828,7 +1828,7 @@ public void afterStart(final StandardContext standardContext) {
}
}
} finally {
servletContextHandler.getContexts().remove(classLoader);
CONTEXTS.remove(classLoader);
}

thread.setContextClassLoader(originalLoader);
Expand Down
Loading

0 comments on commit 7d55f9b

Please sign in to comment.