diff --git a/build.gradle b/build.gradle index e8986cd15ad..1708b1587c5 100644 --- a/build.gradle +++ b/build.gradle @@ -515,6 +515,7 @@ project("spring-websocket") { compile(project(":spring-core")) compile(project(":spring-context")) compile(project(":spring-web")) + optional(project(":spring-webmvc")) optional("org.apache.tomcat:tomcat-servlet-api:8.0-SNAPSHOT") // TODO: replace with "javax.servlet:javax.servlet-api" optional("org.apache.tomcat:tomcat-websocket-api:8.0-SNAPSHOT") // TODO: replace with "javax.websocket:javax.websocket-api" diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/AbstractServerSession.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/AbstractServerSession.java index 89941b9840b..67e193f314b 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/AbstractServerSession.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/AbstractServerSession.java @@ -17,12 +17,9 @@ package org.springframework.sockjs.server; import java.io.EOFException; -import java.io.IOException; import java.util.Date; import java.util.concurrent.ScheduledFuture; -import org.springframework.sockjs.server.SockJsConfiguration; -import org.springframework.sockjs.server.SockJsFrame; import org.springframework.sockjs.SockJsHandler; import org.springframework.sockjs.SockJsSession; import org.springframework.sockjs.SockJsSessionSupport; diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/AbstractSockJsService.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/AbstractSockJsService.java index d56303fa0e6..fb7c5edcf53 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/AbstractSockJsService.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/AbstractSockJsService.java @@ -32,9 +32,7 @@ import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; -import org.springframework.sockjs.SockJsHandler; import org.springframework.sockjs.TransportType; -import org.springframework.sockjs.server.support.DefaultTransportHandlerRegistrar; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.DigestUtils; @@ -56,7 +54,7 @@ public abstract class AbstractSockJsService implements SockJsConfiguration { private static final int ONE_YEAR = 365 * 24 * 60 * 60; - private String sockJsServiceName = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); + private final String prefix; private String clientLibraryUrl = "https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js"; @@ -78,31 +76,28 @@ public abstract class AbstractSockJsService implements SockJsConfiguration { /** * Class constructor... * + * @param prefix the path prefix for the SockJS service. All requests with a path + * that begins with the specified prefix will be handled by this service. In a + * Servlet container this is the path within the current servlet mapping. */ - public AbstractSockJsService() { + public AbstractSockJsService(String prefix) { + Assert.hasText(prefix, "prefix is required"); + this.prefix = prefix; this.heartbeatScheduler = createScheduler("SockJs-heartbeat-"); } protected TaskScheduler createScheduler(String threadNamePrefix) { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setThreadNamePrefix(threadNamePrefix); + scheduler.afterPropertiesSet(); return scheduler; } /** - * A unique name for the service, possibly the prefix at which it is deployed. - * Used mainly for logging purposes. + * The path prefix to which the SockJS service is mapped. */ - public void setSockJsServiceName(String serviceName) { - this.sockJsServiceName = serviceName; - } - - /** - * The SockJS service name. - * @see #setSockJsServiceName(String) - */ - public String getSockJsServiceName() { - return this.sockJsServiceName; + public String getPrefix() { + return this.prefix; } /** @@ -178,9 +173,8 @@ public abstract class AbstractSockJsService implements SockJsConfiguration { this.heartbeatScheduler = heartbeatScheduler; } - public AbstractSockJsService setDisconnectDelay(long disconnectDelay) { + public void setDisconnectDelay(long disconnectDelay) { this.disconnectDelay = disconnectDelay; - return this; } public long getDisconnectDelay() { @@ -193,9 +187,8 @@ public abstract class AbstractSockJsService implements SockJsConfiguration { *

* The default value is "true". */ - public AbstractSockJsService setWebSocketsEnabled(boolean webSocketsEnabled) { + public void setWebSocketsEnabled(boolean webSocketsEnabled) { this.webSocketsEnabled = webSocketsEnabled; - return this; } /** @@ -212,9 +205,8 @@ public abstract class AbstractSockJsService implements SockJsConfiguration { * heartbeats, only raw WebSocket protocol. This property allows setting a * handler for requests for raw WebSocket communication. */ - public AbstractSockJsService setWebsocketHandler(HandshakeRequestHandler handshakeRequestHandler) { + public void setWebsocketHandler(HandshakeRequestHandler handshakeRequestHandler) { this.handshakeRequestHandler = handshakeRequestHandler; - return this; } diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/WebSocketSockJsHandlerAdapter.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsWebSocketHandler.java similarity index 78% rename from spring-websocket/src/main/java/org/springframework/sockjs/server/WebSocketSockJsHandlerAdapter.java rename to spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsWebSocketHandler.java index 95bb75ff086..b2719948620 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/WebSocketSockJsHandlerAdapter.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsWebSocketHandler.java @@ -29,38 +29,41 @@ import com.fasterxml.jackson.databind.ObjectMapper; /** + * An implementation of {@link WebSocketHandler} supporting the SockJS protocol. + * Methods merely delegate to a {@link StandardWebSocketServerSession}. * * @author Rossen Stoyanchev * @since 4.0 */ -public class WebSocketSockJsHandlerAdapter implements WebSocketHandler { +public class SockJsWebSocketHandler implements WebSocketHandler { - private static final Log logger = LogFactory.getLog(WebSocketSockJsHandlerAdapter.class); + private static final Log logger = LogFactory.getLog(SockJsWebSocketHandler.class); - private final SockJsWebSocketSessionAdapter sockJsSession; + private final StandardWebSocketServerSession sockJsSession; // TODO: the JSON library used must be configurable private final ObjectMapper objectMapper = new ObjectMapper(); - public WebSocketSockJsHandlerAdapter(SockJsWebSocketSessionAdapter sockJsSession) { + public SockJsWebSocketHandler(StandardWebSocketServerSession sockJsSession) { this.sockJsSession = sockJsSession; } @Override public void newSession(WebSocketSession webSocketSession) throws Exception { - logger.debug("WebSocket connection established"); - webSocketSession.sendText(SockJsFrame.openFrame().getContent()); + if (logger.isDebugEnabled()) { + logger.debug("New session: " + webSocketSession); + } this.sockJsSession.setWebSocketSession(webSocketSession); } @Override public void handleTextMessage(WebSocketSession session, String message) throws Exception { - if (logger.isDebugEnabled()) { - logger.debug("Received payload " + message + " for " + sockJsSession); + if (logger.isTraceEnabled()) { + logger.trace("Received payload " + message + " for " + sockJsSession); } if (StringUtils.isEmpty(message)) { - logger.debug("Ignoring empty payload"); + logger.trace("Ignoring empty payload"); return; } try { diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsWebSocketSessionAdapter.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/StandardWebSocketServerSession.java similarity index 81% rename from spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsWebSocketSessionAdapter.java rename to spring-websocket/src/main/java/org/springframework/sockjs/server/StandardWebSocketServerSession.java index ea2610ab0d9..183e964305b 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsWebSocketSessionAdapter.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/StandardWebSocketServerSession.java @@ -27,26 +27,27 @@ import org.springframework.websocket.WebSocketSession; * @author Rossen Stoyanchev * @since 4.0 */ -public class SockJsWebSocketSessionAdapter extends AbstractServerSession { +public class StandardWebSocketServerSession extends AbstractServerSession { - private static Log logger = LogFactory.getLog(SockJsWebSocketSessionAdapter.class); + private static Log logger = LogFactory.getLog(StandardWebSocketServerSession.class); private WebSocketSession webSocketSession; - public SockJsWebSocketSessionAdapter(String sessionId, SockJsHandler delegate, SockJsConfiguration sockJsConfig) { + public StandardWebSocketServerSession(String sessionId, SockJsHandler delegate, SockJsConfiguration sockJsConfig) { super(sessionId, delegate, sockJsConfig); } public void setWebSocketSession(WebSocketSession webSocketSession) throws Exception { this.webSocketSession = webSocketSession; + webSocketSession.sendText(SockJsFrame.openFrame().getContent()); scheduleHeartbeat(); connectionInitialized(); } @Override public boolean isActive() { - return (this.webSocketSession != null); + return ((this.webSocketSession != null) && this.webSocketSession.isOpen()); } @Override diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/support/DefaultSockJsService.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/support/DefaultSockJsService.java index c00c49222ad..d6dcb875627 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/support/DefaultSockJsService.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/support/DefaultSockJsService.java @@ -64,7 +64,8 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans * Class constructor... * */ - public DefaultSockJsService(SockJsHandler sockJsHandler) { + public DefaultSockJsService(String prefix, SockJsHandler sockJsHandler) { + super(prefix); Assert.notNull(sockJsHandler, "sockJsHandler is required"); this.sockJsHandler = sockJsHandler; this.sessionTimeoutScheduler = createScheduler("SockJs-sessionTimeout-"); @@ -105,23 +106,23 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans try { int count = sessions.size(); if (logger.isTraceEnabled() && (count != 0)) { - logger.trace("Checking " + count + " session(s) for timeouts [" + getSockJsServiceName() + "]"); + logger.trace("Checking " + count + " session(s) for timeouts [" + getPrefix() + "]"); } for (SockJsSessionSupport session : sessions.values()) { if (session.getTimeSinceLastActive() > getDisconnectDelay()) { if (logger.isTraceEnabled()) { - logger.trace("Removing " + session + " for [" + getSockJsServiceName() + "]"); + logger.trace("Removing " + session + " for [" + getPrefix() + "]"); } session.close(); sessions.remove(session.getId()); } } if (logger.isTraceEnabled() && (count != 0)) { - logger.trace(sessions.size() + " remaining session(s) [" + getSockJsServiceName() + "]"); + logger.trace(sessions.size() + " remaining session(s) [" + getPrefix() + "]"); } } catch (Throwable t) { - logger.error("Failed to complete session timeout checks for [" + getSockJsServiceName() + "]", t); + logger.error("Failed to complete session timeout checks for [" + getPrefix() + "]", t); } } }, getDisconnectDelay()); diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/support/DefaultTransportHandlerRegistrar.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/support/DefaultTransportHandlerRegistrar.java index a6c50e49779..6f41298d60c 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/support/DefaultTransportHandlerRegistrar.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/support/DefaultTransportHandlerRegistrar.java @@ -21,6 +21,7 @@ import org.springframework.sockjs.server.transport.EventSourceTransportHandler; import org.springframework.sockjs.server.transport.HtmlFileTransportHandler; import org.springframework.sockjs.server.transport.JsonpPollingTransportHandler; import org.springframework.sockjs.server.transport.JsonpTransportHandler; +import org.springframework.sockjs.server.transport.WebSocketTransportHandler; import org.springframework.sockjs.server.transport.XhrPollingTransportHandler; import org.springframework.sockjs.server.transport.XhrStreamingTransportHandler; import org.springframework.sockjs.server.transport.XhrTransportHandler; @@ -36,6 +37,8 @@ public class DefaultTransportHandlerRegistrar implements TransportHandlerRegistr public void registerTransportHandlers(TransportHandlerRegistry registry) { + registry.registerHandler(new WebSocketTransportHandler()); + registry.registerHandler(new XhrPollingTransportHandler()); registry.registerHandler(new XhrTransportHandler()); diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/support/SockJsServiceHandlerMapping.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/support/SockJsServiceHandlerMapping.java new file mode 100644 index 00000000000..c40565ecacf --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/support/SockJsServiceHandlerMapping.java @@ -0,0 +1,70 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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.springframework.sockjs.server.support; + +import java.util.Arrays; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.sockjs.server.AbstractSockJsService; +import org.springframework.web.servlet.handler.AbstractHandlerMapping; + +/** + * A Spring MVC HandlerMapping matching requests to SockJS services by prefix. + * + * @author Rossen Stoyanchev + * @since 4.0 + */ +public class SockJsServiceHandlerMapping extends AbstractHandlerMapping { + + private static Log logger = LogFactory.getLog(SockJsServiceHandlerMapping.class); + + private final List sockJsServices; + + + public SockJsServiceHandlerMapping(AbstractSockJsService... sockJsServices) { + this.sockJsServices = Arrays.asList(sockJsServices); + } + + @Override + protected Object getHandlerInternal(HttpServletRequest request) throws Exception { + + String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); + if (logger.isDebugEnabled()) { + logger.debug("Looking for SockJS service match to path " + lookupPath); + } + + for (AbstractSockJsService service : this.sockJsServices) { + if (lookupPath.startsWith(service.getPrefix())) { + if (logger.isDebugEnabled()) { + logger.debug("Matched to " + service); + } + String sockJsPath = lookupPath.substring(service.getPrefix().length()); + return new SockJsServiceHttpRequestHandler(sockJsPath, service); + } + } + + if (logger.isDebugEnabled()) { + logger.debug("Did not find a match"); + } + + return null; + } + +} diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/support/SockJsServiceHttpRequestHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/support/SockJsServiceHttpRequestHandler.java new file mode 100644 index 00000000000..57d3baf3330 --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/support/SockJsServiceHttpRequestHandler.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * 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.springframework.sockjs.server.support; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.http.server.AsyncServletServerHttpRequest; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.http.server.ServletServerHttpResponse; +import org.springframework.sockjs.server.AbstractSockJsService; +import org.springframework.web.HttpRequestHandler; +import org.springframework.web.util.NestedServletException; + +/** + * A Spring MVC {@link HttpRequestHandler} wrapping the invocation of a SockJS service. + * + * @author Rossen Stoyanchev + * @since 4.0 + */ +public class SockJsServiceHttpRequestHandler implements HttpRequestHandler { + + private final String sockJsPath; + + private final AbstractSockJsService sockJsService; + + + public SockJsServiceHttpRequestHandler(String sockJsPath, AbstractSockJsService sockJsService) { + this.sockJsService = sockJsService; + this.sockJsPath = sockJsPath; + } + + @Override + public void handleRequest(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + ServerHttpRequest httpRequest = new AsyncServletServerHttpRequest(request, response); + ServerHttpResponse httpResponse = new ServletServerHttpResponse(response); + + try { + this.sockJsService.handleRequest(httpRequest, httpResponse, this.sockJsPath); + } + catch (Exception ex) { + // TODO + throw new NestedServletException("SockJS service failure", ex); + } + } + +} diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpSendingTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpSendingTransportHandler.java index 90ef3c29a62..9a59a79e674 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpSendingTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpSendingTransportHandler.java @@ -15,6 +15,8 @@ */ package org.springframework.sockjs.server.transport; +import java.io.IOException; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.http.MediaType; @@ -37,14 +39,19 @@ public abstract class AbstractHttpSendingTransportHandler implements TransportHa @Override - public void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session) - throws Exception { - - AbstractHttpServerSession httpServerSession = (AbstractHttpServerSession) session; + public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response, + SockJsSessionSupport session) throws Exception { // Set content type before writing response.getHeaders().setContentType(getContentType()); + AbstractHttpServerSession httpServerSession = (AbstractHttpServerSession) session; + handleRequestInternal(request, response, httpServerSession); + } + + protected void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response, + AbstractHttpServerSession httpServerSession) throws Exception, IOException { + if (httpServerSession.isNew()) { handleNewSession(request, response, httpServerSession); } diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractStreamingTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractStreamingTransportHandler.java index 10816de521c..e7740d6d490 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractStreamingTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractStreamingTransportHandler.java @@ -20,7 +20,6 @@ import java.io.IOException; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.sockjs.SockJsHandler; -import org.springframework.sockjs.SockJsSessionSupport; import org.springframework.sockjs.server.SockJsConfiguration; @@ -39,12 +38,11 @@ public abstract class AbstractStreamingTransportHandler extends AbstractHttpSend } @Override - public void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session) - throws Exception { + public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response, + AbstractHttpServerSession session) throws Exception { writePrelude(request, response); - - super.handleRequest(request, response, session); + super.handleRequestInternal(request, response, session); } protected abstract void writePrelude(ServerHttpRequest request, ServerHttpResponse response) diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/HtmlFileTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/HtmlFileTransportHandler.java index cc1e98ac224..adc983e0b41 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/HtmlFileTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/HtmlFileTransportHandler.java @@ -22,7 +22,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; -import org.springframework.sockjs.SockJsSessionSupport; import org.springframework.sockjs.TransportType; import org.springframework.sockjs.server.SockJsFrame.DefaultFrameFormat; import org.springframework.sockjs.server.SockJsFrame.FrameFormat; @@ -78,8 +77,8 @@ public class HtmlFileTransportHandler extends AbstractStreamingTransportHandler } @Override - public void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session) - throws Exception { + public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response, + AbstractHttpServerSession session) throws Exception { String callback = request.getQueryParams().getFirst("c"); if (! StringUtils.hasText(callback)) { @@ -87,8 +86,7 @@ public class HtmlFileTransportHandler extends AbstractStreamingTransportHandler response.getBody().write("\"callback\" parameter required".getBytes("UTF-8")); return; } - - super.handleRequest(request, response, session); + super.handleRequestInternal(request, response, session); } @Override diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/JsonpPollingTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/JsonpPollingTransportHandler.java index c696c6cf127..bff94ad72e8 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/JsonpPollingTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/JsonpPollingTransportHandler.java @@ -22,7 +22,6 @@ import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.sockjs.SockJsHandler; -import org.springframework.sockjs.SockJsSessionSupport; import org.springframework.sockjs.TransportType; import org.springframework.sockjs.server.SockJsConfiguration; import org.springframework.sockjs.server.SockJsFrame; @@ -56,8 +55,8 @@ public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHa } @Override - public void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session) - throws Exception { + public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response, + AbstractHttpServerSession session) throws Exception { String callback = request.getQueryParams().getFirst("c"); if (! StringUtils.hasText(callback)) { @@ -65,7 +64,6 @@ public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHa response.getBody().write("\"callback\" parameter required".getBytes("UTF-8")); return; } - super.handleRequest(request, response, session); } diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/StreamingHttpServerSession.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/StreamingHttpServerSession.java index d327568331f..2eb846683fc 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/StreamingHttpServerSession.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/StreamingHttpServerSession.java @@ -43,7 +43,8 @@ public class StreamingHttpServerSession extends AbstractHttpServerSession { this.byteCount += frame.getContentBytes().length + 1; if (logger.isTraceEnabled()) { - logger.trace(this.byteCount + " bytes written, " + getMessageCache().size() + " more messages"); + logger.trace(this.byteCount + " bytes written so far, " + + getMessageCache().size() + " more messages not flushed"); } if (this.byteCount >= getSockJsConfig().getStreamBytesLimit()) { if (logger.isTraceEnabled()) { diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/WebSocketTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/WebSocketTransportHandler.java index 9abf049b167..40ced92ad5e 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/WebSocketTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/WebSocketTransportHandler.java @@ -22,9 +22,9 @@ import org.springframework.sockjs.SockJsHandler; import org.springframework.sockjs.SockJsSessionSupport; import org.springframework.sockjs.TransportType; import org.springframework.sockjs.server.SockJsConfiguration; -import org.springframework.sockjs.server.SockJsWebSocketSessionAdapter; +import org.springframework.sockjs.server.StandardWebSocketServerSession; import org.springframework.sockjs.server.TransportHandler; -import org.springframework.sockjs.server.WebSocketSockJsHandlerAdapter; +import org.springframework.sockjs.server.SockJsWebSocketHandler; import org.springframework.websocket.server.HandshakeRequestHandler; import org.springframework.websocket.server.endpoint.EndpointHandshakeRequestHandler; @@ -44,15 +44,15 @@ public class WebSocketTransportHandler implements TransportHandler { @Override public SockJsSessionSupport createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config) { - return new SockJsWebSocketSessionAdapter(sessionId, handler, config); + return new StandardWebSocketServerSession(sessionId, handler, config); } @Override public void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session) throws Exception { - SockJsWebSocketSessionAdapter sockJsSession = (SockJsWebSocketSessionAdapter) session; - WebSocketSockJsHandlerAdapter webSocketHandler = new WebSocketSockJsHandlerAdapter(sockJsSession); + StandardWebSocketServerSession sockJsSession = (StandardWebSocketServerSession) session; + SockJsWebSocketHandler webSocketHandler = new SockJsWebSocketHandler(sockJsSession); HandshakeRequestHandler handshakeRequestHandler = new EndpointHandshakeRequestHandler(webSocketHandler); handshakeRequestHandler.doHandshake(request, response); } diff --git a/spring-websocket/src/main/java/org/springframework/websocket/WebSocketSession.java b/spring-websocket/src/main/java/org/springframework/websocket/WebSocketSession.java index 947a565bb73..d930dde9d0f 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/WebSocketSession.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/WebSocketSession.java @@ -25,6 +25,8 @@ package org.springframework.websocket; */ public interface WebSocketSession { + boolean isOpen(); + void sendText(String text) throws Exception; void close(); diff --git a/spring-websocket/src/main/java/org/springframework/websocket/endpoint/WebSocketStandardSessionAdapter.java b/spring-websocket/src/main/java/org/springframework/websocket/endpoint/StandardWebSocketSession.java similarity index 76% rename from spring-websocket/src/main/java/org/springframework/websocket/endpoint/WebSocketStandardSessionAdapter.java rename to spring-websocket/src/main/java/org/springframework/websocket/endpoint/StandardWebSocketSession.java index ce3dd1c5313..302a0333923 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/endpoint/WebSocketStandardSessionAdapter.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/endpoint/StandardWebSocketSession.java @@ -22,21 +22,27 @@ import org.springframework.websocket.WebSocketSession; /** + * A {@link WebSocketSession} that delegates to a {@link javax.websocket.Session}. * * @author Rossen Stoyanchev * @since 4.0 */ -public class WebSocketStandardSessionAdapter implements WebSocketSession { +public class StandardWebSocketSession implements WebSocketSession { - private static Log logger = LogFactory.getLog(WebSocketStandardSessionAdapter.class); + private static Log logger = LogFactory.getLog(StandardWebSocketSession.class); private javax.websocket.Session session; - public WebSocketStandardSessionAdapter(javax.websocket.Session session) { + public StandardWebSocketSession(javax.websocket.Session session) { this.session = session; } + @Override + public boolean isOpen() { + return ((this.session != null) && this.session.isOpen()); + } + @Override public void sendText(String text) throws Exception { logger.trace("Sending text message: " + text); diff --git a/spring-websocket/src/main/java/org/springframework/websocket/endpoint/StandardWebSocketHandlerAdapter.java b/spring-websocket/src/main/java/org/springframework/websocket/endpoint/WebSocketHandlerEndpoint.java similarity index 87% rename from spring-websocket/src/main/java/org/springframework/websocket/endpoint/StandardWebSocketHandlerAdapter.java rename to spring-websocket/src/main/java/org/springframework/websocket/endpoint/WebSocketHandlerEndpoint.java index 2722cb09eda..da3d7a768aa 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/endpoint/StandardWebSocketHandlerAdapter.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/endpoint/WebSocketHandlerEndpoint.java @@ -32,28 +32,31 @@ import org.springframework.websocket.WebSocketHandler; /** + * An {@link Endpoint} that delegates to a {@link WebSocketHandler}. * * @author Rossen Stoyanchev * @since 4.0 */ -public class StandardWebSocketHandlerAdapter extends Endpoint { +public class WebSocketHandlerEndpoint extends Endpoint { - private static Log logger = LogFactory.getLog(StandardWebSocketHandlerAdapter.class); + private static Log logger = LogFactory.getLog(WebSocketHandlerEndpoint.class); private final WebSocketHandler webSocketHandler; private final Map sessionMap = new ConcurrentHashMap(); - public StandardWebSocketHandlerAdapter(WebSocketHandler webSocketHandler) { + public WebSocketHandlerEndpoint(WebSocketHandler webSocketHandler) { this.webSocketHandler = webSocketHandler; } @Override public void onOpen(javax.websocket.Session session, EndpointConfig config) { - logger.debug("New WebSocket session: " + session); + if (logger.isDebugEnabled()) { + logger.debug("New session: " + session); + } try { - WebSocketSession webSocketSession = new WebSocketStandardSessionAdapter(session); + WebSocketSession webSocketSession = new StandardWebSocketSession(session); this.sessionMap.put(session.getId(), webSocketSession); session.addMessageHandler(new StandardMessageHandler(session.getId())); this.webSocketHandler.newSession(webSocketSession); @@ -75,7 +78,6 @@ public class StandardWebSocketHandlerAdapter extends Endpoint { this.sessionMap.remove(id); int code = closeReason.getCloseCode().getCode(); String reason = closeReason.getReasonPhrase(); - webSocketSession.close(code, reason); this.webSocketHandler.sessionClosed(webSocketSession, code, reason); } catch (Throwable ex) { @@ -119,7 +121,7 @@ public class StandardWebSocketHandlerAdapter extends Endpoint { } try { WebSocketSession session = getSession(this.sessionId); - StandardWebSocketHandlerAdapter.this.webSocketHandler.handleTextMessage(session, message); + WebSocketHandlerEndpoint.this.webSocketHandler.handleTextMessage(session, message); } catch (Throwable ex) { // TODO diff --git a/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointHandshakeRequestHandler.java b/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointHandshakeRequestHandler.java index aae44c148cb..abb66ff4cc5 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointHandshakeRequestHandler.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointHandshakeRequestHandler.java @@ -23,7 +23,7 @@ import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.util.ClassUtils; import org.springframework.websocket.WebSocketHandler; -import org.springframework.websocket.endpoint.StandardWebSocketHandlerAdapter; +import org.springframework.websocket.endpoint.WebSocketHandlerEndpoint; import org.springframework.websocket.server.AbstractHandshakeRequestHandler; @@ -43,7 +43,7 @@ public class EndpointHandshakeRequestHandler extends AbstractHandshakeRequestHan public EndpointHandshakeRequestHandler(WebSocketHandler webSocketHandler) { - this(new StandardWebSocketHandlerAdapter(webSocketHandler)); + this(new WebSocketHandlerEndpoint(webSocketHandler)); } public EndpointHandshakeRequestHandler(Endpoint endpoint) { diff --git a/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointRegistration.java b/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointRegistration.java index 843cb075986..9dfc509042e 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointRegistration.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointRegistration.java @@ -36,14 +36,14 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.web.context.ContextLoader; import org.springframework.web.context.WebApplicationContext; -import org.springframework.websocket.endpoint.StandardWebSocketHandlerAdapter; +import org.springframework.websocket.endpoint.WebSocketHandlerEndpoint; /** * An implementation of {@link javax.websocket.server.ServerEndpointConfig} that also * holds the target {@link javax.websocket.Endpoint} as a reference or a bean name. * The target can also be {@link org.springframework.websocket.WebSocketHandler}, in - * which case it will be adapted via {@link StandardWebSocketHandlerAdapter}. + * which case it will be adapted via {@link WebSocketHandlerEndpoint}. * *

* Beans of this type are detected by {@link EndpointExporter} and @@ -60,9 +60,9 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw private final Object endpointBean; - private List> encoders; + private List> encoders = new ArrayList>(); - private List> decoders; + private List> decoders = new ArrayList>(); private List subprotocols = new ArrayList();