Author: remy.maucherat(a)jboss.com
Date: 2014-05-23 18:13:48 -0400 (Fri, 23 May 2014)
New Revision: 2417
Added:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/ClassIntrospecter.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/InstanceFactory.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/InstanceHandle.java
Modified:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsSession.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerPartialBase.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBase.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsServerContainer.java
Log:
BZ1086399: Tentative plumbing for CDI support, submitted by Stuart Douglas.
Added: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/ClassIntrospecter.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/ClassIntrospecter.java
(rev 0)
+++
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/ClassIntrospecter.java 2014-05-23
22:13:48 UTC (rev 2417)
@@ -0,0 +1,30 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2014 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed 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.tomcat.websocket;
+
+/**
+ * Interface that allows the container to hook into the class creation process
+ *
+ * @author Stuart Douglas
+ */
+public interface ClassIntrospecter {
+
+ InstanceFactory createInstanceFactory(final Class<?> clazz) throws
NoSuchMethodException;
+
+}
Added: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/InstanceFactory.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/InstanceFactory.java
(rev 0)
+++
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/InstanceFactory.java 2014-05-23
22:13:48 UTC (rev 2417)
@@ -0,0 +1,35 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2014 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed 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.tomcat.websocket;
+
+/**
+ * Factory that creates fully injected component instances.
+ *
+ * @author Stuart Douglas
+ */
+public interface InstanceFactory {
+
+ /**
+ * Factory that creates a fully injected instance.
+ *
+ * @return The fully injected instance
+ */
+ InstanceHandle createInstance() throws InstantiationException;
+
+}
Added: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/InstanceHandle.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/InstanceHandle.java
(rev 0)
+++
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/InstanceHandle.java 2014-05-23
22:13:48 UTC (rev 2417)
@@ -0,0 +1,40 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2014 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed 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.tomcat.websocket;
+
+/**
+ * A handle for a container managed instance. When the servlet container is
+ * done with it it should call the {@link #release()} method
+ *
+ * @author Stuart Douglas
+ */
+public interface InstanceHandle {
+
+ /**
+ * @return The managed instance
+ *
+ */
+ Object getInstance();
+
+ /**
+ * releases the instance, uninjecting and calling an pre-destroy methods as
appropriate
+ */
+ void release();
+
+}
Modified: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsSession.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsSession.java 2014-05-23
14:13:07 UTC (rev 2416)
+++ branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsSession.java 2014-05-23
22:13:48 UTC (rev 2417)
@@ -45,6 +45,7 @@
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
+import org.apache.catalina.ThreadBindingListener;
import org.apache.tomcat.util.ExceptionUtils;
import org.jboss.web.WebsocketsLogger;
@@ -648,6 +649,14 @@
}
}
+ public ThreadBindingListener getThreadBindingListener() {
+ return webSocketContainer.getThreadBindingListener();
+ }
+
+ public ClassLoader getClassLoader() {
+ return webSocketContainer.getClassLoader();
+ }
+
private static enum State {
OPEN,
CLOSING,
Modified:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
===================================================================
---
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsWebSocketContainer.java 2014-05-23
14:13:07 UTC (rev 2416)
+++
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsWebSocketContainer.java 2014-05-23
22:13:48 UTC (rev 2417)
@@ -63,6 +63,7 @@
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
+import org.apache.catalina.ThreadBindingListener;
import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.websocket.pojo.PojoEndpointClient;
import org.jboss.web.WebsocketsLogger;
@@ -70,6 +71,10 @@
public class WsWebSocketContainer
implements WebSocketContainer, BackgroundProcess {
+ protected static final ThreadBindingListener DEFAULT_THREAD_BINDING_LISTENER = (new
ThreadBindingListener() {
+ public void bind() {}
+ public void unbind() {}
+ });
/**
* Property name to set to configure the value that is passed to
* {@link SSLEngine#setEnabledProtocols(String[])}. The value should be a
@@ -827,5 +832,11 @@
return processPeriod;
}
+ public ThreadBindingListener getThreadBindingListener() {
+ return DEFAULT_THREAD_BINDING_LISTENER;
+ }
+ public ClassLoader getClassLoader() {
+ return WsWebSocketContainer.class.getClassLoader();
+ }
}
Modified:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java
===================================================================
---
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java 2014-05-23
14:13:07 UTC (rev 2416)
+++
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java 2014-05-23
22:13:48 UTC (rev 2417)
@@ -27,7 +27,10 @@
import javax.websocket.MessageHandler;
import javax.websocket.Session;
+import org.apache.catalina.ThreadBindingListener;
import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.websocket.InstanceHandle;
+import org.apache.tomcat.websocket.WsSession;
import org.jboss.web.WebsocketsLogger;
/**
@@ -38,6 +41,7 @@
public abstract class PojoEndpointBase extends Endpoint {
private Object pojo;
+ private InstanceHandle instanceHandle;
private Map<String,String> pathParameters;
private PojoMethodMapping methodMapping;
@@ -56,7 +60,11 @@
}
if (methodMapping.getOnOpen() != null) {
+ ThreadBindingListener tbl = ((WsSession)
session).getThreadBindingListener();
+ ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
+
Thread.currentThread().setContextClassLoader(((WsSession)session).getClassLoader());
+ tbl.bind();
methodMapping.getOnOpen().invoke(pojo,
methodMapping.getOnOpenArgs(
pathParameters, session, config));
@@ -73,6 +81,12 @@
} catch (Throwable t) {
handleOnOpenError(session, t);
return;
+ } finally {
+ try {
+ tbl.unbind();
+ } finally {
+ Thread.currentThread().setContextClassLoader(old);
+ }
}
}
}
@@ -93,37 +107,60 @@
@Override
public final void onClose(Session session, CloseReason closeReason) {
-
- if (methodMapping.getOnClose() != null) {
- try {
- methodMapping.getOnClose().invoke(pojo,
- methodMapping.getOnCloseArgs(pathParameters, session,
closeReason));
- } catch (Throwable t) {
- WebsocketsLogger.ROOT_LOGGER.onCloseFailed(pojo.getClass().getName(),
t);
- handleOnCloseError(session, t);
+ try {
+ if (methodMapping.getOnClose() != null) {
+ ThreadBindingListener tbl = ((WsSession)
session).getThreadBindingListener();
+ ClassLoader old = Thread.currentThread().getContextClassLoader();
+ try {
+
Thread.currentThread().setContextClassLoader(((WsSession)session).getClassLoader());
+ tbl.bind();
+ methodMapping.getOnClose().invoke(pojo,
+ methodMapping.getOnCloseArgs(pathParameters, session,
closeReason));
+ } catch (Throwable t) {
+ WebsocketsLogger.ROOT_LOGGER.onCloseFailed(pojo.getClass().getName(),
t);
+ handleOnCloseError(session, t);
+ } finally {
+ try {
+ tbl.unbind();
+ } finally {
+ Thread.currentThread().setContextClassLoader(old);
+ }
+ }
}
- }
- // Trigger the destroy method for any associated decoders
- Set<MessageHandler> messageHandlers = session.getMessageHandlers();
- for (MessageHandler messageHandler : messageHandlers) {
- if (messageHandler instanceof PojoMessageHandlerWholeBase<?>) {
- ((PojoMessageHandlerWholeBase<?>) messageHandler).onClose();
+ // Trigger the destroy method for any associated decoders
+ Set<MessageHandler> messageHandlers = session.getMessageHandlers();
+ for (MessageHandler messageHandler : messageHandlers) {
+ if (messageHandler instanceof PojoMessageHandlerWholeBase<?>) {
+ ((PojoMessageHandlerWholeBase<?>) messageHandler).onClose();
+ }
}
+ } finally {
+ if (instanceHandle != null) {
+ instanceHandle.release();
+ instanceHandle = null;
+ }
}
}
private void handleOnCloseError(Session session, Throwable t) {
- // If really fatal - re-throw
- ExceptionUtils.handleThrowable(t);
+ try {
+ // If really fatal - re-throw
+ ExceptionUtils.handleThrowable(t);
- // Trigger the error handler and close the session
- onError(session, t);
- try {
- session.close();
- } catch (IOException ioe) {
- WebsocketsLogger.ROOT_LOGGER.closeSessionFailed(ioe);
+ // Trigger the error handler and close the session
+ onError(session, t);
+ try {
+ session.close();
+ } catch (IOException ioe) {
+ WebsocketsLogger.ROOT_LOGGER.closeSessionFailed(ioe);
+ }
+ } finally {
+ if(instanceHandle != null) {
+ instanceHandle.release();
+ instanceHandle = null;
+ }
}
}
@@ -133,7 +170,11 @@
if (methodMapping.getOnError() == null) {
WebsocketsLogger.ROOT_LOGGER.noOnError(pojo.getClass().getName(),
throwable);
} else {
+ ThreadBindingListener tbl = ((WsSession)
session).getThreadBindingListener();
+ ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
+
Thread.currentThread().setContextClassLoader(((WsSession)session).getClassLoader());
+ tbl.bind();
methodMapping.getOnError().invoke(
pojo,
methodMapping.getOnErrorArgs(pathParameters, session,
@@ -141,6 +182,12 @@
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
WebsocketsLogger.ROOT_LOGGER.onErrorFailed(pojo.getClass().getName(),
t);
+ } finally {
+ try {
+ tbl.unbind();
+ } finally {
+ Thread.currentThread().setContextClassLoader(old);
+ }
}
}
}
@@ -148,7 +195,10 @@
protected Object getPojo() { return pojo; }
protected void setPojo(Object pojo) { this.pojo = pojo; }
+ public InstanceHandle getInstanceHandle() { return instanceHandle; }
+ public void setInstanceHandle(InstanceHandle instanceHandle) { this.instanceHandle =
instanceHandle; }
+
protected Map<String,String> getPathParameters() { return pathParameters; }
protected void setPathParameters(Map<String,String> pathParameters) {
this.pathParameters = pathParameters;
Modified:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java
===================================================================
---
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java 2014-05-23
14:13:07 UTC (rev 2416)
+++
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java 2014-05-23
22:13:48 UTC (rev 2417)
@@ -16,6 +16,9 @@
*/
package org.apache.tomcat.websocket.pojo;
+import org.apache.tomcat.websocket.InstanceHandle;
+import org.apache.tomcat.websocket.WsSession;
+
import static org.jboss.web.WebsocketsMessages.MESSAGES;
import java.util.Map;
@@ -42,14 +45,24 @@
ServerEndpointConfig sec = (ServerEndpointConfig) endpointConfig;
+ InstanceHandle instanceHandle = null;
Object pojo;
+ ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
+
Thread.currentThread().setContextClassLoader(((WsSession)session).getClassLoader());
pojo = sec.getConfigurator().getEndpointInstance(
sec.getEndpointClass());
+ if(pojo instanceof InstanceHandle) {
+ instanceHandle = (InstanceHandle) pojo;
+ pojo = instanceHandle.getInstance();
+ }
} catch (InstantiationException e) {
throw MESSAGES.pojoInstanceFailed(sec.getEndpointClass().getName(), e);
+ } finally {
+ Thread.currentThread().setContextClassLoader(old);
}
setPojo(pojo);
+ setInstanceHandle(instanceHandle);
@SuppressWarnings("unchecked")
Map<String,String> pathParameters =
Modified:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerPartialBase.java
===================================================================
---
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerPartialBase.java 2014-05-23
14:13:07 UTC (rev 2416)
+++
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerPartialBase.java 2014-05-23
22:13:48 UTC (rev 2417)
@@ -24,6 +24,7 @@
import javax.websocket.MessageHandler;
import javax.websocket.Session;
+import org.apache.catalina.ThreadBindingListener;
import org.apache.tomcat.websocket.WsSession;
/**
@@ -67,12 +68,22 @@
parameters[indexPayload] = message;
}
Object result;
+ ThreadBindingListener tbl = ((WsSession) session).getThreadBindingListener();
+ ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
+
Thread.currentThread().setContextClassLoader(((WsSession)session).getClassLoader());
+ tbl.bind();
result = method.invoke(pojo, parameters);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException(e);
+ } finally {
+ try {
+ tbl.unbind();
+ } finally {
+ Thread.currentThread().setContextClassLoader(old);
+ }
}
processResult(result);
}
Modified:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBase.java
===================================================================
---
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBase.java 2014-05-23
14:13:07 UTC (rev 2416)
+++
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBase.java 2014-05-23
22:13:48 UTC (rev 2417)
@@ -23,6 +23,7 @@
import javax.websocket.MessageHandler;
import javax.websocket.Session;
+import org.apache.catalina.ThreadBindingListener;
import org.apache.tomcat.websocket.WsSession;
/**
@@ -76,12 +77,22 @@
parameters[indexPayload] = payload;
Object result;
+ ThreadBindingListener tbl = ((WsSession) session).getThreadBindingListener();
+ ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
+
Thread.currentThread().setContextClassLoader(((WsSession)session).getClassLoader());
+ tbl.bind();
result = method.invoke(pojo, parameters);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException(e);
+ } finally {
+ try {
+ tbl.unbind();
+ } finally {
+ Thread.currentThread().setContextClassLoader(old);
+ }
}
processResult(result);
}
Modified:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsServerContainer.java
===================================================================
---
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsServerContainer.java 2014-05-23
14:13:07 UTC (rev 2416)
+++
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsServerContainer.java 2014-05-23
22:13:48 UTC (rev 2417)
@@ -51,6 +51,10 @@
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator;
+import org.apache.catalina.ThreadBindingListener;
+import org.apache.catalina.core.ApplicationContext;
+import org.apache.tomcat.websocket.ClassIntrospecter;
+import org.apache.tomcat.websocket.InstanceFactory;
import org.apache.tomcat.websocket.WsSession;
import org.apache.tomcat.websocket.WsWebSocketContainer;
import org.apache.tomcat.websocket.pojo.PojoEndpointServer;
@@ -88,10 +92,29 @@
private final ExecutorService executorService;
private final ThreadGroup threadGroup;
private volatile boolean endpointsRegistered = false;
+ private final ClassIntrospecter classIntrospecter;
+ private final ThreadBindingListener threadBindingListener;
+
WsServerContainer(ServletContext servletContext) {
+
this.servletContext = servletContext;
+ ClassIntrospecter classIntrospecter = (ClassIntrospecter)
servletContext.getAttribute(ClassIntrospecter.class.getName());
+ servletContext.removeAttribute(ClassIntrospecter.class.getName()); //remove the
attribute as it is only needed for bootstrap
+ if(classIntrospecter == null) {
+ classIntrospecter = null;
+ }
+ this.classIntrospecter = classIntrospecter;
+ //this is a horrible horrible hack.
+ ThreadBindingListener threadBindingListener =
(ThreadBindingListener)servletContext.getAttribute(ThreadBindingListener.class.getName());
+ servletContext.removeAttribute(ThreadBindingListener.class.getName());
+ if(threadBindingListener != null) {
+ this.threadBindingListener = threadBindingListener;
+ } else {
+ this.threadBindingListener = DEFAULT_THREAD_BINDING_LISTENER;
+ }
+
// Configure servlet context wide defaults
String value = servletContext.getInitParameter(
Constants.BINARY_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM);
@@ -246,6 +269,13 @@
throw new
DeploymentException(MESSAGES.configuratorFailed(annotation.configurator().getName(),
pojo.getClass().getName()), e);
}
+ } else if(classIntrospecter != null) {
+ try {
+ configurator = new
ServerInstanceFactoryConfigurator(classIntrospecter.createInstanceFactory(pojo));
+ } catch (NoSuchMethodException e) {
+ throw new
DeploymentException(MESSAGES.configuratorFailed(ServerInstanceFactoryConfigurator.class.getName(),
+ pojo.getClass().getName()), e);
+ }
}
sec = ServerEndpointConfig.Builder.create(pojo, path).
decoders(Arrays.asList(annotation.decoders())).
@@ -443,7 +473,15 @@
return executorService;
}
+ @Override
+ public ThreadBindingListener getThreadBindingListener() {
+ return threadBindingListener;
+ }
+ public ClassLoader getClassLoader() {
+ return servletContext.getClassLoader();
+ }
+
private void shutdownExecutor() {
if (executorService == null) {
return;
@@ -537,4 +575,20 @@
return t;
}
}
+
+
+ private static final class ServerInstanceFactoryConfigurator extends
ServerEndpointConfig.Configurator {
+
+ private final InstanceFactory factory;
+
+ private ServerInstanceFactoryConfigurator(final InstanceFactory factory) {
+ this.factory = factory;
+ }
+
+ @Override
+ public <T> T getEndpointInstance(final Class<T> endpointClass) throws
InstantiationException {
+ return (T) factory.createInstance();
+ }
+ }
+
}