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: [email protected]
For additional commands, e-mail: [email protected]