(window.webpackJsonp=window.webpackJsonp||[]).push([[152],{581:function(e,t,n){"use strict";n.r(t);var o=n(56),a=Object(o.a)({},(function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[n("h1",{attrs:{id:"websockets-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#websockets-support"}},[e._v("#")]),e._v(" WebSockets Support")]),e._v(" "),n("h2",{attrs:{id:"websockets-support-2"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#websockets-support-2"}},[e._v("#")]),e._v(" WebSockets Support")]),e._v(" "),n("p",[e._v("Starting with version 4.1, Spring Integration has WebSocket support.\nIt is based on the architecture, infrastructure, and API from the Spring Framework’s "),n("code",[e._v("web-socket")]),e._v(" module.\nTherefore, many of Spring WebSocket’s components (such as "),n("code",[e._v("SubProtocolHandler")]),e._v(" or "),n("code",[e._v("WebSocketClient")]),e._v(") and configuration options (such as "),n("code",[e._v("@EnableWebSocketMessageBroker")]),e._v(") can be reused within Spring Integration.\nFor more information, see the "),n("a",{attrs:{href:"https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket",target:"_blank",rel:"noopener noreferrer"}},[e._v("Spring Framework WebSocket Support"),n("OutboundLink")],1),e._v(" chapter in the Spring Framework reference manual.")]),e._v(" "),n("p",[e._v("You need to include this dependency into your project:")]),e._v(" "),n("p",[e._v("Maven")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("\n org.springframework.integration\n spring-integration-websocket\n 5.5.9\n\n")])])]),n("p",[e._v("Gradle")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('compile "org.springframework.integration:spring-integration-websocket:5.5.9"\n')])])]),n("p",[e._v("For server side, the "),n("code",[e._v("org.springframework:spring-webmvc")]),e._v(" dependency must be included explicitly.")]),e._v(" "),n("p",[e._v("The Spring Framework WebSocket infrastructure is based on the Spring messaging foundation and provides a basic messaging framework based on the same "),n("code",[e._v("MessageChannel")]),e._v(" implementations and "),n("code",[e._v("MessageHandler")]),e._v(" implementations that Spring Integration uses (and some POJO-method annotation mappings).\nConsequently, Spring Integration can be directly involved in a WebSocket flow, even without WebSocket adapters.\nFor this purpose, you can configure a Spring Integration "),n("code",[e._v("@MessagingGateway")]),e._v(" with appropriate annotations, as the following example shows:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@MessagingGateway\n@Controller\npublic interface WebSocketGateway {\n\n @MessageMapping("/greeting")\n @SendToUser("/queue/answer")\n @Gateway(requestChannel = "greetingChannel")\n String greeting(String payload);\n\n}\n')])])]),n("h3",{attrs:{id:"overview"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#overview"}},[e._v("#")]),e._v(" Overview")]),e._v(" "),n("p",[e._v("Since the WebSocket protocol is streaming by definition and we can send and receive messages to and from a WebSocket at the same time, we can deal with an appropriate "),n("code",[e._v("WebSocketSession")]),e._v(", regardless of being on the client or server side.\nTo encapsulate the connection management and "),n("code",[e._v("WebSocketSession")]),e._v(" registry, the "),n("code",[e._v("IntegrationWebSocketContainer")]),e._v(" is provided with "),n("code",[e._v("ClientWebSocketContainer")]),e._v(" and "),n("code",[e._v("ServerWebSocketContainer")]),e._v(" implementations.\nThanks to the "),n("a",{attrs:{href:"https://www.jcp.org/en/jsr/detail?id=356",target:"_blank",rel:"noopener noreferrer"}},[e._v("WebSocket API"),n("OutboundLink")],1),e._v(" 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).\nConsequently, most connection and "),n("code",[e._v("WebSocketSession")]),e._v(" registry options are the same on both sides.\nThat lets us reuse many configuration items and infrastructure hooks to build WebSocket applications on the server side as well as on the client side.\nThe following example shows how components can serve both purposes:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('//Client side\n@Bean\npublic WebSocketClient webSocketClient() {\n return new SockJsClient(Collections.singletonList(new WebSocketTransport(new JettyWebSocketClient())));\n}\n\n@Bean\npublic IntegrationWebSocketContainer clientWebSocketContainer() {\n return new ClientWebSocketContainer(webSocketClient(), "ws://my.server.com/endpoint");\n}\n\n//Server side\n@Bean\npublic IntegrationWebSocketContainer serverWebSocketContainer() {\n return new ServerWebSocketContainer("/endpoint").withSockJs();\n}\n')])])]),n("p",[e._v("The "),n("code",[e._v("IntegrationWebSocketContainer")]),e._v(" is designed to achieve bidirectional messaging and can be shared between inbound and outbound channel adapters (see below), can be referenced from only one of them when using one-way (sending or receiving) WebSocket messaging.\nIt can be used without any channel adapter, but, in this case, "),n("code",[e._v("IntegrationWebSocketContainer")]),e._v(" only plays a role as the "),n("code",[e._v("WebSocketSession")]),e._v(" registry.")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("The "),n("code",[e._v("ServerWebSocketContainer")]),e._v(" implements "),n("code",[e._v("WebSocketConfigurer")]),e._v(" to register an internal "),n("code",[e._v("IntegrationWebSocketContainer.IntegrationWebSocketHandler")]),e._v(" as an "),n("code",[e._v("Endpoint")]),e._v("."),n("br"),e._v("It does so under the provided "),n("code",[e._v("paths")]),e._v(" and other server WebSocket options (such as "),n("code",[e._v("HandshakeHandler")]),e._v(" or "),n("code",[e._v("SockJS fallback")]),e._v(") within the "),n("code",[e._v("ServletWebSocketHandlerRegistry")]),e._v(" for the target vendor WebSocket Container."),n("br"),e._v("This registration is achieved with an infrastructural "),n("code",[e._v("WebSocketIntegrationConfigurationInitializer")]),e._v(" component, which does the same as the "),n("code",[e._v("@EnableWebSocket")]),e._v(" annotation."),n("br"),e._v("This means that, by using "),n("code",[e._v("@EnableIntegration")]),e._v(" (or any Spring Integration namespace in the application context), you can omit the "),n("code",[e._v("@EnableWebSocket")]),e._v(" declaration, because the Spring Integration infrastructure detects all WebSocket endpoints.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("h3",{attrs:{id:"websocket-inbound-channel-adapter"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#websocket-inbound-channel-adapter"}},[e._v("#")]),e._v(" WebSocket Inbound Channel Adapter")]),e._v(" "),n("p",[e._v("The "),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v(" implements the receiving part of "),n("code",[e._v("WebSocketSession")]),e._v(" interaction.\nYou must supply it with a "),n("code",[e._v("IntegrationWebSocketContainer")]),e._v(", and the adapter registers itself as a "),n("code",[e._v("WebSocketListener")]),e._v(" to handle incoming messages and "),n("code",[e._v("WebSocketSession")]),e._v(" events.")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("Only one "),n("code",[e._v("WebSocketListener")]),e._v(" can be registered in the "),n("code",[e._v("IntegrationWebSocketContainer")]),e._v(".")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[e._v("For WebSocket subprotocols, the "),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v(" can be configured with "),n("code",[e._v("SubProtocolHandlerRegistry")]),e._v(" as the second constructor argument.\nThe adapter delegates to the "),n("code",[e._v("SubProtocolHandlerRegistry")]),e._v(" to determine the appropriate "),n("code",[e._v("SubProtocolHandler")]),e._v(" for the accepted "),n("code",[e._v("WebSocketSession")]),e._v(" and to convert a "),n("code",[e._v("WebSocketMessage")]),e._v(" to a "),n("code",[e._v("Message")]),e._v(" according to the sub-protocol implementation.")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("By default, the "),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v(" relies only on the raw "),n("code",[e._v("PassThruSubProtocolHandler")]),e._v(" implementation, which converts the "),n("code",[e._v("WebSocketMessage")]),e._v(" to a "),n("code",[e._v("Message")]),e._v(".")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[e._v("The "),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v(" accepts and sends to the underlying integration flow only "),n("code",[e._v("Message")]),e._v(" instances that have "),n("code",[e._v("SimpMessageType.MESSAGE")]),e._v(" or an empty "),n("code",[e._v("simpMessageType")]),e._v(" header.\nAll other "),n("code",[e._v("Message")]),e._v(" types are handled through the "),n("code",[e._v("ApplicationEvent")]),e._v(" instances emitted from a "),n("code",[e._v("SubProtocolHandler")]),e._v(" implementation (such as "),n("code",[e._v("StompSubProtocolHandler")]),e._v(").")]),e._v(" "),n("p",[e._v("On the server side, if the "),n("code",[e._v("@EnableWebSocketMessageBroker")]),e._v(" configuration is present, you can configure "),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v(" with the "),n("code",[e._v("useBroker = true")]),e._v(" option.\nIn this case, all "),n("code",[e._v("non-MESSAGE")]),e._v(" "),n("code",[e._v("Message")]),e._v(" types are delegated to the provided "),n("code",[e._v("AbstractBrokerMessageHandler")]),e._v(".\nIn addition, if the broker relay is configured with destination prefixes, those messages that match the Broker destinations are routed to the "),n("code",[e._v("AbstractBrokerMessageHandler")]),e._v(" instead of to the "),n("code",[e._v("outputChannel")]),e._v(" of the "),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v(".")]),e._v(" "),n("p",[e._v("If "),n("code",[e._v("useBroker = false")]),e._v(" and the received message is of the "),n("code",[e._v("SimpMessageType.CONNECT")]),e._v(" type, the "),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v(" immediately sends a "),n("code",[e._v("SimpMessageType.CONNECT_ACK")]),e._v(" message to the "),n("code",[e._v("WebSocketSession")]),e._v(" without sending it to the channel.")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("Spring’s WebSocket Support allows the configuration of only one broker relay."),n("br"),e._v("Consequently, we do not require an "),n("code",[e._v("AbstractBrokerMessageHandler")]),e._v(" reference."),n("br"),e._v("It is detected in the Application Context.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[e._v("For more configuration options, see "),n("a",{attrs:{href:"#web-sockets-namespace"}},[e._v("WebSockets Namespace Support")]),e._v(".")]),e._v(" "),n("h3",{attrs:{id:"websocket-outbound-channel-adapter"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#websocket-outbound-channel-adapter"}},[e._v("#")]),e._v(" WebSocket Outbound Channel Adapter")]),e._v(" "),n("p",[e._v("The "),n("code",[e._v("WebSocketOutboundChannelAdapter")]),e._v(":")]),e._v(" "),n("ol",[n("li",[n("p",[e._v("Accepts Spring Integration messages from its "),n("code",[e._v("MessageChannel")])])]),e._v(" "),n("li",[n("p",[e._v("Determines the "),n("code",[e._v("WebSocketSession")]),e._v(" "),n("code",[e._v("id")]),e._v(" from the "),n("code",[e._v("MessageHeaders")])])]),e._v(" "),n("li",[n("p",[e._v("Retrieves the "),n("code",[e._v("WebSocketSession")]),e._v(" from the provided "),n("code",[e._v("IntegrationWebSocketContainer")])])]),e._v(" "),n("li",[n("p",[e._v("Delegates the conversion and sending of "),n("code",[e._v("WebSocketMessage")]),e._v(" work to the appropriate "),n("code",[e._v("SubProtocolHandler")]),e._v(" from the provided "),n("code",[e._v("SubProtocolHandlerRegistry")]),e._v(".")])])]),e._v(" "),n("p",[e._v("On the client side, the "),n("code",[e._v("WebSocketSession")]),e._v(" "),n("code",[e._v("id")]),e._v(" message header is not required, because "),n("code",[e._v("ClientWebSocketContainer")]),e._v(" deals only with a single connection and its "),n("code",[e._v("WebSocketSession")]),e._v(" respectively.")]),e._v(" "),n("p",[e._v("To use the STOMP sub-protocol, you should configure this adapter with a "),n("code",[e._v("StompSubProtocolHandler")]),e._v(".\nThen you can send any STOMP message type to this adapter, using "),n("code",[e._v("StompHeaderAccessor.create(StompCommand…​)")]),e._v(" and a "),n("code",[e._v("MessageBuilder")]),e._v(", or just using a "),n("code",[e._v("HeaderEnricher")]),e._v(" (see "),n("RouterLink",{attrs:{to:"/en/spring-integration/content-enrichment.html#header-enricher"}},[e._v("Header Enricher")]),e._v(").")],1),e._v(" "),n("p",[e._v("The rest of this chapter covers largely additional configuration options.")]),e._v(" "),n("h3",{attrs:{id:"websockets-namespace-support"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#websockets-namespace-support"}},[e._v("#")]),e._v(" WebSockets Namespace Support")]),e._v(" "),n("p",[e._v("The Spring Integration WebSocket namespace includes several components described in the remainder of this chapter.\nTo include it in your configuration, use the following namespace declaration in your application context configuration file:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('\n\n ...\n\n')])])]),n("h4",{attrs:{id:"int-websocket-client-container-attributes"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#int-websocket-client-container-attributes"}},[e._v("#")]),e._v(" "),n("code",[e._v("")]),e._v(" Attributes")]),e._v(" "),n("p",[e._v("The following listing shows the attributes available for the "),n("code",[e._v("")]),e._v(" element:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v(' (9)\n \n \n (10)\n\n')])])]),n("table",[n("thead",[n("tr",[n("th",[n("strong",[e._v("1")])]),e._v(" "),n("th",[e._v("The component bean name.")])])]),e._v(" "),n("tbody",[n("tr",[n("td",[n("strong",[e._v("2")])]),e._v(" "),n("td",[e._v("The "),n("code",[e._v("WebSocketClient")]),e._v(" bean reference.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("3")])]),e._v(" "),n("td",[e._v("The "),n("code",[e._v("uri")]),e._v(" or "),n("code",[e._v("uriTemplate")]),e._v(" to the target WebSocket service."),n("br"),e._v("If you use it as a "),n("code",[e._v("uriTemplate")]),e._v(" with URI variable placeholders, the "),n("code",[e._v("uri-variables")]),e._v(" attribute is required.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("4")])]),e._v(" "),n("td",[e._v("Comma-separated values for the URI variable placeholders within the "),n("code",[e._v("uri")]),e._v(" attribute value."),n("br"),e._v("The values are replaced into the placeholders according to their order in the "),n("code",[e._v("uri")]),e._v("."),n("br"),e._v("See "),n("a",{attrs:{href:"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/util/UriComponents.html#expand-java.lang.Object",target:"_blank",rel:"noopener noreferrer"}},[n("code",[e._v("UriComponents.expand(Object…​uriVariableValues)")]),n("OutboundLink")],1),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("5")])]),e._v(" "),n("td",[e._v("The "),n("code",[e._v("Origin")]),e._v(" Handshake HTTP header value.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("6")])]),e._v(" "),n("td",[e._v("The WebSocket session 'send' timeout limit."),n("br"),e._v("Defaults to "),n("code",[e._v("10000")]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("7")])]),e._v(" "),n("td",[e._v("The WebSocket session 'send' message size limit."),n("br"),e._v("Defaults to "),n("code",[e._v("524288")]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("8")])]),e._v(" "),n("td",[e._v("Boolean value indicating whether this endpoint should start automatically."),n("br"),e._v("Defaults to "),n("code",[e._v("false")]),e._v(", assuming that this container is started from the "),n("a",{attrs:{href:"#web-socket-inbound-adapter"}},[e._v("WebSocket inbound adapter")]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("9")])]),e._v(" "),n("td",[e._v("The lifecycle phase within which this endpoint should start and stop."),n("br"),e._v("The lower the value, the earlier this endpoint starts and the later it stops."),n("br"),e._v("The default is "),n("code",[e._v("Integer.MAX_VALUE")]),e._v("."),n("br"),e._v("Values can be negative."),n("br"),e._v("See "),n("a",{attrs:{href:"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/SmartLifecycle.html",target:"_blank",rel:"noopener noreferrer"}},[n("code",[e._v("SmartLifeCycle")]),n("OutboundLink")],1),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("10")])]),e._v(" "),n("td",[e._v("A "),n("code",[e._v("Map")]),e._v(" of "),n("code",[e._v("HttpHeaders")]),e._v(" to be used with the Handshake request.")])])])]),e._v(" "),n("h4",{attrs:{id:"int-websocket-server-container-attributes"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#int-websocket-server-container-attributes"}},[e._v("#")]),e._v(" "),n("code",[e._v("")]),e._v(" Attributes")]),e._v(" "),n("p",[e._v("The following listing shows the attributes available for the "),n("code",[e._v("")]),e._v(" element:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v(' (8)\n (19)\n\n')])])]),n("table",[n("thead",[n("tr",[n("th",[n("strong",[e._v("1")])]),e._v(" "),n("th",[e._v("The component bean name.")])])]),e._v(" "),n("tbody",[n("tr",[n("td",[n("strong",[e._v("2")])]),e._v(" "),n("td",[e._v("A path (or comma-separated paths) that maps a particular request to a "),n("code",[e._v("WebSocketHandler")]),e._v("."),n("br"),e._v("Supports exact path mapping URIs (such as "),n("code",[e._v("/myPath")]),e._v(") and ant-style path patterns (such as "),n("code",[e._v("/myPath/**")]),e._v(").")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("3")])]),e._v(" "),n("td",[e._v("The "),n("code",[e._v("HandshakeHandler")]),e._v(" bean reference."),n("br"),e._v("Defaults to "),n("code",[e._v("DefaultHandshakeHandler")]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("4")])]),e._v(" "),n("td",[e._v("List of "),n("code",[e._v("HandshakeInterceptor")]),e._v(" bean references.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("5")])]),e._v(" "),n("td",[e._v("List of one or more factories ("),n("code",[e._v("WebSocketHandlerDecoratorFactory")]),e._v(") that decorate the handler used to process WebSocket messages."),n("br"),e._v("This may be useful for some advanced use cases (for example, to allow Spring Security to forcibly close"),n("br"),e._v("the WebSocket session when the corresponding HTTP session expires)."),n("br"),e._v("See the "),n("a",{attrs:{href:"https://docs.spring.io/spring-session/docs/current/reference/html5/#websocket",target:"_blank",rel:"noopener noreferrer"}},[e._v("Spring Session Project"),n("OutboundLink")],1),e._v(" for more information.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("6")])]),e._v(" "),n("td",[e._v("See the same option on the "),n("a",{attrs:{href:"#websocket-client-container-attributes"}},[n("code",[e._v("")])]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("7")])]),e._v(" "),n("td",[e._v("See the same option on the "),n("a",{attrs:{href:"#websocket-client-container-attributes"}},[n("code",[e._v("")])]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("8")])]),e._v(" "),n("td",[e._v("The allowed origin header values."),n("br"),e._v("You can specify multiple origins as a comma-separated list."),n("br"),e._v("This check is mostly designed for browser clients."),n("br"),e._v("There is nothing preventing other types of client from modifying the origin header value."),n("br"),e._v("When SockJS is enabled and allowed origins are restricted, transport types that do not use origin headers for cross-origin requests ("),n("code",[e._v("jsonp-polling")]),e._v(", "),n("code",[e._v("iframe-xhr-polling")]),e._v(", "),n("code",[e._v("iframe-eventsource")]),e._v(", and "),n("code",[e._v("iframe-htmlfile")]),e._v(") are disabled."),n("br"),e._v("As a consequence, IE6 and IE7 are not supported, and IE8 and IE9 are supported only without cookies."),n("br"),e._v("By default, all origins are allowed.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("9")])]),e._v(" "),n("td",[e._v("Transports with no native cross-domain communication (such as "),n("code",[e._v("eventsource")]),e._v(" and "),n("code",[e._v("htmlfile")]),e._v(") 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."),n("br"),e._v("Since the iframe needs to load the SockJS javascript client library, this property lets you specify the location from which to load it."),n("br"),e._v("By default, it points to "),n("code",[e._v("[https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js](https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js)")]),e._v("."),n("br"),e._v("However, you can also set it to point to a URL served by the application."),n("br"),e._v("Note that it is possible to specify a relative URL, in which case the URL must be relative to the iframe URL."),n("br"),e._v("For example, assuming a SockJS endpoint mapped to "),n("code",[e._v("/sockjs")]),e._v(" and the resulting iframe URL is "),n("code",[e._v("/sockjs/iframe.html")]),e._v(', the relative URL must start with "../../" to traverse up to the location above the SockJS mapping.'),n("br"),e._v("For prefix-based servlet mapping, you may need one more traversal.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("10")])]),e._v(" "),n("td",[e._v("Minimum number of bytes that can be sent over a single HTTP streaming request before it is closed."),n("br"),e._v("Defaults to "),n("code",[e._v("128K")]),e._v(" (that is, 128*1024 or 131072 bytes).")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("11")])]),e._v(" "),n("td",[e._v("The "),n("code",[e._v("cookie_needed")]),e._v(" value in the response from the SockJs "),n("code",[e._v("/info")]),e._v(" endpoint."),n("br"),e._v("This property indicates whether a "),n("code",[e._v("JSESSIONID")]),e._v(" cookie is required for the application to function correctly (for example, for load balancing or in Java Servlet containers for the use of an HTTP session).")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("12")])]),e._v(" "),n("td",[e._v("The amount of time (in milliseconds) when the server has not sent any messages and after which the server should"),n("br"),e._v("send a heartbeat frame to the client in order to keep the connection from breaking."),n("br"),e._v("The default value is "),n("code",[e._v("25,000")]),e._v(" (25 seconds).")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("13")])]),e._v(" "),n("td",[e._v("The amount of time (in milliseconds) before a client is considered disconnected after not having a receiving connection (that is, an active connection over which the server can send data to the client)."),n("br"),e._v("The default value is "),n("code",[e._v("5000")]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("14")])]),e._v(" "),n("td",[e._v("The number of server-to-client messages that a session can cache while waiting for the next HTTP polling request from the client."),n("br"),e._v("The default size is "),n("code",[e._v("100")]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("15")])]),e._v(" "),n("td",[e._v("Some load balancers do not support WebSockets."),n("br"),e._v("Set this option to "),n("code",[e._v("false")]),e._v(" to disable the WebSocket transport on the server side."),n("br"),e._v("The default value is "),n("code",[e._v("true")]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("16")])]),e._v(" "),n("td",[e._v("The "),n("code",[e._v("TaskScheduler")]),e._v(" bean reference."),n("br"),e._v("A new "),n("code",[e._v("ThreadPoolTaskScheduler")]),e._v(" instance is created if no value is provided."),n("br"),e._v("This scheduler instance is used for scheduling heart-beat messages.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("17")])]),e._v(" "),n("td",[e._v("The "),n("code",[e._v("SockJsMessageCodec")]),e._v(" bean reference to use for encoding and decoding SockJS messages."),n("br"),e._v("By default, "),n("code",[e._v("Jackson2SockJsMessageCodec")]),e._v(" is used, which requires the Jackson library to be present on the classpath.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("18")])]),e._v(" "),n("td",[e._v("List of "),n("code",[e._v("TransportHandler")]),e._v(" bean references.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("19")])]),e._v(" "),n("td",[e._v("Whether to disable automatic addition of CORS headers for SockJS requests."),n("br"),e._v("The default value is "),n("code",[e._v("false")]),e._v(".")])])])]),e._v(" "),n("h4",{attrs:{id:"int-websocket-outbound-channel-adapter-attributes"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#int-websocket-outbound-channel-adapter-attributes"}},[e._v("#")]),e._v(" "),n("code",[e._v("")]),e._v(" Attributes")]),e._v(" "),n("p",[e._v("The following listing shows the attributes available for the "),n("code",[e._v("")]),e._v(" element:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v(' (9)\n')])])]),n("table",[n("thead",[n("tr",[n("th",[n("strong",[e._v("1")])]),e._v(" "),n("th",[e._v("The component bean name."),n("br"),e._v("If you do not provide the "),n("code",[e._v("channel")]),e._v(" attribute, a "),n("code",[e._v("DirectChannel")]),e._v(" is created and registered in the application context with this "),n("code",[e._v("id")]),e._v(" attribute as the bean name."),n("br"),e._v("In this case, the endpoint is registered with the bean name "),n("code",[e._v("id")]),e._v(" plus "),n("code",[e._v(".adapter")]),e._v("."),n("br"),e._v("And the "),n("code",[e._v("MessageHandler")]),e._v(" is registered with the bean alias "),n("code",[e._v("id")]),e._v(" plus "),n("code",[e._v(".handler")]),e._v(".")])])]),e._v(" "),n("tbody",[n("tr",[n("td",[n("strong",[e._v("2")])]),e._v(" "),n("td",[e._v("Identifies the channel attached to this adapter.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("3")])]),e._v(" "),n("td",[e._v("The reference to the "),n("code",[e._v("IntegrationWebSocketContainer")]),e._v(" bean, which encapsulates the low-level connection and "),n("code",[e._v("WebSocketSession")]),e._v(" handling operations."),n("br"),e._v("Required.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("4")])]),e._v(" "),n("td",[e._v("Optional reference to a "),n("code",[e._v("SubProtocolHandler")]),e._v(" instance."),n("br"),e._v("It is used when the client did not request a sub-protocol or it is a single protocol-handler."),n("br"),e._v("If this reference or a "),n("code",[e._v("protocol-handlers")]),e._v(" list is not provided, the "),n("code",[e._v("PassThruSubProtocolHandler")]),e._v(" is used by default.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("5")])]),e._v(" "),n("td",[e._v("List of "),n("code",[e._v("SubProtocolHandler")]),e._v(" bean references for this channel adapter."),n("br"),e._v("If you provide only a single bean reference and do not provide a "),n("code",[e._v("default-protocol-handler")]),e._v(", that single "),n("code",[e._v("SubProtocolHandler")]),e._v(" is used as the "),n("code",[e._v("default-protocol-handler")]),e._v("."),n("br"),e._v("If you do not set this attribute or "),n("code",[e._v("default-protocol-handler")]),e._v(", the "),n("code",[e._v("PassThruSubProtocolHandler")]),e._v(" is used by default.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("6")])]),e._v(" "),n("td",[e._v("List of "),n("code",[e._v("MessageConverter")]),e._v(" bean references for this channel adapter.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("7")])]),e._v(" "),n("td",[e._v("Boolean value indicating whether the default converters should be registered after any custom converters."),n("br"),e._v("This flag is used only if "),n("code",[e._v("message-converters")]),e._v(" is provided."),n("br"),e._v("Otherwise, all default converters are registered."),n("br"),e._v("Defaults to "),n("code",[e._v("false")]),e._v("."),n("br"),e._v("The default converters are (in order): "),n("code",[e._v("StringMessageConverter")]),e._v(", "),n("code",[e._v("ByteArrayMessageConverter")]),e._v(", and "),n("code",[e._v("MappingJackson2MessageConverter")]),e._v(" (if the Jackson library is present on the classpath).")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("8")])]),e._v(" "),n("td",[e._v("Boolean value indicating whether this endpoint should start automatically."),n("br"),e._v("Defaults to "),n("code",[e._v("true")]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("9")])]),e._v(" "),n("td",[e._v("The lifecycle phase within which this endpoint should start and stop."),n("br"),e._v("The lower the value, the earlier this endpoint starts and the later it stops."),n("br"),e._v("The default is "),n("code",[e._v("Integer.MIN_VALUE")]),e._v("."),n("br"),e._v("Values can be negative."),n("br"),e._v("See "),n("a",{attrs:{href:"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/SmartLifecycle.html",target:"_blank",rel:"noopener noreferrer"}},[n("code",[e._v("SmartLifeCycle")]),n("OutboundLink")],1),e._v(".")])])])]),e._v(" "),n("h4",{attrs:{id:"int-websocket-inbound-channel-adapter-attributes"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#int-websocket-inbound-channel-adapter-attributes"}},[e._v("#")]),e._v(" "),n("code",[e._v("")]),e._v(" Attributes")]),e._v(" "),n("p",[e._v("The following listing shows the attributes available for the "),n("code",[e._v("")]),e._v(" element:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v(' (13)\n')])])]),n("table",[n("thead",[n("tr",[n("th",[n("strong",[e._v("1")])]),e._v(" "),n("th",[e._v("The component bean name."),n("br"),e._v("If you do not set the "),n("code",[e._v("channel")]),e._v(" attribute, a "),n("code",[e._v("DirectChannel")]),e._v(" is created and registered in the application context with this "),n("code",[e._v("id")]),e._v(" attribute as the bean name."),n("br"),e._v("In this case, the endpoint is registered with the bean name "),n("code",[e._v("id")]),e._v(" plus "),n("code",[e._v(".adapter")]),e._v(".")])])]),e._v(" "),n("tbody",[n("tr",[n("td",[n("strong",[e._v("2")])]),e._v(" "),n("td",[e._v("Identifies the channel attached to this adapter.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("3")])]),e._v(" "),n("td",[e._v("The "),n("code",[e._v("MessageChannel")]),e._v(" bean reference to which the "),n("code",[e._v("ErrorMessage")]),e._v(" instances should be sent.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("4")])]),e._v(" "),n("td",[e._v("See the same option on the "),n("a",{attrs:{href:"#websocket-outbound-channel-adapter-attributes"}},[n("code",[e._v("")])]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("5")])]),e._v(" "),n("td",[e._v("See the same option on the "),n("a",{attrs:{href:"#websocket-outbound-channel-adapter-attributes"}},[n("code",[e._v("")])]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("6")])]),e._v(" "),n("td",[e._v("See the same option on the "),n("a",{attrs:{href:"#websocket-outbound-channel-adapter-attributes"}},[n("code",[e._v("")])]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("7")])]),e._v(" "),n("td",[e._v("See the same option on the "),n("a",{attrs:{href:"#websocket-outbound-channel-adapter-attributes"}},[n("code",[e._v("")])]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("8")])]),e._v(" "),n("td",[e._v("See the same option on the "),n("a",{attrs:{href:"#websocket-outbound-channel-adapter-attributes"}},[n("code",[e._v("")])]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("9")])]),e._v(" "),n("td",[e._v("Maximum amount of time (in milliseconds) to wait when sending a message to the channel if the channel can block."),n("br"),e._v("For example, a "),n("code",[e._v("QueueChannel")]),e._v(" can block until space is available if its maximum capacity has been reached.")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("10")])]),e._v(" "),n("td",[e._v("Fully qualified name of the Java type for the target "),n("code",[e._v("payload")]),e._v(" to convert from the incoming "),n("code",[e._v("WebSocketMessage")]),e._v("."),n("br"),e._v("Defaults to "),n("code",[e._v("java.lang.String")]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("11")])]),e._v(" "),n("td",[e._v("Indicates whether this adapter sends "),n("code",[e._v("non-MESSAGE")]),e._v(" "),n("code",[e._v("WebSocketMessage")]),e._v(" instances and messages with broker destinations to the "),n("code",[e._v("AbstractBrokerMessageHandler")]),e._v(" from the application context."),n("br"),e._v("When this attribute is "),n("code",[e._v("true")]),e._v(", the "),n("code",[e._v("Broker Relay")]),e._v(" configuration is required."),n("br"),e._v("This attribute is used only on the server side."),n("br"),e._v("On the client side, it is ignored."),n("br"),e._v("Defaults to "),n("code",[e._v("false")]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("12")])]),e._v(" "),n("td",[e._v("See the same option on the "),n("a",{attrs:{href:"#websocket-outbound-channel-adapter-attributes"}},[n("code",[e._v("")])]),e._v(".")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("13")])]),e._v(" "),n("td",[e._v("See the same option on the "),n("a",{attrs:{href:"#websocket-outbound-channel-adapter-attributes"}},[n("code",[e._v("")])]),e._v(".")])])])]),e._v(" "),n("h3",{attrs:{id:"using-clientstompencoder"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#using-clientstompencoder"}},[e._v("#")]),e._v(" Using "),n("code",[e._v("ClientStompEncoder")])]),e._v(" "),n("p",[e._v("Starting with version 4.3.13, Spring Integration provides "),n("code",[e._v("ClientStompEncoder")]),e._v(" (as an extension of the standard "),n("code",[e._v("StompEncoder")]),e._v(") for use on the client side of WebSocket channel adapters.\nFor proper client side message preparation, you must inject an instance of the "),n("code",[e._v("ClientStompEncoder")]),e._v(" into the "),n("code",[e._v("StompSubProtocolHandler")]),e._v(".\nOne problem with the default "),n("code",[e._v("StompSubProtocolHandler")]),e._v(" is that it was designed for the server side, so it updates the "),n("code",[e._v("SEND")]),e._v(" "),n("code",[e._v("stompCommand")]),e._v(" header into "),n("code",[e._v("MESSAGE")]),e._v(" (as required by the STOMP protocol for the server side).\nIf the client does not send its messages in the proper "),n("code",[e._v("SEND")]),e._v(" web socket frame, some STOMP brokers do not accept them.\nThe purpose of the "),n("code",[e._v("ClientStompEncoder")]),e._v(", in this case, is to override the "),n("code",[e._v("stompCommand")]),e._v(" header and set it to the "),n("code",[e._v("SEND")]),e._v(" value before encoding the message to the "),n("code",[e._v("byte[]")]),e._v(".")]),e._v(" "),n("h3",{attrs:{id:"dynamic-websocket-endpoints-registration"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#dynamic-websocket-endpoints-registration"}},[e._v("#")]),e._v(" Dynamic WebSocket Endpoints Registration")]),e._v(" "),n("p",[e._v("Starting with version 5.5, the WebSocket server endpoints (channel adapters based on a "),n("code",[e._v("ServerWebSocketContainer")]),e._v(") can now be registered (and removed) at runtime - the "),n("code",[e._v("paths")]),e._v(" a "),n("code",[e._v("ServerWebSocketContainer")]),e._v(" is mapped is exposed via "),n("code",[e._v("HandlerMapping")]),e._v(" into a "),n("code",[e._v("DispatcherServlet")]),e._v(" and accessible for WebSocket clients.\nThe "),n("RouterLink",{attrs:{to:"/en/spring-integration/dsl.html#java-dsl-runtime-flows"}},[e._v("Dynamic and Runtime Integration Flows")]),e._v(" support helps to register these endpoints in a transparent manner:")],1),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@Autowired\nIntegrationFlowContext integrationFlowContext;\n\n@Autowired\nHandshakeHandler handshakeHandler;\n...\nServerWebSocketContainer serverWebSocketContainer =\n new ServerWebSocketContainer("/dynamic")\n .setHandshakeHandler(this.handshakeHandler);\n\nWebSocketInboundChannelAdapter webSocketInboundChannelAdapter =\n new WebSocketInboundChannelAdapter(serverWebSocketContainer);\n\nQueueChannel dynamicRequestsChannel = new QueueChannel();\n\nIntegrationFlow serverFlow =\n IntegrationFlows.from(webSocketInboundChannelAdapter)\n .channel(dynamicRequestsChannel)\n .get();\n\nIntegrationFlowContext.IntegrationFlowRegistration dynamicServerFlow =\n this.integrationFlowContext.registration(serverFlow)\n .addBean(serverWebSocketContainer)\n .register();\n...\ndynamicServerFlow.destroy();\n')])])]),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("It is important to call "),n("code",[e._v(".addBean(serverWebSocketContainer)")]),e._v(" on the dynamic flow registration to add the instance of "),n("code",[e._v("ServerWebSocketContainer")]),e._v(" into an "),n("code",[e._v("ApplicationContext")]),e._v(" for endpoint registration."),n("br"),e._v("When a dynamic flow registration is destroyed, the associated "),n("code",[e._v("ServerWebSocketContainer")]),e._v(" instance is destroyed, too, as well as the respective endpoint registration, including URL path mappings.")])])]),e._v(" "),n("tbody")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("The dynamic Websocket endpoints can only be registered via Spring Integration mechanism: when regular Spring "),n("code",[e._v("@EnableWebsocket")]),e._v(" is used, Spring Integration configuration backs off and no infrastructure for dynamic endpoints is registered.")])])]),e._v(" "),n("tbody")])])}),[],!1,null,null,null);t.default=a.exports}}]);