You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by rm...@apache.org on 2015/11/02 19:08:37 UTC
tomee git commit: TOMEE-1649 adding eviction for 'cdi' websockets
Repository: tomee
Updated Branches:
refs/heads/master 58ec24151 -> 59e366a86
TOMEE-1649 adding eviction for 'cdi' websockets
Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/59e366a8
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/59e366a8
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/59e366a8
Branch: refs/heads/master
Commit: 59e366a86f7b9455701ef520873b7656d9626537
Parents: 58ec241
Author: Romain Manni-Bucau <rm...@gmail.com>
Authored: Mon Nov 2 10:08:13 2015 -0800
Committer: Romain Manni-Bucau <rm...@gmail.com>
Committed: Mon Nov 2 10:08:29 2015 -0800
----------------------------------------------------------------------
.../org/apache/openejb/core/WebContext.java | 37 ++++-
.../tomee/catalina/JavaeeInstanceManager.java | 16 +-
.../tomee/catalina/OpenEJBContextConfig.java | 36 +++-
.../tomee/catalina/TomEEWebappClassLoader.java | 15 ++
.../JavaEEDefaultServerEnpointConfigurator.java | 166 ++++++++++++++++++-
5 files changed, 258 insertions(+), 12 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tomee/blob/59e366a8/container/openejb-core/src/main/java/org/apache/openejb/core/WebContext.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/core/WebContext.java b/container/openejb-core/src/main/java/org/apache/openejb/core/WebContext.java
index 8674bfa..55d98c1 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/core/WebContext.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/core/WebContext.java
@@ -53,7 +53,7 @@ public class WebContext {
private Context jndiEnc;
private final AppContext appContext;
private Map<String, Object> bindings;
- private final Map<Object, CreationalContext<?>> creatonalContexts = new ConcurrentHashMap<Object, CreationalContext<?>>();
+ private final Map<Object, CreationalContext<?>> creationalContexts = new ConcurrentHashMap<>();
private WebBeansContext webbeansContext;
private String contextRoot;
private String host;
@@ -129,8 +129,7 @@ public class WebContext {
return appContext;
}
- public Object newInstance(final Class beanClass) throws OpenEJBException {
-
+ public <T> Instance newWeakableInstance(final Class<T> beanClass) throws OpenEJBException {
final WebBeansContext webBeansContext = getWebBeansContext();
final ConstructorInjectionBean<Object> beanDefinition = getConstructorInjectionBean(beanClass, webBeansContext);
final CreationalContext<Object> creationalContext;
@@ -156,10 +155,16 @@ public class WebContext {
if (webBeansContext != null) {
final InjectionTargetBean<Object> bean = InjectionTargetBean.class.cast(beanDefinition);
bean.getInjectionTarget().inject(beanInstance, creationalContext);
+ }
+ return new Instance(beanInstance, creationalContext);
+ }
- creatonalContexts.put(beanInstance, creationalContext);
+ public Object newInstance(final Class beanClass) throws OpenEJBException {
+ final Instance instance = newWeakableInstance(beanClass);
+ if (instance.getCreationalContext() != null) {
+ creationalContexts.put(instance.getValue(), instance.getCreationalContext());
}
- return beanInstance;
+ return instance.getValue();
}
private ConstructorInjectionBean<Object> getConstructorInjectionBean(final Class beanClass, final WebBeansContext webBeansContext) {
@@ -227,7 +232,7 @@ public class WebContext {
// if the bean is dependent simply cleanup the creational context once it is created
final Class<? extends Annotation> scope = beanDefinition.getScope();
if (scope == null || Dependent.class.equals(scope)) {
- creatonalContexts.put(beanInstance, creationalContext);
+ creationalContexts.put(beanInstance, creationalContext);
}
}
@@ -262,9 +267,27 @@ public class WebContext {
}
public void destroy(final Object o) {
- final CreationalContext<?> ctx = creatonalContexts.remove(o);
+ final CreationalContext<?> ctx = creationalContexts.remove(o);
if (ctx != null) {
ctx.release();
}
}
+
+ public static class Instance {
+ private final Object value;
+ private final CreationalContext<?> cc;
+
+ public Instance(final Object value, final CreationalContext<?> cc) {
+ this.value = value;
+ this.cc = cc;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public CreationalContext<?> getCreationalContext() {
+ return cc;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/tomee/blob/59e366a8/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/JavaeeInstanceManager.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/JavaeeInstanceManager.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/JavaeeInstanceManager.java
index 506bd00..3a4540e 100644
--- a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/JavaeeInstanceManager.java
+++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/JavaeeInstanceManager.java
@@ -25,6 +25,7 @@ import org.apache.webbeans.exception.WebBeansCreationException;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.naming.NamingException;
+import javax.servlet.ServletContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -40,6 +41,10 @@ public class JavaeeInstanceManager implements InstanceManager {
this.webContext = webContext;
}
+ public ServletContext getServletContext() {
+ return webContext == null ? null : webContext.getServletContext();
+ }
+
@Override
public Object newInstance(final Class<?> clazz) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException {
try {
@@ -51,6 +56,16 @@ public class JavaeeInstanceManager implements InstanceManager {
}
}
+ public WebContext.Instance newWeakableInstance(final Class<?> clazz) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException {
+ try {
+ final WebContext.Instance object = webContext.newWeakableInstance(clazz);
+ postConstruct(object.getValue(), clazz);
+ return object;
+ } catch (final OpenEJBException | WebBeansCreationException | WebBeansConfigurationException e) {
+ throw (InstantiationException) new InstantiationException(e.getMessage()).initCause(e);
+ }
+ }
+
@Override
public Object newInstance(final String className) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, ClassNotFoundException {
final ClassLoader classLoader = webContext.getClassLoader();
@@ -178,5 +193,4 @@ public class JavaeeInstanceManager implements InstanceManager {
preDestroy.setAccessible(accessibility);
}
}
-
}
http://git-wip-us.apache.org/repos/asf/tomee/blob/59e366a8/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/OpenEJBContextConfig.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/OpenEJBContextConfig.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/OpenEJBContextConfig.java
index 357a912..7e2e410 100644
--- a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/OpenEJBContextConfig.java
+++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/OpenEJBContextConfig.java
@@ -63,6 +63,13 @@ import org.apache.webbeans.config.WebBeansContext;
import org.apache.webbeans.web.context.WebConversationFilter;
import org.apache.xbean.finder.IAnnotationFinder;
+import javax.servlet.ServletContainerInitializer;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.ws.rs.core.Application;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -83,9 +90,6 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
-import javax.servlet.ServletContainerInitializer;
-import javax.servlet.http.HttpServlet;
-import javax.ws.rs.core.Application;
public class OpenEJBContextConfig extends ContextConfig {
private static Logger logger = Logger.getInstance(LogCategory.OPENEJB, OpenEJBContextConfig.class);
@@ -427,6 +431,32 @@ public class OpenEJBContextConfig extends ContextConfig {
final ClassLoader classLoader = context.getLoader().getClassLoader();
+ try {
+ classLoader.loadClass("org.apache.tomcat.websocket.server.DefaultServerEndpointConfigurator");
+ context.addServletContainerInitializer(new ServletContainerInitializer() {
+ @Override
+ public void onStartup(final Set<Class<?>> c, final ServletContext ctx) throws ServletException {
+ ctx.addListener(new ServletContextListener() {
+ @Override
+ public void contextInitialized(final ServletContextEvent sce) {
+ //no -op
+ }
+
+ @Override
+ public void contextDestroyed(final ServletContextEvent sce) { // ensure we cleanup our "eviction" processes
+ try {
+ org.apache.tomee.catalina.websocket.JavaEEDefaultServerEnpointConfigurator.unregisterProcesses(sce.getServletContext());
+ } catch (final Throwable th) {
+ // no-op
+ }
+ }
+ });
+ }
+ }, null);
+ } catch (final Throwable noWebSocket) {
+ // no-op: ok
+ }
+
// add myfaces auto-initializer if mojarra is not present
try {
classLoader.loadClass("com.sun.faces.context.SessionMap");
http://git-wip-us.apache.org/repos/asf/tomee/blob/59e366a8/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomEEWebappClassLoader.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomEEWebappClassLoader.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomEEWebappClassLoader.java
index 1b672b6..17fd006 100644
--- a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomEEWebappClassLoader.java
+++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomEEWebappClassLoader.java
@@ -430,6 +430,21 @@ public class TomEEWebappClassLoader extends ParallelWebappClassLoader {
}
return Collections.enumeration(list);
}
+ if ("META-INF/services/javax.websocket.ContainerProvider".equals(name)) {
+ final Collection<URL> list = new ArrayList<>(Collections.list(super.getResources(name)));
+ final Iterator<URL> it = list.iterator();
+ while (it.hasNext()) {
+ final URL next = it.next();
+ final File file = Files.toFile(next);
+ if (!file.isFile() && NewLoaderLogic.skip(next)) {
+ it.remove();
+ }
+ }
+ if (list.size() == 1) {
+ return Collections.enumeration(list);
+ }
+ return Collections.enumeration(list);
+ }
return URLClassLoaderFirst.filterResources(name, super.getResources(name));
}
http://git-wip-us.apache.org/repos/asf/tomee/blob/59e366a8/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/websocket/JavaEEDefaultServerEnpointConfigurator.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/websocket/JavaEEDefaultServerEnpointConfigurator.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/websocket/JavaEEDefaultServerEnpointConfigurator.java
index 34df1dc..6613c70 100644
--- a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/websocket/JavaEEDefaultServerEnpointConfigurator.java
+++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/websocket/JavaEEDefaultServerEnpointConfigurator.java
@@ -16,14 +16,51 @@
*/
package org.apache.tomee.catalina.websocket;
+import org.apache.openejb.core.WebContext;
import org.apache.openejb.loader.SystemInstance;
import org.apache.tomcat.InstanceManager;
+import org.apache.tomcat.websocket.BackgroundProcess;
+import org.apache.tomcat.websocket.BackgroundProcessManager;
+import org.apache.tomcat.websocket.WsWebSocketContainer;
+import org.apache.tomcat.websocket.pojo.PojoEndpointBase;
+import org.apache.tomcat.websocket.server.Constants;
import org.apache.tomcat.websocket.server.DefaultServerEndpointConfigurator;
+import org.apache.tomee.catalina.JavaeeInstanceManager;
import org.apache.tomee.catalina.TomcatWebAppBuilder;
+import javax.servlet.ServletContext;
+import javax.websocket.Endpoint;
+import javax.websocket.Session;
+import javax.websocket.WebSocketContainer;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.LinkedList;
import java.util.Map;
+import java.util.Set;
public class JavaEEDefaultServerEnpointConfigurator extends DefaultServerEndpointConfigurator {
+ private static final String BG_PROCESSES_LIST = JavaEEDefaultServerEnpointConfigurator.class.getName() + ".bgProcesses";
+
+ // for websocket eviction
+ private static final Field END_POINT_SESSION_MAP_LOCK;
+ private static final Field ENDPOINT_SESSION_MAP;
+ private static final Method GET_POJO;
+ static {
+ try {
+ ENDPOINT_SESSION_MAP = WsWebSocketContainer.class.getDeclaredField("endpointSessionMap");
+ ENDPOINT_SESSION_MAP.setAccessible(true);
+ END_POINT_SESSION_MAP_LOCK = WsWebSocketContainer.class.getDeclaredField("endPointSessionMapLock");
+ END_POINT_SESSION_MAP_LOCK.setAccessible(true);
+
+ GET_POJO = PojoEndpointBase.class.getDeclaredMethod("getPojo");
+ GET_POJO.setAccessible(true);
+ } catch (final Exception e) {
+ throw new IllegalStateException("Toncat not compatible with tomee", e);
+ }
+ }
+
private final Map<ClassLoader, InstanceManager> instanceManagers;
public JavaEEDefaultServerEnpointConfigurator() {
@@ -50,7 +87,38 @@ public class JavaEEDefaultServerEnpointConfigurator extends DefaultServerEndpoin
}
try {
- return clazz.cast(instanceManager.newInstance(clazz));
+ final Object instance;
+ if (JavaeeInstanceManager.class.isInstance(instanceManager)) {
+ final JavaeeInstanceManager javaeeInstanceManager = JavaeeInstanceManager.class.cast(instanceManager);
+ final WebContext.Instance cdiInstance = javaeeInstanceManager.newWeakableInstance(clazz);
+ instance = cdiInstance.getValue();
+ if (cdiInstance.getCreationalContext() != null) { // TODO: if we manage to have better listeners on tomcat we can use it rather than it
+ final ServletContext sc = javaeeInstanceManager.getServletContext();
+ if (sc != null) {
+ Collection<CdiCleanUpBackgroundProcess> processes;
+ synchronized (sc) {
+ processes = (Collection<CdiCleanUpBackgroundProcess>) sc.getAttribute(BG_PROCESSES_LIST);
+ if (processes == null) {
+ processes = new LinkedList<>();
+ sc.setAttribute(BG_PROCESSES_LIST, processes);
+ }
+ }
+
+ final WebSocketContainer wsc = WebSocketContainer.class.cast(sc.getAttribute(Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE));
+ final Object lock = END_POINT_SESSION_MAP_LOCK.get(wsc);
+ if (wsc != null && WsWebSocketContainer.class.isInstance(wsc)) {
+ final CdiCleanUpBackgroundProcess process = new CdiCleanUpBackgroundProcess(wsc, cdiInstance, lock);
+ synchronized (processes) {
+ processes.add(process);
+ }
+ BackgroundProcessManager.getInstance().register(process);
+ }
+ }
+ }
+ } else {
+ instance = instanceManager.newInstance(clazz);
+ }
+ return clazz.cast(instance);
} catch (final Exception e) {
if (InstantiationException.class.isInstance(e)) {
throw InstantiationException.class.cast(e);
@@ -58,4 +126,100 @@ public class JavaEEDefaultServerEnpointConfigurator extends DefaultServerEndpoin
throw new InstantiationException(e.getMessage());
}
}
+
+ public static void unregisterProcesses(final ServletContext sc) { // no sync needed at this point - no more "runtime"
+ final Collection<CdiCleanUpBackgroundProcess> processes = (Collection<CdiCleanUpBackgroundProcess>) sc.getAttribute(BG_PROCESSES_LIST);
+ if (processes == null) {
+ return;
+ }
+ for (final CdiCleanUpBackgroundProcess p : processes) {
+ try {
+ p.stop();
+ } catch (final RuntimeException e) {
+ // no-op
+ }
+ }
+ }
+
+ private static class CdiCleanUpBackgroundProcess implements BackgroundProcess {
+ private volatile int period = 1; // 1s by default
+ private volatile int acceptRetries = 3; // in case there is latency between this call and registerSession()
+ private volatile Set<Session> sessions;
+ private volatile boolean stopped;
+
+ private final WebSocketContainer container;
+ private final Object lock;
+ private final WebContext.Instance cdiInstance;
+
+ private CdiCleanUpBackgroundProcess(final WebSocketContainer wsc, final WebContext.Instance cdiInstance, final Object lock) {
+ this.container = wsc;
+ this.cdiInstance = cdiInstance;
+ this.lock = lock;
+ }
+
+ @Override
+ public void backgroundProcess() {
+ if (!hasSession() && --acceptRetries > 0) {
+ stop();
+ }
+ }
+
+ @Override
+ public void setProcessPeriod(final int period) {
+ this.period = period;
+ }
+
+ @Override
+ public int getProcessPeriod() {
+ return period;
+ }
+
+ private boolean hasSession() {
+ try {
+ if (sessions == null) { // needs to be lazy cause tomcat register sessions after
+ final Map<Endpoint, Set<Session>> sessionsByEndpoint = (Map<Endpoint, Set<Session>>) ENDPOINT_SESSION_MAP.get(container);
+ if (sessionsByEndpoint != null) { // find sessions
+ synchronized (lock) {
+ for (final Map.Entry<Endpoint, Set<Session>> e : sessionsByEndpoint.entrySet()) {
+ if (e.getKey() == cdiInstance.getValue()) {
+ sessions = e.getValue();
+ break;
+ }
+ if (PojoEndpointBase.class.isInstance(e.getKey())) {
+ try {
+ final Object pojo = GET_POJO.invoke(e.getKey());
+ if (pojo == cdiInstance.getValue()) {
+ sessions = e.getValue();
+ break;
+ }
+ } catch (final InvocationTargetException e1) {
+ // no-op
+ }
+ }
+ }
+ }
+ }
+ }
+
+ synchronized (lock) {
+ return sessions != null && !sessions.isEmpty();
+ }
+ } catch (final IllegalAccessException e) {
+ // no-op
+ }
+ return false;
+ }
+
+ public void stop() {
+ if (stopped) {
+ return;
+ }
+ stopped = true;
+ try {
+ cdiInstance.getCreationalContext().release();
+ } finally {
+ BackgroundProcessManager.getInstance().unregister(this);
+ }
+ }
+ }
}