Add HandlerProvider interface

HandlerProvider is now an interface that can be used to plug in
WebSocket handlers with per-connection scope semantics. There are two
implementations, of the interface, one simple and a second that creates
handler instances through AutowireCapableBeanFactory.

HandlerProvider also provides a destroy method that is used to
apply a destroy callback whenever a client connection closes.
This commit is contained in:
Rossen Stoyanchev
2013-04-23 11:48:10 -04:00
parent f9078c947f
commit 84089bf396
34 changed files with 371 additions and 367 deletions

View File

@@ -22,6 +22,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.websocket.CloseStatus;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.TextMessage;
import org.springframework.websocket.TextMessageHandler;
import org.springframework.websocket.WebSocketHandler;
@@ -40,6 +41,8 @@ public abstract class AbstractSockJsSession implements WebSocketSession {
private final String sessionId;
private final HandlerProvider<WebSocketHandler> handlerProvider;
private final TextMessageHandler handler;
private State state = State.NEW;
@@ -52,14 +55,17 @@ public abstract class AbstractSockJsSession implements WebSocketSession {
/**
*
* @param sessionId
* @param handler the recipient of SockJS messages
* @param handlerProvider the recipient of SockJS messages
*/
public AbstractSockJsSession(String sessionId, WebSocketHandler webSocketHandler) {
public AbstractSockJsSession(String sessionId, HandlerProvider<WebSocketHandler> handlerProvider) {
Assert.notNull(sessionId, "sessionId is required");
Assert.notNull(webSocketHandler, "webSocketHandler is required");
Assert.isInstanceOf(TextMessageHandler.class, webSocketHandler, "Expected a TextMessageHandler");
Assert.notNull(handlerProvider, "handlerProvider is required");
this.sessionId = sessionId;
WebSocketHandler webSocketHandler = handlerProvider.getHandler();
Assert.isInstanceOf(TextMessageHandler.class, webSocketHandler, "Expected a TextMessageHandler");
this.handler = (TextMessageHandler) webSocketHandler;
this.handlerProvider = handlerProvider;
}
public String getId() {
@@ -180,7 +186,12 @@ public abstract class AbstractSockJsSession implements WebSocketSession {
}
finally {
this.state = State.CLOSED;
this.handler.afterConnectionClosed(status, this);
try {
this.handler.afterConnectionClosed(status, this);
}
finally {
this.handlerProvider.destroy(this.handler);
}
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.sockjs;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession;
@@ -28,6 +29,6 @@ import org.springframework.websocket.WebSocketSession;
*/
public interface SockJsSessionFactory<S extends WebSocketSession>{
S createSession(String sessionId, WebSocketHandler webSocketHandler);
S createSession(String sessionId, HandlerProvider<WebSocketHandler> handler);
}

View File

@@ -25,6 +25,7 @@ import java.util.concurrent.ScheduledFuture;
import org.springframework.sockjs.AbstractSockJsSession;
import org.springframework.util.Assert;
import org.springframework.websocket.CloseStatus;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.TextMessage;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketMessage;
@@ -45,9 +46,9 @@ public abstract class AbstractServerSockJsSession extends AbstractSockJsSession
public AbstractServerSockJsSession(String sessionId, SockJsConfiguration config,
WebSocketHandler webSocketHandler) {
HandlerProvider<WebSocketHandler> handler) {
super(sessionId, webSocketHandler);
super(sessionId, handler);
this.sockJsConfig = config;
}

View File

@@ -39,6 +39,7 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
@@ -218,7 +219,7 @@ public abstract class AbstractSockJsService
* @throws Exception
*/
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
String sockJsPath, WebSocketHandler webSocketHandler) throws Exception {
String sockJsPath, HandlerProvider<WebSocketHandler> handler) throws Exception {
logger.debug(request.getMethod() + " [" + sockJsPath + "]");
@@ -244,7 +245,7 @@ public abstract class AbstractSockJsService
return;
}
else if (sockJsPath.equals("/websocket")) {
handleRawWebSocketRequest(request, response, webSocketHandler);
handleRawWebSocketRequest(request, response, handler);
return;
}
@@ -264,7 +265,7 @@ public abstract class AbstractSockJsService
return;
}
handleTransportRequest(request, response, sessionId, TransportType.fromValue(transport), webSocketHandler);
handleTransportRequest(request, response, sessionId, TransportType.fromValue(transport), handler);
}
finally {
response.flush();
@@ -272,10 +273,10 @@ public abstract class AbstractSockJsService
}
protected abstract void handleRawWebSocketRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler) throws Exception;
HandlerProvider<WebSocketHandler> handler) throws Exception;
protected abstract void handleTransportRequest(ServerHttpRequest request, ServerHttpResponse response,
String sessionId, TransportType transportType, WebSocketHandler webSocketHandler) throws Exception;
String sessionId, TransportType transportType, HandlerProvider<WebSocketHandler> handler) throws Exception;
protected boolean validateRequest(String serverId, String sessionId, String transport) {

View File

@@ -18,6 +18,7 @@ package org.springframework.sockjs.server;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
@@ -30,6 +31,6 @@ public interface SockJsService {
void handleRequest(ServerHttpRequest request, ServerHttpResponse response, String sockJsPath,
WebSocketHandler webSocketHandler) throws Exception;
HandlerProvider<WebSocketHandler> handler) throws Exception;
}

View File

@@ -18,6 +18,7 @@ package org.springframework.sockjs.server;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.sockjs.AbstractSockJsSession;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
@@ -31,6 +32,6 @@ public interface TransportHandler {
TransportType getTransportType();
void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws Exception;
HandlerProvider<WebSocketHandler> handler, AbstractSockJsSession session) throws Exception;
}

View File

