Refactor SockJS and WebSocket layer configuration

Add HandlerProvider<T> class
Modify HandshakeHandler to accept + adapt WebSocketHandler at runtime
Modify SockJsService to accept + adapt SockJsHandler at runtime
This commit is contained in:
Rossen Stoyanchev 2013-04-12 09:34:18 -04:00
parent 71e82069ce
commit 177e082172
48 changed files with 919 additions and 829 deletions

View File

@ -24,6 +24,6 @@ package org.springframework.sockjs;
*/
public interface SockJsSessionFactory<S extends SockJsSession>{
S createSession(String sessionId);
S createSession(String sessionId, SockJsHandler sockJsHandler);
}

View File

@ -49,7 +49,7 @@ public abstract class SockJsSessionSupport implements SockJsSession {
*/
public SockJsSessionSupport(String sessionId, SockJsHandler sockJsHandler) {
Assert.notNull(sessionId, "sessionId is required");
Assert.notNull(sockJsHandler, "SockJsHandler is required");
Assert.notNull(sockJsHandler, "sockJsHandler is required");
this.sessionId = sessionId;
this.sockJsHandler = sockJsHandler;
}
@ -58,10 +58,6 @@ public abstract class SockJsSessionSupport implements SockJsSession {
return this.sessionId;
}
public SockJsHandler getSockJsHandler() {
return this.sockJsHandler;
}
public boolean isNew() {
return State.NEW.equals(this.state);
}

View File

@ -42,16 +42,11 @@ public abstract class AbstractServerSession extends SockJsSessionSupport {
private ScheduledFuture<?> heartbeatTask;
public AbstractServerSession(String sessionId, SockJsConfiguration sockJsConfig) {
super(sessionId, getSockJsHandler(sockJsConfig));
public AbstractServerSession(String sessionId, SockJsConfiguration sockJsConfig, SockJsHandler sockJsHandler) {
super(sessionId, sockJsHandler);
this.sockJsConfig = sockJsConfig;
}
private static SockJsHandler getSockJsHandler(SockJsConfiguration sockJsConfig) {
Assert.notNull(sockJsConfig, "sockJsConfig is required");
return sockJsConfig.getSockJsHandler();
}
protected SockJsConfiguration getSockJsConfig() {
return this.sockJsConfig;
}

View File

@ -32,11 +32,13 @@ import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.sockjs.SockJsHandler;
import org.springframework.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.web.util.UriUtils;
/**
@ -45,7 +47,7 @@ import org.springframework.util.StringUtils;
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class AbstractSockJsService implements SockJsConfiguration {
public abstract class AbstractSockJsService implements SockJsService, SockJsConfiguration {
protected final Log logger = LogFactory.getLog(getClass());
@ -169,10 +171,20 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
this.heartbeatScheduler = heartbeatScheduler;
}
/**
* The amount of time in milliseconds before a client is considered
* disconnected after not having a receiving connection, i.e. an active
* connection over which the server can send data to the client.
* <p>
* The default value is 5000.
*/
public void setDisconnectDelay(long disconnectDelay) {
this.disconnectDelay = disconnectDelay;
}
/**
* Return the amount of time in milliseconds before a client is considered disconnected.
*/
public long getDisconnectDelay() {
return this.disconnectDelay;
}
@ -191,7 +203,7 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
* Whether WebSocket transport is enabled.
* @see #setWebSocketsEnabled(boolean)
*/
public boolean isWebSocketsEnabled() {
public boolean isWebSocketEnabled() {
return this.webSocketsEnabled;
}
@ -205,8 +217,8 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
*
* @throws Exception
*/
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response, String sockJsPath)
throws Exception {
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
String sockJsPath, SockJsHandler sockJsHandler) throws Exception {
logger.debug(request.getMethod() + " [" + sockJsPath + "]");
@ -217,6 +229,10 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
// Ignore invalid Content-Type (TODO)
}
String path = UriUtils.decode(request.getURI().getPath(), "URF-8");
int index = path.indexOf(this.prefix);
sockJsPath = path.substring(index + this.prefix.length());
try {
if (sockJsPath.equals("") || sockJsPath.equals("/")) {
response.getHeaders().setContentType(new MediaType("text", "plain", Charset.forName("UTF-8")));
@ -232,7 +248,7 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
return;
}
else if (sockJsPath.equals("/websocket")) {
handleRawWebSocket(request, response);
handleRawWebSocketRequest(request, response, sockJsHandler);
return;
}
@ -252,18 +268,19 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
return;
}
handleTransportRequest(request, response, sessionId, TransportType.fromValue(transport));
handleTransportRequest(request, response, sessionId, TransportType.fromValue(transport), sockJsHandler);
}
finally {
response.flush();
}
}
protected abstract void handleRawWebSocket(ServerHttpRequest request, ServerHttpResponse response)
throws Exception;
protected abstract void handleRawWebSocketRequest(ServerHttpRequest request, ServerHttpResponse response,
SockJsHandler sockJsHandler) throws Exception;
protected abstract void handleTransportRequest(ServerHttpRequest request, ServerHttpResponse response,
String sessionId, TransportType transportType) throws Exception;
String sessionId, TransportType transportType, SockJsHandler sockJsHandler) throws Exception;
protected boolean validateRequest(String serverId, String sessionId, String transport) {
@ -278,7 +295,7 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
return false;
}
if (!isWebSocketsEnabled() && transport.equals(TransportType.WEBSOCKET.value())) {
if (!isWebSocketEnabled() && transport.equals(TransportType.WEBSOCKET.value())) {
logger.debug("Websocket transport is disabled");
return false;
}
@ -344,7 +361,7 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
addCorsHeaders(request, response);
addNoCacheHeaders(response);
String content = String.format(INFO_CONTENT, random.nextInt(), isJsessionIdCookieRequired(), isWebSocketsEnabled());
String content = String.format(INFO_CONTENT, random.nextInt(), isJsessionIdCookieRequired(), isWebSocketEnabled());
response.getBody().write(content.getBytes());
}
else if (HttpMethod.OPTIONS.equals(request.getMethod())) {

View File

@ -13,16 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.sockjs.server;
import java.util.Collection;
import org.springframework.sockjs.SockJsHandler;
import org.springframework.websocket.WebSocketHandler;
/**
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface TransportHandlerRegistrar {
public interface ConfigurableTransportHandler extends TransportHandler {
void registerTransportHandlers(TransportHandlerRegistry registry, SockJsConfiguration config);
void setSockJsConfiguration(SockJsConfiguration sockJsConfig);
/**
* Pre-register {@link SockJsHandler} instances so they can be adapted to
* {@link WebSocketHandler} and hence re-used at runtime.
*/
void registerSockJsHandlers(Collection<SockJsHandler> sockJsHandlers);
}

View File

@ -17,18 +17,14 @@ package org.springframework.sockjs.server;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.sockjs.SockJsHandler;
/**
*
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface SockJsConfiguration {
/**
* Streaming transports save responses on the client side and don't free
* memory used by delivered messages. Such transports need to recycle the
@ -42,15 +38,6 @@ public interface SockJsConfiguration {
*/
public int getStreamBytesLimit();
/**
* The amount of time in milliseconds before a client is considered
* disconnected after not having a receiving connection, i.e. an active
* connection over which the server can send data to the client.
* <p>
* The default value is 5000.
*/
public long getDisconnectDelay();
/**
* The amount of time in milliseconds when the server has not sent any
* messages and after which the server should send a heartbeat frame to the
@ -67,11 +54,4 @@ 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();
}

View File

@ -14,29 +14,34 @@
* limitations under the License.
*/
package org.springframework.websocket.server.endpoint.handshake;
package org.springframework.sockjs.server;
import javax.websocket.Endpoint;
import java.util.Collection;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.sockjs.SockJsHandler;
import org.springframework.websocket.WebSocketHandler;
/**
* A strategy for performing the container-specific steps for upgrading an HTTP request
* as part of a WebSocket handshake.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface RequestUpgradeStrategy {
public interface SockJsService {
String[] getSupportedVersions();
String getPrefix();
/**
* Invoked after the handshake checks have been performed and succeeded.
* Pre-register {@link SockJsHandler} instances so they can be adapted to
* {@link WebSocketHandler} and hence re-used at runtime when
* {@link #handleRequest(ServerHttpRequest, ServerHttpResponse, String, SockJsHandler) handleRequest}
* is called.
*/
void upgrade(ServerHttpRequest request, ServerHttpResponse response, String protocol, Endpoint endpoint)
throws Exception;
void registerSockJsHandlers(Collection<SockJsHandler> sockJsHandlers);
void handleRequest(ServerHttpRequest request, ServerHttpResponse response, String sockJsPath,
SockJsHandler handler) throws Exception;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the toriginal author or authors.
* 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.
@ -17,6 +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;
@ -29,7 +30,7 @@ public interface TransportHandler {
TransportType getTransportType();
void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session)
throws Exception;
void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
SockJsHandler sockJsHandler, SockJsSessionSupport session) throws Exception;
}

