summaryrefslogtreecommitdiff
path: root/spring-websocket/src
diff options
context:
space:
mode:
Diffstat (limited to 'spring-websocket/src')
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/WebSocketHttpHeaders.java2
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSession.java21
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java4
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java32
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocketMessageBroker.java4
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java19
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompWebSocketEndpointRegistration.java2
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java70
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/messaging/DefaultSimpUserRegistry.java134
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/messaging/SessionConnectEvent.java2
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketAnnotationMethodMessageHandler.java26
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractStandardUpgradeStrategy.java18
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java8
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServletServerContainerFactoryBean.java13
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java369
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java3
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java18
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java9
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java8
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/sockjs/frame/Jackson2SockJsMessageCodec.java4
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractHttpSockJsSession.java16
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java1
-rw-r--r--spring-websocket/src/main/resources/META-INF/spring.schemas3
-rw-r--r--spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.0.xsd2
-rw-r--r--spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.1.xsd2
-rw-r--r--spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.2.xsd2
-rw-r--r--spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.3.xsd931
-rw-r--r--spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket.gifbin0 -> 1025 bytes
-rw-r--r--spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java13
-rw-r--r--spring-websocket/src/test/java/org/springframework/web/socket/ContextLoaderTestUtils.java3
-rw-r--r--spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java (renamed from spring-websocket/src/test/java/org/springframework/web/socket/WebSocketIntegrationTests.java)13
-rw-r--r--spring-websocket/src/test/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSessionTests.java20
-rw-r--r--spring-websocket/src/test/java/org/springframework/web/socket/adapter/standard/StandardWebSocketSessionTests.java22
-rw-r--r--spring-websocket/src/test/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParserTests.java22
-rw-r--r--spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java4
-rw-r--r--spring-websocket/src/test/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecoratorTests.java82
-rw-r--r--spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/RestTemplateXhrTransportTests.java2
-rw-r--r--spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java4
-rw-r--r--spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-customchannels.xml4
39 files changed, 1492 insertions, 420 deletions
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketHttpHeaders.java b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketHttpHeaders.java
index 8efb5fab..4cda2747 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketHttpHeaders.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketHttpHeaders.java
@@ -172,7 +172,7 @@ public class WebSocketHttpHeaders extends HttpHeaders {
return Collections.emptyList();
}
else if (values.size() == 1) {
- return getFirstValueAsList(SEC_WEBSOCKET_PROTOCOL);
+ return getValuesAsList(SEC_WEBSOCKET_PROTOCOL);
}
else {
return values;
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSession.java
index cfb0aec6..2825a482 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSession.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -24,7 +24,9 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.springframework.http.HttpHeaders;
@@ -185,22 +187,31 @@ public class JettyWebSocketSession extends AbstractWebSocketSession<Session> {
@Override
protected void sendTextMessage(TextMessage message) throws IOException {
- getNativeSession().getRemote().sendString(message.getPayload());
+ getRemoteEndpoint().sendString(message.getPayload());
}
@Override
protected void sendBinaryMessage(BinaryMessage message) throws IOException {
- getNativeSession().getRemote().sendBytes(message.getPayload());
+ getRemoteEndpoint().sendBytes(message.getPayload());
}
@Override
protected void sendPingMessage(PingMessage message) throws IOException {
- getNativeSession().getRemote().sendPing(message.getPayload());
+ getRemoteEndpoint().sendPing(message.getPayload());
}
@Override
protected void sendPongMessage(PongMessage message) throws IOException {
- getNativeSession().getRemote().sendPong(message.getPayload());
+ getRemoteEndpoint().sendPong(message.getPayload());
+ }
+
+ private RemoteEndpoint getRemoteEndpoint() throws IOException {
+ try {
+ return getNativeSession().getRemote();
+ }
+ catch (WebSocketException ex) {
+ throw new IOException("Unable to obtain RemoteEndpoint in session=" + getId(), ex);
+ }
}
@Override
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java b/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java
index 2dbd8157..faa32b69 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -26,7 +26,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Callable;
-
import javax.websocket.ClientEndpointConfig;
import javax.websocket.ClientEndpointConfig.Configurator;
import javax.websocket.ContainerProvider;
@@ -51,7 +50,6 @@ import org.springframework.web.socket.adapter.standard.StandardWebSocketSession;
import org.springframework.web.socket.adapter.standard.WebSocketToStandardExtensionAdapter;
import org.springframework.web.socket.client.AbstractWebSocketClient;
-
/**
* A WebSocketClient based on standard Java WebSocket API.
*
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java
index 12778c9d..9caa5a8f 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -109,6 +109,9 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
private static final boolean jackson2Present = ClassUtils.isPresent(
"com.fasterxml.jackson.databind.ObjectMapper", MessageBrokerBeanDefinitionParser.class.getClassLoader());
+ private static final boolean javaxValidationPresent =
+ ClassUtils.isPresent("javax.validation.Validator", MessageBrokerBeanDefinitionParser.class.getClassLoader());
+
@Override
public BeanDefinition parse(Element element, ParserContext context) {
@@ -516,6 +519,11 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
beanDef.getPropertyValues().add("pathMatcher", new RuntimeBeanReference(pathMatcherRef));
}
+ RuntimeBeanReference validatorRef = getValidator(messageBrokerElement, source, context);
+ if (validatorRef != null) {
+ beanDef.getPropertyValues().add("validator", validatorRef);
+ }
+
Element resolversElement = DomUtils.getChildElementByTagName(messageBrokerElement, "argument-resolvers");
if (resolversElement != null) {
values.add("customArgumentResolvers", extractBeanSubElements(resolversElement, context));
@@ -529,6 +537,24 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
registerBeanDef(beanDef, context, source);
}
+ private RuntimeBeanReference getValidator(Element messageBrokerElement, Object source, ParserContext parserContext) {
+ if (messageBrokerElement.hasAttribute("validator")) {
+ return new RuntimeBeanReference(messageBrokerElement.getAttribute("validator"));
+ }
+ else if (javaxValidationPresent) {
+ RootBeanDefinition validatorDef = new RootBeanDefinition(
+ "org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean");
+ validatorDef.setSource(source);
+ validatorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ String validatorName = parserContext.getReaderContext().registerWithGeneratedName(validatorDef);
+ parserContext.registerComponent(new BeanComponentDefinition(validatorDef, validatorName));
+ return new RuntimeBeanReference(validatorName);
+ }
+ else {
+ return null;
+ }
+ }
+
private ManagedList<Object> extractBeanSubElements(Element parentElement, ParserContext parserContext) {
ManagedList<Object> list = new ManagedList<Object>();
list.setSource(parserContext.extractSource(parentElement));
@@ -547,6 +573,10 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
if (brokerElem.hasAttribute("user-destination-prefix")) {
beanDef.getPropertyValues().add("userDestinationPrefix", brokerElem.getAttribute("user-destination-prefix"));
}
+ if (brokerElem.hasAttribute("path-matcher")) {
+ String pathMatcherRef = brokerElem.getAttribute("path-matcher");
+ beanDef.getPropertyValues().add("pathMatcher", new RuntimeBeanReference(pathMatcherRef));
+ }
return new RuntimeBeanReference(registerBeanDef(beanDef, context, source));
}
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocketMessageBroker.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocketMessageBroker.java
index 716489ee..e9d4d85e 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocketMessageBroker.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocketMessageBroker.java
@@ -45,12 +45,12 @@ import org.springframework.context.annotation.Import;
* &#064;EnableWebSocketMessageBroker
* public class MyConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
*
- * &#064;Override
+ * &#064;Override
* public void registerStompEndpoints(StompEndpointRegistry registry) {
* registry.addEndpoint("/portfolio").withSockJS();
* }
*
- * &#064;Bean
+ * &#064;Bean
* public void configureMessageBroker(MessageBrokerRegistry registry) {
* registry.enableStompBrokerRelay("/queue/", "/topic/");
* registry.setApplicationDestinationPrefixes("/app/");
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java
index bbe55538..048cd3a0 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -67,8 +67,8 @@ public class WebMvcStompEndpointRegistry implements StompEndpointRegistry {
org.springframework.messaging.simp.user.UserSessionRegistry userSessionRegistry,
TaskScheduler defaultSockJsTaskScheduler) {
- Assert.notNull(webSocketHandler, "'webSocketHandler' is required ");
- Assert.notNull(transportRegistration, "'transportRegistration' is required");
+ Assert.notNull(webSocketHandler, "WebSocketHandler is required ");
+ Assert.notNull(transportRegistration, "WebSocketTransportRegistration is required");
this.webSocketHandler = webSocketHandler;
this.subProtocolWebSocketHandler = unwrapSubProtocolWebSocketHandler(webSocketHandler);
@@ -87,19 +87,17 @@ public class WebMvcStompEndpointRegistry implements StompEndpointRegistry {
this.stompHandler.setMessageSizeLimit(transportRegistration.getMessageSizeLimit());
}
-
this.sockJsScheduler = defaultSockJsTaskScheduler;
}
private static SubProtocolWebSocketHandler unwrapSubProtocolWebSocketHandler(WebSocketHandler handler) {
WebSocketHandler actual = WebSocketHandlerDecorator.unwrap(handler);
- Assert.isInstanceOf(SubProtocolWebSocketHandler.class, actual, "No SubProtocolWebSocketHandler in " + handler);
+ if (!(actual instanceof SubProtocolWebSocketHandler)) {
+ throw new IllegalArgumentException("No SubProtocolWebSocketHandler in " + handler);
+ };
return (SubProtocolWebSocketHandler) actual;
}
- protected void setApplicationContext(ApplicationContext applicationContext) {
- this.stompHandler.setApplicationEventPublisher(applicationContext);
- }
@Override
public StompWebSocketEndpointRegistration addEndpoint(String... paths) {
@@ -144,6 +142,11 @@ public class WebMvcStompEndpointRegistry implements StompEndpointRegistry {
return this;
}
+ protected void setApplicationContext(ApplicationContext applicationContext) {
+ this.stompHandler.setApplicationEventPublisher(applicationContext);
+ }
+
+
/**
* Return a handler mapping with the mapped ViewControllers; or {@code null}
* in case of no registrations.
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompWebSocketEndpointRegistration.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompWebSocketEndpointRegistration.java
index ecac1596..56582796 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompWebSocketEndpointRegistration.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompWebSocketEndpointRegistration.java
@@ -36,7 +36,7 @@ import org.springframework.web.socket.sockjs.transport.handler.WebSocketTranspor
import java.util.ArrayList;
import java.util.List;
/**
- * An abstract base class class for configuring STOMP over WebSocket/SockJS endpoints.
+ * An abstract base class for configuring STOMP over WebSocket/SockJS endpoints.
*
* @author Rossen Stoyanchev
* @since 4.0
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java b/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java
index 16de9aa4..15dd486a 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java
@@ -31,13 +31,14 @@ import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
/**
- * Wraps a {@link org.springframework.web.socket.WebSocketSession} and guarantees
- * only one thread can send messages at a time.
+ * Wrap a {@link org.springframework.web.socket.WebSocketSession WebSocketSession}
+ * to guarantee only one thread can send messages at a time.
*
- * <p>If a send is slow, subsequent attempts to send more messages from a different
- * thread will fail to acquire the flush lock and the messages will be buffered
- * instead: At that time, the specified buffer-size limit and send-time limit will
- * be checked and the session closed if the limits are exceeded.
+ * <p>If a send is slow, subsequent attempts to send more messages from other
+ * threads will not be able to acquire the flush lock and messages will be
+ * buffered instead -- at that time, the specified buffer-size limit and
+ * send-time limit will be checked and the session closed if the limits are
+ * exceeded.
*
* @author Rossen Stoyanchev
* @since 4.0.3
@@ -47,21 +48,20 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat
private static final Log logger = LogFactory.getLog(ConcurrentWebSocketSessionDecorator.class);
- private final Queue<WebSocketMessage<?>> buffer = new LinkedBlockingQueue<WebSocketMessage<?>>();
-
- private final AtomicInteger bufferSize = new AtomicInteger();
+ private final int sendTimeLimit;
private final int bufferSizeLimit;
- private volatile long sendStartTime;
+ private final Queue<WebSocketMessage<?>> buffer = new LinkedBlockingQueue<WebSocketMessage<?>>();
- private final int sendTimeLimit;
+ private final AtomicInteger bufferSize = new AtomicInteger();
+ private volatile long sendStartTime;
private volatile boolean limitExceeded;
- private volatile boolean shutdownInProgress;
+ private volatile boolean closeInProgress;
private final Lock flushLock = new ReentrantLock();
@@ -93,7 +93,7 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat
public void sendMessage(WebSocketMessage<?> message) throws IOException {
- if (isDisabled()) {
+ if (shouldNotSend()) {
return;
}
@@ -103,32 +103,33 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat
do {
if (!tryFlushMessageBuffer()) {
if (logger.isTraceEnabled()) {
- logger.trace("Another send already in progress, session id '" +
- getId() + "'" + ", in-progress send time " + getTimeSinceSendStarted() +
- " (ms)" + ", buffer size " + this.bufferSize + " bytes");
+ String text = String.format("Another send already in progress: " +
+ "session id '%s':, \"in-progress\" send time %d (ms), buffer size %d bytes",
+ getId(), getTimeSinceSendStarted(), this.bufferSize.get());
+ logger.trace(text);
}
checkSessionLimits();
break;
}
}
- while (!this.buffer.isEmpty() && !isDisabled());
+ while (!this.buffer.isEmpty() && !shouldNotSend());
}
- private boolean isDisabled() {
- return (this.limitExceeded || this.shutdownInProgress);
+ private boolean shouldNotSend() {
+ return (this.limitExceeded || this.closeInProgress);
}
private boolean tryFlushMessageBuffer() throws IOException {
if (this.flushLock.tryLock()) {
try {
while (true) {
- WebSocketMessage<?> messageToSend = this.buffer.poll();
- if (messageToSend == null || isDisabled()) {
+ WebSocketMessage<?> message = this.buffer.poll();
+ if (message == null || shouldNotSend()) {
break;
}
- this.bufferSize.addAndGet(messageToSend.getPayloadLength() * -1);
+ this.bufferSize.addAndGet(message.getPayloadLength() * -1);
this.sendStartTime = System.currentTimeMillis();
- getDelegate().sendMessage(messageToSend);
+ getDelegate().sendMessage(message);
this.sendStartTime = 0;
}
}
@@ -142,18 +143,17 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat
}
private void checkSessionLimits() throws IOException {
- if (!isDisabled() && this.closeLock.tryLock()) {
+ if (!shouldNotSend() && this.closeLock.tryLock()) {
try {
if (getTimeSinceSendStarted() > this.sendTimeLimit) {
- String errorMessage = "Message send time " + getTimeSinceSendStarted() +
- " (ms) exceeded the allowed limit " + this.sendTimeLimit;
- sessionLimitReached(errorMessage, CloseStatus.SESSION_NOT_RELIABLE);
+ String format = "Message send time %d (ms) for session '%s' exceeded the allowed limit %d";
+ String reason = String.format(format, getTimeSinceSendStarted(), getId(), this.sendTimeLimit);
+ setLimitExceeded(reason);
}
else if (this.bufferSize.get() > this.bufferSizeLimit) {
- String errorMessage = "The send buffer size " + this.bufferSize.get() + " bytes for " +
- "session '" + getId() + " exceeded the allowed limit " + this.bufferSizeLimit;
- sessionLimitReached(errorMessage,
- (getTimeSinceSendStarted() >= 10000 ? CloseStatus.SESSION_NOT_RELIABLE : null));
+ String format = "The send buffer size %d bytes for session '%s' exceeded the allowed limit %d";
+ String reason = String.format(format, this.bufferSize.get(), getId(), this.bufferSizeLimit);
+ setLimitExceeded(reason);
}
}
finally {
@@ -162,16 +162,16 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat
}
}
- private void sessionLimitReached(String reason, CloseStatus status) {
+ private void setLimitExceeded(String reason) {
this.limitExceeded = true;
- throw new SessionLimitExceededException(reason, status);
+ throw new SessionLimitExceededException(reason, CloseStatus.SESSION_NOT_RELIABLE);
}
@Override
public void close(CloseStatus status) throws IOException {
this.closeLock.lock();
try {
- if (this.shutdownInProgress) {
+ if (this.closeInProgress) {
return;
}
if (!CloseStatus.SESSION_NOT_RELIABLE.equals(status)) {
@@ -188,7 +188,7 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat
status = CloseStatus.SESSION_NOT_RELIABLE;
}
}
- this.shutdownInProgress = true;
+ this.closeInProgress = true;
super.close(status);
}
finally {
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/DefaultSimpUserRegistry.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/DefaultSimpUserRegistry.java
index 4cd575bd..9ea63ea2 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/DefaultSimpUserRegistry.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/DefaultSimpUserRegistry.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -37,41 +37,30 @@ import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.util.Assert;
/**
- * Default, mutable, thread-safe implementation of {@link SimpUserRegistry} that
- * listens ApplicationContext events of type {@link AbstractSubProtocolEvent} to
- * keep track of user presence and subscription information.
+ * A default implementation of {@link SimpUserRegistry} that relies on
+ * {@link AbstractSubProtocolEvent} application context events to keep track of
+ * connected users and their subscriptions.
*
* @author Rossen Stoyanchev
* @since 4.2
*/
public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicationListener {
- private final Map<String, DefaultSimpUser> users = new ConcurrentHashMap<String, DefaultSimpUser>();
+ /* Primary lookup that holds all users and their sessions */
+ private final Map<String, LocalSimpUser> users = new ConcurrentHashMap<String, LocalSimpUser>();
- private final Map<String, DefaultSimpSession> sessions = new ConcurrentHashMap<String, DefaultSimpSession>();
+ /* Secondary lookup across all sessions by id */
+ private final Map<String, LocalSimpSession> sessions = new ConcurrentHashMap<String, LocalSimpSession>();
+ private final Object sessionLock = new Object();
- @Override
- public SimpUser getUser(String userName) {
- return this.users.get(userName);
- }
@Override
- public Set<SimpUser> getUsers() {
- return new HashSet<SimpUser>(this.users.values());
+ public int getOrder() {
+ return Ordered.LOWEST_PRECEDENCE;
}
- public Set<SimpSubscription> findSubscriptions(SimpSubscriptionMatcher matcher) {
- Set<SimpSubscription> result = new HashSet<SimpSubscription>();
- for (DefaultSimpSession session : this.sessions.values()) {
- for (SimpSubscription subscription : session.subscriptions.values()) {
- if (matcher.match(subscription)) {
- result.add(subscription);
- }
- }
- }
- return result;
- }
+ // SmartApplicationListener methods
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
@@ -79,11 +68,6 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
}
@Override
- public boolean supportsSourceType(Class<?> sourceType) {
- return true;
- }
-
- @Override
public void onApplicationEvent(ApplicationEvent event) {
AbstractSubProtocolEvent subProtocolEvent = (AbstractSubProtocolEvent) event;
@@ -92,7 +76,7 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
String sessionId = accessor.getSessionId();
if (event instanceof SessionSubscribeEvent) {
- DefaultSimpSession session = this.sessions.get(sessionId);
+ LocalSimpSession session = this.sessions.get(sessionId);
if (session != null) {
String id = accessor.getSubscriptionId();
String destination = accessor.getDestination();
@@ -108,23 +92,22 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
if (user instanceof DestinationUserNameProvider) {
name = ((DestinationUserNameProvider) user).getDestinationUserName();
}
- synchronized (this) {
- DefaultSimpUser simpUser = this.users.get(name);
+ synchronized (this.sessionLock) {
+ LocalSimpUser simpUser = this.users.get(name);
if (simpUser == null) {
- simpUser = new DefaultSimpUser(name, sessionId);
+ simpUser = new LocalSimpUser(name);
this.users.put(name, simpUser);
}
- else {
- simpUser.addSession(sessionId);
- }
- this.sessions.put(sessionId, (DefaultSimpSession) simpUser.getSession(sessionId));
+ LocalSimpSession session = new LocalSimpSession(sessionId, simpUser);
+ simpUser.addSession(session);
+ this.sessions.put(sessionId, session);
}
}
else if (event instanceof SessionDisconnectEvent) {
- synchronized (this) {
- DefaultSimpSession session = this.sessions.remove(sessionId);
+ synchronized (this.sessionLock) {
+ LocalSimpSession session = this.sessions.remove(sessionId);
if (session != null) {
- DefaultSimpUser user = session.getUser();
+ LocalSimpUser user = session.getUser();
user.removeSession(sessionId);
if (!user.hasSessions()) {
this.users.remove(user.getName());
@@ -133,7 +116,7 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
}
}
else if (event instanceof SessionUnsubscribeEvent) {
- DefaultSimpSession session = this.sessions.get(sessionId);
+ LocalSimpSession session = this.sessions.get(sessionId);
if (session != null) {
String subscriptionId = accessor.getSubscriptionId();
session.removeSubscription(subscriptionId);
@@ -142,28 +125,52 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
}
@Override
- public int getOrder() {
- return Ordered.LOWEST_PRECEDENCE;
+ public boolean supportsSourceType(Class<?> sourceType) {
+ return true;
+ }
+
+ // SimpUserRegistry methods
+
+ @Override
+ public SimpUser getUser(String userName) {
+ return this.users.get(userName);
+ }
+
+ @Override
+ public Set<SimpUser> getUsers() {
+ return new HashSet<SimpUser>(this.users.values());
}
+ public Set<SimpSubscription> findSubscriptions(SimpSubscriptionMatcher matcher) {
+ Set<SimpSubscription> result = new HashSet<SimpSubscription>();
+ for (LocalSimpSession session : this.sessions.values()) {
+ for (SimpSubscription subscription : session.subscriptions.values()) {
+ if (matcher.match(subscription)) {
+ result.add(subscription);
+ }
+ }
+ }
+ return result;
+ }
+
+
@Override
public String toString() {
return "users=" + this.users;
}
- private static class DefaultSimpUser implements SimpUser {
+
+ private static class LocalSimpUser implements SimpUser {
private final String name;
- private final Map<String, SimpSession> sessions =
+ private final Map<String, SimpSession> userSessions =
new ConcurrentHashMap<String, SimpSession>(1);
- public DefaultSimpUser(String userName, String sessionId) {
+ public LocalSimpUser(String userName) {
Assert.notNull(userName);
- Assert.notNull(sessionId);
this.name = userName;
- this.sessions.put(sessionId, new DefaultSimpSession(sessionId, this));
}
@Override
@@ -173,26 +180,25 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
@Override
public boolean hasSessions() {
- return !this.sessions.isEmpty();
+ return !this.userSessions.isEmpty();
}
@Override
public SimpSession getSession(String sessionId) {
- return (sessionId != null ? this.sessions.get(sessionId) : null);
+ return (sessionId != null ? this.userSessions.get(sessionId) : null);
}
@Override
public Set<SimpSession> getSessions() {
- return new HashSet<SimpSession>(this.sessions.values());
+ return new HashSet<SimpSession>(this.userSessions.values());
}
- void addSession(String sessionId) {
- DefaultSimpSession session = new DefaultSimpSession(sessionId, this);
- this.sessions.put(sessionId, session);
+ void addSession(SimpSession session) {
+ this.userSessions.put(session.getId(), session);
}
void removeSession(String sessionId) {
- this.sessions.remove(sessionId);
+ this.userSessions.remove(sessionId);
}
@Override
@@ -213,20 +219,20 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
@Override
public String toString() {
- return "name=" + this.name + ", sessions=" + this.sessions;
+ return "name=" + this.name + ", sessions=" + this.userSessions;
}
}
- private static class DefaultSimpSession implements SimpSession {
+ private static class LocalSimpSession implements SimpSession {
private final String id;
- private final DefaultSimpUser user;
+ private final LocalSimpUser user;
private final Map<String, SimpSubscription> subscriptions = new ConcurrentHashMap<String, SimpSubscription>(4);
- public DefaultSimpSession(String id, DefaultSimpUser user) {
+ public LocalSimpSession(String id, LocalSimpUser user) {
Assert.notNull(id);
Assert.notNull(user);
this.id = id;
@@ -239,7 +245,7 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
}
@Override
- public DefaultSimpUser getUser() {
+ public LocalSimpUser getUser() {
return this.user;
}
@@ -249,7 +255,7 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
}
void addSubscription(String id, String destination) {
- this.subscriptions.put(id, new DefaultSimpSubscription(id, destination, this));
+ this.subscriptions.put(id, new LocalSimpSubscription(id, destination, this));
}
void removeSubscription(String id) {
@@ -278,16 +284,16 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
}
}
- private static class DefaultSimpSubscription implements SimpSubscription {
+ private static class LocalSimpSubscription implements SimpSubscription {
private final String id;
- private final DefaultSimpSession session;
+ private final LocalSimpSession session;
private final String destination;
- public DefaultSimpSubscription(String id, String destination, DefaultSimpSession session) {
+ public LocalSimpSubscription(String id, String destination, LocalSimpSession session) {
Assert.notNull(id);
Assert.hasText(destination);
Assert.notNull(session);
@@ -302,7 +308,7 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
}
@Override
- public DefaultSimpSession getSession() {
+ public LocalSimpSession getSession() {
return this.session;
}
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SessionConnectEvent.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SessionConnectEvent.java
index 41006fea..a372a0e2 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SessionConnectEvent.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SessionConnectEvent.java
@@ -25,7 +25,7 @@ import org.springframework.messaging.Message;
* (e.g. STOMP) as the WebSocket sub-protocol issues a connect request.
*
* <p>Note that this is not the same as the WebSocket session getting established
- * but rather the client's first attempt to connect within the the sub-protocol,
+ * but rather the client's first attempt to connect within the sub-protocol,
* for example sending the STOMP CONNECT frame.
*
* @author Rossen Stoyanchev
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketAnnotationMethodMessageHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketAnnotationMethodMessageHandler.java
index 42994516..9f1c4a69 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketAnnotationMethodMessageHandler.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketAnnotationMethodMessageHandler.java
@@ -28,6 +28,7 @@ import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
import org.springframework.web.method.ControllerAdviceBean;
+
/**
* A sub-class of {@link SimpAnnotationMethodMessageHandler} to provide support
* for {@link org.springframework.web.bind.annotation.ControllerAdvice
@@ -38,8 +39,9 @@ import org.springframework.web.method.ControllerAdviceBean;
*/
public class WebSocketAnnotationMethodMessageHandler extends SimpAnnotationMethodMessageHandler {
- public WebSocketAnnotationMethodMessageHandler(SubscribableChannel clientInChannel, MessageChannel clientOutChannel,
- SimpMessageSendingOperations brokerTemplate) {
+
+ public WebSocketAnnotationMethodMessageHandler(SubscribableChannel clientInChannel,
+ MessageChannel clientOutChannel, SimpMessageSendingOperations brokerTemplate) {
super(clientInChannel, clientOutChannel, brokerTemplate);
}
@@ -58,9 +60,9 @@ public class WebSocketAnnotationMethodMessageHandler extends SimpAnnotationMetho
if (logger.isDebugEnabled()) {
logger.debug("Looking for @MessageExceptionHandler mappings: " + getApplicationContext());
}
- List<ControllerAdviceBean> controllerAdvice = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
- AnnotationAwareOrderComparator.sort(controllerAdvice);
- initMessagingAdviceCache(MessagingControllerAdviceBean.createFromList(controllerAdvice));
+ List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
+ AnnotationAwareOrderComparator.sort(beans);
+ initMessagingAdviceCache(MessagingControllerAdviceBean.createFromList(beans));
}
private void initMessagingAdviceCache(List<MessagingAdviceBean> beans) {
@@ -68,8 +70,8 @@ public class WebSocketAnnotationMethodMessageHandler extends SimpAnnotationMetho
return;
}
for (MessagingAdviceBean bean : beans) {
- Class<?> beanType = bean.getBeanType();
- AnnotationExceptionHandlerMethodResolver resolver = new AnnotationExceptionHandlerMethodResolver(beanType);
+ Class<?> type = bean.getBeanType();
+ AnnotationExceptionHandlerMethodResolver resolver = new AnnotationExceptionHandlerMethodResolver(type);
if (resolver.hasExceptionMappings()) {
registerExceptionHandlerAdvice(bean, resolver);
logger.info("Detected @MessageExceptionHandler methods in " + bean);
@@ -89,12 +91,12 @@ public class WebSocketAnnotationMethodMessageHandler extends SimpAnnotationMetho
this.adviceBean = adviceBean;
}
- public static List<MessagingAdviceBean> createFromList(List<ControllerAdviceBean> controllerAdvice) {
- List<MessagingAdviceBean> messagingAdvice = new ArrayList<MessagingAdviceBean>(controllerAdvice.size());
- for (ControllerAdviceBean bean : controllerAdvice) {
- messagingAdvice.add(new MessagingControllerAdviceBean(bean));
+ public static List<MessagingAdviceBean> createFromList(List<ControllerAdviceBean> beans) {
+ List<MessagingAdviceBean> result = new ArrayList<MessagingAdviceBean>(beans.size());
+ for (ControllerAdviceBean bean : beans) {
+ result.add(new MessagingControllerAdviceBean(bean));
}
- return messagingAdvice;
+ return result;
}
@Override
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractStandardUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractStandardUpgradeStrategy.java
index 7ca2f7ac..2dd7ff41 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractStandardUpgradeStrategy.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractStandardUpgradeStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -105,8 +105,20 @@ public abstract class AbstractStandardUpgradeStrategy implements RequestUpgradeS
WebSocketHandler wsHandler, Map<String, Object> attrs) throws HandshakeFailureException {
HttpHeaders headers = request.getHeaders();
- InetSocketAddress localAddr = request.getLocalAddress();
- InetSocketAddress remoteAddr = request.getRemoteAddress();
+ InetSocketAddress localAddr = null;
+ try {
+ localAddr = request.getLocalAddress();
+ }
+ catch (Exception ex) {
+ // Ignore
+ }
+ InetSocketAddress remoteAddr = null;
+ try {
+ remoteAddr = request.getRemoteAddress();
+ }
+ catch (Exception ex) {
+ // Ignore
+ }
StandardWebSocketSession session = new StandardWebSocketSession(headers, attrs, localAddr, remoteAddr, user);
StandardWebSocketHandlerAdapter endpoint = new StandardWebSocketHandlerAdapter(wsHandler, session);
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java
index 0e6a31df..35886efc 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -193,7 +193,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
private static final Method registerMethod;
- private static final Method unRegisterMethod;
+ private static final Method unregisterMethod;
static {
try {
@@ -204,7 +204,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
throw new IllegalStateException("Expected TyrusEndpointWrapper constructor with 9 or 10 arguments");
}
registerMethod = TyrusWebSocketEngine.class.getDeclaredMethod("register", TyrusEndpointWrapper.class);
- unRegisterMethod = TyrusWebSocketEngine.class.getDeclaredMethod("unregister", TyrusEndpointWrapper.class);
+ unregisterMethod = TyrusWebSocketEngine.class.getDeclaredMethod("unregister", TyrusEndpointWrapper.class);
ReflectionUtils.makeAccessible(registerMethod);
}
catch (Exception ex) {
@@ -259,7 +259,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda
@Override
public void unregister(TyrusWebSocketEngine engine, Object endpoint) {
try {
- unRegisterMethod.invoke(engine, endpoint);
+ unregisterMethod.invoke(engine, endpoint);
}
catch (Exception ex) {
throw new HandshakeFailureException("Failed to unregister " + endpoint, ex);
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServletServerContainerFactoryBean.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServletServerContainerFactoryBean.java
index 6460c96d..7fe86146 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServletServerContainerFactoryBean.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServletServerContainerFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -40,6 +40,7 @@ import org.springframework.web.context.ServletContextAware;
* to customize the properties of the (one and only) {@code ServerContainer} instance.
*
* @author Rossen Stoyanchev
+ * @author Sam Brannen
* @since 4.0
*/
public class ServletServerContainerFactoryBean
@@ -53,6 +54,8 @@ public class ServletServerContainerFactoryBean
private Integer maxBinaryMessageBufferSize;
+ private ServletContext servletContext;
+
private ServerContainer serverContainer;
@@ -90,14 +93,18 @@ public class ServletServerContainerFactoryBean
@Override
public void setServletContext(ServletContext servletContext) {
- this.serverContainer = (ServerContainer) servletContext.getAttribute("javax.websocket.server.ServerContainer");
+ this.servletContext = servletContext;
}
@Override
public void afterPropertiesSet() {
- Assert.state(this.serverContainer != null,
+ Assert.state(this.servletContext != null,
"A ServletContext is required to access the javax.websocket.server.ServerContainer instance");
+ this.serverContainer = (ServerContainer) this.servletContext.getAttribute(
+ "javax.websocket.server.ServerContainer");
+ Assert.state(this.serverContainer != null,
+ "Attribute 'javax.websocket.server.ServerContainer' not found in ServletContext");
if (this.asyncSendTimeout != null) {
this.serverContainer.setAsyncSendTimeout(this.asyncSendTimeout);
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java
index 6d5edef2..327df2f2 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java
@@ -16,13 +16,15 @@
package org.springframework.web.socket.server.standard;
+import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.Decoder;
@@ -59,209 +61,262 @@ import org.springframework.util.ReflectionUtils;
import org.springframework.web.socket.server.HandshakeFailureException;
/**
- * A WebSocket {@code RequestUpgradeStrategy} for use with WildFly and its
- * underlying Undertow web server. Also compatible with embedded Undertow usage.
+ * A WebSocket {@code RequestUpgradeStrategy} for WildFly and its underlying
+ * Undertow web server. Also compatible with embedded Undertow usage.
*
- * <p>Compatible with Undertow 1.0 to 1.3 - as included in WildFly 8.x, 9 and 10.
+ * <p>Designed for Undertow 1.3.5+ as of Spring Framework 4.3, with a fallback
+ * strategy for Undertow 1.0 to 1.3 - as included in WildFly 8.x, 9 and 10.
*
* @author Rossen Stoyanchev
- * @author Brian Clozel
- * @author Juergen Hoeller
* @since 4.0.1
*/
public class UndertowRequestUpgradeStrategy extends AbstractStandardUpgradeStrategy {
- private static final Constructor<ServletWebSocketHttpExchange> exchangeConstructor;
+ private static final boolean HAS_DO_UPGRADE = ClassUtils.hasMethod(ServerWebSocketContainer.class, "doUpgrade",
+ HttpServletRequest.class, HttpServletResponse.class, ServerEndpointConfig.class, Map.class);
- private static final boolean exchangeConstructorWithPeerConnections;
+ private static final FallbackStrategy FALLBACK_STRATEGY = (HAS_DO_UPGRADE ? null : new FallbackStrategy());
- private static final Constructor<ConfiguredServerEndpoint> endpointConstructor;
-
- private static final boolean endpointConstructorWithEndpointFactory;
-
- private static final Method getBufferPoolMethod;
-
- private static final Method createChannelMethod;
-
- static {
- try {
- Class<ServletWebSocketHttpExchange> exchangeType = ServletWebSocketHttpExchange.class;
- Class<?>[] exchangeParamTypes =
- new Class<?>[] {HttpServletRequest.class, HttpServletResponse.class, Set.class};
- Constructor<ServletWebSocketHttpExchange> exchangeCtor =
- ClassUtils.getConstructorIfAvailable(exchangeType, exchangeParamTypes);
- if (exchangeCtor != null) {
- // Undertow 1.1+
- exchangeConstructor = exchangeCtor;
- exchangeConstructorWithPeerConnections = true;
- }
- else {
- // Undertow 1.0
- exchangeParamTypes = new Class<?>[] {HttpServletRequest.class, HttpServletResponse.class};
- exchangeConstructor = exchangeType.getConstructor(exchangeParamTypes);
- exchangeConstructorWithPeerConnections = false;
- }
-
- Class<ConfiguredServerEndpoint> endpointType = ConfiguredServerEndpoint.class;
- Class<?>[] endpointParamTypes = new Class<?>[] {ServerEndpointConfig.class, InstanceFactory.class,
- PathTemplate.class, EncodingFactory.class, AnnotatedEndpointFactory.class};
- Constructor<ConfiguredServerEndpoint> endpointCtor =
- ClassUtils.getConstructorIfAvailable(endpointType, endpointParamTypes);
- if (endpointCtor != null) {
- // Undertow 1.1+
- endpointConstructor = endpointCtor;
- endpointConstructorWithEndpointFactory = true;
- }
- else {
- // Undertow 1.0
- endpointParamTypes = new Class<?>[] {ServerEndpointConfig.class, InstanceFactory.class,
- PathTemplate.class, EncodingFactory.class};
- endpointConstructor = endpointType.getConstructor(endpointParamTypes);
- endpointConstructorWithEndpointFactory = false;
- }
-
- // Adapting between different Pool API types in Undertow 1.0-1.2 vs 1.3
- getBufferPoolMethod = WebSocketHttpExchange.class.getMethod("getBufferPool");
- createChannelMethod = ReflectionUtils.findMethod(Handshake.class, "createChannel", (Class<?>[]) null);
- }
- catch (Throwable ex) {
- throw new IllegalStateException("Incompatible Undertow API version", ex);
- }
- }
-
- private static final String[] supportedVersions = new String[] {
+ private static final String[] VERSIONS = new String[] {
WebSocketVersion.V13.toHttpHeaderValue(),
WebSocketVersion.V08.toHttpHeaderValue(),
WebSocketVersion.V07.toHttpHeaderValue()
};
- private final Set<WebSocketChannel> peerConnections;
+ @Override
+ public String[] getSupportedVersions() {
+ return VERSIONS;
+ }
+ @Override
+ protected void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
+ String selectedProtocol, List<Extension> selectedExtensions, Endpoint endpoint)
+ throws HandshakeFailureException {
+
+ if (HAS_DO_UPGRADE) {
+ HttpServletRequest servletRequest = getHttpServletRequest(request);
+ HttpServletResponse servletResponse = getHttpServletResponse(response);
+
+ StringBuffer requestUrl = servletRequest.getRequestURL();
+ String path = servletRequest.getRequestURI(); // shouldn't matter
+ Map<String, String> pathParams = Collections.<String, String>emptyMap();
- public UndertowRequestUpgradeStrategy() {
- if (exchangeConstructorWithPeerConnections) {
- this.peerConnections = Collections.newSetFromMap(new ConcurrentHashMap<WebSocketChannel, Boolean>());
+ ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(path, endpoint);
+ endpointConfig.setSubprotocols(Collections.singletonList(selectedProtocol));
+ endpointConfig.setExtensions(selectedExtensions);
+
+ try {
+ getContainer(servletRequest).doUpgrade(servletRequest, servletResponse, endpointConfig, pathParams);
+ }
+ catch (ServletException ex) {
+ throw new HandshakeFailureException(
+ "Servlet request failed to upgrade to WebSocket: " + requestUrl, ex);
+ }
+ catch (IOException ex) {
+ throw new HandshakeFailureException(
+ "Response update failed during upgrade to WebSocket: " + requestUrl, ex);
+ }
}
else {
- this.peerConnections = null;
+ FALLBACK_STRATEGY.upgradeInternal(request, response, selectedProtocol, selectedExtensions, endpoint);
}
}
-
- @Override
- public String[] getSupportedVersions() {
- return supportedVersions;
+ public ServerWebSocketContainer getContainer(HttpServletRequest request) {
+ return (ServerWebSocketContainer) super.getContainer(request);
}
- @Override
- protected void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
- String selectedProtocol, List<Extension> selectedExtensions, final Endpoint endpoint)
- throws HandshakeFailureException {
- HttpServletRequest servletRequest = getHttpServletRequest(request);
- HttpServletResponse servletResponse = getHttpServletResponse(response);
+ /**
+ * Strategy for use with Undertow 1.0 to 1.3 before there was a public API
+ * to perform a WebSocket upgrade.
+ */
+ private static class FallbackStrategy extends AbstractStandardUpgradeStrategy {
- final ServletWebSocketHttpExchange exchange = createHttpExchange(servletRequest, servletResponse);
- exchange.putAttachment(HandshakeUtil.PATH_PARAMS, Collections.<String, String>emptyMap());
+ private static final Constructor<ServletWebSocketHttpExchange> exchangeConstructor;
- ServerWebSocketContainer wsContainer = (ServerWebSocketContainer) getContainer(servletRequest);
- final EndpointSessionHandler endpointSessionHandler = new EndpointSessionHandler(wsContainer);
+ private static final boolean exchangeConstructorWithPeerConnections;
- final ConfiguredServerEndpoint configuredServerEndpoint = createConfiguredServerEndpoint(
- selectedProtocol, selectedExtensions, endpoint, servletRequest);
+ private static final Constructor<ConfiguredServerEndpoint> endpointConstructor;
- final Handshake handshake = getHandshakeToUse(exchange, configuredServerEndpoint);
+ private static final boolean endpointConstructorWithEndpointFactory;
- exchange.upgradeChannel(new HttpUpgradeListener() {
- @Override
- public void handleUpgrade(StreamConnection connection, HttpServerExchange serverExchange) {
- Object bufferPool = ReflectionUtils.invokeMethod(getBufferPoolMethod, exchange);
- WebSocketChannel channel = (WebSocketChannel) ReflectionUtils.invokeMethod(
- createChannelMethod, handshake, exchange, connection, bufferPool);
- if (peerConnections != null) {
- peerConnections.add(channel);
+ private static final Method getBufferPoolMethod;
+
+ private static final Method createChannelMethod;
+
+ static {
+ try {
+ Class<ServletWebSocketHttpExchange> exchangeType = ServletWebSocketHttpExchange.class;
+ Class<?>[] exchangeParamTypes =
+ new Class<?>[] {HttpServletRequest.class, HttpServletResponse.class, Set.class};
+ Constructor<ServletWebSocketHttpExchange> exchangeCtor =
+ ClassUtils.getConstructorIfAvailable(exchangeType, exchangeParamTypes);
+ if (exchangeCtor != null) {
+ // Undertow 1.1+
+ exchangeConstructor = exchangeCtor;
+ exchangeConstructorWithPeerConnections = true;
+ }
+ else {
+ // Undertow 1.0
+ exchangeParamTypes = new Class<?>[] {HttpServletRequest.class, HttpServletResponse.class};
+ exchangeConstructor = exchangeType.getConstructor(exchangeParamTypes);
+ exchangeConstructorWithPeerConnections = false;
}
- endpointSessionHandler.onConnect(exchange, channel);
- }
- });
- handshake.handshake(exchange);
- }
+ Class<ConfiguredServerEndpoint> endpointType = ConfiguredServerEndpoint.class;
+ Class<?>[] endpointParamTypes = new Class<?>[] {ServerEndpointConfig.class, InstanceFactory.class,
+ PathTemplate.class, EncodingFactory.class, AnnotatedEndpointFactory.class};
+ Constructor<ConfiguredServerEndpoint> endpointCtor =
+ ClassUtils.getConstructorIfAvailable(endpointType, endpointParamTypes);
+ if (endpointCtor != null) {
+ // Undertow 1.1+
+ endpointConstructor = endpointCtor;
+ endpointConstructorWithEndpointFactory = true;
+ }
+ else {
+ // Undertow 1.0
+ endpointParamTypes = new Class<?>[] {ServerEndpointConfig.class, InstanceFactory.class,
+ PathTemplate.class, EncodingFactory.class};
+ endpointConstructor = endpointType.getConstructor(endpointParamTypes);
+ endpointConstructorWithEndpointFactory = false;
+ }
- private ServletWebSocketHttpExchange createHttpExchange(HttpServletRequest request, HttpServletResponse response) {
- try {
- return (this.peerConnections != null ?
- exchangeConstructor.newInstance(request, response, this.peerConnections) :
- exchangeConstructor.newInstance(request, response));
+ // Adapting between different Pool API types in Undertow 1.0-1.2 vs 1.3
+ getBufferPoolMethod = WebSocketHttpExchange.class.getMethod("getBufferPool");
+ createChannelMethod = ReflectionUtils.findMethod(Handshake.class, "createChannel", (Class<?>[]) null);
+ }
+ catch (Throwable ex) {
+ throw new IllegalStateException("Incompatible Undertow API version", ex);
+ }
}
- catch (Exception ex) {
- throw new HandshakeFailureException("Failed to instantiate ServletWebSocketHttpExchange", ex);
+
+ private final Set<WebSocketChannel> peerConnections;
+
+ public FallbackStrategy() {
+ if (exchangeConstructorWithPeerConnections) {
+ this.peerConnections = Collections.newSetFromMap(new ConcurrentHashMap<WebSocketChannel, Boolean>());
+ }
+ else {
+ this.peerConnections = null;
+ }
}
- }
- private Handshake getHandshakeToUse(ServletWebSocketHttpExchange exchange, ConfiguredServerEndpoint endpoint) {
- Handshake handshake = new JsrHybi13Handshake(endpoint);
- if (handshake.matches(exchange)) {
- return handshake;
+ @Override
+ public String[] getSupportedVersions() {
+ return VERSIONS;
}
- handshake = new JsrHybi08Handshake(endpoint);
- if (handshake.matches(exchange)) {
- return handshake;
+
+ @Override
+ protected void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
+ String selectedProtocol, List<Extension> selectedExtensions, final Endpoint endpoint)
+ throws HandshakeFailureException {
+
+ HttpServletRequest servletRequest = getHttpServletRequest(request);
+ HttpServletResponse servletResponse = getHttpServletResponse(response);
+
+ final ServletWebSocketHttpExchange exchange = createHttpExchange(servletRequest, servletResponse);
+ exchange.putAttachment(HandshakeUtil.PATH_PARAMS, Collections.<String, String>emptyMap());
+
+ ServerWebSocketContainer wsContainer = (ServerWebSocketContainer) getContainer(servletRequest);
+ final EndpointSessionHandler endpointSessionHandler = new EndpointSessionHandler(wsContainer);
+
+ final ConfiguredServerEndpoint configuredServerEndpoint = createConfiguredServerEndpoint(
+ selectedProtocol, selectedExtensions, endpoint, servletRequest);
+
+ final Handshake handshake = getHandshakeToUse(exchange, configuredServerEndpoint);
+
+ exchange.upgradeChannel(new HttpUpgradeListener() {
+ @Override
+ public void handleUpgrade(StreamConnection connection, HttpServerExchange serverExchange) {
+ Object bufferPool = ReflectionUtils.invokeMethod(getBufferPoolMethod, exchange);
+ WebSocketChannel channel = (WebSocketChannel) ReflectionUtils.invokeMethod(
+ createChannelMethod, handshake, exchange, connection, bufferPool);
+ if (peerConnections != null) {
+ peerConnections.add(channel);
+ }
+ endpointSessionHandler.onConnect(exchange, channel);
+ }
+ });
+
+ handshake.handshake(exchange);
}
- handshake = new JsrHybi07Handshake(endpoint);
- if (handshake.matches(exchange)) {
- return handshake;
+
+ private ServletWebSocketHttpExchange createHttpExchange(HttpServletRequest request, HttpServletResponse response) {
+ try {
+ return (this.peerConnections != null ?
+ exchangeConstructor.newInstance(request, response, this.peerConnections) :
+ exchangeConstructor.newInstance(request, response));
+ }
+ catch (Exception ex) {
+ throw new HandshakeFailureException("Failed to instantiate ServletWebSocketHttpExchange", ex);
+ }
}
- // Should never occur
- throw new HandshakeFailureException("No matching Undertow Handshake found: " + exchange.getRequestHeaders());
- }
- private ConfiguredServerEndpoint createConfiguredServerEndpoint(String selectedProtocol,
- List<Extension> selectedExtensions, Endpoint endpoint, HttpServletRequest servletRequest) {
-
- String path = servletRequest.getRequestURI(); // shouldn't matter
- ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration(path, endpoint);
- endpointRegistration.setSubprotocols(Arrays.asList(selectedProtocol));
- endpointRegistration.setExtensions(selectedExtensions);
-
- EncodingFactory encodingFactory = new EncodingFactory(
- Collections.<Class<?>, List<InstanceFactory<? extends Encoder>>>emptyMap(),
- Collections.<Class<?>, List<InstanceFactory<? extends Decoder>>>emptyMap(),
- Collections.<Class<?>, List<InstanceFactory<? extends Encoder>>>emptyMap(),
- Collections.<Class<?>, List<InstanceFactory<? extends Decoder>>>emptyMap());
- try {
- return (endpointConstructorWithEndpointFactory ?
- endpointConstructor.newInstance(endpointRegistration,
- new EndpointInstanceFactory(endpoint), null, encodingFactory, null) :
- endpointConstructor.newInstance(endpointRegistration,
- new EndpointInstanceFactory(endpoint), null, encodingFactory));
+ private Handshake getHandshakeToUse(ServletWebSocketHttpExchange exchange, ConfiguredServerEndpoint endpoint) {
+ Handshake handshake = new JsrHybi13Handshake(endpoint);
+ if (handshake.matches(exchange)) {
+ return handshake;
+ }
+ handshake = new JsrHybi08Handshake(endpoint);
+ if (handshake.matches(exchange)) {
+ return handshake;
+ }
+ handshake = new JsrHybi07Handshake(endpoint);
+ if (handshake.matches(exchange)) {
+ return handshake;
+ }
+ // Should never occur
+ throw new HandshakeFailureException("No matching Undertow Handshake found: " + exchange.getRequestHeaders());
}
- catch (Exception ex) {
- throw new HandshakeFailureException("Failed to instantiate ConfiguredServerEndpoint", ex);
+
+ private ConfiguredServerEndpoint createConfiguredServerEndpoint(String selectedProtocol,
+ List<Extension> selectedExtensions, Endpoint endpoint, HttpServletRequest servletRequest) {
+
+ String path = servletRequest.getRequestURI(); // shouldn't matter
+ ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration(path, endpoint);
+ endpointRegistration.setSubprotocols(Collections.singletonList(selectedProtocol));
+ endpointRegistration.setExtensions(selectedExtensions);
+
+ EncodingFactory encodingFactory = new EncodingFactory(
+ Collections.<Class<?>, List<InstanceFactory<? extends Encoder>>>emptyMap(),
+ Collections.<Class<?>, List<InstanceFactory<? extends Decoder>>>emptyMap(),
+ Collections.<Class<?>, List<InstanceFactory<? extends Encoder>>>emptyMap(),
+ Collections.<Class<?>, List<InstanceFactory<? extends Decoder>>>emptyMap());
+ try {
+ return (endpointConstructorWithEndpointFactory ?
+ endpointConstructor.newInstance(endpointRegistration,
+ new EndpointInstanceFactory(endpoint), null, encodingFactory, null) :
+ endpointConstructor.newInstance(endpointRegistration,
+ new EndpointInstanceFactory(endpoint), null, encodingFactory));
+ }
+ catch (Exception ex) {
+ throw new HandshakeFailureException("Failed to instantiate ConfiguredServerEndpoint", ex);
+ }
}
- }
- private static class EndpointInstanceFactory implements InstanceFactory<Endpoint> {
+ private static class EndpointInstanceFactory implements InstanceFactory<Endpoint> {
- private final Endpoint endpoint;
+ private final Endpoint endpoint;
- public EndpointInstanceFactory(Endpoint endpoint) {
- this.endpoint = endpoint;
- }
+ public EndpointInstanceFactory(Endpoint endpoint) {
+ this.endpoint = endpoint;
+ }
- @Override
- public InstanceHandle<Endpoint> createInstance() throws InstantiationException {
- return new InstanceHandle<Endpoint>() {
- @Override
- public Endpoint getInstance() {
- return endpoint;
- }
- @Override
- public void release() {
- }
- };
+ @Override
+ public InstanceHandle<Endpoint> createInstance() throws InstantiationException {
+ return new InstanceHandle<Endpoint>() {
+ @Override
+ public Endpoint getInstance() {
+ return endpoint;
+ }
+ @Override
+ public void release() {
+ }
+ };
+ }
}
}
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java
index c16d747a..58b29407 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -60,7 +60,6 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS
private static final WebLogicServletWriterHelper servletWriterHelper = new WebLogicServletWriterHelper();
private static final Connection.CloseListener noOpCloseListener = new Connection.CloseListener() {
-
@Override
public void close(CloseReason reason) {
}
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java
index d76ed66d..ab51f293 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -72,25 +72,23 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Life
private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
- private static final ClassLoader classLoader = AbstractHandshakeHandler.class.getClassLoader();
-
private static final boolean jettyWsPresent = ClassUtils.isPresent(
- "org.eclipse.jetty.websocket.server.WebSocketServerFactory", classLoader);
+ "org.eclipse.jetty.websocket.server.WebSocketServerFactory", AbstractHandshakeHandler.class.getClassLoader());
private static final boolean tomcatWsPresent = ClassUtils.isPresent(
- "org.apache.tomcat.websocket.server.WsHttpUpgradeHandler", classLoader);
+ "org.apache.tomcat.websocket.server.WsHttpUpgradeHandler", AbstractHandshakeHandler.class.getClassLoader());
private static final boolean undertowWsPresent = ClassUtils.isPresent(
- "io.undertow.websockets.jsr.ServerWebSocketContainer", classLoader);
+ "io.undertow.websockets.jsr.ServerWebSocketContainer", AbstractHandshakeHandler.class.getClassLoader());
private static final boolean glassfishWsPresent = ClassUtils.isPresent(
- "org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler", classLoader);
+ "org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler", AbstractHandshakeHandler.class.getClassLoader());
private static final boolean weblogicWsPresent = ClassUtils.isPresent(
- "weblogic.websocket.tyrus.TyrusServletWriter", classLoader);
+ "weblogic.websocket.tyrus.TyrusServletWriter", AbstractHandshakeHandler.class.getClassLoader());
private static final boolean websphereWsPresent = ClassUtils.isPresent(
- "com.ibm.websphere.wsoc.WsWsocServerContainer", classLoader);
+ "com.ibm.websphere.wsoc.WsWsocServerContainer", AbstractHandshakeHandler.class.getClassLoader());
protected final Log logger = LogFactory.getLog(getClass());
@@ -146,7 +144,7 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Life
}
try {
- Class<?> clazz = ClassUtils.forName(className, classLoader);
+ Class<?> clazz = ClassUtils.forName(className, AbstractHandshakeHandler.class.getClassLoader());
return (RequestUpgradeStrategy) clazz.newInstance();
}
catch (Throwable ex) {
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java
index 7b7f51d2..2d0fac29 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -54,6 +54,7 @@ public abstract class AbstractXhrTransport implements XhrTransport {
PRELUDE = new String(bytes, SockJsFrame.CHARSET);
}
+
protected Log logger = LogFactory.getLog(getClass());
private boolean xhrStreamingDisabled;
@@ -137,6 +138,7 @@ public abstract class AbstractXhrTransport implements XhrTransport {
URI receiveUrl, HttpHeaders handshakeHeaders, XhrClientSockJsSession session,
SettableListenableFuture<WebSocketSession> connectFuture);
+
// InfoReceiver methods
@Override
@@ -165,6 +167,7 @@ public abstract class AbstractXhrTransport implements XhrTransport {
protected abstract ResponseEntity<String> executeInfoRequestInternal(URI infoUrl, HttpHeaders headers);
+
// XhrTransport methods
@Override
@@ -184,8 +187,8 @@ public abstract class AbstractXhrTransport implements XhrTransport {
}
}
- protected abstract ResponseEntity<String> executeSendRequestInternal(URI url,
- HttpHeaders headers, TextMessage message);
+ protected abstract ResponseEntity<String> executeSendRequestInternal(
+ URI url, HttpHeaders headers, TextMessage message);
@Override
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java
index 863059a4..4f70016a 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java
@@ -1,11 +1,11 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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
+ * 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,
@@ -326,14 +326,12 @@ public class UndertowXhrTransport extends AbstractXhrTransport {
result.getResponse().putAttachment(RESPONSE_BODY, string);
latch.countDown();
}
-
@Override
protected void error(IOException ex) {
onFailure(latch, ex);
}
}.setup(result.getResponseChannel());
}
-
@Override
public void failed(IOException ex) {
onFailure(latch, ex);
@@ -473,7 +471,7 @@ public class UndertowXhrTransport extends AbstractXhrTransport {
public void onFailure(Throwable failure) {
IoUtils.safeClose(this.connection);
- if (connectFuture.setException(failure)) {
+ if (this.connectFuture.setException(failure)) {
return;
}
if (this.session.isDisconnected()) {
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/frame/Jackson2SockJsMessageCodec.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/frame/Jackson2SockJsMessageCodec.java
index e879b26f..8ed46882 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/frame/Jackson2SockJsMessageCodec.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/frame/Jackson2SockJsMessageCodec.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -28,7 +28,7 @@ import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.Assert;
/**
- * A Jackson 2.x codec for encoding and decoding SockJS messages.
+ * A Jackson 2.6+ codec for encoding and decoding SockJS messages.
*
* <p>It customizes Jackson's default properties with the following ones:
* <ul>
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractHttpSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractHttpSockJsSession.java
index df0e2771..451dd6bd 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractHttpSockJsSession.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractHttpSockJsSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -196,8 +196,18 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
this.uri = request.getURI();
this.handshakeHeaders = request.getHeaders();
this.principal = request.getPrincipal();
- this.localAddress = request.getLocalAddress();
- this.remoteAddress = request.getRemoteAddress();
+ try {
+ this.localAddress = request.getLocalAddress();
+ }
+ catch (Exception ex) {
+ // Ignore
+ }
+ try {
+ this.remoteAddress = request.getRemoteAddress();
+ }
+ catch (Exception ex) {
+ // Ignore
+ }
synchronized (this.responseLock) {
try {
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java
index e3cb4488..d8066c2e 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java
@@ -35,7 +35,6 @@ import org.springframework.web.socket.sockjs.transport.SockJsServiceConfig;
*/
public class PollingSockJsSession extends AbstractHttpSockJsSession {
-
public PollingSockJsSession(String sessionId, SockJsServiceConfig config,
WebSocketHandler wsHandler, Map<String, Object> attributes) {
diff --git a/spring-websocket/src/main/resources/META-INF/spring.schemas b/spring-websocket/src/main/resources/META-INF/spring.schemas
index 338e12b0..1b557438 100644
--- a/spring-websocket/src/main/resources/META-INF/spring.schemas
+++ b/spring-websocket/src/main/resources/META-INF/spring.schemas
@@ -1,4 +1,5 @@
http\://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd=org/springframework/web/socket/config/spring-websocket-4.0.xsd
http\://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd=org/springframework/web/socket/config/spring-websocket-4.1.xsd
http\://www.springframework.org/schema/websocket/spring-websocket-4.2.xsd=org/springframework/web/socket/config/spring-websocket-4.2.xsd
-http\://www.springframework.org/schema/websocket/spring-websocket.xsd=org/springframework/web/socket/config/spring-websocket-4.2.xsd
+http\://www.springframework.org/schema/websocket/spring-websocket-4.3.xsd=org/springframework/web/socket/config/spring-websocket-4.3.xsd
+http\://www.springframework.org/schema/websocket/spring-websocket.xsd=org/springframework/web/socket/config/spring-websocket-4.3.xsd
diff --git a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.0.xsd b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.0.xsd
index 36fa8af6..861fd865 100644
--- a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.0.xsd
+++ b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.0.xsd
@@ -349,7 +349,7 @@
This is essentially the "Unbounded queues" strategy as explained in java.util.concurrent.ThreadPoolExecutor.
When this strategy is used, the max pool size is effectively ignored.
By default this is set to twice the value of Runtime.availableProcessors().
- In an an application where tasks do not block frequently,
+ In an application where tasks do not block frequently,
the number should be closer to or equal to the number of available CPUs/cores.
]]></xsd:documentation>
</xsd:annotation>
diff --git a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.1.xsd b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.1.xsd
index a5cabbd6..943a7db4 100644
--- a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.1.xsd
+++ b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.1.xsd
@@ -367,7 +367,7 @@
This is essentially the "Unbounded queues" strategy as explained in java.util.concurrent.ThreadPoolExecutor.
When this strategy is used, the max pool size is effectively ignored.
By default this is set to twice the value of Runtime.availableProcessors().
- In an an application where tasks do not block frequently,
+ In an application where tasks do not block frequently,
the number should be closer to or equal to the number of available CPUs/cores.
]]></xsd:documentation>
</xsd:annotation>
diff --git a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.2.xsd b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.2.xsd
index f6a8718a..f60e11cb 100644
--- a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.2.xsd
+++ b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.2.xsd
@@ -407,7 +407,7 @@
This is essentially the "Unbounded queues" strategy as explained in java.util.concurrent.ThreadPoolExecutor.
When this strategy is used, the max pool size is effectively ignored.
By default this is set to twice the value of Runtime.availableProcessors().
- In an an application where tasks do not block frequently,
+ In an application where tasks do not block frequently,
the number should be closer to or equal to the number of available CPUs/cores.
]]></xsd:documentation>
</xsd:annotation>
diff --git a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.3.xsd b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.3.xsd
new file mode 100644
index 00000000..10348c4a
--- /dev/null
+++ b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.3.xsd
@@ -0,0 +1,931 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<xsd:schema xmlns="http://www.springframework.org/schema/websocket"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ xmlns:tool="http://www.springframework.org/schema/tool"
+ targetNamespace="http://www.springframework.org/schema/websocket"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified">
+
+ <xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"/>
+ <xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-4.3.xsd"/>
+
+ <xsd:complexType name="mapping">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ An entry in the registered HandlerMapping that matches a path with a handler.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="path" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ A path that maps a particular request to a handler.
+ Exact path mapping URIs (such as "/myPath") are supported as well as Ant-type path patterns (such as /myPath/**).
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="handler" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.WebSocketHandler"><![CDATA[
+ The bean name of a WebSocketHandler to use for requests that match the path configuration.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="handshake-handler">
+ <xsd:attribute name="ref" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.server.HandshakeHandler"><![CDATA[
+ The bean name of a HandshakeHandler to use for processing WebSocket handshake requests.
+ If none specified, a DefaultHandshakeHandler will be configured by default.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="handshake-interceptors">
+ <xsd:annotation>
+ <xsd:documentation source="org.springframework.web.socket.server.HandshakeInterceptor"><![CDATA[
+ A list of HandshakeInterceptor beans definition and references.
+ A HandshakeInterceptor can be used to inspect the handshake request and response as well as to pass attributes to the target WebSocketHandler.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element ref="beans:bean">
+ <xsd:annotation>
+ <xsd:documentation source="org.springframework.web.socket.server.HandshakeInterceptor"><![CDATA[
+ A HandshakeInterceptor bean definition.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element ref="beans:ref">
+ <xsd:annotation>
+ <xsd:documentation source="org.springframework.web.socket.server.HandshakeInterceptor"><![CDATA[
+ A reference to a HandshakeInterceptor bean.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="sockjs-service">
+ <xsd:annotation>
+ <xsd:documentation source="org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService"><![CDATA[
+ Configures a DefaultSockJsService for processing HTTP requests from SockJS clients.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="transport-handlers" minOccurs="0" maxOccurs="1">
+ <xsd:annotation>
+ <xsd:documentation source="org.springframework.web.socket.sockjs.transport.TransportHandler"><![CDATA[
+ List of TransportHandler beans to be configured for the current handlers element.
+ One can choose not to register the default TransportHandlers and/or override those using
+ custom TransportHandlers.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element ref="beans:bean">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ A TransportHandler bean definition.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element ref="beans:ref">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ A reference to a TransportHandler bean.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:sequence>
+ <xsd:attribute name="register-defaults" type="xsd:boolean" default="true">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Whether or not default TransportHandlers registrations should be added in addition to the ones provided within this element.
+ Default registrations include XhrPollingTransportHandler, XhrReceivingTransportHandler,
+ JsonpPollingTransportHandler, JsonpReceivingTransportHandler, XhrStreamingTransportHandler,
+ EventSourceTransportHandler, HtmlFileTransportHandler, and WebSocketTransportHandler.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
+ A unique name for the service, mainly for logging purposes.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="client-library-url" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
+ Transports with no native cross-domain communication (e.g. "eventsource",
+ "htmlfile") must get a simple page from the "foreign" domain in an invisible
+ iframe so that code in the iframe can run from a domain local to the SockJS
+ server. Since the iframe needs to load the SockJS javascript client library,
+ this property allows specifying where to load it from.
+
+ By default this is set to point to
+ "https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js". However it can
+ also be set to point to a URL served by the application.
+
+ Note that it's possible to specify a relative URL in which case the URL
+ must be relative to the iframe URL. For example assuming a SockJS endpoint
+ mapped to "/sockjs", and resulting iframe URL "/sockjs/iframe.html", then the
+ The relative URL must start with "../../" to traverse up to the location
+ above the SockJS mapping. In case of a prefix-based Servlet mapping one more
+ traversal may be needed.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="stream-bytes-limit" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
+ Minimum number of bytes that can be send over a single HTTP streaming request before it will be closed.
+ Defaults to 128K (i.e. 128 1024).
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="session-cookie-needed" type="xsd:boolean">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
+ The "cookie_needed" value in the response from the SockJs "/info" endpoint.
+ This property indicates whether the use of a JSESSIONID cookie is required for the application to function correctly,
+ e.g. for load balancing or in Java Servlet containers for the use of an HTTP session.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="heartbeat-time" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
+ 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 client in order to keep the connection from breaking.
+ The default value is 25,000 (25 seconds).
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="disconnect-delay" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
+ 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.
+ The default value is 5000.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="message-cache-size" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
+ The number of server-to-client messages that a session can cache while waiting for
+ the next HTTP polling request from the client.
+ The default size is 100.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="websocket-enabled" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
+ Some load balancers don't support websockets. Set this option to "false" to disable the WebSocket transport on the server side.
+ The default value is "true".
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="scheduler" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
+ The bean name of a TaskScheduler; a new ThreadPoolTaskScheduler instance will be created if no value is provided.
+ This scheduler instance will be used for scheduling heart-beat messages.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="message-codec" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
+ The bean name of a SockJsMessageCodec to use for encoding and decoding SockJS messages.
+ By default Jackson2SockJsMessageCodec is used requiring the Jackson library to be present on the classpath.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="suppress-cors" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.sockjs.support.AbstractSockJsService"><![CDATA[
+ This option can be used to disable automatic addition of CORS headers for SockJS requests.
+ The default value is "false".
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="stomp-broker-relay">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ Configures a MessageHandler that handles messages by forwarding them to a STOMP broker.
+ This MessageHandler also opens a default "system" TCP connection to the message
+ broker that is used for sending messages that originate from the server application (as
+ opposed to from a client).
+ The "login", "password", "heartbeat-send-interval" and "heartbeat-receive-interval" attributes
+ are provided to configure this "system" connection.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="prefix" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ Comma-separated list of destination prefixes supported by the broker being configured.
+ Destinations that do not match the given prefix(es) are ignored.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="relay-host" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ The STOMP message broker host.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="relay-port" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ The STOMP message broker port.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="client-login" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ The login to use when creating connections to the STOMP broker on behalf of connected clients.
+ By default this is set to "guest".
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="client-passcode" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ The passcode to use when creating connections to the STOMP broker on behalf of connected clients.
+ By default this is set to "guest".
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="system-login" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ The login for the shared "system" connection used to send messages to
+ the STOMP broker from within the application, i.e. messages not associated
+ with a specific client session (e.g. REST/HTTP request handling method).
+ By default this is set to "guest".
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="system-passcode" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ The passcode for the shared "system" connection used to send messages to
+ the STOMP broker from within the application, i.e. messages not associated
+ with a specific client session (e.g. REST/HTTP request handling method).
+ By default this is set to "guest".
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="heartbeat-send-interval" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ The interval, in milliseconds, at which the "system" connection will send heartbeats to the STOMP broker.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="heartbeat-receive-interval" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ The interval, in milliseconds, at which the "system" connection expects to receive heartbeats from the STOMP broker.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="auto-startup" type="xsd:boolean">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ Whether or not the StompBrokerRelay should be automatically started as part of its SmartLifecycle,
+ i.e. at the time of an application context refresh.
+ Default value is "true".
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="virtual-host" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler"><![CDATA[
+ The value of the "host" header to use in STOMP CONNECT frames sent to the STOMP broker.
+ This may be useful for example in a cloud environment where the actual host to which
+ the TCP connection is established is different from the host providing the cloud-based STOMP service.
+ By default this property is not set.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="user-destination-broadcast" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Set a destination to broadcast messages to that remain unresolved because
+ the user is not connected. In a multi-application server scenario this
+ gives other application servers a chance to try.
+ By default this is not set.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="user-registry-broadcast" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Set a destination to broadcast the content of the local user registry to
+ and to listen for such broadcasts from other servers. In a multi-application
+ server scenarios this allows each server's user registry to be aware of
+ users connected to other servers.
+ By default this is not set.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="simple-broker">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler"><![CDATA[
+ Configures a SimpleBrokerMessageHandler that handles messages as a simple message broker implementation.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="prefix" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.SimpleBrokerMessageHandler"><![CDATA[
+ Comma-separated list of destination prefixes supported by the broker being configured.
+ Destinations that do not match the given prefix(es) are ignored.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="heartbeat" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.SimpleBrokerMessageHandler"><![CDATA[
+ Configure the value for the heartbeat settings. The first number represents how often the server will
+ write or send a heartbeat. The second is how often the client should write. 0 means no heartbeats.
+ By default this is set to "0, 0" unless the scheduler attribute is also set in which case the
+ default becomes "10000,10000" (in milliseconds).
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="scheduler" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.simp.stomp.SimpleBrokerMessageHandler"><![CDATA[
+ The name of a task TaskScheduler to use for heartbeat support. Setting this property also
+ automatically sets the heartbeat attribute to "10000, 10000".
+ By default this attribute is not set.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="channel">
+ <xsd:sequence>
+ <xsd:element name="executor" type="channel-executor" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="interceptors" type="channel-interceptors" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="channel-executor">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><![CDATA[
+ Configuration for the ThreadPoolTaskExecutor that sends messages for the message channel.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="core-pool-size" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><![CDATA[
+ Set the core pool size of the ThreadPoolExecutor.
+ NOTE: the core pool size is effectively the max pool size when an unbounded queue-capacity is configured (the default).
+ This is essentially the "Unbounded queues" strategy as explained in java.util.concurrent.ThreadPoolExecutor.
+ When this strategy is used, the max pool size is effectively ignored.
+ By default this is set to twice the value of Runtime.availableProcessors().
+ In an application where tasks do not block frequently,
+ the number should be closer to or equal to the number of available CPUs/cores.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="max-pool-size" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><![CDATA[
+ Set the max pool size of the ThreadPoolExecutor.
+ NOTE: when an unbounded queue-capacity is configured (the default), the max pool size is effectively ignored.
+ See the "Unbounded queues" strategy in java.util.concurrent.ThreadPoolExecutor for more details.
+ By default this is set to Integer.MAX_VALUE.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="keep-alive-seconds" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><![CDATA[
+ Set the time limit for which threads may remain idle before being terminated.
+ If there are more than the core number of threads currently in the pool, after waiting this amount of time without
+ processing a task, excess threads will be terminated. This overrides any value set in the constructor.
+ By default this is set to 60.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="queue-capacity" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><![CDATA[
+ Set the queue capacity for the ThreadPoolExecutor.
+ NOTE: when an unbounded queue-capacity is configured (the default) the core pool size is effectively the max pool size.
+ This is essentially the "Unbounded queues" strategy as explained in java.util.concurrent.ThreadPoolExecutor.
+ When this strategy is used, the max pool size is effectively ignored.
+ By default this is set to Integer.MAX_VALUE.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="channel-interceptors">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.messaging.support.ChannelInterceptor"><![CDATA[
+ List of ChannelInterceptor beans to be used with this channel.
+ Empty by default.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element ref="beans:bean">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ A ChannelInterceptor bean definition.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element ref="beans:ref">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ A reference to a ChannelInterceptor bean.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <!-- Elements definitions -->
+
+ <xsd:element name="handlers">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configures WebSocket support by registering a SimpleUrlHandlerMapping and mapping
+ paths to registered WebSocketHandlers.
+
+ If a sockjs service is configured within this element, then a
+ SockJsHttpRequestHandler will handle
+ requests mapped to the given path.
+
+ Otherwise a WebSocketHttpRequestHandler
+ will be registered for that purpose.
+
+ See EnableWebSocket Javadoc for
+ information on code-based alternatives to enabling WebSocket support.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="mapping" type="mapping" minOccurs="1" maxOccurs="unbounded"/>
+ <xsd:element name="handshake-handler" type="handshake-handler" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="handshake-interceptors" type="handshake-interceptors" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="sockjs" type="sockjs-service" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+ <xsd:attribute name="order" type="xsd:token">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Order value for this SimpleUrlHandlerMapping.
+ Default value is 1.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="allowed-origins" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configure allowed {@code Origin} header values. Multiple origins may be specified
+ as a comma-separated list.
+
+ This check is mostly designed for browser clients. There is noting preventing other
+ types of client to modify the Origin header value.
+
+ When SockJS is enabled and allowed origins are restricted, transport types that do not
+ use {@code Origin} headers for cross origin requests (jsonp-polling, iframe-xhr-polling,
+ iframe-eventsource and iframe-htmlfile) are disabled. As a consequence, IE6/IE7 won't be
+ supported anymore and IE8/IE9 will only be supported without cookies.
+
+ By default, all origins are allowed.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="message-broker">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configures broker-backed messaging over WebSocket using a higher-level messaging sub-protocol.
+ Registers a SimpleUrlHandlerMapping and maps paths to registered Controllers.
+
+ A StompSubProtocolHandler is registered to handle various versions of the STOMP protocol.
+
+ See EnableWebSocketMessageBroker javadoc for information on code-based alternatives to enabling broker-backed messaging.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="transport" minOccurs="0" maxOccurs="1">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configure options related to the processing of messages received from and sent to WebSocket clients.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="decorator-factories" maxOccurs="1" minOccurs="0">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation source="org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory"><![CDATA[
+ Configure one or more factories to decorate the handler used to process WebSocket
+ messages. This may be useful for some advanced use cases, for example to allow
+ Spring Security to forcibly close the WebSocket session when the corresponding
+ HTTP session expires.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:element ref="beans:bean">
+ <xsd:annotation>
+ <xsd:documentation source="org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory"><![CDATA[
+ A WebSocketHandlerDecoratorFactory bean definition.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element ref="beans:ref">
+ <xsd:annotation>
+ <xsd:documentation source="org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory"><![CDATA[
+ A reference to a WebSocketHandlerDecoratorFactory bean.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ <xsd:attribute name="message-size" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configure the maximum size for an incoming sub-protocol message.
+ For example a STOMP message may be received as multiple WebSocket messages
+ or multiple HTTP POST requests when SockJS fallback options are in use.
+
+ In theory a WebSocket message can be almost unlimited in size.
+ In practice WebSocket servers impose limits on incoming message size.
+ STOMP clients for example tend to split large messages around 16K
+ boundaries. Therefore a server must be able to buffer partial content
+ and decode when enough data is received. Use this property to configure
+ the max size of the buffer to use.
+
+ The default value is 64K (i.e. 64 * 1024).
+
+ NOTE that the current version 1.2 of the STOMP spec
+ does not specifically discuss how to send STOMP messages over WebSocket.
+ Version 2 of the spec will but in the mean time existing client libraries
+ have already established a practice that servers must handle.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="send-timeout" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configure a time limit (in milliseconds) for the maximum amount of a time
+ allowed when sending messages to a WebSocket session or writing to an
+ HTTP response when SockJS fallback option are in use.
+
+ In general WebSocket servers expect that messages to a single WebSocket
+ session are sent from a single thread at a time. This is automatically
+ guaranteed when using {@code @EnableWebSocketMessageBroker} configuration.
+ If message sending is slow, or at least slower than rate of messages sending,
+ subsequent messages are buffered until either the {@code sendTimeLimit}
+ or the {@code sendBufferSizeLimit} are reached at which point the session
+ state is cleared and an attempt is made to close the session.
+
+ NOTE that the session time limit is checked only
+ on attempts to send additional messages. So if only a single message is
+ sent and it hangs, the session will not time out until another message is
+ sent or the underlying physical socket times out. So this is not a
+ replacement for WebSocket server or HTTP connection timeout but is rather
+ intended to control the extent of buffering of unsent messages.
+
+ NOTE that closing the session may not succeed in
+ actually closing the physical socket and may also hang. This is true
+ especially when using blocking IO such as the BIO connector in Tomcat
+ that is used by default on Tomcat 7. Therefore it is recommended to ensure
+ the server is using non-blocking IO such as Tomcat's NIO connector that
+ is used by default on Tomcat 8. If you must use blocking IO consider
+ customizing OS-level TCP settings, for example
+ {@code /proc/sys/net/ipv4/tcp_retries2} on Linux.
+
+ The default value is 10 seconds (i.e. 10 * 10000).
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="send-buffer-size" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configure the maximum amount of data to buffer when sending messages
+ to a WebSocket session, or an HTTP response when SockJS fallback
+ option are in use.
+
+ In general WebSocket servers expect that messages to a single WebSocket
+ session are sent from a single thread at a time. This is automatically
+ guaranteed when using {@code @EnableWebSocketMessageBroker} configuration.
+ If message sending is slow, or at least slower than rate of messages sending,
+ subsequent messages are buffered until either the {@code sendTimeLimit}
+ or the {@code sendBufferSizeLimit} are reached at which point the session
+ state is cleared and an attempt is made to close the session.
+
+ NOTE that closing the session may not succeed in
+ actually closing the physical socket and may also hang. This is true
+ especially when using blocking IO such as the BIO connector in Tomcat
+ configured by default on Tomcat 7. Therefore it is recommended to ensure
+ the server is using non-blocking IO such as Tomcat's NIO connector used
+ by default on Tomcat 8. If you must use blocking IO consider customizing
+ OS-level TCP settings, for example {@code /proc/sys/net/ipv4/tcp_retries2}
+ on Linux.
+
+ The default value is 512K (i.e. 512 * 1024).
+
+ @param sendBufferSizeLimit the maximum number of bytes to buffer when
+ sending messages; if the value is less than or equal to 0 then buffering
+ is effectively disabled.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="stomp-endpoint" minOccurs="1" maxOccurs="unbounded">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Registers STOMP over WebSocket endpoints.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="handshake-handler" type="handshake-handler" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="handshake-interceptors" type="handshake-interceptors" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="sockjs" type="sockjs-service" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+ <xsd:attribute name="path" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ A path that maps a particular message destination to a handler method.
+ Exact path mapping URIs (such as "/myPath") are supported as well as Ant-stype path patterns (such as /myPath/**).
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="allowed-origins" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configure allowed {@code Origin} header values. Multiple origins may be specified
+ as a comma-separated list.
+
+ This check is mostly designed for browser clients. There is noting preventing other
+ types of client to modify the Origin header value.
+
+ When SockJS is enabled and allowed origins are restricted, transport types that do not
+ use {@code Origin} headers for cross origin requests (jsonp-polling, iframe-xhr-polling,
+ iframe-eventsource and iframe-htmlfile) are disabled. As a consequence, IE6/IE7 won't be
+ supported anymore and IE8/IE9 will only be supported without cookies.
+
+ By default, all origins are allowed.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="stomp-error-handler" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configures a StompSubProtocolErrorHandler to customize or handle STOMP ERROR to clients.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:attribute name="ref" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation source="java:org.springframework.web.socket.messaging.StompSubProtocolErrorHandler"><![CDATA[
+ The bean name of a StompSubProtocolErrorHandler.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:choice>
+ <xsd:element name="simple-broker" type="simple-broker"/>
+ <xsd:element name="stomp-broker-relay" type="stomp-broker-relay"/>
+ </xsd:choice>
+ <xsd:element name="argument-resolvers" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configures HandlerMethodArgumentResolver types to support custom controller method argument types.
+ Using this option does not override the built-in support for resolving handler method arguments.
+ To customize the built-in support for argument resolution configure WebSocketAnnotationMethodMessageHandler directly.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:element ref="beans:bean" minOccurs="0" maxOccurs="unbounded">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ The HandlerMethodArgumentResolver (or WebArgumentResolver for backwards compatibility) bean definition.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element ref="beans:ref" minOccurs="0" maxOccurs="unbounded">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ A reference to a HandlerMethodArgumentResolver bean definition.
+ ]]></xsd:documentation>
+ <xsd:appinfo>
+ <tool:annotation kind="ref">
+ <tool:expected-type type="java:org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver"/>
+ </tool:annotation>
+ </xsd:appinfo>
+ </xsd:annotation>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="return-value-handlers" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configures HandlerMethodReturnValueHandler types to support custom controller method return value handling.
+ Using this option does not override the built-in support for handling return values.
+ To customize the built-in support for handling return values configure WebSocketAnnotationMethodMessageHandler directly.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:element ref="beans:bean" minOccurs="0" maxOccurs="unbounded">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ The HandlerMethodReturnValueHandler bean definition.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element ref="beans:ref" minOccurs="0" maxOccurs="unbounded">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ A reference to a HandlerMethodReturnValueHandler bean definition.
+ ]]></xsd:documentation>
+ <xsd:appinfo>
+ <tool:annotation kind="ref">
+ <tool:expected-type type="java:org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler"/>
+ </tool:annotation>
+ </xsd:appinfo>
+ </xsd:annotation>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="message-converters" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Configure the message converters to use when extracting the payload of messages in annotated methods
+ and when sending messages (e.g. through the "broker" SimpMessagingTemplate.
+ MessageConverter registrations provided here will take precedence over MessageConverter types registered by default.
+ Also see the register-defaults attribute if you want to turn off default registrations entirely.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element ref="beans:bean">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ A MessageConverter bean definition.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element ref="beans:ref">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ A reference to an HttpMessageConverter bean.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:sequence>
+ <xsd:attribute name="register-defaults" type="xsd:boolean" default="true">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Whether or not default MessageConverter registrations should be added in addition to the ones provided within this element.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="client-inbound-channel" type="channel" minOccurs="0" maxOccurs="1">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ The channel for receiving messages from clients (e.g. WebSocket clients).
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name="client-outbound-channel" type="channel" minOccurs="0" maxOccurs="1">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ The channel for sending messages to clients (e.g. WebSocket clients).
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name="broker-channel" type="channel" minOccurs="0" maxOccurs="1">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ The channel for sending messages with translated user destinations.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ </xsd:sequence>
+ <xsd:attribute name="application-destination-prefix" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Comma-separated list of prefixes to match to the destinations of handled messages.
+ Messages whose destination does not start with one of the configured prefixes are ignored.
+
+ Prefix is removed from the destination part and then messages are delegated to
+ @SubscribeMapping and @MessageMapping}annotated methods.
+
+ Prefixes without a trailing slash will have one appended automatically.
+ By default the list of prefixes is empty in which case all destinations match.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="user-destination-prefix" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ The prefix used to identify user destinations.
+ Any destinations that do not start with the given prefix are not be resolved.
+ The default value is "/user/".
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="path-matcher" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ A reference to the PathMatcher to use to match the destinations of incoming
+ messages to @MessageMapping and @SubscribeMapping methods.
+
+ By default AntPathMatcher is configured.
+ However applications may provide an AntPathMatcher instance
+ customized to use "." (commonly used in messaging) instead of "/" as path
+ separator or provide a completely different PathMatcher implementation.
+
+ Note that the configured PathMatcher is only used for matching the
+ portion of the destination after the configured prefix. For example given
+ application destination prefix "/app" and destination "/app/price.stock.**",
+ the message might be mapped to a controller with "price" and "stock.**"
+ as its type and method-level mappings respectively.
+
+ When the simple broker is enabled, the PathMatcher configured here is
+ also used to match message destinations when brokering messages.
+ ]]></xsd:documentation>
+ <xsd:appinfo>
+ <tool:annotation kind="ref">
+ <tool:expected-type type="java:org.springframework.util.PathMatcher"/>
+ </tool:annotation>
+ </xsd:appinfo>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="order" type="xsd:token">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ Order value for this SimpleUrlHandlerMapping.
+ Default value is 1.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="path-helper" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ The bean name of the UrlPathHelper to use for the HandlerMapping used to map handshake requests.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="validator" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+ The bean name of the Validator instance used for validating @Payload arguments.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+</xsd:schema>
diff --git a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket.gif b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket.gif
new file mode 100644
index 00000000..9e4c800e
--- /dev/null
+++ b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket.gif
Binary files differ
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java
index 7067ef25..2309f74f 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -21,7 +21,6 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -48,9 +47,7 @@ import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
*/
public abstract class AbstractWebSocketIntegrationTests {
- protected Log logger = LogFactory.getLog(getClass());
-
- private static Map<Class<?>, Class<?>> upgradeStrategyConfigTypes = new HashMap<Class<?>, Class<?>>();
+ private static Map<Class<?>, Class<?>> upgradeStrategyConfigTypes = new HashMap<>();
static {
upgradeStrategyConfigTypes.put(JettyWebSocketTestServer.class, JettyUpgradeStrategyConfig.class);
@@ -58,6 +55,7 @@ public abstract class AbstractWebSocketIntegrationTests {
upgradeStrategyConfigTypes.put(UndertowTestServer.class, UndertowUpgradeStrategyConfig.class);
}
+
@Rule
public final TestName testName = new TestName();
@@ -67,12 +65,13 @@ public abstract class AbstractWebSocketIntegrationTests {
@Parameter(1)
public WebSocketClient webSocketClient;
+ protected final Log logger = LogFactory.getLog(getClass());
+
protected AnnotationConfigWebApplicationContext wac;
@Before
public void setup() throws Exception {
-
logger.debug("Setting up '" + this.testName.getMethodName() + "', client=" +
this.webSocketClient.getClass().getSimpleName() + ", server=" +
this.server.getClass().getSimpleName());
@@ -155,6 +154,7 @@ public abstract class AbstractWebSocketIntegrationTests {
}
}
+
@Configuration
static class TomcatUpgradeStrategyConfig extends AbstractRequestUpgradeStrategyConfig {
@@ -164,6 +164,7 @@ public abstract class AbstractWebSocketIntegrationTests {
}
}
+
@Configuration
static class UndertowUpgradeStrategyConfig extends AbstractRequestUpgradeStrategyConfig {
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/ContextLoaderTestUtils.java b/spring-websocket/src/test/java/org/springframework/web/socket/ContextLoaderTestUtils.java
index ba56ae97..8853d824 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/ContextLoaderTestUtils.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/ContextLoaderTestUtils.java
@@ -38,7 +38,8 @@ public class ContextLoaderTestUtils {
public static void setCurrentWebApplicationContext(ClassLoader classLoader, WebApplicationContext applicationContext) {
if (applicationContext != null) {
currentContextPerThread.put(classLoader, applicationContext);
- } else {
+ }
+ else {
currentContextPerThread.remove(classLoader);
}
}
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketIntegrationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java
index 1d86998d..813b7b5f 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketIntegrationTests.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -46,9 +46,10 @@ import static org.junit.Assert.*;
* Client and server-side WebSocket integration tests.
*
* @author Rossen Stoyanchev
+ * @author Juergen Hoeller
*/
@RunWith(Parameterized.class)
-public class WebSocketIntegrationTests extends AbstractWebSocketIntegrationTests {
+public class WebSocketHandshakeTests extends AbstractWebSocketIntegrationTests {
@Parameters(name = "server [{0}], client [{1}]")
public static Iterable<Object[]> arguments() {
@@ -62,7 +63,7 @@ public class WebSocketIntegrationTests extends AbstractWebSocketIntegrationTest
@Override
protected Class<?>[] getAnnotatedConfigClasses() {
- return new Class<?>[] { TestConfig.class };
+ return new Class<?>[] {TestConfig.class};
}
@Test
@@ -75,11 +76,8 @@ public class WebSocketIntegrationTests extends AbstractWebSocketIntegrationTest
session.close();
}
- // SPR-12727
-
- @Test
+ @Test // SPR-12727
public void unsolicitedPongWithEmptyPayload() throws Exception {
-
String url = getWsBaseUrl() + "/ws";
WebSocketSession session = this.webSocketClient.doHandshake(new AbstractWebSocketHandler() {}, url).get();
@@ -126,7 +124,6 @@ public class WebSocketIntegrationTests extends AbstractWebSocketIntegrationTest
private Throwable transportError;
-
public void setWaitMessageCount(int waitMessageCount) {
this.waitMessageCount = waitMessageCount;
}
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSessionTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSessionTests.java
index 594f0828..a8377521 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSessionTests.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSessionTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 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.
@@ -22,8 +22,9 @@ import java.util.Map;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
-import org.junit.Before;
+
import org.junit.Test;
+
import org.mockito.Mockito;
import org.springframework.web.socket.handler.TestPrincipal;
@@ -38,16 +39,11 @@ import static org.mockito.BDDMockito.*;
*/
public class JettyWebSocketSessionTests {
- private Map<String,Object> attributes;
-
-
- @Before
- public void setup() {
- this.attributes = new HashMap<>();
- }
+ private final Map<String, Object> attributes = new HashMap<>();
@Test
+ @SuppressWarnings("resource")
public void getPrincipalWithConstructorArg() {
TestPrincipal user = new TestPrincipal("joe");
JettyWebSocketSession session = new JettyWebSocketSession(attributes, user);
@@ -56,8 +52,8 @@ public class JettyWebSocketSessionTests {
}
@Test
+ @SuppressWarnings("resource")
public void getPrincipalFromNativeSession() {
-
TestPrincipal user = new TestPrincipal("joe");
UpgradeRequest request = Mockito.mock(UpgradeRequest.class);
@@ -80,8 +76,8 @@ public class JettyWebSocketSessionTests {
}
@Test
+ @SuppressWarnings("resource")
public void getPrincipalNotAvailable() {
-
UpgradeRequest request = Mockito.mock(UpgradeRequest.class);
given(request.getUserPrincipal()).willReturn(null);
@@ -102,8 +98,8 @@ public class JettyWebSocketSessionTests {
}
@Test
+ @SuppressWarnings("resource")
public void getAcceptedProtocol() {
-
String protocol = "foo";
UpgradeRequest request = Mockito.mock(UpgradeRequest.class);
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/adapter/standard/StandardWebSocketSessionTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/adapter/standard/StandardWebSocketSessionTests.java
index edd17640..8c832aad 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/adapter/standard/StandardWebSocketSessionTests.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/adapter/standard/StandardWebSocketSessionTests.java
@@ -1,4 +1,4 @@
-/* Copyright 2002-2014 the original author or authors.
+/* Copyright 2002-2015 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.
@@ -19,8 +19,8 @@ import java.util.HashMap;
import java.util.Map;
import javax.websocket.Session;
-import org.junit.Before;
import org.junit.Test;
+
import org.mockito.Mockito;
import org.springframework.http.HttpHeaders;
@@ -36,19 +36,13 @@ import static org.mockito.BDDMockito.*;
*/
public class StandardWebSocketSessionTests {
- private HttpHeaders headers;
-
- private Map<String,Object> attributes;
+ private final HttpHeaders headers = new HttpHeaders();
-
- @Before
- public void setup() {
- this.headers = new HttpHeaders();
- this.attributes = new HashMap<>();
- }
+ private final Map<String, Object> attributes = new HashMap<>();
@Test
+ @SuppressWarnings("resource")
public void getPrincipalWithConstructorArg() {
TestPrincipal user = new TestPrincipal("joe");
StandardWebSocketSession session = new StandardWebSocketSession(this.headers, this.attributes, null, null, user);
@@ -57,8 +51,8 @@ public class StandardWebSocketSessionTests {
}
@Test
+ @SuppressWarnings("resource")
public void getPrincipalWithNativeSession() {
-
TestPrincipal user = new TestPrincipal("joe");
Session nativeSession = Mockito.mock(Session.class);
@@ -71,8 +65,8 @@ public class StandardWebSocketSessionTests {
}
@Test
+ @SuppressWarnings("resource")
public void getPrincipalNone() {
-
Session nativeSession = Mockito.mock(Session.class);
given(nativeSession.getUserPrincipal()).willReturn(null);
@@ -86,8 +80,8 @@ public class StandardWebSocketSessionTests {
}
@Test
+ @SuppressWarnings("resource")
public void getAcceptedProtocol() {
-
String protocol = "foo";
Session nativeSession = Mockito.mock(Session.class);
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParserTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParserTests.java
index aa52922b..824cba62 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParserTests.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParserTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -60,6 +60,8 @@ import org.springframework.mock.web.test.MockServletContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.util.MimeTypeUtils;
+import org.springframework.validation.Errors;
+import org.springframework.validation.Validator;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.servlet.HandlerMapping;
@@ -356,6 +358,14 @@ public class MessageBrokerBeanDefinitionParserTests {
public void customChannels() {
loadBeanDefinitions("websocket-config-broker-customchannels.xml");
+ SimpAnnotationMethodMessageHandler annotationMethodMessageHandler =
+ this.appContext.getBean(SimpAnnotationMethodMessageHandler.class);
+
+ Validator validator = annotationMethodMessageHandler.getValidator();
+ assertNotNull(validator);
+ assertSame(this.appContext.getBean("myValidator"), validator);
+ assertThat(validator, Matchers.instanceOf(TestValidator.class));
+
List<Class<? extends MessageHandler>> subscriberTypes =
Arrays.<Class<? extends MessageHandler>>asList(SimpAnnotationMethodMessageHandler.class,
UserDestinationMessageHandler.class, SimpleBrokerMessageHandler.class);
@@ -520,3 +530,13 @@ class TestWebSocketHandlerDecorator extends WebSocketHandlerDecorator {
class TestStompErrorHandler extends StompSubProtocolErrorHandler {
}
+
+class TestValidator implements Validator {
+ @Override
+ public boolean supports(Class<?> clazz) {
+ return false;
+ }
+
+ @Override
+ public void validate(Object target, Errors errors) { }
+}
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java
index 1a66397b..f4aa0460 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -61,7 +61,7 @@ public class WebSocketConfigurationTests extends AbstractWebSocketIntegrationTes
@Override
protected Class<?>[] getAnnotatedConfigClasses() {
- return new Class<?>[] { TestConfig.class };
+ return new Class<?>[] {TestConfig.class};
}
@Test
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecoratorTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecoratorTests.java
index c4cdb02a..5af30742 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecoratorTests.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecoratorTests.java
@@ -40,6 +40,7 @@ import static org.junit.Assert.*;
@SuppressWarnings("resource")
public class ConcurrentWebSocketSessionDecoratorTests {
+
@Test
public void send() throws IOException {
@@ -70,16 +71,13 @@ public class ConcurrentWebSocketSessionDecoratorTests {
final ConcurrentWebSocketSessionDecorator concurrentSession =
new ConcurrentWebSocketSessionDecorator(blockingSession, 10 * 1000, 1024);
- Executors.newSingleThreadExecutor().submit(new Runnable() {
- @Override
- public void run() {
- TextMessage textMessage = new TextMessage("slow message");
- try {
- concurrentSession.sendMessage(textMessage);
- }
- catch (IOException e) {
- e.printStackTrace();
- }
+ Executors.newSingleThreadExecutor().submit((Runnable) () -> {
+ TextMessage message = new TextMessage("slow message");
+ try {
+ concurrentSession.sendMessage(message);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
}
});
@@ -90,7 +88,7 @@ public class ConcurrentWebSocketSessionDecoratorTests {
assertTrue(concurrentSession.getTimeSinceSendStarted() > 0);
TextMessage payload = new TextMessage("payload");
- for (int i=0; i < 5; i++) {
+ for (int i = 0; i < 5; i++) {
concurrentSession.sendMessage(payload);
}
@@ -103,6 +101,7 @@ public class ConcurrentWebSocketSessionDecoratorTests {
public void sendTimeLimitExceeded() throws IOException, InterruptedException {
BlockingSession blockingSession = new BlockingSession();
+ blockingSession.setId("123");
blockingSession.setOpen(true);
CountDownLatch sentMessageLatch = blockingSession.getSentMessageLatch();
@@ -112,16 +111,13 @@ public class ConcurrentWebSocketSessionDecoratorTests {
final ConcurrentWebSocketSessionDecorator concurrentSession =
new ConcurrentWebSocketSessionDecorator(blockingSession, sendTimeLimit, bufferSizeLimit);
- Executors.newSingleThreadExecutor().submit(new Runnable() {
- @Override
- public void run() {
- TextMessage textMessage = new TextMessage("slow message");
- try {
- concurrentSession.sendMessage(textMessage);
- }
- catch (IOException e) {
- e.printStackTrace();
- }
+ Executors.newSingleThreadExecutor().submit((Runnable) () -> {
+ TextMessage message = new TextMessage("slow message");
+ try {
+ concurrentSession.sendMessage(message);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
}
});
@@ -136,6 +132,9 @@ public class ConcurrentWebSocketSessionDecoratorTests {
fail("Expected exception");
}
catch (SessionLimitExceededException ex) {
+ String actual = ex.getMessage();
+ String regex = "Message send time [\\d]+ \\(ms\\) for session '123' exceeded the allowed limit 100";
+ assertTrue("Unexpected message: " + actual, actual.matches(regex));
assertEquals(CloseStatus.SESSION_NOT_RELIABLE, ex.getStatus());
}
}
@@ -144,6 +143,7 @@ public class ConcurrentWebSocketSessionDecoratorTests {
public void sendBufferSizeExceeded() throws IOException, InterruptedException {
BlockingSession blockingSession = new BlockingSession();
+ blockingSession.setId("123");
blockingSession.setOpen(true);
CountDownLatch sentMessageLatch = blockingSession.getSentMessageLatch();
@@ -153,23 +153,20 @@ public class ConcurrentWebSocketSessionDecoratorTests {
final ConcurrentWebSocketSessionDecorator concurrentSession =
new ConcurrentWebSocketSessionDecorator(blockingSession, sendTimeLimit, bufferSizeLimit);
- Executors.newSingleThreadExecutor().submit(new Runnable() {
- @Override
- public void run() {
- TextMessage textMessage = new TextMessage("slow message");
- try {
- concurrentSession.sendMessage(textMessage);
- }
- catch (IOException e) {
- e.printStackTrace();
- }
+ Executors.newSingleThreadExecutor().submit((Runnable) () -> {
+ TextMessage message = new TextMessage("slow message");
+ try {
+ concurrentSession.sendMessage(message);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
}
});
assertTrue(sentMessageLatch.await(5, TimeUnit.SECONDS));
StringBuilder sb = new StringBuilder();
- for (int i=0 ; i < 1023; i++) {
+ for (int i = 0 ; i < 1023; i++) {
sb.append("a");
}
@@ -184,6 +181,10 @@ public class ConcurrentWebSocketSessionDecoratorTests {
fail("Expected exception");
}
catch (SessionLimitExceededException ex) {
+ String actual = ex.getMessage();
+ String regex = "The send buffer size [\\d]+ bytes for session '123' exceeded the allowed limit 1024";
+ assertTrue("Unexpected message: " + actual, actual.matches(regex));
+ assertEquals(CloseStatus.SESSION_NOT_RELIABLE, ex.getStatus());
}
}
@@ -215,16 +216,13 @@ public class ConcurrentWebSocketSessionDecoratorTests {
final ConcurrentWebSocketSessionDecorator concurrentSession =
new ConcurrentWebSocketSessionDecorator(blockingSession, sendTimeLimit, bufferSizeLimit);
- Executors.newSingleThreadExecutor().submit(new Runnable() {
- @Override
- public void run() {
- TextMessage message = new TextMessage("slow message");
- try {
- concurrentSession.sendMessage(message);
- }
- catch (IOException e) {
- e.printStackTrace();
- }
+ Executors.newSingleThreadExecutor().submit((Runnable) () -> {
+ TextMessage message = new TextMessage("slow message");
+ try {
+ concurrentSession.sendMessage(message);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
}
});
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/RestTemplateXhrTransportTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/RestTemplateXhrTransportTests.java
index ca8f2c83..1337fcb2 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/RestTemplateXhrTransportTests.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/RestTemplateXhrTransportTests.java
@@ -91,7 +91,7 @@ public class RestTemplateXhrTransportTests {
@Test
public void connectReceiveAndCloseWithPrelude() throws Exception {
StringBuilder sb = new StringBuilder(2048);
- for (int i=0; i < 2048; i++) {
+ for (int i = 0; i < 2048; i++) {
sb.append('h');
}
String body = sb.toString() + "\n" + "o\n" + "a[\"foo\"]\n" + "c[3000,\"Go away!\"]";
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java
index 8b941778..41911429 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -20,7 +20,6 @@ import java.sql.Date;
import org.junit.Before;
import org.junit.Test;
-
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.AbstractHttpRequestTests;
import org.springframework.web.socket.WebSocketHandler;
@@ -115,6 +114,7 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
setRequest("POST", "/");
if (callbackValue != null) {
+ // need to encode the query parameter
this.servletRequest.setQueryString("c=" + UriUtils.encodeQueryParam(callbackValue, "UTF-8"));
this.servletRequest.addParameter("c", callbackValue);
}
diff --git a/spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-customchannels.xml b/spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-customchannels.xml
index e8908da1..fdf47049 100644
--- a/spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-customchannels.xml
+++ b/spring-websocket/src/test/resources/org/springframework/web/socket/config/websocket-config-broker-customchannels.xml
@@ -4,7 +4,7 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">
- <websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/personal">
+ <websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/personal" validator="myValidator">
<websocket:stomp-endpoint path="/foo,/bar">
<websocket:handshake-handler ref="myHandler"/>
</websocket:stomp-endpoint>
@@ -31,4 +31,6 @@
<bean id="myInterceptor" class="org.springframework.web.socket.config.TestChannelInterceptor"/>
+ <bean id="myValidator" class="org.springframework.web.socket.config.TestValidator"/>
+
</beans>