@@ -21,7 +21,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.Cookie;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
@@ -43,6 +42,7 @@ 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.Assert;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.server.DefaultHandshakeHandler;
import org.springframework.websocket.server.HandshakeHandler;
@@ -54,7 +54,7 @@ import org.springframework.websocket.server.HandshakeHandler;
* @author Rossen Stoyanchev
* @since 4.0
*/
public class DefaultSockJsService extends AbstractSockJsService implements InitializingBean {
public class DefaultSockJsService extends AbstractSockJsService {
private final Map<TransportType, TransportHandler> transportHandlers = new HashMap<TransportType, TransportHandler>();
@@ -157,13 +157,13 @@ public class DefaultSockJsService extends AbstractSockJsService implements Initi
@Override
protected void handleRawWebSocketRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler) throws Exception {
HandlerProvider<WebSocketHandler> handler) throws Exception {
if (isWebSocketEnabled()) {
TransportHandler transportHandler = this.transportHandlers.get(TransportType.WEBSOCKET);
if (transportHandler != null) {
if (transportHandler instanceof HandshakeHandler) {
((HandshakeHandler) transportHandler).doHandshake(request, response, webSocketHandler);
((HandshakeHandler) transportHandler).doHandshake(request, response, handler);
return;
}
}
@@ -174,7 +174,7 @@ public class DefaultSockJsService extends AbstractSockJsService implements Initi
@Override
protected void handleTransportRequest(ServerHttpRequest request, ServerHttpResponse response,
String sessionId, TransportType transportType, WebSocketHandler webSocketHandler) throws Exception {
String sessionId, TransportType transportType, HandlerProvider<WebSocketHandler> handler) throws Exception {
TransportHandler transportHandler = this.transportHandlers.get(transportType);
@@ -201,7 +201,7 @@ public class DefaultSockJsService extends AbstractSockJsService implements Initi
return;
}
AbstractSockJsSession session = getSockJsSession(sessionId, webSocketHandler, transportHandler);
AbstractSockJsSession session = getSockJsSession(sessionId, handler, transportHandler);
if (session != null) {
if (transportType.setsNoCacheHeader()) {
@@ -220,10 +220,10 @@ public class DefaultSockJsService extends AbstractSockJsService implements Initi
}
}
transportHandler.handleRequest(request, response, webSocketHandler, session);
transportHandler.handleRequest(request, response, handler, session);
}
public AbstractSockJsSession getSockJsSession(String sessionId, WebSocketHandler webSocketHandler,
public AbstractSockJsSession getSockJsSession(String sessionId, HandlerProvider<WebSocketHandler> handler,
TransportHandler transportHandler) {
AbstractSockJsSession session = this.sessions.get(sessionId);
@@ -240,7 +240,7 @@ public class DefaultSockJsService extends AbstractSockJsService implements Initi
return session;
}
logger.debug("Creating new session with session id \"" + sessionId + "\"");
session = (AbstractSockJsSession) sessionFactory.createSession(sessionId, webSocketHandler);
session = (AbstractSockJsSession) sessionFactory.createSession(sessionId, handler);
this.sessions.put(sessionId, session);
return session;
}

View File

@@ -22,9 +22,6 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.http.server.AsyncServletServerHttpRequest;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
@@ -36,6 +33,7 @@ import org.springframework.web.util.NestedServletException;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.support.SimpleHandlerProvider;
/**
@@ -43,7 +41,7 @@ import org.springframework.websocket.WebSocketHandler;
* @author Rossen Stoyanchev
* @since 4.0
*/
public class SockJsHttpRequestHandler implements HttpRequestHandler, BeanFactoryAware {
public class SockJsHttpRequestHandler implements HttpRequestHandler {
private final String prefix;
@@ -61,15 +59,15 @@ public class SockJsHttpRequestHandler implements HttpRequestHandler, BeanFactory
* 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 SockJsHttpRequestHandler(String prefix, SockJsService sockJsService, WebSocketHandler webSocketHandler) {
public SockJsHttpRequestHandler(String prefix, SockJsService sockJsService, WebSocketHandler handler) {
Assert.hasText(prefix, "prefix is required");
Assert.notNull(sockJsService, "sockJsService is required");
Assert.notNull(webSocketHandler, "webSocketHandler is required");
Assert.notNull(handler, "webSocketHandler is required");
this.prefix = prefix;
this.sockJsService = sockJsService;
this.handlerProvider = new HandlerProvider<WebSocketHandler>(webSocketHandler);
this.handlerProvider = new SimpleHandlerProvider<WebSocketHandler>(handler);
}
/**
@@ -80,15 +78,15 @@ public class SockJsHttpRequestHandler implements HttpRequestHandler, BeanFactory
* Servlet container this is the path within the current servlet mapping.
*/
public SockJsHttpRequestHandler(String prefix, SockJsService sockJsService,
Class<? extends WebSocketHandler> webSocketHandlerClass) {
HandlerProvider<WebSocketHandler> handlerProvider) {
Assert.hasText(prefix, "prefix is required");
Assert.notNull(sockJsService, "sockJsService is required");
Assert.notNull(webSocketHandlerClass, "webSocketHandlerClass is required");
Assert.notNull(handlerProvider, "handlerProvider is required");
this.prefix = prefix;
this.sockJsService = sockJsService;
this.handlerProvider = new HandlerProvider<WebSocketHandler>(webSocketHandlerClass);
this.handlerProvider = handlerProvider;
}
public String getPrefix() {
@@ -99,11 +97,6 @@ public class SockJsHttpRequestHandler implements HttpRequestHandler, BeanFactory
return this.prefix + "/**";
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.handlerProvider.setBeanFactory(beanFactory);
}
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
@@ -119,8 +112,7 @@ public class SockJsHttpRequestHandler implements HttpRequestHandler, BeanFactory
ServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
try {
WebSocketHandler webSocketHandler = this.handlerProvider.getHandler();
this.sockJsService.handleRequest(httpRequest, httpResponse, sockJsPath, webSocketHandler);
this.sockJsService.handleRequest(httpRequest, httpResponse, sockJsPath, this.handlerProvider);
}
catch (Exception ex) {
// TODO

View File

@@ -27,6 +27,7 @@ import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.sockjs.AbstractSockJsSession;
import org.springframework.sockjs.server.TransportHandler;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
import com.fasterxml.jackson.databind.JsonMappingException;
@@ -53,7 +54,7 @@ public abstract class AbstractHttpReceivingTransportHandler implements Transport
@Override
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws Exception {
HandlerProvider<WebSocketHandler> webSocketHandler, AbstractSockJsSession session) throws Exception {
if (session == null) {
response.setStatusCode(HttpStatus.NOT_FOUND);

View File

@@ -28,6 +28,7 @@ import org.springframework.sockjs.server.ConfigurableTransportHandler;
import org.springframework.sockjs.server.SockJsConfiguration;
import org.springframework.sockjs.server.SockJsFrame;
import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
/**
@@ -55,7 +56,7 @@ public abstract class AbstractHttpSendingTransportHandler
@Override
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws Exception {
HandlerProvider<WebSocketHandler> webSocketHandler, AbstractSockJsSession session) throws Exception {
// Set content type before writing
response.getHeaders().setContentType(getContentType());

View File

@@ -29,6 +29,7 @@ import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
import org.springframework.sockjs.server.TransportHandler;
import org.springframework.util.Assert;
import org.springframework.websocket.CloseStatus;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
/**
@@ -49,9 +50,9 @@ public abstract class AbstractHttpServerSockJsSession extends AbstractServerSock
public AbstractHttpServerSockJsSession(String sessionId, SockJsConfiguration sockJsConfig,
WebSocketHandler webSocketHandler) {
HandlerProvider<WebSocketHandler> handler) {
super(sessionId, sockJsConfig, webSocketHandler);
super(sessionId, sockJsConfig, handler);
}
public void setFrameFormat(FrameFormat frameFormat) {

View File

@@ -20,6 +20,7 @@ import java.io.IOException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
@@ -33,9 +34,9 @@ public abstract class AbstractStreamingTransportHandler extends AbstractHttpSend
@Override
public StreamingServerSockJsSession createSession(String sessionId, WebSocketHandler webSocketHandler) {
public StreamingServerSockJsSession createSession(String sessionId, HandlerProvider<WebSocketHandler> handler) {
Assert.notNull(getSockJsConfig(), "This transport requires SockJsConfiguration");
return new StreamingServerSockJsSession(sessionId, getSockJsConfig(), webSocketHandler);
return new StreamingServerSockJsSession(sessionId, getSockJsConfig(), handler);
}
@Override

View File

@@ -27,6 +27,7 @@ import org.springframework.sockjs.server.TransportType;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.JavaScriptUtils;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
@@ -50,9 +51,9 @@ public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHa
}
@Override
public PollingServerSockJsSession createSession(String sessionId, WebSocketHandler webSocketHandler) {
public PollingServerSockJsSession createSession(String sessionId, HandlerProvider<WebSocketHandler> handler) {
Assert.notNull(getSockJsConfig(), "This transport requires SockJsConfiguration");
return new PollingServerSockJsSession(sessionId, getSockJsConfig(), webSocketHandler);
return new PollingServerSockJsSession(sessionId, getSockJsConfig(), handler);
}
@Override

View File

@@ -17,15 +17,16 @@ package org.springframework.sockjs.server.transport;
import org.springframework.sockjs.server.SockJsConfiguration;
import org.springframework.sockjs.server.SockJsFrame;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
public class PollingServerSockJsSession extends AbstractHttpServerSockJsSession {
public PollingServerSockJsSession(String sessionId, SockJsConfiguration sockJsConfig,
WebSocketHandler webSocketHandler) {
HandlerProvider<WebSocketHandler> handler) {
super(sessionId, sockJsConfig, webSocketHandler);
super(sessionId, sockJsConfig, handler);
}
@Override

View File

@@ -17,8 +17,6 @@
package org.springframework.sockjs.server.transport;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -29,6 +27,7 @@ import org.springframework.sockjs.server.SockJsFrame;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.websocket.CloseStatus;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.TextMessage;
import org.springframework.websocket.TextMessageHandler;
import org.springframework.websocket.WebSocketHandler;
@@ -38,8 +37,8 @@ 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.
* A wrapper around a {@link WebSocketHandler} instance that parses and adds SockJS
* messages frames as well as sends SockJS heartbeat messages.
*
* @author Rossen Stoyanchev
* @since 4.0
@@ -50,34 +49,28 @@ public class SockJsWebSocketHandler implements TextMessageHandler {
private final SockJsConfiguration sockJsConfig;
private final WebSocketHandler webSocketHandler;
private final HandlerProvider<WebSocketHandler> handlerProvider;
private final Map<WebSocketSession, AbstractSockJsSession> sessions =
new ConcurrentHashMap<WebSocketSession, AbstractSockJsSession>();
private AbstractSockJsSession session;
// TODO: JSON library used must be configurable
private final ObjectMapper objectMapper = new ObjectMapper();
public SockJsWebSocketHandler(SockJsConfiguration sockJsConfig, WebSocketHandler webSocketHandler) {
Assert.notNull(sockJsConfig, "sockJsConfig is required");
Assert.notNull(webSocketHandler, "webSocketHandler is required");
this.sockJsConfig = sockJsConfig;
this.webSocketHandler = webSocketHandler;
public SockJsWebSocketHandler(SockJsConfiguration config, HandlerProvider<WebSocketHandler> handlerProvider) {
Assert.notNull(config, "sockJsConfig is required");
Assert.notNull(handlerProvider, "handlerProvider is required");
this.sockJsConfig = config;
this.handlerProvider = handlerProvider;
}
protected SockJsConfiguration getSockJsConfig() {
return this.sockJsConfig;
}
protected AbstractSockJsSession getSockJsSession(WebSocketSession wsSession) {
return this.sessions.get(wsSession);
}
@Override
public void afterConnectionEstablished(WebSocketSession wsSession) throws Exception {
AbstractSockJsSession session = new WebSocketServerSockJsSession(wsSession, getSockJsConfig());
this.sessions.put(wsSession, session);
this.session = new WebSocketServerSockJsSession(wsSession, getSockJsConfig());
}
@Override
@@ -89,8 +82,7 @@ public class SockJsWebSocketHandler implements TextMessageHandler {
}
try {
String[] messages = this.objectMapper.readValue(payload, String[].class);
AbstractSockJsSession session = getSockJsSession(wsSession);
session.delegateMessages(messages);
this.session.delegateMessages(messages);
}
catch (IOException e) {
logger.error("Broken data received. Terminating WebSocket connection abruptly", e);
@@ -100,14 +92,12 @@ public class SockJsWebSocketHandler implements TextMessageHandler {
@Override
public void afterConnectionClosed(CloseStatus status, WebSocketSession wsSession) throws Exception {
AbstractSockJsSession session = this.sessions.remove(wsSession);
session.delegateConnectionClosed(status);
this.session.delegateConnectionClosed(status);
}
@Override
public void handleError(Throwable exception, WebSocketSession webSocketSession) {
AbstractSockJsSession session = getSockJsSession(webSocketSession);
session.delegateError(exception);
this.session.delegateError(exception);
}
private static String getSockJsSessionId(WebSocketSession wsSession) {
@@ -127,7 +117,7 @@ public class SockJsWebSocketHandler implements TextMessageHandler {
public WebSocketServerSockJsSession(WebSocketSession wsSession, SockJsConfiguration sockJsConfig)
throws Exception {
super(getSockJsSessionId(wsSession), sockJsConfig, SockJsWebSocketHandler.this.webSocketHandler);
super(getSockJsSessionId(wsSession), sockJsConfig, SockJsWebSocketHandler.this.handlerProvider);
this.wsSession = wsSession;
TextMessage message = new TextMessage(SockJsFrame.openFrame().getContent());
this.wsSession.sendMessage(message);

View File

@@ -20,6 +20,7 @@ import java.io.IOException;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.sockjs.server.SockJsConfiguration;
import org.springframework.sockjs.server.SockJsFrame;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
@@ -29,9 +30,9 @@ public class StreamingServerSockJsSession extends AbstractHttpServerSockJsSessio
public StreamingServerSockJsSession(String sessionId, SockJsConfiguration sockJsConfig,
WebSocketHandler webSocketHandler) {
HandlerProvider<WebSocketHandler> handler) {
super(sessionId, sockJsConfig, webSocketHandler);
super(sessionId, sockJsConfig, handler);
}
protected void flushCache() throws Exception {

View File

@@ -24,8 +24,10 @@ import org.springframework.sockjs.server.SockJsConfiguration;
import org.springframework.sockjs.server.TransportHandler;
import org.springframework.sockjs.server.TransportType;
import org.springframework.util.Assert;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.server.HandshakeHandler;
import org.springframework.websocket.support.SimpleHandlerProvider;
/**
@@ -60,26 +62,19 @@ public class WebSocketTransportHandler implements ConfigurableTransportHandler,
@Override
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws Exception {
HandlerProvider<WebSocketHandler> handler, AbstractSockJsSession session) throws Exception {
this.handshakeHandler.doHandshake(request, response, adaptSockJsHandler(webSocketHandler));
}
/**
* Adapt the {@link SockJsHandler} to the {@link WebSocketHandler} contract for
* exchanging SockJS message over WebSocket.
*/
protected WebSocketHandler adaptSockJsHandler(WebSocketHandler handler) {
return new SockJsWebSocketHandler(this.sockJsConfig, handler);
WebSocketHandler sockJsWrapper = new SockJsWebSocketHandler(this.sockJsConfig, handler);
this.handshakeHandler.doHandshake(request, response, new SimpleHandlerProvider<WebSocketHandler>(sockJsWrapper));
}
// HandshakeHandler methods
@Override
public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler) throws Exception {
HandlerProvider<WebSocketHandler> handler) throws Exception {
return this.handshakeHandler.doHandshake(request, response, webSocketHandler);
return this.handshakeHandler.doHandshake(request, response, handler);
}
}

View File

@@ -23,6 +23,7 @@ import org.springframework.sockjs.server.SockJsFrame.DefaultFrameFormat;
import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
import org.springframework.sockjs.server.TransportType;
import org.springframework.util.Assert;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
@@ -50,9 +51,9 @@ public class XhrPollingTransportHandler extends AbstractHttpSendingTransportHand
return new DefaultFrameFormat("%s\n");
}
public PollingServerSockJsSession createSession(String sessionId, WebSocketHandler webSocketHandler) {
public PollingServerSockJsSession createSession(String sessionId, HandlerProvider<WebSocketHandler> handler) {
Assert.notNull(getSockJsConfig(), "This transport requires SockJsConfiguration");
return new PollingServerSockJsSession(sessionId, getSockJsConfig(), webSocketHandler);
return new PollingServerSockJsSession(sessionId, getSockJsConfig(), handler);
}
}

View File

@@ -5,7 +5,7 @@
* 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
* 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,
@@ -13,82 +13,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.websocket;
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.util.Assert;
import org.springframework.util.ClassUtils;
/**
* A strategy for obtaining a handler instance that is scoped to external lifecycle events
* such as the opening and closing of a WebSocket connection.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class HandlerProvider<T> implements BeanFactoryAware {
public interface HandlerProvider<T> {
private Log logger = LogFactory.getLog(this.getClass());
/**
* Whether the provided handler is a shared instance or not.
*/
boolean isSingleton();
private final T handlerBean;
/**
* The type of handler provided.
*/
Class<?> getHandlerType();
private final Class<? extends T> handlerClass;
/**
* Obtain the handler instance, either shared or created every time.
*/
T getHandler();
private AutowireCapableBeanFactory beanFactory;
public HandlerProvider(T handlerBean) {
Assert.notNull(handlerBean, "handlerBean is required");
this.handlerBean = handlerBean;
this.handlerClass = null;
}
public HandlerProvider(Class<? extends T> handlerClass) {
Assert.notNull(handlerClass, "handlerClass is required");
this.handlerBean = null;
this.handlerClass = handlerClass;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof AutowireCapableBeanFactory) {
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
}
}
public void setLogger(Log logger) {
this.logger = logger;
}
public boolean isSingleton() {
return (this.handlerBean != null);
}
@SuppressWarnings("unchecked")
public Class<? extends T> getHandlerType() {
if (this.handlerClass != null) {
return this.handlerClass;
}
return (Class<? extends T>) ClassUtils.getUserClass(this.handlerBean.getClass());
}
public T getHandler() {
if (this.handlerBean != null) {
if (logger != null && logger.isTraceEnabled()) {
logger.trace("Returning handler singleton " + this.handlerBean);
}
return this.handlerBean;
}
Assert.isTrue(this.beanFactory != null, "BeanFactory is required to initialize handler instances.");
if (logger != null && logger.isTraceEnabled()) {
logger.trace("Creating handler of type " + this.handlerClass);
}
return this.beanFactory.createBean(this.handlerClass);
}
/**
* Callback to destroy a previously created handler instance if it is not shared.
*/
void destroy(T handler);
}

View File

@@ -18,6 +18,7 @@ package org.springframework.websocket.client;
import java.net.URI;
import org.springframework.http.HttpHeaders;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession;
@@ -36,13 +37,10 @@ import org.springframework.websocket.WebSocketSession;
public interface WebSocketClient {
WebSocketSession doHandshake(WebSocketHandler handler, String uriTemplate, Object... uriVariables)
throws WebSocketConnectFailureException;
WebSocketSession doHandshake(HandlerProvider<WebSocketHandler> handler,
String uriTemplate, Object... uriVariables) throws WebSocketConnectFailureException;
WebSocketSession doHandshake(WebSocketHandler handler, URI uri)
throws WebSocketConnectFailureException;
WebSocketSession doHandshake(WebSocketHandler handler, HttpHeaders headers, URI uri)
WebSocketSession doHandshake(HandlerProvider<WebSocketHandler> handler, HttpHeaders headers, URI uri)
throws WebSocketConnectFailureException;
}

View File

@@ -21,6 +21,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession;
import org.springframework.websocket.support.SimpleHandlerProvider;
/**
@@ -32,7 +33,7 @@ public class WebSocketConnectionManager extends AbstractWebSocketConnectionManag
private final WebSocketClient client;
private final HandlerProvider<WebSocketHandler> webSocketHandlerProvider;
private final HandlerProvider<WebSocketHandler> handlerProvider;
private WebSocketSession webSocketSession;
@@ -43,8 +44,16 @@ public class WebSocketConnectionManager extends AbstractWebSocketConnectionManag
WebSocketHandler webSocketHandler, String uriTemplate, Object... uriVariables) {
super(uriTemplate, uriVariables);
this.webSocketHandlerProvider = new HandlerProvider<WebSocketHandler>(webSocketHandler);
this.client = webSocketClient;
this.handlerProvider = new SimpleHandlerProvider<WebSocketHandler>(webSocketHandler);
}
public WebSocketConnectionManager(WebSocketClient webSocketClient,
HandlerProvider<WebSocketHandler> handlerProvider, String uriTemplate, Object... uriVariables) {
super(uriTemplate, uriVariables);
this.client = webSocketClient;
this.handlerProvider = handlerProvider;
}
public void setSubProtocols(List<String> subProtocols) {
@@ -57,10 +66,9 @@ public class WebSocketConnectionManager extends AbstractWebSocketConnectionManag
@Override
protected void openConnection() throws Exception {
WebSocketHandler webSocketHandler = this.webSocketHandlerProvider.getHandler();
HttpHeaders headers = new HttpHeaders();
headers.setSecWebSocketProtocol(this.subProtocols);
this.webSocketSession = this.client.doHandshake(webSocketHandler, headers, getUri());
this.webSocketSession = this.client.doHandshake(this.handlerProvider, headers, getUri());
}
@Override

View File

@@ -22,6 +22,8 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.support.BeanCreatingHandlerProvider;
import org.springframework.websocket.support.SimpleHandlerProvider;
/**
@@ -32,28 +34,29 @@ import org.springframework.websocket.HandlerProvider;
public class AnnotatedEndpointConnectionManager extends EndpointConnectionManagerSupport
implements BeanFactoryAware {
private final HandlerProvider<Object> endpointProvider;
private final HandlerProvider<Object> handlerProvider;
public AnnotatedEndpointConnectionManager(Class<?> endpointClass, String uriTemplate, Object... uriVariables) {
super(uriTemplate, uriVariables);
this.endpointProvider = new HandlerProvider<Object>(endpointClass);
}
public AnnotatedEndpointConnectionManager(Object endpointBean, String uriTemplate, Object... uriVariables) {
super(uriTemplate, uriVariables);
this.endpointProvider = new HandlerProvider<Object>(endpointBean);
this.handlerProvider = new SimpleHandlerProvider<Object>(endpointBean);
}
public AnnotatedEndpointConnectionManager(Class<?> endpointClass, String uriTemplate, Object... uriVariables) {
super(uriTemplate, uriVariables);
this.handlerProvider = new BeanCreatingHandlerProvider<Object>(endpointClass);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.endpointProvider.setBeanFactory(beanFactory);
if (this.handlerProvider instanceof BeanFactoryAware) {
((BeanFactoryAware) this.handlerProvider).setBeanFactory(beanFactory);
}
}
@Override
protected void openConnection() throws Exception {
Object endpoint = this.endpointProvider.getHandler();
Object endpoint = this.handlerProvider.getHandler();
Session session = getWebSocketContainer().connectToServer(endpoint, getUri());
updateSession(session);
}

View File

@@ -32,6 +32,8 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.util.Assert;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.support.BeanCreatingHandlerProvider;
import org.springframework.websocket.support.SimpleHandlerProvider;
/**
@@ -43,19 +45,19 @@ public class EndpointConnectionManager extends EndpointConnectionManagerSupport
private final ClientEndpointConfig.Builder configBuilder = ClientEndpointConfig.Builder.create();
private final HandlerProvider<Endpoint> endpointProvider;
private final HandlerProvider<Endpoint> handlerProvider;
public EndpointConnectionManager(Endpoint endpointBean, String uriTemplate, Object... uriVariables) {
super(uriTemplate, uriVariables);
Assert.notNull(endpointBean, "endpointBean is required");
this.endpointProvider = new HandlerProvider<Endpoint>(endpointBean);
this.handlerProvider = new SimpleHandlerProvider<Endpoint>(endpointBean);
}
public EndpointConnectionManager(Class<? extends Endpoint> endpointClass, String uriTemplate, Object... uriVars) {
super(uriTemplate, uriVars);
Assert.notNull(endpointClass, "endpointClass is required");
this.endpointProvider = new HandlerProvider<Endpoint>(endpointClass);
this.handlerProvider = new BeanCreatingHandlerProvider<Endpoint>(endpointClass);
}
public void setSubProtocols(String... subprotocols) {
@@ -80,12 +82,14 @@ public class EndpointConnectionManager extends EndpointConnectionManagerSupport
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.endpointProvider.setBeanFactory(beanFactory);
if (this.handlerProvider instanceof BeanFactoryAware) {
((BeanFactoryAware) this.handlerProvider).setBeanFactory(beanFactory);
}
}
@Override
protected void openConnection() throws Exception {
Endpoint endpoint = this.endpointProvider.getHandler();
Endpoint endpoint = this.handlerProvider.getHandler();
ClientEndpointConfig endpointConfig = this.configBuilder.build();
Session session = getWebSocketContainer().connectToServer(endpoint, endpointConfig, getUri());
updateSession(session);

View File

@@ -31,6 +31,7 @@ import javax.websocket.WebSocketContainer;
import org.springframework.http.HttpHeaders;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession;
import org.springframework.websocket.client.WebSocketClient;
@@ -40,6 +41,7 @@ import org.springframework.websocket.endpoint.WebSocketHandlerEndpoint;
/**
* A standard Java {@link WebSocketClient}.
*
* @author Rossen Stoyanchev
* @since 4.0
@@ -57,21 +59,16 @@ public class StandardWebSocketClient implements WebSocketClient {
this.webSocketContainer = container;
}
public WebSocketSession doHandshake(WebSocketHandler handler, String uriTemplate,
Object... uriVariables) throws WebSocketConnectFailureException {
public WebSocketSession doHandshake(HandlerProvider<WebSocketHandler> handler,
String uriTemplate, Object... uriVariables) throws WebSocketConnectFailureException {
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).buildAndExpand(uriVariables).encode().toUri();
return doHandshake(handler, uri);
}
@Override
public WebSocketSession doHandshake(WebSocketHandler handler, URI uri) throws WebSocketConnectFailureException {
return doHandshake(handler, null, uri);
}
@Override
public WebSocketSession doHandshake(WebSocketHandler handler, final HttpHeaders httpHeaders, URI uri)
throws WebSocketConnectFailureException {
public WebSocketSession doHandshake(HandlerProvider<WebSocketHandler> handler,
final HttpHeaders httpHeaders, URI uri) throws WebSocketConnectFailureException {
Endpoint endpoint = new WebSocketHandlerEndpoint(handler);

View File

@@ -16,9 +16,6 @@
package org.springframework.websocket.endpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.CloseReason;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
@@ -27,14 +24,15 @@ import javax.websocket.MessageHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.websocket.CloseStatus;
import org.springframework.websocket.BinaryMessage;
import org.springframework.websocket.BinaryMessageHandler;
import org.springframework.websocket.CloseStatus;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.PartialMessageHandler;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession;
import org.springframework.websocket.TextMessage;
import org.springframework.websocket.TextMessageHandler;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession;
/**
@@ -47,14 +45,17 @@ public class WebSocketHandlerEndpoint extends Endpoint {
private static Log logger = LogFactory.getLog(WebSocketHandlerEndpoint.class);
private final WebSocketHandler webSocketHandler;
private final HandlerProvider<WebSocketHandler> handlerProvider;
private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<String, WebSocketSession>();
private final WebSocketHandler handler;
private WebSocketSession webSocketSession;
public WebSocketHandlerEndpoint(WebSocketHandler handler) {
Assert.notNull(handler, "webSocketHandler is required");
this.webSocketHandler = handler;
public WebSocketHandlerEndpoint(HandlerProvider<WebSocketHandler> handlerProvider) {
Assert.notNull(handlerProvider, "handlerProvider is required");
this.handlerProvider = handlerProvider;
this.handler = handlerProvider.getHandler();
}
@Override
@@ -63,10 +64,9 @@ public class WebSocketHandlerEndpoint extends Endpoint {
logger.debug("Client connected, WebSocket session id=" + session.getId() + ", uri=" + session.getRequestURI());
}
try {
WebSocketSession webSocketSession = new StandardWebSocketSession(session);
this.sessions.put(session.getId(), webSocketSession);
this.webSocketSession = new StandardWebSocketSession(session);
if (this.webSocketHandler instanceof TextMessageHandler) {
if (this.handler instanceof TextMessageHandler) {
session.addMessageHandler(new MessageHandler.Whole<String>() {
@Override
public void onMessage(String message) {
@@ -74,8 +74,8 @@ public class WebSocketHandlerEndpoint extends Endpoint {
}
});
}
else if (this.webSocketHandler instanceof BinaryMessageHandler) {
if (this.webSocketHandler instanceof PartialMessageHandler) {
else if (this.handler instanceof BinaryMessageHandler) {
if (this.handler instanceof PartialMessageHandler) {
session.addMessageHandler(new MessageHandler.Partial<byte[]>() {
@Override
public void onMessage(byte[] messagePart, boolean isLast) {
@@ -93,10 +93,10 @@ public class WebSocketHandlerEndpoint extends Endpoint {
}
}
else {
logger.warn("WebSocketHandler handles neither text nor binary messages: " + this.webSocketHandler);
logger.warn("WebSocketHandler handles neither text nor binary messages: " + this.handler);
}
this.webSocketHandler.afterConnectionEstablished(webSocketSession);
this.handler.afterConnectionEstablished(this.webSocketSession);
}
catch (Throwable ex) {
// TODO
@@ -108,11 +108,9 @@ public class WebSocketHandlerEndpoint extends Endpoint {
if (logger.isTraceEnabled()) {
logger.trace("Received message for WebSocket session id=" + session.getId() + ": " + message);
}
WebSocketSession wsSession = getWebSocketSession(session);
Assert.notNull(wsSession, "WebSocketSession not found");
try {
TextMessage textMessage = new TextMessage(message);
((TextMessageHandler) webSocketHandler).handleTextMessage(textMessage, wsSession);
((TextMessageHandler) handler).handleTextMessage(textMessage, this.webSocketSession);
}
catch (Throwable ex) {
// TODO
@@ -124,11 +122,9 @@ public class WebSocketHandlerEndpoint extends Endpoint {
if (logger.isTraceEnabled()) {
logger.trace("Received binary data for WebSocket session id=" + session.getId());
}
WebSocketSession wsSession = getWebSocketSession(session);
Assert.notNull(wsSession, "WebSocketSession not found");
try {
BinaryMessage binaryMessage = new BinaryMessage(message, isLast);
((BinaryMessageHandler) webSocketHandler).handleBinaryMessage(binaryMessage, wsSession);
((BinaryMessageHandler) handler).handleBinaryMessage(binaryMessage, this.webSocketSession);
}
catch (Throwable ex) {
// TODO
@@ -142,32 +138,23 @@ public class WebSocketHandlerEndpoint extends Endpoint {
logger.debug("Client disconnected, WebSocket session id=" + session.getId() + ", " + reason);
}
try {
WebSocketSession wsSession = this.sessions.remove(session.getId());
if (wsSession != null) {
CloseStatus closeStatus = new CloseStatus(reason.getCloseCode().getCode(), reason.getReasonPhrase());
this.webSocketHandler.afterConnectionClosed(closeStatus, wsSession);
}
else {
Assert.notNull(wsSession, "No WebSocket session");
}
CloseStatus closeStatus = new CloseStatus(reason.getCloseCode().getCode(), reason.getReasonPhrase());
this.handler.afterConnectionClosed(closeStatus, this.webSocketSession);
}
catch (Throwable ex) {
// TODO
logger.error("Error while processing session closing", ex);
}
finally {
this.handlerProvider.destroy(this.handler);
}
}
@Override
public void onError(javax.websocket.Session session, Throwable exception) {
logger.error("Error for WebSocket session id=" + session.getId(), exception);
try {
WebSocketSession wsSession = getWebSocketSession(session);
if (wsSession != null) {
this.webSocketHandler.handleError(exception, wsSession);
}
else {
logger.warn("WebSocketSession not found. Perhaps onError was called after onClose?");
}
this.handler.handleError(exception, this.webSocketSession);
}
catch (Throwable ex) {
// TODO
@@ -175,8 +162,4 @@ public class WebSocketHandlerEndpoint extends Endpoint {
}
}
private WebSocketSession getWebSocketSession(javax.websocket.Session session) {
return this.sessions.get(session.getId());
}
}

View File

@@ -35,6 +35,7 @@ import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
@@ -87,7 +88,7 @@ public class DefaultHandshakeHandler implements HandshakeHandler {
@Override
public final boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler) throws Exception {
HandlerProvider<WebSocketHandler> handler) throws Exception {
logger.debug("Starting handshake for " + request.getURI());
@@ -135,10 +136,10 @@ public class DefaultHandshakeHandler implements HandshakeHandler {
response.flush();
if (logger.isTraceEnabled()) {
logger.trace("Upgrading with " + webSocketHandler);
logger.trace("Upgrading with " + handler);
}
this.requestUpgradeStrategy.upgrade(request, response, selectedProtocol, webSocketHandler);
this.requestUpgradeStrategy.upgrade(request, response, selectedProtocol, handler);
return true;
}

View File

@@ -18,6 +18,7 @@ package org.springframework.websocket.server;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
@@ -29,16 +30,8 @@ import org.springframework.websocket.WebSocketHandler;
*/
public interface HandshakeHandler {
/**
*
* @param request the HTTP request
* @param response the HTTP response
* @param webSocketMessageHandler the handler to process WebSocket messages with
* @return a boolean indicating whether the handshake negotiation was successful
*
* @throws Exception
*/
boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler webSocketHandler)
throws Exception;
boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response,
HandlerProvider<WebSocketHandler> handler) throws Exception;
}

View File

@@ -18,6 +18,7 @@ package org.springframework.websocket.server;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
@@ -39,9 +40,9 @@ public interface RequestUpgradeStrategy {
* Perform runtime specific steps to complete the upgrade.
* Invoked only if the handshake is successful.
*
* @param webSocketHandler the handler for WebSocket messages
* @param handler the handler for WebSocket messages
*/
void upgrade(ServerHttpRequest request, ServerHttpResponse response, String selectedProtocol,
WebSocketHandler webSocketHandler) throws Exception;
HandlerProvider<WebSocketHandler> handler) throws Exception;
}