View File

@ -1,28 +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;
/**
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface TransportHandlerRegistry {
void registerHandler(TransportHandler handler);
}

View File

@ -16,16 +16,13 @@
package org.springframework.sockjs.server.support;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
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;
@ -37,11 +34,21 @@ import org.springframework.sockjs.SockJsHandler;
import org.springframework.sockjs.SockJsSessionFactory;
import org.springframework.sockjs.SockJsSessionSupport;
import org.springframework.sockjs.server.AbstractSockJsService;
import org.springframework.sockjs.server.ConfigurableTransportHandler;
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.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.WebSocketSockJsHandlerAdapter;
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.Assert;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.server.DefaultHandshakeHandler;
import org.springframework.websocket.server.HandshakeHandler;
@ -51,37 +58,22 @@ import org.springframework.websocket.server.HandshakeHandler;
* @author Rossen Stoyanchev
* @since 4.0
*/
public class DefaultSockJsService extends AbstractSockJsService
implements TransportHandlerRegistry, BeanFactoryAware, InitializingBean {
public class DefaultSockJsService extends AbstractSockJsService implements InitializingBean {
private final Class<? extends SockJsHandler> sockJsHandlerClass;
private final Map<TransportType, TransportHandler> transportHandlers = new HashMap<TransportType, TransportHandler>();
private final SockJsHandler sockJsHandler;
private final Map<TransportType, TransportHandler> transportHandlerOverrides = new HashMap<TransportType, TransportHandler>();
private TaskScheduler sessionTimeoutScheduler;
private final Map<String, SockJsSessionSupport> sessions = new ConcurrentHashMap<String, SockJsSessionSupport>();
private final Map<TransportType, TransportHandler> transportHandlers = new HashMap<TransportType, TransportHandler>();
private AutowireCapableBeanFactory beanFactory;
private final Map<SockJsHandler, WebSocketHandler> sockJsHandlers = new HashMap<SockJsHandler, WebSocketHandler>();
public DefaultSockJsService(String prefix, Class<? extends SockJsHandler> sockJsHandlerClass) {
this(prefix, sockJsHandlerClass, null);
}
public DefaultSockJsService(String prefix, SockJsHandler sockJsHandler) {
this(prefix, null, sockJsHandler);
}
private DefaultSockJsService(String prefix, Class<? extends SockJsHandler> handlerClass, SockJsHandler handler) {
public DefaultSockJsService(String prefix) {
super(prefix);
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, this);
}
/**
@ -98,42 +90,61 @@ public class DefaultSockJsService extends AbstractSockJsService
this.sessionTimeoutScheduler = sessionTimeoutScheduler;
}
@Override
public void registerHandler(TransportHandler transportHandler) {
Assert.notNull(transportHandler, "transportHandler is required");
this.transportHandlers.put(transportHandler.getTransportType(), transportHandler);
}
public void setTransportHandlerRegistrar(TransportHandlerRegistrar registrar) {
Assert.notNull(registrar, "registrar is required");
public void setTransportHandlers(TransportHandler... handlers) {
this.transportHandlers.clear();
registrar.registerTransportHandlers(this, this);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof AutowireCapableBeanFactory) {
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
for (TransportHandler handler : handlers) {
this.transportHandlers.put(handler.getTransportType(), handler);
}
}
@Override
public SockJsHandler getSockJsHandler() {
return (this.sockJsHandlerClass != null) ?
this.beanFactory.createBean(this.sockJsHandlerClass) : this.sockJsHandler;
public void setTransportHandlerOverrides(TransportHandler... handlers) {
this.transportHandlerOverrides.clear();
for (TransportHandler handler : handlers) {
this.transportHandlerOverrides.put(handler.getTransportType(), handler);
}
}
public void registerSockJsHandlers(Collection<SockJsHandler> sockJsHandlers) {
for (SockJsHandler sockJsHandler : sockJsHandlers) {
if (!this.sockJsHandlers.containsKey(sockJsHandler)) {
this.sockJsHandlers.put(sockJsHandler, adaptSockJsHandler(sockJsHandler));
}
}
configureTransportHandlers();
}
/**
* Adapt the {@link SockJsHandler} to the {@link WebSocketHandler} contract for
* <em>raw WebSocket</em> communication on SockJS path "/websocket".
*/
protected WebSocketSockJsHandlerAdapter adaptSockJsHandler(SockJsHandler sockJsHandler) {
return new WebSocketSockJsHandlerAdapter(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.isEmpty()) {
if (isWebSocketEnabled() && (this.transportHandlerOverrides.get(TransportType.WEBSOCKET) == null)) {
this.transportHandlers.put(TransportType.WEBSOCKET,
new WebSocketTransportHandler(new DefaultHandshakeHandler()));
}
this.transportHandlers.put(TransportType.XHR, new XhrPollingTransportHandler());
this.transportHandlers.put(TransportType.XHR_SEND, new XhrTransportHandler());
this.transportHandlers.put(TransportType.JSONP, new JsonpPollingTransportHandler());
this.transportHandlers.put(TransportType.JSONP_SEND, new JsonpTransportHandler());
this.transportHandlers.put(TransportType.XHR_STREAMING, new XhrStreamingTransportHandler());
this.transportHandlers.put(TransportType.EVENT_SOURCE, new EventSourceTransportHandler());
this.transportHandlers.put(TransportType.HTML_FILE, new HtmlFileTransportHandler());
}
if (this.transportHandlers.get(TransportType.WEBSOCKET) == null) {
logger.warn("No WebSocket transport handler was registered");
if (!this.transportHandlerOverrides.isEmpty()) {
for (TransportHandler transportHandler : this.transportHandlerOverrides.values()) {
this.transportHandlers.put(transportHandler.getTransportType(), transportHandler);
}
}
configureTransportHandlers();
this.sessionTimeoutScheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
@ -162,22 +173,45 @@ public class DefaultSockJsService extends AbstractSockJsService
}, 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);
private void configureTransportHandlers() {
for (TransportHandler h : this.transportHandlers.values()) {
if (h instanceof ConfigurableTransportHandler) {
((ConfigurableTransportHandler) h).setSockJsConfiguration(this);
if (!this.sockJsHandlers.isEmpty()) {
((ConfigurableTransportHandler) h).registerSockJsHandlers(this.sockJsHandlers.keySet());
if (h instanceof HandshakeHandler) {
((HandshakeHandler) h).registerWebSocketHandlers(this.sockJsHandlers.values());
}
}
}
else {
logger.debug("No handler found for raw WebSocket messages");
response.setStatusCode(HttpStatus.NOT_FOUND);
}
}
@Override
protected void handleRawWebSocketRequest(ServerHttpRequest request, ServerHttpResponse response,
SockJsHandler sockJsHandler) throws Exception {
if (isWebSocketEnabled()) {
TransportHandler transportHandler = this.transportHandlers.get(TransportType.WEBSOCKET);
if (transportHandler != null) {
if (transportHandler instanceof HandshakeHandler) {
WebSocketHandler webSocketHandler = this.sockJsHandlers.get(sockJsHandler);
if (webSocketHandler == null) {
webSocketHandler = adaptSockJsHandler(sockJsHandler);
}
((HandshakeHandler) transportHandler).doHandshake(request, response, webSocketHandler);
return;
}
}
logger.warn("No handler for raw WebSocket messages");
}
response.setStatusCode(HttpStatus.NOT_FOUND);
}
@Override
protected void handleTransportRequest(ServerHttpRequest request, ServerHttpResponse response,
String sessionId, TransportType transportType) throws Exception {
String sessionId, TransportType transportType, SockJsHandler sockJsHandler) throws Exception {
TransportHandler transportHandler = this.transportHandlers.get(transportType);
@ -204,7 +238,7 @@ public class DefaultSockJsService extends AbstractSockJsService
return;
}
SockJsSessionSupport session = getSockJsSession(sessionId, transportHandler);
SockJsSessionSupport session = getSockJsSession(sessionId, sockJsHandler, transportHandler);
if (session != null) {
if (transportType.setsNoCacheHeader()) {
@ -215,7 +249,7 @@ public class DefaultSockJsService extends AbstractSockJsService
Cookie cookie = request.getCookies().getCookie("JSESSIONID");
String jsid = (cookie != null) ? cookie.getValue() : "dummy";
// TODO: bypass use of Cookie object (causes Jetty to set Expires header)
response.getHeaders().set("Set-Cookie", "JSESSIONID=" + jsid + ";path=/"); // TODO
response.getHeaders().set("Set-Cookie", "JSESSIONID=" + jsid + ";path=/");
}
if (transportType.supportsCors()) {
@ -223,10 +257,11 @@ public class DefaultSockJsService extends AbstractSockJsService
}
}
transportHandler.handleRequest(request, response, session);
transportHandler.handleRequest(request, response, sockJsHandler, session);
}
public SockJsSessionSupport getSockJsSession(String sessionId, TransportHandler transportHandler) {
public SockJsSessionSupport getSockJsSession(String sessionId, SockJsHandler sockJsHandler,
TransportHandler transportHandler) {
SockJsSessionSupport session = this.sessions.get(sessionId);
if (session != null) {
@ -242,7 +277,7 @@ public class DefaultSockJsService extends AbstractSockJsService
return session;
}
logger.debug("Creating new session with session id \"" + sessionId + "\"");
session = (SockJsSessionSupport) sessionFactory.createSession(sessionId);
session = (SockJsSessionSupport) sessionFactory.createSession(sessionId, sockJsHandler);
this.sessions.put(sessionId, session);
return session;
}

View File

@ -1,77 +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.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.XhrPollingTransportHandler;
import org.springframework.sockjs.server.transport.XhrStreamingTransportHandler;
import org.springframework.sockjs.server.transport.XhrTransportHandler;
import org.springframework.util.ClassUtils;
/**
* TODO
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class DefaultTransportHandlerRegistrar implements TransportHandlerRegistrar {
private static final boolean standardWebSocketApiPresent = ClassUtils.isPresent(
"javax.websocket.server.ServerEndpointConfig", DefaultTransportHandlerRegistrar.class.getClassLoader());
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(config));
registry.registerHandler(new JsonpTransportHandler());
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);
}
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.sockjs.server.support;
import java.io.IOException;
import java.util.Collections;
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;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.sockjs.SockJsHandler;
import org.springframework.sockjs.server.SockJsService;
import org.springframework.util.Assert;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.util.NestedServletException;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.websocket.HandlerProvider;
/**
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class SockJsHttpRequestHandler implements HttpRequestHandler, BeanFactoryAware {
private final SockJsService sockJsService;
private final HandlerProvider<SockJsHandler> handlerProvider;
private final UrlPathHelper urlPathHelper = new UrlPathHelper();
public SockJsHttpRequestHandler(SockJsService sockJsService, SockJsHandler sockJsHandler) {
Assert.notNull(sockJsService, "sockJsService is required");
Assert.notNull(sockJsHandler, "sockJsHandler is required");
this.sockJsService = sockJsService;
this.sockJsService.registerSockJsHandlers(Collections.singleton(sockJsHandler));
this.handlerProvider = new HandlerProvider<SockJsHandler>(sockJsHandler);
}
public SockJsHttpRequestHandler(SockJsService sockJsService, Class<? extends SockJsHandler> sockJsHandlerClass) {
Assert.notNull(sockJsService, "sockJsService is required");
Assert.notNull(sockJsHandlerClass, "sockJsHandlerClass is required");
this.sockJsService = sockJsService;
this.handlerProvider = new HandlerProvider<SockJsHandler>(sockJsHandlerClass);
}
public String getMappingPattern() {
return this.sockJsService.getPrefix() + "/**";
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.handlerProvider.setBeanFactory(beanFactory);
}
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
String prefix = this.sockJsService.getPrefix();
Assert.isTrue(lookupPath.startsWith(prefix),
"Request path does not match the prefix of the SockJsService " + prefix);
String sockJsPath = lookupPath.substring(prefix.length());
ServerHttpRequest httpRequest = new AsyncServletServerHttpRequest(request, response);
ServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
try {
SockJsHandler sockJsHandler = this.handlerProvider.getHandler();
this.sockJsService.handleRequest(httpRequest, httpResponse, sockJsPath, sockJsHandler);
}
catch (Exception ex) {
// TODO
throw new NestedServletException("SockJS service failure", ex);
}
}
}

View File

@ -1,113 +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.support;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.server.AsyncServletServerHttpRequest;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.sockjs.server.AbstractSockJsService;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.util.NestedServletException;
/**
* A Spring MVC HandlerMapping for matching requests to a SockJS services based on the
* {@link AbstractSockJsService#getPrefix() prefix} property of each service.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class SockJsServiceHandlerMapping extends AbstractHandlerMapping {
private static Log logger = LogFactory.getLog(SockJsServiceHandlerMapping.class);
private final List<AbstractSockJsService> sockJsServices;
public SockJsServiceHandlerMapping(AbstractSockJsService... sockJsServices) {
this.sockJsServices = Arrays.asList(sockJsServices);
}
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking for SockJS service match to path " + lookupPath);
}
for (AbstractSockJsService service : this.sockJsServices) {
if (lookupPath.startsWith(service.getPrefix())) {
if (logger.isDebugEnabled()) {
logger.debug("Matched to " + service);
}
String sockJsPath = lookupPath.substring(service.getPrefix().length());
return new SockJsServiceHttpRequestHandler(service, sockJsPath);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Did not find a match");
}
return null;
}
/**
* {@link HttpRequestHandler} wrapping the invocation of the selected SockJS service.
*/
private static class SockJsServiceHttpRequestHandler implements HttpRequestHandler {
private final String sockJsPath;
private final AbstractSockJsService sockJsService;
public SockJsServiceHttpRequestHandler(AbstractSockJsService sockJsService, String sockJsPath) {
this.sockJsService = sockJsService;
this.sockJsPath = sockJsPath;
}
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServerHttpRequest httpRequest = new AsyncServletServerHttpRequest(request, response);
ServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
try {
this.sockJsService.handleRequest(httpRequest, httpResponse, this.sockJsPath);
}
catch (Exception ex) {
// TODO
throw new NestedServletException("SockJS service failure", ex);
}
}
}
}

View File

@ -25,6 +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.TransportHandler;
@ -51,8 +52,8 @@ public abstract class AbstractHttpReceivingTransportHandler implements Transport
}
@Override
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session)
throws Exception {
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
SockJsHandler sockJsHandler, SockJsSessionSupport session) throws Exception {
if (session == null) {
response.setStatusCode(HttpStatus.NOT_FOUND);

View File

@ -16,18 +16,20 @@
package org.springframework.sockjs.server.transport;
import java.io.IOException;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.SockJsSessionFactory;
import org.springframework.sockjs.SockJsSessionSupport;
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.sockjs.server.TransportHandler;
/**
* TODO
@ -36,24 +38,30 @@ import org.springframework.sockjs.server.TransportHandler;
* @since 4.0
*/
public abstract class AbstractHttpSendingTransportHandler
implements TransportHandler, SockJsSessionFactory<SockJsSessionSupport> {
implements ConfigurableTransportHandler, SockJsSessionFactory<SockJsSessionSupport> {
protected final Log logger = LogFactory.getLog(this.getClass());
private final SockJsConfiguration sockJsConfig;
private SockJsConfiguration sockJsConfig;
public AbstractHttpSendingTransportHandler(SockJsConfiguration sockJsConfig) {
@Override
public void setSockJsConfiguration(SockJsConfiguration sockJsConfig) {
this.sockJsConfig = sockJsConfig;
}
protected SockJsConfiguration getSockJsConfig() {
@Override
public void registerSockJsHandlers(Collection<SockJsHandler> sockJsHandlers) {
// ignore
}
public SockJsConfiguration getSockJsConfig() {
return this.sockJsConfig;
}
@Override
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
SockJsSessionSupport session) throws Exception {
SockJsHandler sockJsHandler, SockJsSessionSupport session) throws Exception {
// Set content type before writing
response.getHeaders().setContentType(getContentType());

View File

@ -22,6 +22,7 @@ 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;
@ -46,8 +47,8 @@ public abstract class AbstractHttpServerSession extends AbstractServerSession {
private ServerHttpResponse response;
public AbstractHttpServerSession(String sessionId, SockJsConfiguration sockJsConfig) {
super(sessionId, sockJsConfig);
public AbstractHttpServerSession(String sessionId, SockJsConfiguration sockJsConfig, SockJsHandler sockJsHandler) {
super(sessionId, sockJsConfig, sockJsHandler);
}
public void setFrameFormat(FrameFormat frameFormat) {

View File

@ -22,8 +22,10 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.sockjs.SockJsHandler;
import org.springframework.sockjs.SockJsSessionSupport;
import org.springframework.sockjs.server.SockJsConfiguration;
import org.springframework.util.Assert;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession;
@ -39,18 +41,27 @@ public abstract class AbstractSockJsWebSocketHandler implements WebSocketHandler
private final SockJsConfiguration sockJsConfig;
private final SockJsHandler sockJsHandler;
private final Map<WebSocketSession, SockJsSessionSupport> sessions =
new ConcurrentHashMap<WebSocketSession, SockJsSessionSupport>();
public AbstractSockJsWebSocketHandler(SockJsConfiguration sockJsConfig) {
public AbstractSockJsWebSocketHandler(SockJsConfiguration sockJsConfig, SockJsHandler sockJsHandler) {
Assert.notNull(sockJsConfig, "sockJsConfig is required");
Assert.notNull(sockJsHandler, "sockJsHandler is required");
this.sockJsConfig = sockJsConfig;
this.sockJsHandler = sockJsHandler;
}
protected SockJsConfiguration getSockJsConfig() {
return this.sockJsConfig;
}
protected SockJsHandler getSockJsHandler() {
return this.sockJsHandler;
}
protected SockJsSessionSupport getSockJsSession(WebSocketSession wsSession) {
return this.sessions.get(wsSession);
}
@ -62,7 +73,6 @@ public abstract class AbstractSockJsWebSocketHandler implements WebSocketHandler
}
SockJsSessionSupport session = createSockJsSession(wsSession);
this.sessions.put(wsSession, session);
session.connectionInitialized();
}
protected abstract SockJsSessionSupport createSockJsSession(WebSocketSession wsSession) throws Exception;

View File

@ -19,7 +19,8 @@ import java.io.IOException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.sockjs.server.SockJsConfiguration;
import org.springframework.sockjs.SockJsHandler;
import org.springframework.util.Assert;
/**
@ -31,13 +32,10 @@ import org.springframework.sockjs.server.SockJsConfiguration;
public abstract class AbstractStreamingTransportHandler extends AbstractHttpSendingTransportHandler {
public AbstractStreamingTransportHandler(SockJsConfiguration sockJsConfig) {
super(sockJsConfig);
}
@Override
public StreamingHttpServerSession createSession(String sessionId) {
return new StreamingHttpServerSession(sessionId, getSockJsConfig());
public StreamingHttpServerSession createSession(String sessionId, SockJsHandler sockJsHandler) {
Assert.notNull(getSockJsConfig(), "This transport requires SockJsConfiguration");
return new StreamingHttpServerSession(sessionId, getSockJsConfig(), sockJsHandler);
}
@Override

View File

@ -1,65 +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.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 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);
}
}

View File

@ -1,42 +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.sockjs.server.SockJsConfiguration;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.server.HandshakeHandler;
import org.springframework.websocket.server.endpoint.handshake.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);
}
}

View File

@ -21,10 +21,9 @@ 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.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.sockjs.server.TransportType;
/**
@ -36,10 +35,6 @@ 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;

View File

@ -22,10 +22,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.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.sockjs.server.TransportType;
import org.springframework.util.StringUtils;
import org.springframework.web.util.JavaScriptUtils;
@ -67,10 +66,6 @@ public class HtmlFileTransportHandler extends AbstractStreamingTransportHandler
}
public HtmlFileTransportHandler(SockJsConfiguration sockJsConfig) {
super(sockJsConfig);
}
@Override
public TransportType getTransportType() {
return TransportType.HTML_FILE;

View File

@ -21,10 +21,11 @@ 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.server.SockJsConfiguration;
import org.springframework.sockjs.SockJsHandler;
import org.springframework.sockjs.server.SockJsFrame;
import org.springframework.sockjs.server.TransportType;
import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
import org.springframework.sockjs.server.TransportType;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.JavaScriptUtils;
@ -38,10 +39,6 @@ import org.springframework.web.util.JavaScriptUtils;
public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHandler {
public JsonpPollingTransportHandler(SockJsConfiguration sockJsConfig) {
super(sockJsConfig);
}
@Override
public TransportType getTransportType() {
return TransportType.JSONP;
@ -53,8 +50,9 @@ public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHa
}
@Override
public PollingHttpServerSession createSession(String sessionId) {
return new PollingHttpServerSession(sessionId, getSockJsConfig());
public PollingHttpServerSession createSession(String sessionId, SockJsHandler sockJsHandler) {
Assert.notNull(getSockJsConfig(), "This transport requires SockJsConfiguration");
return new PollingHttpServerSession(sessionId, getSockJsConfig(), sockJsHandler);
}
@Override
@ -67,7 +65,7 @@ public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHa
response.getBody().write("\"callback\" parameter required".getBytes("UTF-8"));
return;
}
super.handleRequest(request, response, session);
super.handleRequestInternal(request, response, session);
}
@Override

View File

@ -44,7 +44,7 @@ public class JsonpTransportHandler extends AbstractHttpReceivingTransportHandler
}
}
super.handleRequest(request, response, sockJsSession);
super.handleRequestInternal(request, response, sockJsSession);
response.getBody().write("ok".getBytes("UTF-8"));
}

View File

@ -17,14 +17,15 @@ package org.springframework.sockjs.server.transport;
import java.io.IOException;
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, SockJsConfiguration sockJsConfig) {
super(sessionId, sockJsConfig);
public PollingHttpServerSession(String sessionId, SockJsConfiguration sockJsConfig, SockJsHandler sockJsHandler) {
super(sessionId, sockJsConfig, sockJsHandler);
}
@Override

View File

@ -43,8 +43,8 @@ public class SockJsWebSocketHandler extends AbstractSockJsWebSocketHandler {
private final ObjectMapper objectMapper = new ObjectMapper();
public SockJsWebSocketHandler(SockJsConfiguration config) {
super(config);
public SockJsWebSocketHandler(SockJsConfiguration sockJsConfig, SockJsHandler sockJsHandler) {
super(sockJsConfig, sockJsHandler);
}
@Override
@ -78,8 +78,8 @@ public class SockJsWebSocketHandler extends AbstractSockJsWebSocketHandler {
private WebSocketSession webSocketSession;
public WebSocketServerSession(WebSocketSession wsSession, SockJsConfiguration config) throws Exception {
super(String.valueOf(wsSession.hashCode()), config);
public WebSocketServerSession(WebSocketSession wsSession, SockJsConfiguration sockJsConfig) throws Exception {
super(String.valueOf(wsSession.hashCode()), sockJsConfig, getSockJsHandler());
this.webSocketSession = wsSession;
this.webSocketSession.sendText(SockJsFrame.openFrame().getContent());
scheduleHeartbeat();

View File

@ -18,6 +18,7 @@ package org.springframework.sockjs.server.transport;
import java.io.IOException;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.sockjs.SockJsHandler;
import org.springframework.sockjs.server.SockJsConfiguration;
import org.springframework.sockjs.server.SockJsFrame;
@ -27,8 +28,8 @@ public class StreamingHttpServerSession extends AbstractHttpServerSession {
private int byteCount;
public StreamingHttpServerSession(String sessionId, SockJsConfiguration sockJsConfig) {
super(sessionId, sockJsConfig);
public StreamingHttpServerSession(String sessionId, SockJsConfiguration sockJsConfig, SockJsHandler sockJsHandler) {
super(sessionId, sockJsConfig, sockJsHandler);
}
protected void flushCache() throws IOException {

View File

@ -36,12 +36,12 @@ import org.springframework.websocket.WebSocketSession;
public class WebSocketSockJsHandlerAdapter extends AbstractSockJsWebSocketHandler {
public WebSocketSockJsHandlerAdapter(SockJsConfiguration sockJsConfig) {
super(sockJsConfig);
public WebSocketSockJsHandlerAdapter(SockJsConfiguration sockJsConfig, SockJsHandler sockJsHandler) {
super(sockJsConfig, sockJsHandler);
}
@Override
protected SockJsSessionSupport createSockJsSession(WebSocketSession wsSession) {
protected SockJsSessionSupport createSockJsSession(WebSocketSession wsSession) throws Exception {
return new WebSocketSessionAdapter(wsSession);
}
@ -51,9 +51,10 @@ public class WebSocketSockJsHandlerAdapter extends AbstractSockJsWebSocketHandle
private final WebSocketSession wsSession;
public WebSocketSessionAdapter(WebSocketSession wsSession) {
super(String.valueOf(wsSession.hashCode()), getSockJsConfig().getSockJsHandler());
public WebSocketSessionAdapter(WebSocketSession wsSession) throws Exception {
super(String.valueOf(wsSession.hashCode()), getSockJsHandler());
this.wsSession = wsSession;
connectionInitialized();
}
@Override

View File

@ -0,0 +1,125 @@
/*
* 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.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
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.ConfigurableTransportHandler;
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.WebSocketHandler;
import org.springframework.websocket.server.HandshakeHandler;
/**
* A WebSocket {@link TransportHandler} that delegates to a {@link HandshakeHandler}
* passing a SockJS {@link WebSocketHandler}. Also implements {@link HandshakeHandler}
* directly in support for raw WebSocket communication at SockJS URL "/websocket".
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class WebSocketTransportHandler implements ConfigurableTransportHandler, HandshakeHandler {
private final HandshakeHandler handshakeHandler;
private SockJsConfiguration sockJsConfig;
private final Map<SockJsHandler, WebSocketHandler> sockJsHandlers = new HashMap<SockJsHandler, WebSocketHandler>();
private final Collection<WebSocketHandler> rawWebSocketHandlers = new ArrayList<WebSocketHandler>();
public WebSocketTransportHandler(HandshakeHandler handshakeHandler) {
Assert.notNull(handshakeHandler, "handshakeHandler is required");
this.handshakeHandler = handshakeHandler;
}
@Override
public TransportType getTransportType() {
return TransportType.WEBSOCKET;
}
@Override
public void setSockJsConfiguration(SockJsConfiguration sockJsConfig) {
this.sockJsConfig = sockJsConfig;
}
@Override
public void registerSockJsHandlers(Collection<SockJsHandler> sockJsHandlers) {
this.sockJsHandlers.clear();
for (SockJsHandler sockJsHandler : sockJsHandlers) {
this.sockJsHandlers.put(sockJsHandler, adaptSockJsHandler(sockJsHandler));
}
this.handshakeHandler.registerWebSocketHandlers(getAllWebSocketHandlers());
}
/**
* Adapt the {@link SockJsHandler} to the {@link WebSocketHandler} contract for
* exchanging SockJS message over WebSocket.
*/
protected WebSocketHandler adaptSockJsHandler(SockJsHandler sockJsHandler) {
return new SockJsWebSocketHandler(this.sockJsConfig, sockJsHandler);
}
private Collection<WebSocketHandler> getAllWebSocketHandlers() {
Set<WebSocketHandler> handlers = new HashSet<WebSocketHandler>();
handlers.addAll(this.sockJsHandlers.values());
handlers.addAll(this.rawWebSocketHandlers);
return handlers;
}
@Override
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
SockJsHandler sockJsHandler, SockJsSessionSupport session) throws Exception {
WebSocketHandler webSocketHandler = this.sockJsHandlers.get(sockJsHandler);
if (webSocketHandler == null) {
webSocketHandler = adaptSockJsHandler(sockJsHandler);
}
this.handshakeHandler.doHandshake(request, response, webSocketHandler);
}
// HandshakeHandler methods
@Override
public void registerWebSocketHandlers(Collection<WebSocketHandler> webSocketHandlers) {
this.rawWebSocketHandlers.clear();
this.rawWebSocketHandlers.addAll(webSocketHandlers);
this.handshakeHandler.registerWebSocketHandlers(getAllWebSocketHandlers());
}
@Override
public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler) throws Exception {
return this.handshakeHandler.doHandshake(request, response, webSocketHandler);
}
}

View File

@ -19,10 +19,11 @@ import java.nio.charset.Charset;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.sockjs.server.SockJsConfiguration;
import org.springframework.sockjs.server.TransportType;
import org.springframework.sockjs.SockJsHandler;
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;
/**
@ -34,10 +35,6 @@ 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;
@ -53,8 +50,9 @@ public class XhrPollingTransportHandler extends AbstractHttpSendingTransportHand
return new DefaultFrameFormat("%s\n");
}
public PollingHttpServerSession createSession(String sessionId) {
return new PollingHttpServerSession(sessionId, getSockJsConfig());
public PollingHttpServerSession createSession(String sessionId, SockJsHandler sockJsHandler) {
Assert.notNull(getSockJsConfig(), "This transport requires SockJsConfiguration");
return new PollingHttpServerSession(sessionId, getSockJsConfig(), sockJsHandler);
}
}

View File

@ -21,10 +21,9 @@ 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.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.sockjs.server.TransportType;
/**
@ -36,10 +35,6 @@ 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;

View File

@ -0,0 +1,93 @@
/*
* 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;
import org.apache.commons.logging.Log;
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;
/**
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class HandlerProvider<T> implements BeanFactoryAware {
private final T handlerBean;
private final Class<? extends T> handlerClass;
private AutowireCapableBeanFactory beanFactory;
private Log logger;
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);
}
}

View File

@ -26,13 +26,9 @@ import javax.websocket.WebSocketContainer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.util.Assert;
import org.springframework.web.util.UriComponentsBuilder;
@ -41,14 +37,10 @@ import org.springframework.web.util.UriComponentsBuilder;
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class AbstractEndpointConnectionManager implements ApplicationContextAware, SmartLifecycle {
public abstract class AbstractEndpointConnectionManager implements SmartLifecycle {
protected final Log logger = LogFactory.getLog(getClass());
private final Class<?> endpointClass;
private final Object endpointBean;
private final URI uri;
private boolean autoStartup = false;
@ -59,29 +51,13 @@ public abstract class AbstractEndpointConnectionManager implements ApplicationCo
private Session session;
private ApplicationContext applicationContext;
private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("EndpointConnectionManager-");
private final Object lifecycleMonitor = new Object();
public AbstractEndpointConnectionManager(Class<?> endpointClass, String uriTemplate, Object... uriVariables) {
Assert.notNull(endpointClass, "endpointClass is required");
this.endpointClass = endpointClass;
this.endpointBean = null;
this.uri = initUri(uriTemplate, uriVariables);
}
public AbstractEndpointConnectionManager(Object endpointBean, String uriTemplate, Object... uriVariables) {
Assert.notNull(endpointBean, "endpointBean is required");
this.endpointClass = null;
this.endpointBean = endpointBean;
this.uri = initUri(uriTemplate, uriVariables);
}
private static URI initUri(String uri, Object... uriVariables) {
return UriComponentsBuilder.fromUriString(uri).buildAndExpand(uriVariables).encode().toUri();
public AbstractEndpointConnectionManager(String uriTemplate, Object... uriVariables) {
this.uri = UriComponentsBuilder.fromUriString(uriTemplate).buildAndExpand(uriVariables).encode().toUri();
}
public void setAsyncSendTimeout(long timeoutInMillis) {
@ -137,11 +113,6 @@ public abstract class AbstractEndpointConnectionManager implements ApplicationCo
return this.phase;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
protected URI getUri() {
return this.uri;
}
@ -150,17 +121,6 @@ public abstract class AbstractEndpointConnectionManager implements ApplicationCo
return this.webSocketContainer;
}
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 {
return this.endpointBean;
}
}
/**
* Auto-connects to the configured {@link #setDefaultUri(URI) default URI}.
*/
@ -173,10 +133,10 @@ public abstract class AbstractEndpointConnectionManager implements ApplicationCo
synchronized (lifecycleMonitor) {
try {
logger.info("Connecting to endpoint at URI " + uri);
session = connect(getEndpoint());
session = connect();
logger.info("Successfully connected");
}
catch (Exception ex) {
catch (Throwable ex) {
logger.error("Failed to connect to endpoint at " + uri, ex);
}
}
@ -186,7 +146,7 @@ public abstract class AbstractEndpointConnectionManager implements ApplicationCo
}
}
protected abstract Session connect(Object endpoint) throws DeploymentException, IOException;
protected abstract Session connect() throws DeploymentException, IOException;
/**
* Deactivates the configured message endpoint.

View File

@ -21,25 +21,47 @@ import java.io.IOException;
import javax.websocket.DeploymentException;
import javax.websocket.Session;
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.websocket.HandlerProvider;
/**
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class AnnotatedEndpointConnectionManager extends AbstractEndpointConnectionManager {
public class AnnotatedEndpointConnectionManager extends AbstractEndpointConnectionManager
implements BeanFactoryAware {
private static Log logger = LogFactory.getLog(AnnotatedEndpointConnectionManager.class);
private final HandlerProvider<Object> endpointProvider;
public AnnotatedEndpointConnectionManager(Class<?> endpointClass, String uriTemplate, Object... uriVariables) {
super(endpointClass, uriTemplate, uriVariables);
super(uriTemplate, uriVariables);
this.endpointProvider = new HandlerProvider<Object>(endpointClass);
this.endpointProvider.setLogger(logger);
}
public AnnotatedEndpointConnectionManager(Object endpointBean, String uriTemplate, Object... uriVariables) {
super(endpointBean, uriTemplate, uriVariables);
super(uriTemplate, uriVariables);
this.endpointProvider = new HandlerProvider<Object>(endpointBean);
this.endpointProvider.setLogger(logger);
}
@Override
protected Session connect(Object endpoint) throws DeploymentException, IOException {
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.endpointProvider.setBeanFactory(beanFactory);
}
@Override
protected Session connect() throws DeploymentException, IOException {
Object endpoint = this.endpointProvider.getHandler();
return getWebSocketContainer().connectToServer(endpoint, getUri());
}

View File

@ -29,23 +29,41 @@ import javax.websocket.Endpoint;
import javax.websocket.Extension;
import javax.websocket.Session;
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.util.Assert;
import org.springframework.websocket.HandlerProvider;
/**
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class EndpointConnectionManager extends AbstractEndpointConnectionManager {
public class EndpointConnectionManager extends AbstractEndpointConnectionManager implements BeanFactoryAware {
private static Log logger = LogFactory.getLog(EndpointConnectionManager.class);
private final ClientEndpointConfig.Builder configBuilder = ClientEndpointConfig.Builder.create();
private final HandlerProvider<Endpoint> endpointProvider;
public EndpointConnectionManager(Class<? extends Endpoint> endpointClass, String uriTemplate, Object... uriVariables) {
super(endpointClass, uriTemplate, uriVariables);
super(uriTemplate, uriVariables);
Assert.notNull(endpointClass, "endpointClass is required");
this.endpointProvider = new HandlerProvider<Endpoint>(endpointClass);
this.endpointProvider.setLogger(logger);
}
public EndpointConnectionManager(Endpoint endpointBean, String uriTemplate, Object... uriVariables) {
super(endpointBean, uriTemplate, uriVariables);
super(uriTemplate, uriVariables);
Assert.notNull(endpointBean, "endpointBean is required");
this.endpointProvider = new HandlerProvider<Endpoint>(endpointBean);
this.endpointProvider.setLogger(logger);
}
public void setSubProtocols(String... subprotocols) {
@ -69,8 +87,13 @@ public class EndpointConnectionManager extends AbstractEndpointConnectionManager
}
@Override
protected Session connect(Object endpoint) throws DeploymentException, IOException {
Endpoint typedEndpoint = (Endpoint) endpoint;
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.endpointProvider.setBeanFactory(beanFactory);
}
@Override
protected Session connect() throws DeploymentException, IOException {
Endpoint typedEndpoint = this.endpointProvider.getHandler();
ClientEndpointConfig endpointConfig = this.configBuilder.build();
return getWebSocketContainer().connectToServer(typedEndpoint, endpointConfig, getUri());
}

View File

@ -21,6 +21,7 @@ import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -28,48 +29,53 @@ 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.beans.BeanUtils;
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.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.websocket.WebSocketHandler;
/**
* TODO
* <p>
* A container-specific {@link RequestUpgradeStrategy} is required since standard Java
* WebSocket currently does not provide a way to initiate a WebSocket handshake.
* Currently available are implementations for Tomcat and Glassfish.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class AbstractHandshakeHandler implements HandshakeHandler, BeanFactoryAware {
public class DefaultHandshakeHandler implements HandshakeHandler {
private static final String GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
protected Log logger = LogFactory.getLog(getClass());
private final Object webSocketHandler;
private final Class<?> webSocketHandlerClass;
private List<String> supportedProtocols;
private AutowireCapableBeanFactory beanFactory;
private RequestUpgradeStrategy requestUpgradeStrategy;
public AbstractHandshakeHandler(Object handler) {
Assert.notNull(handler, "webSocketHandler is required");
this.webSocketHandler = handler;
this.webSocketHandlerClass = null;
/**
* Default constructor that auto-detects and instantiates a
* {@link RequestUpgradeStrategy} suitable for the runtime container.
*
* @throws IllegalStateException if no {@link RequestUpgradeStrategy} can be found.
*/
public DefaultHandshakeHandler() {
this.requestUpgradeStrategy = new RequestUpgradeStrategyFactory().create();
}
public AbstractHandshakeHandler(Class<?> handlerClass) {
Assert.notNull((handlerClass), "handlerClass is required");
this.webSocketHandler = null;
this.webSocketHandlerClass = handlerClass;
/**
* A constructor that accepts a runtime specific {@link RequestUpgradeStrategy}.
* @param upgradeStrategy the upgrade strategy
*/
public DefaultHandshakeHandler(RequestUpgradeStrategy upgradeStrategy) {
this.requestUpgradeStrategy = upgradeStrategy;
}
public void setSupportedProtocols(String... protocols) {
@ -81,24 +87,13 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Bean
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof AutowireCapableBeanFactory) {
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
}
}
protected Object getWebSocketHandler() {
if (this.webSocketHandlerClass != null) {
Assert.notNull(this.beanFactory, "BeanFactory is required for WebSocket handler instances per request.");
return this.beanFactory.createBean(this.webSocketHandlerClass);
}
else {
return this.webSocketHandler;
}
public void registerWebSocketHandlers(Collection<WebSocketHandler> handlers) {
this.requestUpgradeStrategy.registerWebSocketHandlers(handlers);
}
@Override
public final boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response) throws Exception {
public final boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler) throws Exception {
logger.debug("Starting handshake for " + request.getURI());
@ -131,30 +126,29 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Bean
return false;
}
String protocol = selectProtocol(request.getHeaders().getSecWebSocketProtocol());
String selectedProtocol = selectProtocol(request.getHeaders().getSecWebSocketProtocol());
// TODO: select extensions
logger.debug("Upgrading HTTP request");
response.setStatusCode(HttpStatus.SWITCHING_PROTOCOLS);
response.getHeaders().setUpgrade("WebSocket");
response.getHeaders().setConnection("Upgrade");
response.getHeaders().setSecWebSocketProtocol(protocol);
response.getHeaders().setSecWebSocketProtocol(selectedProtocol);
response.getHeaders().setSecWebSocketAccept(getWebSocketKeyHash(wsKey));
// TODO: response.getHeaders().setSecWebSocketExtensions(extensions);
logger.debug("Successfully negotiated WebSocket handshake");
response.flush();
// TODO: surely there is a better way to flush headers
response.getBody();
if (logger.isTraceEnabled()) {
logger.trace("Upgrading with " + webSocketHandler);
}
doHandshakeInternal(request, response, protocol);
this.requestUpgradeStrategy.upgrade(request, response, selectedProtocol, webSocketHandler);
return true;
}
protected abstract void doHandshakeInternal(ServerHttpRequest request, ServerHttpResponse response,
String protocol) throws Exception;
protected void handleInvalidUpgradeHeader(ServerHttpRequest request, ServerHttpResponse response) throws IOException {
logger.debug("Invalid Upgrade header " + request.getHeaders().getUpgrade());
response.setStatusCode(HttpStatus.BAD_REQUEST);
@ -178,7 +172,7 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Bean
}
protected String[] getSupportedVerions() {
return new String[] { "13" };
return this.requestUpgradeStrategy.getSupportedVersions();
}
protected void handleWebSocketVersionNotSupported(ServerHttpRequest request, ServerHttpResponse response) {
@ -216,4 +210,35 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Bean
return DatatypeConverter.printBase64Binary(bytes);
}
private static class RequestUpgradeStrategyFactory {
private static final boolean tomcatWebSocketPresent = ClassUtils.isPresent(
"org.apache.tomcat.websocket.server.WsHttpUpgradeHandler", DefaultHandshakeHandler.class.getClassLoader());
private static final boolean glassfishWebSocketPresent = ClassUtils.isPresent(
"org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler", DefaultHandshakeHandler.class.getClassLoader());
private RequestUpgradeStrategy create() {
String className;
if (tomcatWebSocketPresent) {
className = "org.springframework.websocket.server.support.TomcatRequestUpgradeStrategy";
}
else if (glassfishWebSocketPresent) {
className = "org.springframework.websocket.server.support.GlassfishRequestUpgradeStrategy";
}
else {
throw new IllegalStateException("No suitable " + RequestUpgradeStrategy.class.getSimpleName());
}
try {
Class<?> clazz = ClassUtils.forName(className, DefaultHandshakeHandler.class.getClassLoader());
return (RequestUpgradeStrategy) BeanUtils.instantiateClass(clazz.getConstructor());
}
catch (Throwable t) {
throw new IllegalStateException("Failed to instantiate " + className, t);
}
}
}
}

View File

@ -16,18 +16,39 @@
package org.springframework.websocket.server;
import java.util.Collection;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.websocket.WebSocketHandler;
/**
* Abstraction for integrating a WebSocket implementation some HTTP processing pipeline.
* Contract for processing a WebSocket handshake request.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface HandshakeHandler {
boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response) throws Exception;
/**
* Pre-register {@link WebSocketHandler} instances so they can be adapted to the
* underlying runtime and hence re-used at runtime when
* {@link #doHandshake(ServerHttpRequest, ServerHttpResponse, WebSocketHandler) doHandshake}
* is called.
*/
void registerWebSocketHandlers(Collection<WebSocketHandler> webSocketHandlers);
/**
*
* @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;
}

View File

@ -0,0 +1,57 @@
/*
* 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;
import java.util.Collection;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.websocket.WebSocketHandler;
/**
* A strategy for performing container-specific steps to upgrade an HTTP request during a
* WebSocket handshake. Intended for use within {@link HandshakeHandler} implementations.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface RequestUpgradeStrategy {
/**
* Return the supported WebSocket protocol versions.
*/
String[] getSupportedVersions();
/**
* Pre-register {@link WebSocketHandler} instances so they can be adapted to the
* underlying runtime and hence re-used at runtime when
* {@link #upgrade(ServerHttpRequest, ServerHttpResponse, String, WebSocketHandler)
* upgrade} is called.
*/
void registerWebSocketHandlers(Collection<WebSocketHandler> webSocketHandlers);
/**
* Perform runtime specific steps to complete the upgrade.
* Invoked only if the handshake is successful.
*
* @param webSocketHandler the handler for WebSocket messages
*/
void upgrade(ServerHttpRequest request, ServerHttpResponse response, String selectedProtocol,
WebSocketHandler webSocketHandler) throws Exception;
}

View File

@ -35,9 +35,7 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
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.HandlerProvider;
import org.springframework.websocket.endpoint.WebSocketHandlerEndpoint;
@ -60,9 +58,7 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
private final String path;
private final Class<? extends Endpoint> endpointClass;
private final Object endpointBean;
private final HandlerProvider<Endpoint> endpointProvider;
private List<Class<? extends Encoder>> encoders = new ArrayList<Class<? extends Encoder>>();
@ -76,8 +72,6 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
private Configurator configurator = new Configurator() {};
private BeanFactory beanFactory;
/**
* Class constructor with the {@code javax.webscoket.Endpoint} class.
@ -87,23 +81,19 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
* @param endpointClass
*/
public EndpointRegistration(String path, Class<? extends Endpoint> endpointClass) {
this(path, endpointClass, null);
}
public EndpointRegistration(String path, Object bean) {
this(path, null, bean);
}
public EndpointRegistration(String path, String beanName) {
this(path, null, beanName);
}
private EndpointRegistration(String path, Class<? extends Endpoint> endpointClass, Object bean) {
Assert.hasText(path, "path must not be empty");
Assert.isTrue((endpointClass != null || bean != null), "Neither endpoint class nor endpoint bean provided");
Assert.notNull(endpointClass, "endpointClass is required");
this.path = path;
this.endpointClass = endpointClass;
this.endpointBean = bean;
this.endpointProvider = new HandlerProvider<Endpoint>(endpointClass);
this.endpointProvider.setLogger(logger);
}
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);
}
@Override
@ -111,40 +101,13 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
return this.path;
}
@SuppressWarnings("unchecked")
@Override
public Class<? extends Endpoint> getEndpointClass() {
if (this.endpointClass != null) {
return this.endpointClass;
}
Class<?> beanClass = this.endpointBean.getClass();
if (beanClass.equals(String.class)) {
beanClass = this.beanFactory.getType((String) this.endpointBean);
}
beanClass = ClassUtils.getUserClass(beanClass);
if (Endpoint.class.isAssignableFrom(beanClass)) {
return (Class<? extends Endpoint>) beanClass;
}
else {
throw new IllegalStateException("Invalid endpoint bean: must be of type ... TODO ");
}
return this.endpointProvider.getHandlerType();
}
public Endpoint getEndpoint() {
if (this.endpointClass != null) {
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
if (wac == null) {
String message = "Failed to find the root WebApplicationContext. Was ContextLoaderListener not used?";
logger.error(message);
throw new IllegalStateException();
}
return wac.getAutowireCapableBeanFactory().createBean(this.endpointClass);
}
Object bean = this.endpointBean;
if (this.endpointBean instanceof String) {
bean = this.beanFactory.getBean((String) this.endpointBean);
}
return (Endpoint) bean;
return this.endpointProvider.getHandler();
}
public void setSubprotocols(List<String> subprotocols) {
@ -194,11 +157,6 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
return this.decoders;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
/**
* The {@link Configurator#getEndpointInstance(Class)} method is always ignored.
*/
@ -233,4 +191,9 @@ public class EndpointRegistration implements ServerEndpointConfig, BeanFactoryAw
};
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.endpointProvider.setBeanFactory(beanFactory);
}
}

