Author: markt
Date: Sun Jun  3 18:47:25 2012
New Revision: 1345737

URL: http://svn.apache.org/viewvc?rev=1345737&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=53339
Ensure WebSocket event calls are made using the web application's class loader. 
Includes a test case.

Modified:
    tomcat/trunk/java/org/apache/catalina/websocket/StreamInbound.java
    tomcat/trunk/test/org/apache/catalina/websocket/TestWebSocket.java

Modified: tomcat/trunk/java/org/apache/catalina/websocket/StreamInbound.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/websocket/StreamInbound.java?rev=1345737&r1=1345736&r2=1345737&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/websocket/StreamInbound.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/websocket/StreamInbound.java Sun Jun  
3 18:47:25 2012
@@ -37,12 +37,18 @@ import org.apache.tomcat.util.net.Abstra
  */
 public abstract class StreamInbound implements UpgradeInbound {
 
+    private final ClassLoader applicationClassLoader;
     private UpgradeProcessor<?> processor = null;
     private WsOutbound outbound;
     private int outboundByteBufferSize = WsOutbound.DEFAULT_BUFFER_SIZE;
     private int outboundCharBufferSize = WsOutbound.DEFAULT_BUFFER_SIZE;
 
 
+    public StreamInbound() {
+        applicationClassLoader = 
Thread.currentThread().getContextClassLoader();
+    }
+
+
     public int getOutboundByteBufferSize() {
         return outboundByteBufferSize;
     }
@@ -121,11 +127,11 @@ public abstract class StreamInbound impl
                 byte opCode = frame.getOpCode();
 
                 if (opCode == Constants.OPCODE_BINARY) {
-                    onBinaryData(wsIs);
+                    doOnBinaryData(wsIs);
                 } else if (opCode == Constants.OPCODE_TEXT) {
                     InputStreamReader r =
                             new InputStreamReader(wsIs, new Utf8Decoder());
-                    onTextData(r);
+                    doOnTextData(r);
                 } else if (opCode == Constants.OPCODE_CLOSE){
                     closeOutboundConnection(frame);
                     return SocketState.CLOSED;
@@ -158,11 +164,37 @@ public abstract class StreamInbound impl
         return SocketState.UPGRADED;
     }
 
+    private void doOnBinaryData(InputStream is) throws IOException {
+        // Need to call onClose using the web application's class loader
+        Thread t = Thread.currentThread();
+        ClassLoader cl = t.getContextClassLoader();
+        t.setContextClassLoader(applicationClassLoader);
+        try {
+            onBinaryData(is);
+        } finally {
+            t.setContextClassLoader(cl);
+        }
+    }
+
+
+    private void doOnTextData(Reader r) throws IOException {
+        // Need to call onClose using the web application's class loader
+        Thread t = Thread.currentThread();
+        ClassLoader cl = t.getContextClassLoader();
+        t.setContextClassLoader(applicationClassLoader);
+        try {
+            onTextData(r);
+        } finally {
+            t.setContextClassLoader(cl);
+        }
+    }
+
+
     private void closeOutboundConnection(int status, ByteBuffer data) throws 
IOException {
         try {
             getWsOutbound().close(status, data);
         } finally {
-            onClose(status);
+            doOnClose(status);
         }
     }
 
@@ -170,13 +202,33 @@ public abstract class StreamInbound impl
         try {
             getWsOutbound().close(frame);
         } finally {
-            onClose(Constants.OPCODE_CLOSE);
+            doOnClose(Constants.OPCODE_CLOSE);
+        }
+    }
+
+    private void doOnClose(int status) {
+        // Need to call onClose using the web application's class loader
+        Thread t = Thread.currentThread();
+        ClassLoader cl = t.getContextClassLoader();
+        t.setContextClassLoader(applicationClassLoader);
+        try {
+            onClose(status);
+        } finally {
+            t.setContextClassLoader(cl);
         }
     }
 
     @Override