View File

@@ -37,6 +37,8 @@ import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.util.Assert;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.endpoint.WebSocketHandlerEndpoint;
import org.springframework.websocket.support.BeanCreatingHandlerProvider;
import org.springframework.websocket.support.SimpleHandlerProvider;
/**
@@ -54,11 +56,9 @@ import org.springframework.websocket.endpoint.WebSocketHandlerEndpoint;
*/
public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAware {
private static Log logger = LogFactory.getLog(EndpointRegistration.class);
private final String path;
private final HandlerProvider<Endpoint> endpointProvider;
private final HandlerProvider<Endpoint> handlerProvider;
private List<Class<? extends Encoder>> encoders = new ArrayList<Class<? extends Encoder>>();
@@ -84,16 +84,14 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
Assert.hasText(path, "path must not be empty");
Assert.notNull(endpointClass, "endpointClass is required");
this.path = path;
this.endpointProvider = new HandlerProvider<Endpoint>(endpointClass);
this.endpointProvider.setLogger(logger);
this.handlerProvider = new BeanCreatingHandlerProvider<Endpoint>(endpointClass);
}
public EndpointRegistration(String path, Endpoint endpointBean) {
Assert.hasText(path, "path must not be empty");
Assert.notNull(endpointBean, "endpointBean is required");
this.path = path;
this.endpointProvider = new HandlerProvider<Endpoint>(endpointBean);
this.endpointProvider.setLogger(logger);
this.handlerProvider = new SimpleHandlerProvider<Endpoint>(endpointBean);
}
@Override
@@ -102,12 +100,13 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
}
@Override
@SuppressWarnings("unchecked")
public Class<? extends Endpoint> getEndpointClass() {
return this.endpointProvider.getHandlerType();
return (Class<? extends Endpoint>) this.handlerProvider.getHandlerType();
}
public Endpoint getEndpoint() {
return this.endpointProvider.getHandler();
return this.handlerProvider.getHandler();
}
public void setSubprotocols(List<String> subprotocols) {
@@ -193,7 +192,9 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.endpointProvider.setBeanFactory(beanFactory);
if (this.handlerProvider instanceof BeanFactoryAware) {
((BeanFactoryAware) this.handlerProvider).setBeanFactory(beanFactory);
}
}
}

