Author: markt
Date: Fri Mar 15 20:40:53 2013
New Revision: 1457104

URL: http://svn.apache.org/r1457104
Log:
Correctly handle two stage close process.

Modified:
    tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
    tomcat/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java
    tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
    tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java

Modified: tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties?rev=1457104&r1=1457103&r2=1457104&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties Fri 
Mar 15 20:40:53 2013
@@ -51,6 +51,7 @@ wsSession.duplicateHandlerBinary=A binar
 wsSession.duplicateHandlerPong=A pong message handler has already been 
configured
 wsSession.duplicateHandlerText=A text message handler has already been 
configured
 wsSession.expireFailed=Unable to close expired session cleanly
+wsSession.sendCloseFail=Failed to send close message to remote endpoint
 wsSession.invalidHandlerTypePong=A pong message handler must implement 
MessageHandler.Basic
 wsSession.removeHandlerFailed=Unable to remove the handler [{0}] as it was not 
registered with this session
 wsSession.unknownHandler=Unable to add the message handler [{0}] as it was for 
the unrecognised type [{1}]

Modified: tomcat/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java?rev=1457104&r1=1457103&r2=1457104&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java Fri Mar 15 
20:40:53 2013
@@ -283,7 +283,7 @@ public abstract class WsFrameBase {
                     reason = controlBufferText.toString();
                 }
             }
-            wsSession.close(new CloseReason(Util.getCloseCode(code), reason));
+            wsSession.onClose(new CloseReason(Util.getCloseCode(code), 
reason));
         } else if (opCode == Constants.OPCODE_PING) {
             if (wsSession.isOpen()) {
                 wsSession.getBasicRemote().sendPong(controlBufferBinary);

Modified: 
tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java?rev=1457104&r1=1457103&r2=1457104&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java 
Fri Mar 15 20:40:53 2013
@@ -243,12 +243,8 @@ public abstract class WsRemoteEndpointIm
             boolean dataMessage) {
         synchronized (messagePartLock) {
 
-            if (closed) {
-                close();
-            } else {
-                fragmented = nextFragmented;
-                text = nextText;
-            }
+            fragmented = nextFragmented;
+            text = nextText;
 
             if (dataMessage) {
                 dataMessageInProgress = false;

Modified: tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java?rev=1457104&r1=1457103&r2=1457104&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java Fri Mar 15 
20:40:53 2013
@@ -70,8 +70,8 @@ public class WsSession implements Sessio
     private MessageHandler textMessageHandler = null;
     private MessageHandler binaryMessageHandler = null;
     private MessageHandler.Whole<PongMessage> pongMessageHandler = null;
-    private volatile boolean open = true;
-    private final Object closeLock = new Object();
+    private State state = State.OPEN;
+    private final Object stateLock = new Object();
     private final Map<String,Object> userProperties = new 
ConcurrentHashMap<>();
     private volatile int maxBinaryMessageBufferSize =
             Constants.DEFAULT_BUFFER_SIZE;
@@ -226,7 +226,7 @@ public class WsSession implements Sessio
 
     @Override
     public boolean isOpen() {
-        return open;
+        return state == State.OPEN;
     }
 
 
@@ -293,45 +293,84 @@ public class WsSession implements Sessio
     @Override
     public void close(CloseReason closeReason) throws IOException {
         // Double-checked locking. OK because open is volatile
-        if (!open) {
+        if (state != State.OPEN) {
             return;
         }
-        synchronized (closeLock) {
-            if (!open) {
+        synchronized (stateLock) {
+            if (state != State.OPEN) {
                 return;
             }
-            open = false;
+            state = State.CLOSING;
 
-            // Send the close message
-            // 125 is maximum size for the payload of a control message
-            ByteBuffer msg = ByteBuffer.allocate(125);
-            msg.putShort((short) closeReason.getCloseCode().getCode());
-            String reason = closeReason.getReasonPhrase();
-            if (reason != null && reason.length() > 0) {
-                msg.put(reason.getBytes(UTF8));
+            sendCloseMessage(closeReason);
+        }
+    }
+
+
+    /**
+     * Called when a close message is received. Should only ever happen once.
+     */
+    void onClose(CloseReason closeReason) {
+
+        boolean sendCloseMessage = false;
+
+        synchronized (stateLock) {
+            if (state == State.OPEN) {
+                sendCloseMessage = true;
             }
-            msg.flip();
-            try {
-                wsRemoteEndpoint.startMessageBlock(
-                        Constants.OPCODE_CLOSE, msg, true);
-            } finally {
-                webSocketContainer.unregisterSession(
-                        localEndpoint.getClass(), this);
 
-                // Fire the onClose event
-                Thread t = Thread.currentThread();
-                ClassLoader cl = t.getContextClassLoader();
-                t.setContextClassLoader(applicationClassLoader);
+            state = State.CLOSED;
+
+            if (sendCloseMessage) {
                 try {
-                    localEndpoint.onClose(this, closeReason);
-                } finally {
-                    t.setContextClassLoader(cl);
+                    sendCloseMessage(closeReason);
+                } catch (IOException ioe) {
+                    log.error(sm.getString("wsSession.sendCloseFail"), ioe);
                 }
             }
+
+            // Close the socket
+            wsRemoteEndpoint.close();
         }
     }
 
 
+    private void sendCloseMessage(CloseReason closeReason) throws IOException {
+        // 125 is maximum size for the payload of a control message
+        ByteBuffer msg = ByteBuffer.allocate(125);
+        msg.putShort((short) closeReason.getCloseCode().getCode());
+        String reason = closeReason.getReasonPhrase();
+        if (reason != null && reason.length() > 0) {
+            msg.put(reason.getBytes(UTF8));
+        }
+        msg.flip();
+        try {
+            wsRemoteEndpoint.startMessageBlock(
+                    Constants.OPCODE_CLOSE, msg, true);
+        } catch (IOException ioe) {
+            // Failed to send close message. Close the socket and let the 
caller
+            // deal with the Exception
+            log.error(sm.getString("wsSession.sendCloseFail"), ioe);
+            wsRemoteEndpoint.close();
+            throw ioe;
+        } finally {
+            webSocketContainer.unregisterSession(
+                    localEndpoint.getClass(), this);
+
+            // Fire the onClose event
+            Thread t = Thread.currentThread();
+            ClassLoader cl = t.getContextClassLoader();
+            t.setContextClassLoader(applicationClassLoader);
+            try {
+                localEndpoint.onClose(this, closeReason);
+            } finally {
+                t.setContextClassLoader(cl);
+            }
+        }
+
+    }
+
+
     @Override
     public URI getRequestURI() {
         if (request == null) {
@@ -426,4 +465,11 @@ public class WsSession implements Sessio
             }
         }
     }
+
+
+    private static enum State {
+        OPEN,
+        CLOSING,
+        CLOSED
+    }
 }



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

Reply via email to