View File

@ -64,10 +64,10 @@ public class SpringConfigurator extends Configurator {
return beans.values().iterator().next();
}
else {
// This should never happen ..
// Should not happen ..
String message = "Found more than one matching @ServerEndpoint beans of type " + endpointClass;
logger.error(message);
throw new IllegalStateException("Found more than one matching beans of type " + endpointClass);
throw new IllegalStateException(message);
}
}

View File

@ -1,126 +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.endpoint.handshake;
import javax.websocket.Endpoint;
import org.springframework.beans.BeanUtils;
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.AbstractHandshakeHandler;
import org.springframework.websocket.server.HandshakeHandler;
/**
* A {@link HandshakeHandler} for use with standard Java WebSocket runtimes. A
* container-specific {@link RequestUpgradeStrategy} is required since standard
* Java WebSocket currently does not provide any means of integrating a WebSocket
* handshake into an HTTP request processing pipeline. Currently available are
* implementations for Tomcat and Glassfish.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class EndpointHandshakeHandler extends AbstractHandshakeHandler {
private final RequestUpgradeStrategy upgradeStrategy;
public EndpointHandshakeHandler(Endpoint endpoint) {
super(endpoint);
this.upgradeStrategy = createRequestUpgradeStrategy();
}
public EndpointHandshakeHandler(WebSocketHandler webSocketHandler) {
super(webSocketHandler);
this.upgradeStrategy = createRequestUpgradeStrategy();
}
public EndpointHandshakeHandler(Class<?> handlerClass) {
super(handlerClass);
this.upgradeStrategy = createRequestUpgradeStrategy();
}
protected RequestUpgradeStrategy createRequestUpgradeStrategy() {
return new RequestUpgradeStrategyFactory().create();
}
@Override
protected String[] getSupportedVerions() {
return this.upgradeStrategy.getSupportedVersions();
}
@Override
public void doHandshakeInternal(ServerHttpRequest request, ServerHttpResponse response, String protocol)
throws Exception {
logger.debug("Upgrading HTTP request");
Object webSocketHandler = getWebSocketHandler();
Endpoint endpoint;
if (webSocketHandler instanceof Endpoint) {
endpoint = (Endpoint) webSocketHandler;
}
else if (webSocketHandler instanceof WebSocketHandler) {
endpoint = new WebSocketHandlerEndpoint((WebSocketHandler) webSocketHandler);
}
else {
String className = webSocketHandler.getClass().getName();
throw new IllegalArgumentException("Unexpected WebSocket handler type: " + className);
}
this.upgradeStrategy.upgrade(request, response, protocol, endpoint);
}
private static class RequestUpgradeStrategyFactory {
private static final String packageName = EndpointHandshakeHandler.class.getPackage().getName();
private static final boolean tomcatWebSocketPresent = ClassUtils.isPresent(
"org.apache.tomcat.websocket.server.WsHttpUpgradeHandler", EndpointHandshakeHandler.class.getClassLoader());
private static final boolean glassfishWebSocketPresent = ClassUtils.isPresent(
"org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler", EndpointHandshakeHandler.class.getClassLoader());
private RequestUpgradeStrategy create() {
String className;
if (tomcatWebSocketPresent) {
className = packageName + ".TomcatRequestUpgradeStrategy";
}
else if (glassfishWebSocketPresent) {
className = packageName + ".GlassfishRequestUpgradeStrategy";
}
else {
throw new IllegalStateException("No suitable " + RequestUpgradeStrategy.class.getSimpleName());
}
try {
Class<?> clazz = ClassUtils.forName(className, EndpointHandshakeHandler.class.getClassLoader());
return (RequestUpgradeStrategy) BeanUtils.instantiateClass(clazz.getConstructor());
}
catch (Throwable t) {
throw new IllegalStateException("Failed to instantiate " + className, t);
}
}
}
}

View File

@ -1,8 +0,0 @@
/**
* WebSocket handshake support for use with standard Java WebSocket runtimes including
* container-specific strategies for upgrading the HttpServletRequest.
*
*/
package org.springframework.websocket.server.endpoint.handshake;