View File

@@ -1,88 +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.websocket.server.support;
import javax.websocket.WebSocketContainer;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class AbstractEndpointContainerFactoryBean implements FactoryBean<WebSocketContainer>, InitializingBean {
private WebSocketContainer container;
public void setAsyncSendTimeout(long timeoutInMillis) {
this.container.setAsyncSendTimeout(timeoutInMillis);
}
public long getAsyncSendTimeout() {
return this.container.getDefaultAsyncSendTimeout();
}
public void setMaxSessionIdleTimeout(long timeoutInMillis) {
this.container.setDefaultMaxSessionIdleTimeout(timeoutInMillis);
}
public long getMaxSessionIdleTimeout() {
return this.container.getDefaultMaxSessionIdleTimeout();
}
public void setMaxTextMessageBufferSize(int bufferSize) {
this.container.setDefaultMaxTextMessageBufferSize(bufferSize);
}
public int getMaxTextMessageBufferSize() {
return this.container.getDefaultMaxTextMessageBufferSize();
}
public void setMaxBinaryMessageBufferSize(int bufferSize) {
this.container.setDefaultMaxBinaryMessageBufferSize(bufferSize);
}
public int getMaxBinaryMessageBufferSize() {
return this.container.getDefaultMaxBinaryMessageBufferSize();
}
@Override
public void afterPropertiesSet() throws Exception {
this.container = getContainer();
}
protected abstract WebSocketContainer getContainer();
@Override
public WebSocketContainer getObject() throws Exception {
return this.container;
}
@Override
public Class<?> getObjectType() {
return WebSocketContainer.class;
}
@Override
public boolean isSingleton() {
return true;
}
}