-    public void onUpgradeComplete() {
-        onOpen(outbound);
+    public final void onUpgradeComplete() {
+        // Need to call onOpen using the web application's class loader
+        Thread t = Thread.currentThread();
+        ClassLoader cl = t.getContextClassLoader();
+        t.setContextClassLoader(applicationClassLoader);
+        try {
+            onOpen(outbound);
+        } finally {
+            t.setContextClassLoader(cl);
+        }
     }
 
     /**

Modified: tomcat/trunk/test/org/apache/catalina/websocket/TestWebSocket.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/websocket/TestWebSocket.java?rev=1345737&r1=1345736&r2=1345737&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/websocket/TestWebSocket.java 
(original)
+++ tomcat/trunk/test/org/apache/catalina/websocket/TestWebSocket.java Sun Jun  
3 18:47:25 2012
@@ -28,16 +28,23 @@ import java.io.Writer;
 import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
 import java.security.MessageDigest;
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
 
+import org.apache.catalina.Context;
+import org.apache.catalina.deploy.ContextEnvironment;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.catalina.util.Base64;
@@ -117,7 +124,6 @@ public class TestWebSocket extends Tomca
         String responseLine = client.reader.readLine();
         assertTrue(responseLine.startsWith("HTTP/1.1 426"));
 
-        // Swallow the headers
         List<String> headerlines = new ArrayList<String>();
 
         String responseHeaderLine = client.reader.readLine();
@@ -208,9 +214,7 @@ public class TestWebSocket extends Tomca
         String responseLine = client.reader.readLine();
         assertTrue(responseLine.startsWith("HTTP/1.1 101"));
 
-        // Swallow the headers
         String accept = null;
-
         String responseHeaderLine = client.reader.readLine();
         while (!responseHeaderLine.equals("")) {
             if(responseHeaderLine.startsWith("Sec-WebSocket-Accept: ")) {
@@ -235,6 +239,105 @@ public class TestWebSocket extends Tomca
     }
 
 
+    @Test
+    public void testBug53339() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+        tomcat.enableNaming();
+
+        // Must have a real docBase - just use temp
+        Context ctx =
+            tomcat.addContext("", System.getProperty("java.io.tmpdir"));
+
+        Tomcat.addServlet(ctx, "Bug53339", new Bug53339Servlet());
+        ctx.addServletMapping("/*", "Bug53339");
+
+        // Create the resource
+        ContextEnvironment env = new ContextEnvironment();
+        env.setName(Bug53339WsInbound.JNDI_NAME);
+        env.setType(String.class.getName());
+        env.setValue(Bug53339WsInbound.TEST_MESSAGE);
+        ctx.getNamingResources().addEnvironment(env);
+
+        tomcat.start();
+
+        WebSocketClient client= new WebSocketClient(getPort());
+
+        // Send the WebSocket handshake
+        client.writer.write("GET / HTTP/1.1" + CRLF);
+        client.writer.write("Host: foo" + CRLF);
+        client.writer.write("Upgrade: websocket" + CRLF);
+        client.writer.write("Connection: upgrade" + CRLF);
+        client.writer.write("Sec-WebSocket-Version: 13" + CRLF);
+        client.writer.write("Sec-WebSocket-Key: TODO" + CRLF);
+        client.writer.write(CRLF);
+        client.writer.flush();
+
+        // Make sure we got an upgrade response
+        String responseLine = client.reader.readLine();
+        assertTrue(responseLine.startsWith("HTTP/1.1 101"));
+
+        // Swallow the headers
+        String responseHeaderLine = client.reader.readLine();
+        while (!responseHeaderLine.equals("")) {
+            responseHeaderLine = client.reader.readLine();
+        }
+
+        // Now we can do WebSocket
+        String msg = client.readMessage();
+        assertEquals(Bug53339WsInbound.TEST_MESSAGE, msg);
+
+        // Finished with the socket
+        client.close();
+    }
+
+
+    private static class Bug53339Servlet extends WebSocketServlet {
+
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected StreamInbound createWebSocketInbound(String subProtocol) {
+            return new Bug53339WsInbound();
+        }
+    }
+
+
+    private static class Bug53339WsInbound extends MessageInbound {
+
+        public static final String TEST_MESSAGE = "Test Message";
+        public static final String JNDI_NAME = "Bug53339Message";
+
+        @Override
+        protected void onOpen(WsOutbound outbound) {
+            String msg = "Error";
+            try {
+                javax.naming.Context initCtx = new InitialContext();
+                msg = (String) initCtx.lookup(
+                        "java:comp/env/" + JNDI_NAME);
+            } catch (NamingException e) {
+                // Ignore - the test checks if the message is sent
+                e.printStackTrace(); // for debug purposes if the test fails
+            }
+            CharBuffer cb = CharBuffer.wrap("" + msg);
+            try {
+                outbound.writeTextMessage(cb);
+            } catch (IOException e) {
+                // Ignore - the test checks if the message is sent
+            }
+
+        }
+
+        @Override
+        protected void onBinaryMessage(ByteBuffer message) throws IOException {
+            // Ignore
+        }
+
+        @Override
+        protected void onTextMessage(CharBuffer message) throws IOException {
+            // Ignore
+        }
+    }
+
     private static class WebSocketClient {
         private OutputStream os;
         private InputStream is;



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to