diff --git a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java index 38d6e3df42..5f4f90e9a8 100644 --- a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java @@ -134,8 +134,10 @@ public class ServletServerHttpRequest implements ServerHttpRequest { public Cookies getCookies() { if (this.cookies == null) { this.cookies = new Cookies(); - for (Cookie cookie : this.servletRequest.getCookies()) { - this.cookies.addCookie(cookie.getName(), cookie.getValue()); + if (this.servletRequest.getCookies() != null) { + for (Cookie cookie : this.servletRequest.getCookies()) { + this.cookies.addCookie(cookie.getName(), cookie.getValue()); + } } } return this.cookies; diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/SockJsSessionSupport.java b/spring-websocket/src/main/java/org/springframework/sockjs/SockJsSessionSupport.java index f35e1e8fd5..6e20a0b131 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/SockJsSessionSupport.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/SockJsSessionSupport.java @@ -33,7 +33,7 @@ public abstract class SockJsSessionSupport implements SockJsSession { private final String sessionId; - private final SockJsHandler delegate; + private final SockJsHandler sockJsHandler; private State state = State.NEW; @@ -45,13 +45,13 @@ public abstract class SockJsSessionSupport implements SockJsSession { /** * * @param sessionId - * @param delegate the recipient of SockJS messages + * @param sockJsHandler the recipient of SockJS messages */ - public SockJsSessionSupport(String sessionId, SockJsHandler delegate) { + public SockJsSessionSupport(String sessionId, SockJsHandler sockJsHandler) { Assert.notNull(sessionId, "sessionId is required"); - Assert.notNull(delegate, "SockJsHandler is required"); + Assert.notNull(sockJsHandler, "SockJsHandler is required"); this.sessionId = sessionId; - this.delegate = delegate; + this.sockJsHandler = sockJsHandler; } public String getId() { @@ -59,7 +59,7 @@ public abstract class SockJsSessionSupport implements SockJsSession { } public SockJsHandler getSockJsHandler() { - return this.delegate; + return this.sockJsHandler; } public boolean isNew() { @@ -105,17 +105,22 @@ public abstract class SockJsSessionSupport implements SockJsSession { public void connectionInitialized() throws Exception { this.state = State.OPEN; - this.delegate.newSession(this); + this.sockJsHandler.newSession(this); } public void delegateMessages(String... messages) throws Exception { for (String message : messages) { - this.delegate.handleMessage(this, message); + this.sockJsHandler.handleMessage(this, message); } } + public void delegateException(Throwable ex) { + this.sockJsHandler.handleException(this, ex); + } + public void close() { this.state = State.CLOSED; + this.sockJsHandler.sessionClosed(this); } public String toString() { 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 67e193f314..b4624914bb 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 @@ -40,13 +40,17 @@ public abstract class AbstractServerSession extends SockJsSessionSupport { private ScheduledFuture heartbeatTask; - public AbstractServerSession(String sessionId, SockJsHandler delegate, SockJsConfiguration sockJsConfig) { - super(sessionId, delegate); - Assert.notNull(sockJsConfig, "sockJsConfig is required"); + public AbstractServerSession(String sessionId, SockJsConfiguration sockJsConfig) { + super(sessionId, getSockJsHandler(sockJsConfig)); this.sockJsConfig = sockJsConfig; } - public SockJsConfiguration getSockJsConfig() { + private static SockJsHandler getSockJsHandler(SockJsConfiguration sockJsConfig) { + Assert.notNull(sockJsConfig, "sockJsConfig is required"); + return sockJsConfig.getSockJsHandler(); + } + + protected SockJsConfiguration getSockJsConfig() { return this.sockJsConfig; } @@ -61,18 +65,15 @@ public abstract class AbstractServerSession extends SockJsSessionSupport { if (!isClosed()) { logger.debug("Closing session"); - // set the status - super.close(); - if (isActive()) { // deliver messages "in flight" before sending close frame writeFrame(SockJsFrame.closeFrameGoAway()); } + super.close(); + cancelHeartbeat(); closeInternal(); - - getSockJsHandler().sessionClosed(this); } } @@ -90,12 +91,12 @@ public abstract class AbstractServerSession extends SockJsSessionSupport { writeFrameInternal(frame); } catch (EOFException ex) { - logger.warn("Failed to send message due to client disconnect. Terminating connection abruptly"); + logger.warn("Client went away. Terminating connection abruptly"); deactivate(); close(); } catch (Throwable t) { - logger.error("Failed to send message. Terminating connection abruptly", t); + logger.warn("Failed to send message. Terminating connection abruptly: " + t.getMessage()); deactivate(); close(); } 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 fb7c5edcf5..412704b335 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,13 +32,11 @@ 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.TransportType; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.DigestUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import org.springframework.websocket.server.HandshakeRequestHandler; /** @@ -70,8 +68,6 @@ public abstract class AbstractSockJsService implements SockJsConfiguration { private boolean webSocketsEnabled = true; - private HandshakeRequestHandler handshakeRequestHandler; - /** * Class constructor... @@ -199,16 +195,6 @@ public abstract class AbstractSockJsService implements SockJsConfiguration { return this.webSocketsEnabled; } - /** - * SockJS exposes an entry point at "/websocket" for raw WebSocket - * communication without additional custom framing, e.g. no open frame, no - * heartbeats, only raw WebSocket protocol. This property allows setting a - * handler for requests for raw WebSocket communication. - */ - public void setWebsocketHandler(HandshakeRequestHandler handshakeRequestHandler) { - this.handshakeRequestHandler = handshakeRequestHandler; - } - /** * TODO @@ -245,8 +231,7 @@ public abstract class AbstractSockJsService implements SockJsConfiguration { return; } else if (sockJsPath.equals("/websocket")) { - Assert.notNull(this.handshakeRequestHandler, "No handler for raw Websockets configured"); - this.handshakeRequestHandler.doHandshake(request, response); + handleRawWebSocket(request, response); return; } @@ -270,6 +255,9 @@ public abstract class AbstractSockJsService implements SockJsConfiguration { } + protected abstract void handleRawWebSocket(ServerHttpRequest request, ServerHttpResponse response) + throws Exception; + protected boolean validateRequest(String serverId, String sessionId, String transport) { if (!StringUtils.hasText(serverId) || !StringUtils.hasText(sessionId) || !StringUtils.hasText(transport)) { diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsConfiguration.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsConfiguration.java index c8242874a6..a122a769e3 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsConfiguration.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsConfiguration.java @@ -17,6 +17,7 @@ package org.springframework.sockjs.server; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.sockjs.SockJsHandler; /** @@ -66,4 +67,11 @@ public interface SockJsConfiguration { */ public TaskScheduler getHeartbeatScheduler(); + /** + * Provides access to the {@link SockJsHandler} that will handle the request. This + * method should be called once per SockJS session. It may return the same or a + * different instance every time it is called. + */ + SockJsHandler getSockJsHandler(); + } diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsWebSocketHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsWebSocketHandler.java deleted file mode 100644 index b271994862..0000000000 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/SockJsWebSocketHandler.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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; - -import java.io.IOException; -import java.io.InputStream; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.util.StringUtils; -import org.springframework.websocket.WebSocketHandler; -import org.springframework.websocket.WebSocketSession; - -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 SockJsWebSocketHandler implements WebSocketHandler { - - private static final Log logger = LogFactory.getLog(SockJsWebSocketHandler.class); - - private final StandardWebSocketServerSession sockJsSession; - - // TODO: the JSON library used must be configurable - private final ObjectMapper objectMapper = new ObjectMapper(); - - - public SockJsWebSocketHandler(StandardWebSocketServerSession sockJsSession) { - this.sockJsSession = sockJsSession; - } - - @Override - public void newSession(WebSocketSession webSocketSession) throws Exception { - if (logger.isDebugEnabled()) { - logger.debug("New session: " + webSocketSession); - } - this.sockJsSession.setWebSocketSession(webSocketSession); - } - - @Override - public void handleTextMessage(WebSocketSession session, String message) throws Exception { - if (logger.isTraceEnabled()) { - logger.trace("Received payload " + message + " for " + sockJsSession); - } - if (StringUtils.isEmpty(message)) { - logger.trace("Ignoring empty payload"); - return; - } - try { - String[] messages = this.objectMapper.readValue(message, String[].class); - this.sockJsSession.delegateMessages(messages); - } - catch (IOException e) { - logger.error("Broken data received. Terminating WebSocket connection abruptly", e); - session.close(); - } - } - - @Override - public void handleBinaryMessage(WebSocketSession session, InputStream message) throws Exception { - // should not happen - throw new UnsupportedOperationException(); - } - - @Override - public void handleException(WebSocketSession session, Throwable exception) { - exception.printStackTrace(); - } - - @Override - public void sessionClosed(WebSocketSession session, int statusCode, String reason) throws Exception { - logger.debug("WebSocket connection closed for " + this.sockJsSession); - this.sockJsSession.close(); - } - -} diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/StandardWebSocketServerSession.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/StandardWebSocketServerSession.java deleted file mode 100644 index 183e964305..0000000000 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/StandardWebSocketServerSession.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.sockjs.SockJsHandler; -import org.springframework.websocket.WebSocketSession; - - -/** - * - * @author Rossen Stoyanchev - * @since 4.0 - */ -public class StandardWebSocketServerSession extends AbstractServerSession { - - private static Log logger = LogFactory.getLog(StandardWebSocketServerSession.class); - - private WebSocketSession webSocketSession; - - - 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) && this.webSocketSession.isOpen()); - } - - @Override - public void sendMessageInternal(String message) { - cancelHeartbeat(); - writeFrame(SockJsFrame.messageFrame(message)); - scheduleHeartbeat(); - } - - @Override - protected void writeFrameInternal(SockJsFrame frame) throws Exception { - if (logger.isTraceEnabled()) { - logger.trace("Write " + frame); - } - this.webSocketSession.sendText(frame.getContent()); - } - - @Override - public void closeInternal() { - this.webSocketSession.close(); - this.webSocketSession = null; - updateLastActiveTime(); - } - - @Override - protected void deactivate() { - this.webSocketSession.close(); - } - -} diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/TransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/TransportHandler.java index 3efe70404d..93ada27d1e 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/TransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/TransportHandler.java @@ -17,9 +17,7 @@ package org.springframework.sockjs.server; 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; /** @@ -31,7 +29,11 @@ public interface TransportHandler { TransportType getTransportType(); - SockJsSessionSupport createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config); + boolean canCreateSession(); + + SockJsSessionSupport createSession(String sessionId); + + boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response); void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session) throws Exception; diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/TransportHandlerRegistrar.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/TransportHandlerRegistrar.java index 7ff8b8f553..f89c2ea81b 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/TransportHandlerRegistrar.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/TransportHandlerRegistrar.java @@ -23,6 +23,6 @@ package org.springframework.sockjs.server; */ public interface TransportHandlerRegistrar { - void registerTransportHandlers(TransportHandlerRegistry registry); + void registerTransportHandlers(TransportHandlerRegistry registry, SockJsConfiguration config); } diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/TransportType.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/TransportType.java similarity index 92% rename from spring-websocket/src/main/java/org/springframework/sockjs/TransportType.java rename to spring-websocket/src/main/java/org/springframework/sockjs/server/TransportType.java index 130e1f2f44..5373d3edc8 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/TransportType.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/TransportType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.sockjs; +package org.springframework.sockjs.server; import org.springframework.http.HttpMethod; @@ -25,7 +25,7 @@ import org.springframework.http.HttpMethod; */ public enum TransportType { - WEBSOCKET("websocket", HttpMethod.GET, false /* CORS ? */), + WEBSOCKET("websocket", HttpMethod.GET, false), XHR("xhr", HttpMethod.POST, true), XHR_SEND("xhr_send", HttpMethod.POST, true), @@ -45,10 +45,10 @@ public enum TransportType { private final boolean corsSupported; - private TransportType(String value, HttpMethod httpMethod, boolean supportsCors) { + private TransportType(String value, HttpMethod httpMethod, boolean corsSupported) { this.value = value; this.httpMethod = httpMethod; - this.corsSupported = supportsCors; + this.corsSupported = corsSupported; } public String value() { 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 d6dcb87562..69f325da2c 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 @@ -20,9 +20,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.http.Cookie; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -32,12 +35,13 @@ import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.sockjs.SockJsHandler; import org.springframework.sockjs.SockJsSessionSupport; -import org.springframework.sockjs.TransportType; import org.springframework.sockjs.server.AbstractSockJsService; import org.springframework.sockjs.server.TransportHandler; import org.springframework.sockjs.server.TransportHandlerRegistrar; import org.springframework.sockjs.server.TransportHandlerRegistry; +import org.springframework.sockjs.server.TransportType; import org.springframework.util.Assert; +import org.springframework.websocket.server.HandshakeHandler; /** @@ -46,10 +50,10 @@ import org.springframework.util.Assert; * @author Rossen Stoyanchev * @since 4.0 */ -public class DefaultSockJsService extends AbstractSockJsService implements TransportHandlerRegistry, InitializingBean { - - private static final AtomicLong webSocketSessionIdSuffix = new AtomicLong(); +public class DefaultSockJsService extends AbstractSockJsService + implements TransportHandlerRegistry, BeanFactoryAware, InitializingBean { + private final Class sockJsHandlerClass; private final SockJsHandler sockJsHandler; @@ -59,17 +63,24 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans private final Map transportHandlers = new HashMap(); + private AutowireCapableBeanFactory beanFactory; + + + public DefaultSockJsService(String prefix, Class sockJsHandlerClass) { + this(prefix, sockJsHandlerClass, null); + } - /** - * Class constructor... - * - */ public DefaultSockJsService(String prefix, SockJsHandler sockJsHandler) { + this(prefix, null, sockJsHandler); + } + + private DefaultSockJsService(String prefix, Class handlerClass, SockJsHandler handler) { super(prefix); - Assert.notNull(sockJsHandler, "sockJsHandler is required"); - this.sockJsHandler = sockJsHandler; + Assert.isTrue(((handlerClass != null) || (handler != null)), "A sockJsHandler class or instance is required"); + this.sockJsHandlerClass = handlerClass; + this.sockJsHandler = handler; this.sessionTimeoutScheduler = createScheduler("SockJs-sessionTimeout-"); - new DefaultTransportHandlerRegistrar().registerTransportHandlers(this); + new DefaultTransportHandlerRegistrar().registerTransportHandlers(this, this); } /** @@ -95,12 +106,34 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans public void setTransportHandlerRegistrar(TransportHandlerRegistrar registrar) { Assert.notNull(registrar, "registrar is required"); this.transportHandlers.clear(); - registrar.registerTransportHandlers(this); + registrar.registerTransportHandlers(this, this); + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + if (beanFactory instanceof AutowireCapableBeanFactory) { + this.beanFactory = (AutowireCapableBeanFactory) beanFactory; + } + } + + @Override + public SockJsHandler getSockJsHandler() { + return (this.sockJsHandlerClass != null) ? + this.beanFactory.createBean(this.sockJsHandlerClass) : this.sockJsHandler; } @Override public void afterPropertiesSet() throws Exception { + if (this.sockJsHandler != null) { + Assert.notNull(this.beanFactory, + "An AutowirecapableBeanFactory is required to initialize SockJS handler instances per request."); + } + + if (this.transportHandlers.get(TransportType.WEBSOCKET) == null) { + logger.warn("No WebSocket transport handler was registered"); + } + this.sessionTimeoutScheduler.scheduleAtFixedRate(new Runnable() { public void run() { try { @@ -128,6 +161,19 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans }, getDisconnectDelay()); } + @Override + protected void handleRawWebSocket(ServerHttpRequest request, ServerHttpResponse response) throws Exception { + TransportHandler transportHandler = this.transportHandlers.get(TransportType.WEBSOCKET); + if ((transportHandler != null) && transportHandler instanceof HandshakeHandler) { + HandshakeHandler handshakeHandler = (HandshakeHandler) transportHandler; + handshakeHandler.doHandshake(request, response); + } + else { + logger.debug("No handler found for raw WebSocket messages"); + response.setStatusCode(HttpStatus.NOT_FOUND); + } + } + @Override protected void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response, String sessionId, TransportType transportType) throws Exception { @@ -159,8 +205,7 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans } SockJsSessionSupport session = getSockJsSession(sessionId, transportHandler); - if (session == null) { - response.setStatusCode(HttpStatus.NOT_FOUND); + if ((session == null) && !transportHandler.handleNoSession(request, response)) { return; } @@ -184,19 +229,12 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans public SockJsSessionSupport getSockJsSession(String sessionId, TransportHandler transportHandler) { - TransportType transportType = transportHandler.getTransportType(); - - // Always create new session for WebSocket requests - sessionId = TransportType.WEBSOCKET.equals(transportType) ? - sessionId + "#" + webSocketSessionIdSuffix.getAndIncrement() : sessionId; - SockJsSessionSupport session = this.sessions.get(sessionId); if (session != null) { return session; } - if (TransportType.XHR_SEND.equals(transportType) || TransportType.JSONP_SEND.equals(transportType)) { - logger.debug(transportType + " did not find session"); + if (!transportHandler.canCreateSession()) { return null; } @@ -207,7 +245,7 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans } logger.debug("Creating new session with session id \"" + sessionId + "\""); - session = transportHandler.createSession(sessionId, this.sockJsHandler, this); + session = transportHandler.createSession(sessionId); this.sessions.put(sessionId, session); return session; 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 6f41298d60..fbbeab48a4 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 @@ -15,16 +15,21 @@ */ package org.springframework.sockjs.server.support; +import java.lang.reflect.Constructor; + +import org.springframework.beans.BeanUtils; +import org.springframework.sockjs.server.SockJsConfiguration; +import org.springframework.sockjs.server.TransportHandler; import org.springframework.sockjs.server.TransportHandlerRegistrar; import org.springframework.sockjs.server.TransportHandlerRegistry; 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; +import org.springframework.util.ClassUtils; /** @@ -35,19 +40,38 @@ import org.springframework.sockjs.server.transport.XhrTransportHandler; */ public class DefaultTransportHandlerRegistrar implements TransportHandlerRegistrar { - public void registerTransportHandlers(TransportHandlerRegistry registry) { + private static final boolean standardWebSocketApiPresent = ClassUtils.isPresent( + "javax.websocket.server.ServerEndpointConfig", DefaultTransportHandlerRegistrar.class.getClassLoader()); - registry.registerHandler(new WebSocketTransportHandler()); - registry.registerHandler(new XhrPollingTransportHandler()); + public void registerTransportHandlers(TransportHandlerRegistry registry, SockJsConfiguration config) { + + if (standardWebSocketApiPresent) { + registry.registerHandler(createEndpointWebSocketTransportHandler(config)); + } + + registry.registerHandler(new XhrPollingTransportHandler(config)); registry.registerHandler(new XhrTransportHandler()); - registry.registerHandler(new JsonpPollingTransportHandler()); + registry.registerHandler(new JsonpPollingTransportHandler(config)); registry.registerHandler(new JsonpTransportHandler()); - registry.registerHandler(new XhrStreamingTransportHandler()); - registry.registerHandler(new EventSourceTransportHandler()); - registry.registerHandler(new HtmlFileTransportHandler()); + registry.registerHandler(new XhrStreamingTransportHandler(config)); + registry.registerHandler(new EventSourceTransportHandler(config)); + registry.registerHandler(new HtmlFileTransportHandler(config)); + + } + + private TransportHandler createEndpointWebSocketTransportHandler(SockJsConfiguration config) { + try { + String className = "org.springframework.sockjs.server.transport.EndpointWebSocketTransportHandler"; + Class clazz = ClassUtils.forName(className, DefaultTransportHandlerRegistrar.class.getClassLoader()); + Constructor constructor = clazz.getConstructor(SockJsConfiguration.class); + return (TransportHandler) BeanUtils.instantiateClass(constructor, config); + } + catch (Throwable t) { + throw new IllegalStateException("Failed to instantiate EndpointWebSocketTransportHandler", t); + } } } diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpReceivingTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpReceivingTransportHandler.java index 08ad9b4e76..0597e8621f 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpReceivingTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpReceivingTransportHandler.java @@ -25,9 +25,7 @@ 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.SockJsHandler; import org.springframework.sockjs.SockJsSessionSupport; -import org.springframework.sockjs.server.SockJsConfiguration; import org.springframework.sockjs.server.TransportHandler; import com.fasterxml.jackson.databind.JsonMappingException; @@ -53,8 +51,19 @@ public abstract class AbstractHttpReceivingTransportHandler implements Transport } @Override - public SockJsSessionSupport createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config) { - return null; + public boolean canCreateSession() { + return false; + } + + @Override + public SockJsSessionSupport createSession(String sessionId) { + throw new IllegalStateException("Transport handlers receiving messages do not create new sessions"); + } + + @Override + public boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response) { + response.setStatusCode(HttpStatus.NOT_FOUND); + return false; } @Override 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 9a59a79e67..dffa4389c0 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 @@ -23,9 +23,10 @@ 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.server.SockJsConfiguration; import org.springframework.sockjs.server.SockJsFrame; -import org.springframework.sockjs.server.TransportHandler; import org.springframework.sockjs.server.SockJsFrame.FrameFormat; +import org.springframework.sockjs.server.TransportHandler; /** * TODO @@ -37,6 +38,26 @@ public abstract class AbstractHttpSendingTransportHandler implements TransportHa protected final Log logger = LogFactory.getLog(this.getClass()); + private final SockJsConfiguration sockJsConfig; + + + public AbstractHttpSendingTransportHandler(SockJsConfiguration sockJsConfig) { + this.sockJsConfig = sockJsConfig; + } + + protected SockJsConfiguration getSockJsConfig() { + return this.sockJsConfig; + } + + @Override + public boolean canCreateSession() { + return true; + } + + @Override + public boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response) { + return true; + } @Override public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response, diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpServerSession.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpServerSession.java index ad4939c900..0c0d60ae66 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpServerSession.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractHttpServerSession.java @@ -23,12 +23,11 @@ import java.util.concurrent.BlockingQueue; import org.springframework.http.server.AsyncServerHttpRequest; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; -import org.springframework.sockjs.SockJsHandler; import org.springframework.sockjs.server.AbstractServerSession; import org.springframework.sockjs.server.SockJsConfiguration; import org.springframework.sockjs.server.SockJsFrame; -import org.springframework.sockjs.server.TransportHandler; import org.springframework.sockjs.server.SockJsFrame.FrameFormat; +import org.springframework.sockjs.server.TransportHandler; import org.springframework.util.Assert; /** @@ -48,8 +47,8 @@ public abstract class AbstractHttpServerSession extends AbstractServerSession { private OutputStream outputStream; - public AbstractHttpServerSession(String sessionId, SockJsHandler delegate, SockJsConfiguration sockJsConfig) { - super(sessionId, delegate, sockJsConfig); + public AbstractHttpServerSession(String sessionId, SockJsConfiguration sockJsConfig) { + super(sessionId, sockJsConfig); } public void setFrameFormat(FrameFormat frameFormat) { diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractSockJsWebSocketHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractSockJsWebSocketHandler.java new file mode 100644 index 0000000000..9f13cd10b1 --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractSockJsWebSocketHandler.java @@ -0,0 +1,98 @@ +/* + * 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.transport; + +import java.io.InputStream; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.sockjs.SockJsSessionSupport; +import org.springframework.sockjs.server.SockJsConfiguration; +import org.springframework.websocket.WebSocketHandler; +import org.springframework.websocket.WebSocketSession; + + +/** + * + * @author Rossen Stoyanchev + * @since 4.0 + */ +public abstract class AbstractSockJsWebSocketHandler implements WebSocketHandler { + + protected final Log logger = LogFactory.getLog(getClass()); + + private final SockJsConfiguration sockJsConfig; + + private final Map sessions = + new ConcurrentHashMap(); + + + public AbstractSockJsWebSocketHandler(SockJsConfiguration sockJsConfig) { + this.sockJsConfig = sockJsConfig; + } + + protected SockJsConfiguration getSockJsConfig() { + return this.sockJsConfig; + } + + protected SockJsSessionSupport getSockJsSession(WebSocketSession wsSession) { + return this.sessions.get(wsSession); + } + + @Override + public void newSession(WebSocketSession wsSession) throws Exception { + if (logger.isDebugEnabled()) { + logger.debug("New session: " + wsSession); + } + SockJsSessionSupport session = createSockJsSession(wsSession); + this.sessions.put(wsSession, session); + session.connectionInitialized(); + } + + protected abstract SockJsSessionSupport createSockJsSession(WebSocketSession wsSession) throws Exception; + + @Override + public void handleTextMessage(WebSocketSession wsSession, String message) throws Exception { + if (logger.isTraceEnabled()) { + logger.trace("Received payload " + message); + } + SockJsSessionSupport session = getSockJsSession(wsSession); + session.delegateMessages(message); + } + + @Override + public void handleBinaryMessage(WebSocketSession session, InputStream message) throws Exception { + // should not happen + throw new UnsupportedOperationException(); + } + + @Override + public void handleException(WebSocketSession webSocketSession, Throwable exception) { + SockJsSessionSupport session = getSockJsSession(webSocketSession); + session.delegateException(exception); + } + + @Override + public void sessionClosed(WebSocketSession webSocketSession, int statusCode, String reason) throws Exception { + logger.debug("WebSocket connection closed " + webSocketSession); + SockJsSessionSupport session = this.sessions.remove(webSocketSession); + session.close(); + } + +} 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 e7740d6d49..89b076d93f 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 @@ -19,7 +19,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.server.SockJsConfiguration; @@ -32,9 +31,13 @@ import org.springframework.sockjs.server.SockJsConfiguration; public abstract class AbstractStreamingTransportHandler extends AbstractHttpSendingTransportHandler { + public AbstractStreamingTransportHandler(SockJsConfiguration sockJsConfig) { + super(sockJsConfig); + } + @Override - public StreamingHttpServerSession createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config) { - return new StreamingHttpServerSession(sessionId, handler, config); + public StreamingHttpServerSession createSession(String sessionId) { + return new StreamingHttpServerSession(sessionId, getSockJsConfig()); } @Override diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractWebSocketTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractWebSocketTransportHandler.java new file mode 100644 index 0000000000..0221c33a77 --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/AbstractWebSocketTransportHandler.java @@ -0,0 +1,80 @@ +/* + * 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.transport; + +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.sockjs.SockJsSessionSupport; +import org.springframework.sockjs.server.SockJsConfiguration; +import org.springframework.sockjs.server.TransportHandler; +import org.springframework.sockjs.server.TransportType; +import org.springframework.websocket.WebSocketHandler; +import org.springframework.websocket.server.HandshakeHandler; + + +/** + * + * @author Rossen Stoyanchev + * @since 4.0 + */ +public abstract class AbstractWebSocketTransportHandler implements TransportHandler, HandshakeHandler { + + private final HandshakeHandler sockJsHandshakeHandler; + + private final HandshakeHandler handshakeHandler; + + + public AbstractWebSocketTransportHandler(SockJsConfiguration sockJsConfig) { + this.sockJsHandshakeHandler = createHandshakeHandler(new SockJsWebSocketHandler(sockJsConfig)); + this.handshakeHandler = createHandshakeHandler(new WebSocketSockJsHandlerAdapter(sockJsConfig)); + } + + protected abstract HandshakeHandler createHandshakeHandler(WebSocketHandler webSocketHandler); + + @Override + public TransportType getTransportType() { + return TransportType.WEBSOCKET; + } + + @Override + public boolean canCreateSession() { + return false; + } + + @Override + public SockJsSessionSupport createSession(String sessionId) { + throw new IllegalStateException("WebSocket transport handlers do not create new sessions"); + } + + @Override + public boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response) { + return true; + } + + @Override + public void handleRequest(ServerHttpRequest request, ServerHttpResponse response, + SockJsSessionSupport session) throws Exception { + + this.sockJsHandshakeHandler.doHandshake(request, response); + } + + @Override + public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response) throws Exception { + return this.handshakeHandler.doHandshake(request, response); + } + +} diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/EndpointWebSocketTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/EndpointWebSocketTransportHandler.java new file mode 100644 index 0000000000..b6762111f3 --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/EndpointWebSocketTransportHandler.java @@ -0,0 +1,42 @@ +/* + * 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.transport; + +import org.springframework.sockjs.server.SockJsConfiguration; +import org.springframework.websocket.WebSocketHandler; +import org.springframework.websocket.server.HandshakeHandler; +import org.springframework.websocket.server.endpoint.EndpointHandshakeHandler; + + +/** + * + * @author Rossen Stoyanchev + * @since 4.0 + */ +public class EndpointWebSocketTransportHandler extends AbstractWebSocketTransportHandler { + + + public EndpointWebSocketTransportHandler(SockJsConfiguration sockJsConfig) { + super(sockJsConfig); + } + + @Override + protected HandshakeHandler createHandshakeHandler(WebSocketHandler webSocketHandler) { + return new EndpointHandshakeHandler(webSocketHandler); + } + +} diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/EventSourceTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/EventSourceTransportHandler.java index 2e05a41927..0725e6284c 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/EventSourceTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/EventSourceTransportHandler.java @@ -21,7 +21,8 @@ import java.nio.charset.Charset; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; -import org.springframework.sockjs.TransportType; +import org.springframework.sockjs.server.SockJsConfiguration; +import org.springframework.sockjs.server.TransportType; import org.springframework.sockjs.server.SockJsFrame.DefaultFrameFormat; import org.springframework.sockjs.server.SockJsFrame.FrameFormat; @@ -35,6 +36,10 @@ import org.springframework.sockjs.server.SockJsFrame.FrameFormat; public class EventSourceTransportHandler extends AbstractStreamingTransportHandler { + public EventSourceTransportHandler(SockJsConfiguration sockJsConfig) { + super(sockJsConfig); + } + @Override public TransportType getTransportType() { return TransportType.EVENT_SOURCE; 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 adc983e0b4..188c59d9ab 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,8 @@ 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.TransportType; +import org.springframework.sockjs.server.SockJsConfiguration; +import org.springframework.sockjs.server.TransportType; import org.springframework.sockjs.server.SockJsFrame.DefaultFrameFormat; import org.springframework.sockjs.server.SockJsFrame.FrameFormat; import org.springframework.util.StringUtils; @@ -66,6 +67,10 @@ public class HtmlFileTransportHandler extends AbstractStreamingTransportHandler } + public HtmlFileTransportHandler(SockJsConfiguration sockJsConfig) { + super(sockJsConfig); + } + @Override public TransportType getTransportType() { return TransportType.HTML_FILE; 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 bff94ad72e..28fe888714 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 @@ -21,10 +21,9 @@ 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.SockJsHandler; -import org.springframework.sockjs.TransportType; import org.springframework.sockjs.server.SockJsConfiguration; import org.springframework.sockjs.server.SockJsFrame; +import org.springframework.sockjs.server.TransportType; import org.springframework.sockjs.server.SockJsFrame.FrameFormat; import org.springframework.util.StringUtils; import org.springframework.web.util.JavaScriptUtils; @@ -39,6 +38,10 @@ import org.springframework.web.util.JavaScriptUtils; public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHandler { + public JsonpPollingTransportHandler(SockJsConfiguration sockJsConfig) { + super(sockJsConfig); + } + @Override public TransportType getTransportType() { return TransportType.JSONP; @@ -50,8 +53,8 @@ public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHa } @Override - public PollingHttpServerSession createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config) { - return new PollingHttpServerSession(sessionId, handler, config); + public PollingHttpServerSession createSession(String sessionId) { + return new PollingHttpServerSession(sessionId, getSockJsConfig()); } @Override diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/JsonpTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/JsonpTransportHandler.java index 694bb94453..e839b77f4e 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/JsonpTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/JsonpTransportHandler.java @@ -22,7 +22,7 @@ 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.TransportType; public class JsonpTransportHandler extends AbstractHttpReceivingTransportHandler { diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/PollingHttpServerSession.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/PollingHttpServerSession.java index f7b6b13a07..3dd0e8d517 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/PollingHttpServerSession.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/PollingHttpServerSession.java @@ -15,15 +15,14 @@ */ package org.springframework.sockjs.server.transport; -import org.springframework.sockjs.SockJsHandler; import org.springframework.sockjs.server.SockJsConfiguration; import org.springframework.sockjs.server.SockJsFrame; public class PollingHttpServerSession extends AbstractHttpServerSession { - public PollingHttpServerSession(String sessionId, SockJsHandler delegate, SockJsConfiguration sockJsConfig) { - super(sessionId, delegate, sockJsConfig); + public PollingHttpServerSession(String sessionId, SockJsConfiguration sockJsConfig) { + super(sessionId, sockJsConfig); } @Override diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/SockJsWebSocketHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/SockJsWebSocketHandler.java new file mode 100644 index 0000000000..db5da4c362 --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/SockJsWebSocketHandler.java @@ -0,0 +1,122 @@ +/* + * 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.transport; + +import java.io.IOException; + +import org.springframework.sockjs.SockJsHandler; +import org.springframework.sockjs.SockJsSessionSupport; +import org.springframework.sockjs.server.AbstractServerSession; +import org.springframework.sockjs.server.SockJsConfiguration; +import org.springframework.sockjs.server.SockJsFrame; +import org.springframework.util.StringUtils; +import org.springframework.websocket.WebSocketHandler; +import org.springframework.websocket.WebSocketSession; + +import com.fasterxml.jackson.databind.ObjectMapper; + + +/** + * A SockJS implementation of {@link WebSocketHandler}. Delegates messages to and from a + * {@link SockJsHandler} and adds SockJS message framing. + * + * @author Rossen Stoyanchev + * @since 4.0 + */ +public class SockJsWebSocketHandler extends AbstractSockJsWebSocketHandler { + + // TODO: JSON library used must be configurable + private final ObjectMapper objectMapper = new ObjectMapper(); + + + public SockJsWebSocketHandler(SockJsConfiguration config) { + super(config); + } + + @Override + protected SockJsSessionSupport createSockJsSession(WebSocketSession wsSession) throws Exception { + return new WebSocketServerSession(wsSession, getSockJsConfig()); + } + + @Override + public void handleTextMessage(WebSocketSession wsSession, String message) throws Exception { + if (logger.isTraceEnabled()) { + logger.trace("Received payload " + message + " for " + wsSession); + } + if (StringUtils.isEmpty(message)) { + logger.trace("Ignoring empty payload"); + return; + } + try { + String[] messages = this.objectMapper.readValue(message, String[].class); + SockJsSessionSupport session = getSockJsSession(wsSession); + session.delegateMessages(messages); + } + catch (IOException e) { + logger.error("Broken data received. Terminating WebSocket connection abruptly", e); + wsSession.close(); + } + } + + + private class WebSocketServerSession extends AbstractServerSession { + + private WebSocketSession webSocketSession; + + + public WebSocketServerSession(WebSocketSession wsSession, SockJsConfiguration config) throws Exception { + super(String.valueOf(wsSession.hashCode()), config); + this.webSocketSession = wsSession; + this.webSocketSession.sendText(SockJsFrame.openFrame().getContent()); + scheduleHeartbeat(); + connectionInitialized(); + } + + @Override + public boolean isActive() { + return ((this.webSocketSession != null) && this.webSocketSession.isOpen()); + } + + @Override + public void sendMessageInternal(String message) { + cancelHeartbeat(); + writeFrame(SockJsFrame.messageFrame(message)); + scheduleHeartbeat(); + } + + @Override + protected void writeFrameInternal(SockJsFrame frame) throws Exception { + if (logger.isTraceEnabled()) { + logger.trace("Write " + frame); + } + this.webSocketSession.sendText(frame.getContent()); + } + + @Override + public void closeInternal() { + this.webSocketSession.close(); + this.webSocketSession = null; + updateLastActiveTime(); + } + + @Override + protected void deactivate() { + this.webSocketSession.close(); + } + } + +} 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 2eb846683f..accff89ee6 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 @@ -18,7 +18,6 @@ package org.springframework.sockjs.server.transport; import java.io.IOException; import java.io.OutputStream; -import org.springframework.sockjs.SockJsHandler; import org.springframework.sockjs.server.SockJsConfiguration; import org.springframework.sockjs.server.SockJsFrame; @@ -28,8 +27,8 @@ public class StreamingHttpServerSession extends AbstractHttpServerSession { private int byteCount; - public StreamingHttpServerSession(String sessionId, SockJsHandler delegate, SockJsConfiguration sockJsConfig) { - super(sessionId, delegate, sockJsConfig); + public StreamingHttpServerSession(String sessionId, SockJsConfiguration sockJsConfig) { + super(sessionId, sockJsConfig); } protected void flush() { diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/WebSocketSockJsHandlerAdapter.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/WebSocketSockJsHandlerAdapter.java new file mode 100644 index 0000000000..e617ab794f --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/WebSocketSockJsHandlerAdapter.java @@ -0,0 +1,76 @@ +/* + * 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.transport; + +import org.springframework.sockjs.SockJsHandler; +import org.springframework.sockjs.SockJsSessionSupport; +import org.springframework.sockjs.server.SockJsConfiguration; +import org.springframework.websocket.WebSocketHandler; +import org.springframework.websocket.WebSocketSession; + + +/** + * A {@link WebSocketHandler} that merely delegates to a {@link SockJsHandler} without any + * SockJS message framing. For use with raw WebSocket communication at SockJS path + * "/websocket". + * + * @author Rossen Stoyanchev + * @since 4.0 + */ +public class WebSocketSockJsHandlerAdapter extends AbstractSockJsWebSocketHandler { + + + public WebSocketSockJsHandlerAdapter(SockJsConfiguration sockJsConfig) { + super(sockJsConfig); + } + + @Override + protected SockJsSessionSupport createSockJsSession(WebSocketSession wsSession) { + return new WebSocketSessionAdapter(wsSession); + } + + + private class WebSocketSessionAdapter extends SockJsSessionSupport { + + private final WebSocketSession wsSession; + + + public WebSocketSessionAdapter(WebSocketSession wsSession) { + super(String.valueOf(wsSession.hashCode()), getSockJsConfig().getSockJsHandler()); + this.wsSession = wsSession; + } + + @Override + public boolean isActive() { + return (!isClosed() && this.wsSession.isOpen()); + } + + @Override + public void sendMessage(String message) throws Exception { + this.wsSession.sendText(message); + } + + public void close() { + if (!isClosed()) { + logger.debug("Closing session"); + super.close(); + this.wsSession.close(); + } + } + } + +} 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 deleted file mode 100644 index 40ced92ad5..0000000000 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/WebSocketTransportHandler.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.transport; - -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.StandardWebSocketServerSession; -import org.springframework.sockjs.server.TransportHandler; -import org.springframework.sockjs.server.SockJsWebSocketHandler; -import org.springframework.websocket.server.HandshakeRequestHandler; -import org.springframework.websocket.server.endpoint.EndpointHandshakeRequestHandler; - - -/** - * - * @author Rossen Stoyanchev - * @since 4.0 - */ -public class WebSocketTransportHandler implements TransportHandler { - - - @Override - public TransportType getTransportType() { - return TransportType.WEBSOCKET; - } - - @Override - public SockJsSessionSupport createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config) { - return new StandardWebSocketServerSession(sessionId, handler, config); - } - - @Override - public void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session) - throws Exception { - - 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/sockjs/server/transport/XhrPollingTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/XhrPollingTransportHandler.java index 7301377608..78733099e0 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/XhrPollingTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/XhrPollingTransportHandler.java @@ -19,9 +19,8 @@ import java.nio.charset.Charset; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; -import org.springframework.sockjs.SockJsHandler; -import org.springframework.sockjs.TransportType; import org.springframework.sockjs.server.SockJsConfiguration; +import org.springframework.sockjs.server.TransportType; import org.springframework.sockjs.server.SockJsFrame.DefaultFrameFormat; import org.springframework.sockjs.server.SockJsFrame.FrameFormat; @@ -35,6 +34,10 @@ import org.springframework.sockjs.server.SockJsFrame.FrameFormat; public class XhrPollingTransportHandler extends AbstractHttpSendingTransportHandler { + public XhrPollingTransportHandler(SockJsConfiguration sockJsConfig) { + super(sockJsConfig); + } + @Override public TransportType getTransportType() { return TransportType.XHR; @@ -50,8 +53,8 @@ public class XhrPollingTransportHandler extends AbstractHttpSendingTransportHand return new DefaultFrameFormat("%s\n"); } - public PollingHttpServerSession createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config) { - return new PollingHttpServerSession(sessionId, handler, config); + public PollingHttpServerSession createSession(String sessionId) { + return new PollingHttpServerSession(sessionId, getSockJsConfig()); } } diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/XhrStreamingTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/XhrStreamingTransportHandler.java index d141cb2c7e..6a69747497 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/XhrStreamingTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/XhrStreamingTransportHandler.java @@ -21,7 +21,8 @@ import java.nio.charset.Charset; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; -import org.springframework.sockjs.TransportType; +import org.springframework.sockjs.server.SockJsConfiguration; +import org.springframework.sockjs.server.TransportType; import org.springframework.sockjs.server.SockJsFrame.DefaultFrameFormat; import org.springframework.sockjs.server.SockJsFrame.FrameFormat; @@ -35,6 +36,10 @@ import org.springframework.sockjs.server.SockJsFrame.FrameFormat; public class XhrStreamingTransportHandler extends AbstractStreamingTransportHandler { + public XhrStreamingTransportHandler(SockJsConfiguration sockJsConfig) { + super(sockJsConfig); + } + @Override public TransportType getTransportType() { return TransportType.XHR_STREAMING; diff --git a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/XhrTransportHandler.java b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/XhrTransportHandler.java index f5fe4b13ff..e38417ac5a 100644 --- a/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/XhrTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/sockjs/server/transport/XhrTransportHandler.java @@ -19,7 +19,7 @@ import java.io.IOException; import org.springframework.http.HttpStatus; import org.springframework.http.server.ServerHttpRequest; -import org.springframework.sockjs.TransportType; +import org.springframework.sockjs.server.TransportType; public class XhrTransportHandler extends AbstractHttpReceivingTransportHandler { diff --git a/spring-websocket/src/main/java/org/springframework/websocket/client/AbstractEndpointConnectionManager.java b/spring-websocket/src/main/java/org/springframework/websocket/client/AbstractEndpointConnectionManager.java index a95b9ab53f..8e5d3f754a 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/client/AbstractEndpointConnectionManager.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/client/AbstractEndpointConnectionManager.java @@ -152,6 +152,8 @@ public abstract class AbstractEndpointConnectionManager implements ApplicationCo protected Object getEndpoint() { if (this.endpointClass != null) { + Assert.notNull(this.applicationContext, + "An ApplicationContext is required to initialize endpoint instances per request."); return this.applicationContext.getAutowireCapableBeanFactory().createBean(this.endpointClass); } else { diff --git a/spring-websocket/src/main/java/org/springframework/websocket/server/AbstractHandshakeRequestHandler.java b/spring-websocket/src/main/java/org/springframework/websocket/server/AbstractHandshakeHandler.java similarity index 76% rename from spring-websocket/src/main/java/org/springframework/websocket/server/AbstractHandshakeRequestHandler.java rename to spring-websocket/src/main/java/org/springframework/websocket/server/AbstractHandshakeHandler.java index 0f8de38855..d3807d0146 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/server/AbstractHandshakeRequestHandler.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/server/AbstractHandshakeHandler.java @@ -28,11 +28,17 @@ import javax.xml.bind.DatatypeConverter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; +import org.springframework.util.Assert; import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.websocket.WebSocketHandler; /** @@ -40,14 +46,32 @@ import org.springframework.web.util.UriComponentsBuilder; * @author Rossen Stoyanchev * @since 4.0 */ -public abstract class AbstractHandshakeRequestHandler implements HandshakeRequestHandler { +public abstract class AbstractHandshakeHandler implements HandshakeHandler, BeanFactoryAware { private static final String GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; protected Log logger = LogFactory.getLog(getClass()); + private final WebSocketHandler webSocketHandler; + + private final Class handlerClass; + private List protocols; + private AutowireCapableBeanFactory beanFactory; + + + public AbstractHandshakeHandler(WebSocketHandler webSocketHandler) { + Assert.notNull(webSocketHandler, "webSocketHandler is required"); + this.webSocketHandler = webSocketHandler; + this.handlerClass = null; + } + + public AbstractHandshakeHandler(Class handlerClass) { + Assert.notNull((handlerClass), "handlerClass is required"); + this.webSocketHandler = null; + this.handlerClass = handlerClass; + } public void setProtocols(String... protocols) { this.protocols = Arrays.asList(protocols); @@ -58,7 +82,24 @@ public abstract class AbstractHandshakeRequestHandler implements HandshakeReques } @Override - public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response) throws Exception { + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + if (beanFactory instanceof AutowireCapableBeanFactory) { + this.beanFactory = (AutowireCapableBeanFactory) beanFactory; + } + } + + protected WebSocketHandler getWebSocketHandler() { + if (this.handlerClass != null) { + Assert.notNull(this.beanFactory, "BeanFactory is required for WebSocketHandler instance per request."); + return this.beanFactory.createBean(this.handlerClass); + } + else { + return this.webSocketHandler; + } + } + + @Override + public final boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response) throws Exception { logger.debug("Starting handshake for " + request.getURI()); @@ -106,6 +147,9 @@ public abstract class AbstractHandshakeRequestHandler implements HandshakeReques return true; } + protected abstract void doHandshakeInternal(ServerHttpRequest request, ServerHttpResponse response, + String protocol) throws Exception; + protected boolean validateUpgradeHeader(ServerHttpRequest request, ServerHttpResponse response) throws IOException { if (!"WebSocket".equalsIgnoreCase(request.getHeaders().getUpgrade())) { response.setStatusCode(HttpStatus.BAD_REQUEST); @@ -168,7 +212,4 @@ public abstract class AbstractHandshakeRequestHandler implements HandshakeReques return DatatypeConverter.printBase64Binary(bytes); } - protected abstract void doHandshakeInternal(ServerHttpRequest request, ServerHttpResponse response, String protocol) - throws Exception; - } diff --git a/spring-websocket/src/main/java/org/springframework/websocket/server/HandshakeRequestHandler.java b/spring-websocket/src/main/java/org/springframework/websocket/server/HandshakeHandler.java similarity index 95% rename from spring-websocket/src/main/java/org/springframework/websocket/server/HandshakeRequestHandler.java rename to spring-websocket/src/main/java/org/springframework/websocket/server/HandshakeHandler.java index 9e2f9f1962..684bc36253 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/server/HandshakeRequestHandler.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/server/HandshakeHandler.java @@ -25,7 +25,7 @@ import org.springframework.http.server.ServerHttpResponse; * @author Rossen Stoyanchev * @since 4.0 */ -public interface HandshakeRequestHandler { +public interface HandshakeHandler { boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response) throws Exception; 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/EndpointHandshakeHandler.java similarity index 62% rename from spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointHandshakeRequestHandler.java rename to spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointHandshakeHandler.java index abb66ff4cc..8eeb33026d 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/EndpointHandshakeHandler.java @@ -23,35 +23,36 @@ 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.WebSocketHandlerEndpoint; -import org.springframework.websocket.server.AbstractHandshakeRequestHandler; +import org.springframework.websocket.server.AbstractHandshakeHandler; +import org.springframework.websocket.server.HandshakeHandler; +import org.springframework.websocket.support.WebSocketHandlerEndpoint; /** + * A {@link HandshakeHandler} for use with standard Java WebSocket. * * @author Rossen Stoyanchev * @since 4.0 */ -public class EndpointHandshakeRequestHandler extends AbstractHandshakeRequestHandler { +public class EndpointHandshakeHandler extends AbstractHandshakeHandler { private static final boolean tomcatWebSocketPresent = ClassUtils.isPresent( - "org.apache.tomcat.websocket.server.WsHandshakeRequest", EndpointHandshakeRequestHandler.class.getClassLoader()); + "org.apache.tomcat.websocket.server.WsHandshakeRequest", EndpointHandshakeHandler.class.getClassLoader()); - private final EndpointRegistration endpointRegistration; - - private final EndpointRequestUpgradeStrategy upgradeStrategy; + private final WebSocketRequestUpgradeStrategy upgradeStrategy; - public EndpointHandshakeRequestHandler(WebSocketHandler webSocketHandler) { - this(new WebSocketHandlerEndpoint(webSocketHandler)); - } - - public EndpointHandshakeRequestHandler(Endpoint endpoint) { - this.endpointRegistration = new EndpointRegistration("/dummy", endpoint); + public EndpointHandshakeHandler(WebSocketHandler webSocketHandler) { + super(webSocketHandler); this.upgradeStrategy = createRequestUpgradeStrategy(); } - private static EndpointRequestUpgradeStrategy createRequestUpgradeStrategy() { + public EndpointHandshakeHandler(Class handlerClass) { + super(handlerClass); + this.upgradeStrategy = createRequestUpgradeStrategy(); + } + + private static WebSocketRequestUpgradeStrategy createRequestUpgradeStrategy() { String className; if (tomcatWebSocketPresent) { className = "org.springframework.websocket.server.endpoint.TomcatRequestUpgradeStrategy"; @@ -60,24 +61,21 @@ public class EndpointHandshakeRequestHandler extends AbstractHandshakeRequestHan throw new IllegalStateException("No suitable EndpointRequestUpgradeStrategy"); } try { - Class clazz = ClassUtils.forName(className, EndpointHandshakeRequestHandler.class.getClassLoader()); - return (EndpointRequestUpgradeStrategy) BeanUtils.instantiateClass(clazz.getConstructor()); + Class clazz = ClassUtils.forName(className, EndpointHandshakeHandler.class.getClassLoader()); + return (WebSocketRequestUpgradeStrategy) BeanUtils.instantiateClass(clazz.getConstructor()); } catch (Throwable t) { throw new IllegalStateException("Failed to instantiate " + className, t); } } - public EndpointRegistration getEndpointRegistration() { - return this.endpointRegistration; - } - @Override public void doHandshakeInternal(ServerHttpRequest request, ServerHttpResponse response, String protocol) throws Exception { logger.debug("Upgrading HTTP request"); - this.upgradeStrategy.upgrade(request, response, protocol, this.endpointRegistration); + Endpoint endpoint = new WebSocketHandlerEndpoint(getWebSocketHandler()); + this.upgradeStrategy.upgrade(request, response, protocol, new EndpointRegistration("/dummy", endpoint)); } } \ No newline at end of file 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 9dfc509042..8e373c0261 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,7 +36,7 @@ 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.WebSocketHandlerEndpoint; +import org.springframework.websocket.support.WebSocketHandlerEndpoint; /** diff --git a/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/TomcatRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/TomcatRequestUpgradeStrategy.java index b25c1a47c3..4e1f8d633b 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/TomcatRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/TomcatRequestUpgradeStrategy.java @@ -36,7 +36,7 @@ import org.springframework.util.ReflectionUtils; * @author Rossen Stoyanchev * @since 4.0 */ -public class TomcatRequestUpgradeStrategy implements EndpointRequestUpgradeStrategy { +public class TomcatRequestUpgradeStrategy implements WebSocketRequestUpgradeStrategy { @Override diff --git a/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/WebSocketRequestUpgradeStrategy.java similarity index 81% rename from spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointRequestUpgradeStrategy.java rename to spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/WebSocketRequestUpgradeStrategy.java index 745001b206..9a1b43dc12 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/EndpointRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/server/endpoint/WebSocketRequestUpgradeStrategy.java @@ -21,14 +21,16 @@ import org.springframework.http.server.ServerHttpResponse; /** - * A strategy for performing the actual request upgrade after the handshake checks have - * passed, encapsulating runtime-specific steps of the handshake. + * A strategy for performing the runtime-specific parts of a WebSocket upgrade request. * * @author Rossen Stoyanchev * @since 4.0 */ -public interface EndpointRequestUpgradeStrategy { +public interface WebSocketRequestUpgradeStrategy { + /** + * Invoked after the handshake checks have been performed and succeeded. + */ void upgrade(ServerHttpRequest request, ServerHttpResponse response, String protocol, EndpointRegistration registration) throws Exception; diff --git a/spring-websocket/src/main/java/org/springframework/websocket/endpoint/StandardWebSocketSession.java b/spring-websocket/src/main/java/org/springframework/websocket/support/StandardWebSocketSession.java similarity index 97% rename from spring-websocket/src/main/java/org/springframework/websocket/endpoint/StandardWebSocketSession.java rename to spring-websocket/src/main/java/org/springframework/websocket/support/StandardWebSocketSession.java index 302a033392..d3384ed6b0 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/endpoint/StandardWebSocketSession.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/support/StandardWebSocketSession.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.websocket.endpoint; +package org.springframework.websocket.support; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/spring-websocket/src/main/java/org/springframework/websocket/endpoint/WebSocketHandlerEndpoint.java b/spring-websocket/src/main/java/org/springframework/websocket/support/WebSocketHandlerEndpoint.java similarity index 92% rename from spring-websocket/src/main/java/org/springframework/websocket/endpoint/WebSocketHandlerEndpoint.java rename to spring-websocket/src/main/java/org/springframework/websocket/support/WebSocketHandlerEndpoint.java index da3d7a768a..017d0181ea 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/endpoint/WebSocketHandlerEndpoint.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/support/WebSocketHandlerEndpoint.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.websocket.endpoint; +package org.springframework.websocket.support; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -43,7 +43,7 @@ public class WebSocketHandlerEndpoint extends Endpoint { private final WebSocketHandler webSocketHandler; - private final Map sessionMap = new ConcurrentHashMap(); + private final Map sessions = new ConcurrentHashMap(); public WebSocketHandlerEndpoint(WebSocketHandler webSocketHandler) { @@ -57,7 +57,7 @@ public class WebSocketHandlerEndpoint extends Endpoint { } try { WebSocketSession webSocketSession = new StandardWebSocketSession(session); - this.sessionMap.put(session.getId(), webSocketSession); + this.sessions.put(session.getId(), webSocketSession); session.addMessageHandler(new StandardMessageHandler(session.getId())); this.webSocketHandler.newSession(webSocketSession); } @@ -75,7 +75,7 @@ public class WebSocketHandlerEndpoint extends Endpoint { } try { WebSocketSession webSocketSession = getSession(id); - this.sessionMap.remove(id); + this.sessions.remove(id); int code = closeReason.getCloseCode().getCode(); String reason = closeReason.getReasonPhrase(); this.webSocketHandler.sessionClosed(webSocketSession, code, reason); @@ -100,7 +100,7 @@ public class WebSocketHandlerEndpoint extends Endpoint { } private WebSocketSession getSession(String sourceSessionId) { - WebSocketSession webSocketSession = this.sessionMap.get(sourceSessionId); + WebSocketSession webSocketSession = this.sessions.get(sourceSessionId); Assert.notNull(webSocketSession, "No session"); return webSocketSession; } diff --git a/spring-websocket/src/main/java/org/springframework/websocket/endpoint/package-info.java b/spring-websocket/src/main/java/org/springframework/websocket/support/package-info.java similarity index 62% rename from spring-websocket/src/main/java/org/springframework/websocket/endpoint/package-info.java rename to spring-websocket/src/main/java/org/springframework/websocket/support/package-info.java index 1e5962b911..b30d21c7ab 100644 --- a/spring-websocket/src/main/java/org/springframework/websocket/endpoint/package-info.java +++ b/spring-websocket/src/main/java/org/springframework/websocket/support/package-info.java @@ -4,5 +4,5 @@ * Support for working with standard Java WebSocket Endpoint's. * */ -package org.springframework.websocket.endpoint; +package org.springframework.websocket.support;