diff --git a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/IntegrationWebSocketContainer.java b/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/IntegrationWebSocketContainer.java index ac814fe94d..ff31a37e64 100644 --- a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/IntegrationWebSocketContainer.java +++ b/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/IntegrationWebSocketContainer.java @@ -26,9 +26,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.DisposableBean; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.util.Assert; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.SubProtocolCapable; @@ -36,7 +33,6 @@ import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator; -import org.springframework.web.socket.messaging.SessionDisconnectEvent; /** * The high-level 'connection factory pattern' contract over low-level Web-Socket @@ -59,7 +55,7 @@ import org.springframework.web.socket.messaging.SessionDisconnectEvent; * @see org.springframework.integration.websocket.inbound.WebSocketInboundChannelAdapter * @see org.springframework.integration.websocket.outbound.WebSocketOutboundMessageHandler */ -public abstract class IntegrationWebSocketContainer implements ApplicationEventPublisherAware, DisposableBean { +public abstract class IntegrationWebSocketContainer implements DisposableBean { protected final Log logger = LogFactory.getLog(this.getClass()); @@ -75,8 +71,6 @@ public abstract class IntegrationWebSocketContainer implements ApplicationEventP private volatile int sendBufferSizeLimit = 512 * 1024; - private ApplicationEventPublisher eventPublisher; - public void setSendTimeLimit(int sendTimeLimit) { this.sendTimeLimit = sendTimeLimit; } @@ -85,11 +79,6 @@ public abstract class IntegrationWebSocketContainer implements ApplicationEventP this.sendBufferSizeLimit = sendBufferSizeLimit; } - @Override - public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { - this.eventPublisher = applicationEventPublisher; - } - public void setMessageListener(WebSocketListener messageListener) { Assert.state(this.messageListener == null || this.messageListener == messageListener, "'messageListener' is already configured"); @@ -146,15 +135,6 @@ public abstract class IntegrationWebSocketContainer implements ApplicationEventP this.sessions.clear(); } - private void publishEvent(ApplicationEvent event) { - try { - this.eventPublisher.publishEvent(event); - } - catch (Throwable ex) { - logger.error("Error while publishing " + event, ex); - } - } - /** * An internal {@link WebSocketHandler} implementation to be used with native * Web-Socket containers. @@ -192,9 +172,6 @@ public abstract class IntegrationWebSocketContainer implements ApplicationEventP if (IntegrationWebSocketContainer.this.messageListener != null) { IntegrationWebSocketContainer.this.messageListener.afterSessionEnded(session, closeStatus); } - else if (IntegrationWebSocketContainer.this.eventPublisher != null) { - publishEvent(new SessionDisconnectEvent(this, session.getId(), closeStatus)); - } } } @@ -203,10 +180,8 @@ public abstract class IntegrationWebSocketContainer implements ApplicationEventP WebSocketSession removed = IntegrationWebSocketContainer.this.sessions.remove(session.getId()); if (removed != null) { IntegrationWebSocketContainer.this.sessions.remove(session.getId()); - if (IntegrationWebSocketContainer.this.eventPublisher != null) { - publishEvent(new SessionErrorEvent(this, session.getId(), exception)); - } } + throw new Exception(exception); } @Override diff --git a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/SessionErrorEvent.java b/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/SessionErrorEvent.java deleted file mode 100644 index 1fd6a5b329..0000000000 --- a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/SessionErrorEvent.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2014 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.integration.websocket; - -import org.springframework.context.ApplicationEvent; -import org.springframework.util.Assert; - -/** - * The {@link ApplicationEvent} implementation to represent the - * {@link org.springframework.web.socket.WebSocketSession} errors. - * - * @author Artem Bilan - * @since 4.1 - */ -@SuppressWarnings("serial") -public class SessionErrorEvent extends ApplicationEvent { - - private final String sessionId; - - private final Throwable exception; - - - /** - * Create a new {@link ApplicationEvent} represented the error on the session. - * @param source the component that published the event (never {@code null}) - * @param sessionId the id of the session - * @param exception the exception on the session - */ - public SessionErrorEvent(Object source, String sessionId, Throwable exception) { - super(source); - Assert.notNull(sessionId, "'sessionId' must not be null"); - this.sessionId = sessionId; - this.exception = exception; - } - - /** - * Return the session id. - * @return the sessionId - */ - public String getSessionId() { - return this.sessionId; - } - - /** - * Return the exception for the session id. - * @return the exception - */ - public Throwable getException() { - return exception; - } - - @Override - public String toString() { - return "SessionErrorEvent: sessionId=" + this.sessionId; - } - -} diff --git a/spring-integration-websocket/src/main/resources/org/springframework/integration/websocket/config/spring-integration-websocket-4.1.xsd b/spring-integration-websocket/src/main/resources/org/springframework/integration/websocket/config/spring-integration-websocket-4.1.xsd index ff79e5954c..126bd4b2d7 100644 --- a/spring-integration-websocket/src/main/resources/org/springframework/integration/websocket/config/spring-integration-websocket-4.1.xsd +++ b/spring-integration-websocket/src/main/resources/org/springframework/integration/websocket/config/spring-integration-websocket-4.1.xsd @@ -234,7 +234,8 @@ - + @@ -357,7 +358,7 @@ - Fully qualified name of the java type for the target `payload` + Fully qualified name of the java type for the target 'payload' to convert from the incoming WebSocketMessage. @@ -365,11 +366,10 @@ - Flag to indicate if this adapter will send non-MESSAGE type WebSocketMessages - and messages with broker destinations to the `AbstractBrokerMessageHandler` + Flag to indicate if this adapter will send non-MESSAGE type 'WebSocketMessage's + and messages with broker destinations to the 'AbstractBrokerMessageHandler' from the application context. - If the `AbstractBrokerMessageHandler` bean isn't present the warn log is emitted - and adapter behaviour is falling back to default like this attribute is 'false'. + The 'Broker Relay' configuration is required when 'use-broker' is 'true'. This attribute is used only on server side. On client side it is ignored. Defaults to 'false'. @@ -441,7 +441,7 @@ Flag to indicate if the default converters should be registered after any custom - converters. This flag is used only if message-converters + converters. This flag is used only if 'message-converters' are provided, otherwise all default converters will be registered. Defaults to 'false'. diff --git a/src/reference/docbook/aggregator.xml b/src/reference/docbook/aggregator.xml index 787199e422..242c5f1422 100644 --- a/src/reference/docbook/aggregator.xml +++ b/src/reference/docbook/aggregator.xml @@ -882,8 +882,7 @@ then you should simply provide an implementation of the ReleaseStrate
- Managing State in an Aggregator: - MessageGroupStore + Managing State in an Aggregator: MessageGroupStore Aggregator (and some other patterns in Spring Integration) is a stateful pattern that requires decisions to be made based on a group of diff --git a/src/reference/docbook/index.xml b/src/reference/docbook/index.xml index dca7fcea06..d6adcd4a02 100644 --- a/src/reference/docbook/index.xml +++ b/src/reference/docbook/index.xml @@ -144,6 +144,7 @@ + diff --git a/src/reference/docbook/web-sockets.xml b/src/reference/docbook/web-sockets.xml new file mode 100644 index 0000000000..5afe4bb4cc --- /dev/null +++ b/src/reference/docbook/web-sockets.xml @@ -0,0 +1,615 @@ + + + WebSockets Support + +
+ Introduction + + Starting with version 4.1 Spring Integration has introduced + WebSocket support. It is based on architecture, infrastructure and API + from the Spring Framework's web-socket module. Therefore, many of Spring WebSocket's + components (e.g. SubProtocolHandler or + WebSocketClient) and configuration options (e.g. + @EnableWebSocketMessageBroker) can be reused within Spring Integration. + For more information, please, refer to the + Spring Framework WebSocket Support chapter in the Spring Framework reference manual. + + + Since the Spring Framework WebSocket infrastructure is based on the Spring Messaging + foundation and provides a basic Messaging framework based on the same + MessageChannels, MessageHandlers + that Spring Integration uses, + and some POJO-method annotation mappings, Spring Integration can be directly involved in a + WebSocket flow, even without WebSocket adapters. For this purpose you can simply configure a + Spring Integration @MessagingGateway with appropriate annotations: + + +
+ +
+ Overview + + Since the WebSocket protocol is streaming by definition and we can + send and receive messages to/from a WebSocket at the same time, + we can simply deal with an appropriate WebSocketSession, + regardless of being on the client or server side. To encapsulate the connection management and + WebSocketSession registry, the IntegrationWebSocketContainer + is provided with ClientWebSocketContainer and ServerWebSocketContainer + implementations. Thanks to the WebSocket API + and its implementation in the Spring Framework, with many extensions, the same classes are used on the server + side as well as the client side (from a Java perspective, of course). Hence most connection and + WebSocketSession registry options are the same on both sides. That allows us + to reuse many configuration items and infrastructure hooks to build WebSocket applications on the server + side as well as on the client side: + singletonList(new WebSocketTransport(new JettyWebSocketClient()))); +} + +@Bean +public IntegrationWebSocketContainer clientWebSocketContainer() { + return new ClientWebSocketContainer(webSocketClient(), "ws://my.server.com/endpoint"); +} + +//Server side +@Bean +public IntegrationWebSocketContainer serverWebSocketContainer() { + return new ServerWebSocketContainer("/endpoint").withSockJs(); +}]]> + + + The IntegrationWebSocketContainer is designed to achieve + bidirectional messaging and can be shared between Inbound and Outbound + Channel Adapters (see below), can be referenced only from one of them (when using + one-way - sending or receiving - WebSocket messaging). It can be used without any Channel + Adapter, but in this case, IntegrationWebSocketContainer only plays a role + as the WebSocketSession registry. + + + The ServerWebSocketContainer implements WebSocketConfigurer + to register an internal IntegrationWebSocketContainer.IntegrationWebSocketHandler + as an Endpoint under the provided paths and other server WebSocket options (such as + HandshakeHandler or SockJS fallback) within the + ServletWebSocketHandlerRegistry for the target vendor WebSocket Container. This + registration is achieved with an infrastructural WebSocketIntegrationConfigurationInitializer + component, which does the same as the @EnableWebSocket annotation. This means that + using just @EnableIntegration (or any Spring Integration Namespace in the + application context) + you can omit the @EnableWebSocket declaration, because all WebSocket + Endpoints are detected by the Spring Integration infrastructure. + +
+ +
+ WebSocket Inbound Channel Adapter + + The WebSocketInboundChannelAdapter implements the receiving part of + WebSocketSession interaction. It must be supplied with a + IntegrationWebSocketContainer, and the adapter registers itself as a + WebSocketListener to handle incoming messages and + WebSocketSession events. + + + Only one WebSocketListener can be registered in the + IntegrationWebSocketContainer. + + + For WebSocket sub-protocols, the + WebSocketInboundChannelAdapter can be configured with + SubProtocolHandlerRegistry as the second constructor argument. The adapter delegates + to the SubProtocolHandlerRegistry to determine the appropriate + SubProtocolHandler for the accepted WebSocketSession + and to convert WebSocketMessage to a Message + according to the sub-protocol implementation. + + + By default, the WebSocketInboundChannelAdapter relies just only on the raw + PassThruSubProtocolHandler implementation, which simply converts the + WebSocketMessage to a Message. + + + The WebSocketInboundChannelAdapter accepts and sends to the underlying integration + flow only Messages with SimpMessageType.MESSAGE or an empty + simpMessageType header. All other Message types are handled + through the ApplicationEvents emitted from a + SubProtocolHandler implementation (e.g. + StompSubProtocolHandler). + + + On the server side WebSocketInboundChannelAdapter can be configured with the + useBroker = true option, if the @EnableWebSocketMessageBroker + configuration is present. In this case all non-MESSAGE Message + types are delegated to the provided AbstractBrokerMessageHandler. In addition, if the + Broker Relay is configured with destination prefixes, those Messages, which match to the Broker + destinations, are routed to the AbstractBrokerMessageHandler, instead of to the + outputChannel of the WebSocketInboundChannelAdapter. + + + Spring's WebSocket Support allows the configuration of only one Broker Relay, hence we don't require an + AbstractBrokerMessageHandler reference, it is detected in the + Application Context. + + + For more configuration option see . + +
+ +
+ WebSocket Outbound Channel Adapter + + The WebSocketOutboundChannelAdapter accepts Spring Integration messages from its + MessageChannel, determines the + WebSocketSession id from the MessageHeaders, + retrieves the WebSocketSession from the provided + IntegrationWebSocketContainer and delegates the conversion and sending + WebSocketMessage work to the appropriate + SubProtocolHandler from the provided + SubProtocolHandlerRegistry. + + + On the client side, the WebSocketSession id message header isn't + required, because ClientWebSocketContainer deals only with a single connection and + its WebSocketSession respectively. + + + To use the STOMP sub-protocol, this adapter should be configured with a + StompSubProtocolHandler. Then you can send + any STOMP message type to this adapter, using StompHeaderAccessor.create(StompCommand...) and + a MessageBuilder, or just using a HeaderEnricher (see + ). + + + For more configuration option see below. + +
+ +
+ WebSockets Namespace Support + + Spring Integration WebSocket namespace includes several components described below. + To include it in your configuration, simply provide the following namespace declaration in your + application context configuration file: + + + + + ... +]]> + + <int-websocket:client-container> + + ]]> + + ]]>]]> + + + + + The component bean name. + + + + + The WebSocketClient bean reference. + + + + + The uri or uriTemplate to the target WebSocket service. If it is used as + a uriTemplate with URI variable placeholders, the uri-variables + attribute is required. + + + + + Comma-separated values for the URI variable placeholders within the uri attribute + value. The values are replaced into the placeholders according + to the order in the uri. See UriComponents.expand(Object... uriVariableValues). + + + + + The Origin Handshake HTTP header value. + + + + + The WebSocket session 'send' timeout limit. Defaults to 10000. + + + + + The WebSocket session 'send' message size limit. Defaults to 524288. + + + + + Boolean value indicating whether this endpoint should start automatically. + Defaults to false, assuming that this container will be started from the + . + + + + + The lifecycle phase within which this endpoint should start and stop. + The lower the value the earlier this endpoint will start and the later it will stop. The + default is Integer.MAX_VALUE. Values can be negative. See + SmartLifeCycle. + + + + + A Map of HttpHeaders to be used with the + Handshake request. + + + + + + + <int-websocket:server-container> + + ]]> ]]>]]> + + + + + The component bean name. + + + + + A path (or comma-separated paths) that maps a particular request to a + WebSocketHandler. + Exact path mapping URIs (such as "/myPath") are supported as well + as ant-style path patterns (such as /myPath/**). + + + + + The HandshakeHandler bean reference. + Default to DefaultHandshakeHandler. + + + + + List of HandshakeInterceptor bean references. + + + + + See the same option on the <int-websocket:client-container>. + + + + + See the same option on the <int-websocket:client-container>. + + + + + 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. + + + + + 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 bytes). + + + + + 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. + + + + + 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). + + + + + 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. + + + + + 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. + + + + + 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. + + + + + The TaskScheduler bean reference; a new + ThreadPoolTaskScheduler instance will be + created if no value is provided. This scheduler instance will be used for scheduling + heart-beat messages. + + + + + The SockJsMessageCodec bean reference to use for + encoding and decoding SockJS messages. + By default Jackson2SockJsMessageCodec is used requiring the + Jackson library to be present on the classpath. + + + + + List of TransportHandler bean references. + + + + + + + <int-websocket:outbound-channel-adapter> + + + ]]> + + + + The component bean name. If the channel attribute isn't provided, a + DirectChannel is created and registered with the application context + with this id attribute as the bean name. In this case, the endpoint is registered + with the bean name id + '.adapter'. And the + MessageHandler is registered with the bean alias + id +'.adapter'. + + + + + Identifies the channel attached to this adapter. + + + + + The reference to the IntegrationWebSocketContainer bean, + which encapsulates the low-level connection and WebSocketSession handling operations. Required. + + + + + Optional reference to a SubProtocolHandler instance. + It is used when the client did not request a sub-protocol or it is a single protocol-handler. + If this reference or protocol-handlers list aren't provided the + PassThruSubProtocolHandler is used by default. + + + + + List of SubProtocolHandler bean references + for this Channel Adapter. If only a single bean reference is provided and a + default-protocol-handler isn't provided, that single + SubProtocolHandler will be used as the + default-protocol-handler. If this attribute or + default-protocol-handler aren't provided, the + PassThruSubProtocolHandler is used by default. + + + + + List of MessageConverter bean references for this Channel + Adapter. + + + + + Flag to indicate if the default converters should be registered after any custom + converters. This flag is used only if message-converters + are provided, otherwise all default converters will be registered. + Defaults to false. The default converters are (in the order): + StringMessageConverter, ByteArrayMessageConverter + and MappingJackson2MessageConverter if the Jackson library is present on + the classpath. + + + + + Boolean value indicating whether this endpoint should start automatically. + Default to true. + + + + + The lifecycle phase within which this endpoint should start and stop. + The lower the value the earlier this endpoint will start and the later it will stop. The + default is Integer.MIN_VALUE. Values can be negative. + See SmartLifeCycle. + + + + + + + <int-websocket:inbound-channel-adapter> + + + ]]> + + + + The component bean name. If the channel attribute isn't provided, a + DirectChannel is created and registered with the application context + with this id attribute as the bean name. In this case, the endpoint is registered + with the bean name id + '.adapter'. + + + + + Identifies the channel attached to this adapter. + + + + + The MessageChannel bean reference to which + the ErrorMessages should be sent. + + + + + See the same option on the <int-websocket:outbound-channel-adapter>. + + + + + See the same option on the <int-websocket:outbound-channel-adapter>. + + + + + See the same option on the <int-websocket:outbound-channel-adapter>. + + + + + See the same option on the <int-websocket:outbound-channel-adapter>. + + + + + See the same option on the <int-websocket:outbound-channel-adapter>. + + + + + Maximum amount of time in milliseconds to wait when sending a message + to the channel if the channel may block. + For example, a QueueChannel can block until space is available + if its maximum capacity has been reached. + + + + + Fully qualified name of the java type for the target payload + to convert from the incoming WebSocketMessage. + Default to String. + + + + + Flag to indicate if this adapter will send non-MESSAGE + WebSocketMessages and messages with broker destinations to the + AbstractBrokerMessageHandler from the application context. + The Broker Relay configuration is required when this attribute is + true. + This attribute is used only on the server side. On the client side, it is ignored. + Defaults to false. + + + + + See the same option on the <int-websocket:outbound-channel-adapter>. + + + + + See the same option on the <int-websocket:outbound-channel-adapter>. + + + + +
+
diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index 2b9ac647c3..b906ffc559 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -18,13 +18,13 @@ See .
-
+
WebSocket support The WebSocket module is now available. It is fully based on the Spring WebSocket and Spring Messaging modules and provides an <inbound-channel-adapter> and an <outbound-channel-adapter>. - More documentation to follow. + See for more information.