View File

@@ -22,6 +22,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.endpoint.WebSocketHandlerEndpoint;
import org.springframework.websocket.server.RequestUpgradeStrategy;
@@ -41,12 +42,12 @@ public abstract class AbstractEndpointUpgradeStrategy implements RequestUpgradeS
@Override
public void upgrade(ServerHttpRequest request, ServerHttpResponse response,
String protocol, WebSocketHandler webSocketHandler) throws Exception {
String protocol, HandlerProvider<WebSocketHandler> handler) throws Exception {
upgradeInternal(request, response, protocol, adaptWebSocketHandler(webSocketHandler));
upgradeInternal(request, response, protocol, adaptWebSocketHandler(handler));
}
protected Endpoint adaptWebSocketHandler(WebSocketHandler handler) {
protected Endpoint adaptWebSocketHandler(HandlerProvider<WebSocketHandler> handler) {
return new WebSocketHandlerEndpoint(handler);
}

View File

@@ -22,9 +22,6 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
@@ -36,6 +33,7 @@ import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.server.DefaultHandshakeHandler;
import org.springframework.websocket.server.HandshakeHandler;
import org.springframework.websocket.support.SimpleHandlerProvider;
/**
@@ -44,7 +42,7 @@ import org.springframework.websocket.server.HandshakeHandler;
* @author Rossen Stoyanchev
* @since 4.0
*/
public class WebSocketHttpRequestHandler implements HttpRequestHandler, BeanFactoryAware {
public class WebSocketHttpRequestHandler implements HttpRequestHandler {
private HandshakeHandler handshakeHandler;
@@ -53,13 +51,13 @@ public class WebSocketHttpRequestHandler implements HttpRequestHandler, BeanFact
public WebSocketHttpRequestHandler(WebSocketHandler webSocketHandler) {
Assert.notNull(webSocketHandler, "webSocketHandler is required");
this.handlerProvider = new HandlerProvider<WebSocketHandler>(webSocketHandler);
this.handlerProvider = new SimpleHandlerProvider<WebSocketHandler>(webSocketHandler);
this.handshakeHandler = new DefaultHandshakeHandler();
}
public WebSocketHttpRequestHandler( Class<? extends WebSocketHandler> webSocketHandlerClass) {
Assert.notNull(webSocketHandlerClass, "webSocketHandlerClass is required");
this.handlerProvider = new HandlerProvider<WebSocketHandler>(webSocketHandlerClass);
public WebSocketHttpRequestHandler( HandlerProvider<WebSocketHandler> handlerProvider) {
Assert.notNull(handlerProvider, "handlerProvider is required");
this.handlerProvider = handlerProvider;
}
public void setHandshakeHandler(HandshakeHandler handshakeHandler) {
@@ -67,11 +65,6 @@ public class WebSocketHttpRequestHandler implements HttpRequestHandler, BeanFact
this.handshakeHandler = handshakeHandler;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.handlerProvider.setBeanFactory(beanFactory);
}
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
@@ -80,8 +73,7 @@ public class WebSocketHttpRequestHandler implements HttpRequestHandler, BeanFact
ServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
try {
WebSocketHandler webSocketHandler = this.handlerProvider.getHandler();
this.handshakeHandler.doHandshake(httpRequest, httpResponse, webSocketHandler);
this.handshakeHandler.doHandshake(httpRequest, httpResponse, this.handlerProvider);
}
catch (Exception e) {
// TODO

View File

@@ -0,0 +1,94 @@
/*
* 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.websocket.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
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.util.Assert;
import org.springframework.websocket.HandlerProvider;
/**
* A {@link HandlerProvider} that uses {@link AutowireCapableBeanFactory#createBean(Class)
* creating a fresh instance every time #getHandler() is called.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class BeanCreatingHandlerProvider<T> implements HandlerProvider<T>, BeanFactoryAware {
private static final Log logger = LogFactory.getLog(BeanCreatingHandlerProvider.class);
private final Class<? extends T> handlerClass;
private AutowireCapableBeanFactory beanFactory;
public BeanCreatingHandlerProvider(Class<? extends T> handlerClass) {
Assert.notNull(handlerClass, "handlerClass is required");
this.handlerClass = handlerClass;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof AutowireCapableBeanFactory) {
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
}
}
public boolean isSingleton() {
return false;
}
public Class<? extends T> getHandlerType() {
return this.handlerClass;
}
public T getHandler() {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance for handler type " + this.handlerClass);
}
if (this.beanFactory == null) {
logger.warn("No BeanFactory available, attempting to use default constructor");
return BeanUtils.instantiate(this.handlerClass);
}
else {
return this.beanFactory.createBean(this.handlerClass);
}
}
@Override
public void destroy(T handler) {
if (this.beanFactory != null) {
if (logger.isTraceEnabled()) {
logger.trace("Destroying handler instance " + handler);
}
this.beanFactory.destroyBean(handler);
}
}
@Override
public String toString() {
return "BeanCreatingHandlerProvider [handlerClass=" + handlerClass + "]";
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.websocket.support;
import org.springframework.util.ClassUtils;
import org.springframework.websocket.HandlerProvider;
/**
* A {@link HandlerProvider} that returns a singleton instance.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class SimpleHandlerProvider<T> implements HandlerProvider<T> {
private final T handler;
public SimpleHandlerProvider(T handler) {
this.handler = handler;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public Class<?> getHandlerType() {
return ClassUtils.getUserClass(this.handler);
}
@Override
public T getHandler() {
return this.handler;
}
@Override
public void destroy(T handler) {
}
@Override
public String toString() {
return "SimpleHandlerProvider [handler=" + handler + "]";
}
}