diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java index bfb60c3f0ab..975749433a1 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java @@ -85,7 +85,8 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem private final LogicalConnection connection; private final Executor executor; private final AtomicConnectionState connectionState = new AtomicConnectionState(); - private final AtomicBoolean closeSent = new AtomicBoolean(); + private final AtomicBoolean closeSent = new AtomicBoolean(false); + private final AtomicBoolean closeNotified = new AtomicBoolean(false); // The websocket endpoint object itself private final Object endpoint; @@ -338,6 +339,16 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem return this.containerScope; } + public Object getEndpoint() + { + Object ret = endpoint; + while (ret instanceof ManagedEndpoint) + { + ret = ((ManagedEndpoint) ret).getRawEndpoint(); + } + return ret; + } + public Executor getExecutor() { return executor; @@ -458,6 +469,11 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem { try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(classLoader)) { + if (LOG.isDebugEnabled()) + { + LOG.debug("incomingFrame({}, {}) - this.state={}, connectionState={}, endpointFunctions={}", + frame, callback, getState(), connectionState.get(), endpointFunctions); + } if (connectionState.get() != AtomicConnectionState.State.CLOSED) { // For endpoints that want to see raw frames. @@ -469,19 +485,23 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem { case OpCode.CLOSE: { - CloseInfo closeInfo = null; if (connectionState.onClosing()) { if (LOG.isDebugEnabled()) LOG.debug("ConnectionState: Transition to CLOSING"); CloseFrame closeframe = (CloseFrame) frame; - closeInfo = new CloseInfo(closeframe, true); + CloseInfo closeInfo = new CloseInfo(closeframe, true); + notifyClose(closeInfo); + close(closeInfo, onDisconnectCallback); } else if (connectionState.onClosed()) { if (LOG.isDebugEnabled()) LOG.debug("ConnectionState: Transition to CLOSED"); + CloseFrame closeframe = (CloseFrame) frame; + CloseInfo closeInfo = new CloseInfo(closeframe, true); + notifyClose(closeInfo); connection.disconnect(); } else @@ -490,12 +510,6 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem LOG.debug("ConnectionState: {} - Close Frame Received", connectionState); } - if (closeInfo != null) - { - notifyClose(closeInfo.getStatusCode(), closeInfo.getReason()); - close(closeInfo, onDisconnectCallback); - } - // let fill/parse continue callback.succeed(); @@ -601,15 +615,18 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem return "wss".equalsIgnoreCase(requestURI.getScheme()); } - public void notifyClose(int statusCode, String reason) + public void notifyClose(CloseInfo closeInfo) { if (LOG.isDebugEnabled()) { - LOG.debug("notifyClose({},{}) [{}]", statusCode, reason, getState()); + LOG.debug("notifyClose({}) closeNotified={} [{}]", closeInfo, closeNotified.get(), getState()); } - CloseInfo closeInfo = new CloseInfo(statusCode, reason); - endpointFunctions.onClose(closeInfo); + // only notify once + if (closeNotified.compareAndSet(false, true)) + { + endpointFunctions.onClose(closeInfo); + } } /** diff --git a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/BasicEndpointTest.java b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/EndpointViaConfigTest.java similarity index 86% rename from jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/BasicEndpointTest.java rename to jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/EndpointViaConfigTest.java index cb625b500d1..9cb418313cd 100644 --- a/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/BasicEndpointTest.java +++ b/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/jsr356/EndpointViaConfigTest.java @@ -25,19 +25,21 @@ import java.util.concurrent.TimeUnit; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.websocket.DeploymentException; -import javax.websocket.Endpoint; import javax.websocket.EndpointConfig; import javax.websocket.MessageHandler; import javax.websocket.server.ServerEndpointConfig; import org.eclipse.jetty.toolchain.test.TestingDir; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.WebSocketClient; import org.eclipse.jetty.websocket.jsr356.server.ServerContainer; import org.eclipse.jetty.websocket.tests.LeakTrackingBufferPoolRule; +import org.eclipse.jetty.websocket.tests.TrackingEndpoint; import org.eclipse.jetty.websocket.tests.WSServer; -import org.eclipse.jetty.websocket.tests.client.jsr356.JsrClientTrackingSocket; +import org.eclipse.jetty.websocket.tests.jsr356.AbstractJsrTrackingEndpoint; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -46,15 +48,16 @@ import org.junit.Test; * Example of an {@link javax.websocket.Endpoint} extended echo server added programmatically via the * {@link ServerContainer#addEndpoint(javax.websocket.server.ServerEndpointConfig)} */ -public class BasicEndpointTest +public class EndpointViaConfigTest { - public static class BasicEchoEndpoint extends Endpoint implements MessageHandler.Whole + private static final Logger LOG = Log.getLogger(EndpointViaConfigTest.class); + + public static class BasicEchoEndpoint extends AbstractJsrTrackingEndpoint implements MessageHandler.Whole { - private javax.websocket.Session session; - @Override public void onMessage(String msg) { + super.onWsText(msg); // reply with echo session.getAsyncRemote().sendText(msg); } @@ -62,7 +65,7 @@ public class BasicEndpointTest @Override public void onOpen(javax.websocket.Session session, EndpointConfig config) { - this.session = session; + super.onOpen(session, config); this.session.addMessageHandler(this); } } @@ -124,7 +127,7 @@ public class BasicEndpointTest try { client.start(); - JsrClientTrackingSocket clientSocket = new JsrClientTrackingSocket(); + TrackingEndpoint clientSocket = new TrackingEndpoint("Client"); Future clientConnectFuture = client.connect(clientSocket,uri.resolve("/app/echo")); // wait for connect Session clientSession = clientConnectFuture.get(5,TimeUnit.SECONDS); @@ -134,15 +137,18 @@ public class BasicEndpointTest Assert.assertEquals("Expected message","Hello World",incomingMessage); clientSession.close(); + clientSocket.awaitCloseEvent("Client"); } finally { client.stop(); + LOG.debug("Stopped - " + client); } } finally { wsb.stop(); + LOG.debug("Stopped - " + wsb); } } } diff --git a/jetty-websocket/websocket-tests/src/test/resources/basic-echo-endpoint-config-web.xml b/jetty-websocket/websocket-tests/src/test/resources/basic-echo-endpoint-config-web.xml index ab7bb915158..1fa56a2aa35 100644 --- a/jetty-websocket/websocket-tests/src/test/resources/basic-echo-endpoint-config-web.xml +++ b/jetty-websocket/websocket-tests/src/test/resources/basic-echo-endpoint-config-web.xml @@ -7,6 +7,6 @@ version="3.0"> - org.eclipse.jetty.websocket.tests.server.jsr356.BasicEndpointTest$BasicEchoEndpointConfigContextListener + org.eclipse.jetty.websocket.tests.server.jsr356.EndpointViaConfigTest$BasicEchoEndpointConfigContextListener \ No newline at end of file