View File

@ -6,7 +6,7 @@
* registering type-based endpoints,
* {@link org.springframework.websocket.server.endpoint.SpringConfigurator} for
* instantiating annotated endpoints through Spring, and
* {@link org.springframework.websocket.server.endpoint.handshake.EndpointHandshakeHandler}
* {@link org.springframework.websocket.server.support.EndpointHandshakeHandler}
* for integrating endpoints into HTTP request processing.
*/
package org.springframework.websocket.server.endpoint;

View File

@ -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.websocket.server.support;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.websocket.Endpoint;
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.WebSocketHandler;
import org.springframework.websocket.endpoint.WebSocketHandlerEndpoint;
import org.springframework.websocket.server.RequestUpgradeStrategy;
/**
* A {@link RequestUpgradeStrategy} that supports WebSocket handlers of type
* {@link WebSocketHandler} as well as {@link javax.websocket.Endpoint}.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class AbstractEndpointUpgradeStrategy implements RequestUpgradeStrategy {
protected final Log logger = LogFactory.getLog(getClass());
private final Map<WebSocketHandler, Endpoint> webSocketHandlers = new HashMap<WebSocketHandler, Endpoint>();
@Override
public void registerWebSocketHandlers(Collection<WebSocketHandler> webSocketHandlers) {
for (WebSocketHandler webSocketHandler : webSocketHandlers) {
if (!this.webSocketHandlers.containsKey(webSocketHandler)) {
this.webSocketHandlers.put(webSocketHandler, adaptWebSocketHandler(webSocketHandler));
}
}
}
protected Endpoint adaptWebSocketHandler(WebSocketHandler handler) {
return new WebSocketHandlerEndpoint(handler);
}
@Override
public void upgrade(ServerHttpRequest request, ServerHttpResponse response,
String protocol, WebSocketHandler webSocketHandler) throws Exception {
Endpoint endpoint = this.webSocketHandlers.get(webSocketHandler);
if (endpoint == null) {
endpoint = adaptWebSocketHandler(webSocketHandler);
}
if (logger.isTraceEnabled()) {
logger.trace("Upgrading with " + endpoint);
}
upgradeInternal(request, response, protocol, endpoint);
}
protected abstract void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
String protocol, Endpoint endpoint) throws Exception;
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.websocket.server.endpoint.handshake;
package org.springframework.websocket.server.support;
import java.lang.reflect.Constructor;
import java.net.URI;
@ -54,7 +54,7 @@ import org.springframework.websocket.server.endpoint.EndpointRegistration;
* @author Rossen Stoyanchev
* @since 4.0
*/
public class GlassfishRequestUpgradeStrategy implements RequestUpgradeStrategy {
public class GlassfishRequestUpgradeStrategy extends AbstractEndpointUpgradeStrategy {
private final static Random random = new Random();
@ -65,8 +65,8 @@ public class GlassfishRequestUpgradeStrategy implements RequestUpgradeStrategy {
}
@Override
public void upgrade(ServerHttpRequest request, ServerHttpResponse response, String protocol,
Endpoint endpoint) throws Exception {
public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
String protocol, Endpoint endpoint) throws Exception {
Assert.isTrue(request instanceof ServletServerHttpRequest);
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.websocket.server.endpoint.handshake;
package org.springframework.websocket.server.support;
import java.io.IOException;
import java.lang.reflect.Method;
@ -42,7 +42,7 @@ import org.springframework.websocket.server.endpoint.EndpointRegistration;
* @author Rossen Stoyanchev
* @since 4.0
*/
public class TomcatRequestUpgradeStrategy implements RequestUpgradeStrategy {
public class TomcatRequestUpgradeStrategy extends AbstractEndpointUpgradeStrategy {
@Override
@ -51,8 +51,8 @@ public class TomcatRequestUpgradeStrategy implements RequestUpgradeStrategy {
}
@Override
public void upgrade(ServerHttpRequest request, ServerHttpResponse response, String protocol,
Endpoint endpoint) throws IOException {
public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
String protocol, Endpoint endpoint) throws IOException {
Assert.isTrue(request instanceof ServletServerHttpRequest);
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();

View File

@ -17,11 +17,15 @@
package org.springframework.websocket.server.support;
import java.io.IOException;
import java.util.Collections;
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;
@ -29,24 +33,48 @@ import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.util.NestedServletException;
import org.springframework.websocket.HandlerProvider;
import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.server.HandshakeHandler;
import org.springframework.websocket.server.DefaultHandshakeHandler;
/**
* A Spring MVC {@link HttpRequestHandler} wrapping the invocation of a WebSocket
* {@link HandshakeHandler};
* An {@link HttpRequestHandler} that wraps the invocation of a {@link HandshakeHandler}.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class HandshakeHttpRequestHandler implements HttpRequestHandler {
public class WebSocketHttpRequestHandler implements HttpRequestHandler, BeanFactoryAware {
private final HandshakeHandler handshakeHandler;
private HandshakeHandler handshakeHandler;
private final HandlerProvider<WebSocketHandler> handlerProvider;
public HandshakeHttpRequestHandler(HandshakeHandler handshakeHandler) {
public WebSocketHttpRequestHandler(WebSocketHandler webSocketHandler) {
Assert.notNull(webSocketHandler, "webSocketHandler is required");
this.handlerProvider = new HandlerProvider<WebSocketHandler>(webSocketHandler);
this.handshakeHandler = new DefaultHandshakeHandler();
this.handshakeHandler.registerWebSocketHandlers(Collections.singleton(webSocketHandler));
}
public WebSocketHttpRequestHandler( Class<? extends WebSocketHandler> webSocketHandlerClass) {
Assert.notNull(webSocketHandlerClass, "webSocketHandlerClass is required");
this.handlerProvider = new HandlerProvider<WebSocketHandler>(webSocketHandlerClass);
}
public void setHandshakeHandler(HandshakeHandler handshakeHandler) {
Assert.notNull(handshakeHandler, "handshakeHandler is required");
this.handshakeHandler = handshakeHandler;
if (this.handlerProvider.isSingleton()) {
this.handshakeHandler.registerWebSocketHandlers(Collections.singleton(this.handlerProvider.getHandler()));
}
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.handlerProvider.setBeanFactory(beanFactory);
}
@Override
@ -57,12 +85,16 @@ public class HandshakeHttpRequestHandler implements HttpRequestHandler {
ServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
try {
this.handshakeHandler.doHandshake(httpRequest, httpResponse);
WebSocketHandler webSocketHandler = this.handlerProvider.getHandler();
this.handshakeHandler.doHandshake(httpRequest, httpResponse, webSocketHandler);
}
catch (Exception e) {
// TODO
throw new NestedServletException("HandshakeHandler failure", e);
}
finally {
httpResponse.flush();
}
}
}

View File

@ -1,6 +1,6 @@
/**
* Server-side support classes for WebSocket applications.
* Server-side support classes including container-specific strategies for upgrading a request.
*
*/
package org.springframework.websocket.server.support;