diff --git a/artifacts/index.html b/artifacts/index.html index f211123c0..b5a4af914 100644 --- a/artifacts/index.html +++ b/artifacts/index.html @@ -953,7 +953,7 @@
It is built on a web socket client abstraction, and provides a bunch of adapters for popular web socket clients (OkHttp, Ktor, Spring, SockJS...). It also provides out-of-the-box a built-in web socket implementation (without third-party dependencies) for most platforms (see below).
Krossbow can also be used as a multiplatform web socket client without STOMP protocol.
"},{"location":"#features","title":"Features","text":"All the STOMP 1.2 specification is implemented:
ACK
/NACK
and transactionsRECEIPT
frame based on receipt header)Additional features:
receipt
headers to ensure no frame is lost)If you find a bug or a feature that's missing compared to the specification, please open an issue.
"},{"location":"#supported-targets","title":"Supported targets","text":"Krossbow supports most Kotlin targets in its STOMP and web socket API modules: JVM, JS (browser and nodeJS), iOS, watchOS, tvOS, macOSX64, linuxX64, mingwX64.
However, each web socket client implementation has its own subset of supported targets (see below).
Android not tested on CI
Android 5.0+ (API level 21+) is supported by using JVM artifacts (e.g. OkHttp). However, the Android tooling's desugaring is currently not tested as part of the build, so any feedback on this use case is more than welcome. Please upvote the corresponding issue if you'd like to see proper CI or special packaging for the Android target.
"},{"location":"#web-socket-clients-target-support","title":"Web socket clients target support","text":"Krossbow can use built-in web socket implementations without third-party dependencies on some platforms. It also provides adapters for third-party implementations which have different platform support. Here is a summary of the supported platforms by module:
Module Browser NodeJS JVM iOS / tvOS / watchOS macOS / Linux / Windows Transitive dependencies Built-in (JDK\u00a011+) None Ktor Ktor, and the relevant Ktor engine(s) OkHttp OkHttp SockJS sockjs-client (on JS), Spring websocket (on JVM) Spring Spring websocketsupported with actual web socket transport (RFC6455)
supported using SockJS protocol (requires a SockJS server)
"},{"location":"#contribute","title":"Contribute","text":"Don't hesitate to open GitHub issues, even to ask questions or discuss a new feature. Pull-requests are welcome, but please open an issue first so that we can discuss the initial design or fix, which may avoid unnecessary work.
"},{"location":"artifacts/","title":"Artifacts summary","text":"Krossbow offers a lot of possibilities so here is a summary of all available artifacts.
All these artifacts are published to Maven Central under the group ID org.hildan.krossbow
and a common version.
You should pick only one of the krossbow-stomp-*
artifacts, depending on whether you need automatic serialization of frame bodies:
krossbow-stomp-coreThe basic multiplatform STOMP client. It implements the STOMP 1.2 protocol on top of the web socket abstraction defined by the
krossbow-websocket-core
module. krossbow-stomp-jacksonA superset of
krossbow-stomp-core
adding JSON conversion features using Jackson (JVM only) krossbow-stomp-moshiA superset of
krossbow-stomp-core
adding JSON conversion features using Moshi (JVM only) krossbow-stomp-kxserializationA superset of
krossbow-stomp-core
adding conversion features using Kotlinx Serialization library (multiplatform). You can leverage the multi-format capabilities of Kotlinx Serialization (JSON, protobuf, CBOR, ...). krossbow-stomp-kxserialization-jsonA superset of
krossbow-stomp-kxserialization
adding JSON helpers and the JSON format dependency. Then add the dependency of your choice to your Gradle build. For instance, if you intend to use Krossbow with Kotlinx Serialization:
implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization:7.0.0\")\n
Don't need STOMP?
If you're just interested in the web socket client without STOMP protocol, don't declare a STOMP artifact, but instead choose one of the web socket artifacts below.
"},{"location":"artifacts/#web-socket-artifacts","title":"Web Socket artifacts","text":"The STOMP artifacts depend on a web socket API that needs an implementation. Krossbow provides implementations for the built-in web socket API of most platforms, and also adapters for 3rd-party web socket implementations:
Artifact Descriptionkrossbow-websocket-builtinA multiplatform
WebSocketClient
implementation that adapts the built-in client for each supported platform without transitive dependency. krossbow-websocket-ktorA multiplatform
WebSocketClient
implementation based on Ktor 2.3.9's HttpClient
. krossbow-websocket-okhttpA JVM implementation of the web socket API using OkHttp's client.
krossbow-websocket-sockjsA multiplatform
WebSocketClient
implementation for use with SockJS servers. It uses Spring's SockJSClient on JVM, and npm sockjs-client
for JavaScript (NodeJS and browser). krossbow-websocket-springA JVM 8+ implementation of the web socket API using Spring's WebSocketClient. Provides both a normal WebSocket client and a SockJS one.
Peer dependencies
Some Krossbow modules are not opinionated and require some extra third-party peer dependencies. Make sure to read the usage section corresponding to the module of your choice for more details.
"},{"location":"license/","title":"MIT License","text":"Copyright (c) 2019-2023 Joffrey Bion
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"},{"location":"migration-guides/","title":"Migration guides","text":"Here are some details about how to migrate from one major version to another.
"},{"location":"migration-guides/#from-6x-to-7x","title":"From 6.x to 7.x","text":""},{"location":"migration-guides/#web-socket-subprotocol-negotiation","title":"Web socket subprotocol negotiation","text":"As part of issue #493, a separate protocols
parameter was added to WebSocketClient.connect
to enable web socket subprotocol negotiation.
Binary compatibility is preserved through some hidden synthetic functions. However, source compatibility isn't: usages of the connect()
method that passed custom headers without a named headers
parameter will no longer compile. Adding the headers =
parameter name will solve the issue.
For full negotiation support, clients need to be aware of which subprotocol the server chose to speak. This is why the protocol
property was added to WebSocketConnection
(#498). Implementers of this interface must implement this property. There shouldn't be many 3rd party implementations of the connection interface, so binary compatibility should not be a real issue here.
Some servers like ActiveMQ require negotiating the STOMP protocol as a web socket subprotocol during the web socket handshake (see issue #492), and cannot work otherwise.
Breaking change: To make the experience smoother, Krossbow v7.0.0 now automatically sends STOMP subprotocols (in all supported versions) during the web socket handshake via the Sec-WebSocket-Protocol
header.
If your server doesn't support it, you can customize the web socket handshake by manually connecting using WebSocketClient.connect()
with the parameters that suit you best, and then connect at STOMP level using the WebSocketConnection.stomp()
extension (without the need for a StompClient
at all):
val client: WebSocketClient = TODO(\"get some web socket client implementation\")\nval wsConnection = client.connect(url) // without any subprotocols\nval stompConfig = StompConfig().apply { \n // set your config here if needed\n}\nval stompSession = wsConnection.stomp(config)\n
"},{"location":"migration-guides/#no-host-header-sent-for-stomp-10","title":"No host
header sent for STOMP 1.0","text":"Thanks to the aforementioned changes, we can now detect the STOMP protocol version used by the server before sending the first STOMP frame (CONNECT
or STOMP
). If STOMP 1.0 is detected as web socket subprotocol during the web socket handshake, we no longer send by default the host
header which was introduced in 1.1 (and effectively breaks some old servers, see #122). It can still be sent by manually specifying it of course.
The STOMP protocol itself supports negotiation of the version via headers in the CONNECT
(or STOMP
) frame. So far, Krossbow only specified 1.2
as supported version. From now on, all 3 versions 1.0
, 1.1
, and 1.2
are advertised as supported by the client.
If necessary, this behavior can be overridden by sending the accept-version
header manually in customStompConnectHeaders
.
Because the protocol version can be negotiated both via web socket subprotocol and at STOMP level, there could potentially be a mismatch. If this happens, the connect()
call throws an exception. This can be disabled with StompConfig.failOnStompVersionMismatch
.
StompClient.connect()
now throws a different WebSocketConnectionException
","text":"The org.hildan.krossbow.stomp.WebSocketConnectionException
is deprecated in favor of org.hildan.krossbow.websocket.WebSocketConnectionException
. That exception has been around for a few years now and encapsulates all connection failures from different client implementations already, so there is no need for a similar exception at the StompClient
level.
If you used to catch this exception, make sure to update your import (the error-level deprecation should mitigate any risk of missing the new exception).
"},{"location":"migration-guides/#from-5x-to-6x","title":"From 5.x to 6.x","text":""},{"location":"migration-guides/#switch-to-kotlinx-io-and-the-bytestring-type","title":"Switch tokotlinx-io
and the ByteString
type","text":"The kotlinx-io
library has been revamped, with an implementation that closely matches Okio now. Krossbow has now internally switched from Okio to kotlinx-io
as a result, but this part should have no visible effect for the consumers of the library.
However, since kotlinx-io
is a somewhat \"standard\" extension library for Kotlin, Krossbow can now more legitimately use its ByteString
type in public APIs that handled binary data (for both web socket and STOMP sessions). This type represents immutable sequences of bytes, which is more convenient API-wise than byte arrays.
You will have to switch to these types if you used binary-based APIs.
"},{"location":"migration-guides/#deprecations-cleanup","title":"Deprecations cleanup","text":"This is a major version, and therefore we allowed ourselves some cleanup by removing a bunch of deprecated APIs. If you see unresolved references and are not sure how to fix them, please switch back to the Krossbow version 5 and fix deprecation warnings by following the corresponding instructions.
Please check the changelog for the list of removals.
"},{"location":"migration-guides/#from-4x-to-5x","title":"From 4.x to 5.x","text":""},{"location":"migration-guides/#end-of-ktor-1x-support","title":"End of Ktor 1.x support","text":"krossbow-websocket-ktor-legacy
artifact was removed. This means Krossbow no longer works with Ktor 1.x. If you were using this module, please migrate to Ktor 2, and use the non-legacy krossbow-websocket-ktor module.
This update of Krossbow brings Kotlin 1.8, which might bring some incompatible changes to the Kotlin stdlib. Please check the compatibility guide if you were using an older version of Kotlin.
"},{"location":"migration-guides/#from-3x-to-4x","title":"From 3.x to 4.x","text":""},{"location":"migration-guides/#withjsonconversions-moved-to-its-own-module","title":"withJsonConversions moved to its own module","text":"If you were using Krossbow with krossbow-stomp-kxserialization
, the withJsonConversions
helper has moved to a new module called krossbow-stomp-kxserialization-json
. This new module now transitively brings kotlinx-serialization-json
so you don't need to depend on that one explicitly.
StompClient
constructor removed","text":"Up to (and including) version 3.x of Krossbow, the built-in web socket clients for the supported platforms were part of the krossbow-websocket-core
module. This module provided a WebSocketClient.Companion.default()
factory function to provide the built-in web socket implementation of the current platform. Likewise, the krossbow-stomp-core
module provided a StompClient
constructor that used the \"default\" built-in web socket implementation for the current platform.
This approach limited the targets supported by those 2 core modules, even though all of their functionality was target-agnostic. In order to support all Kotlin platforms in pure Kotlin modules, the built-in websocket implementations had to be moved to a separate module, and the constructor without web socket client was moved to a separate module (and later removed completely for simplicity).
Breaking dependency changes, in short:
WebSocketClient.default()
from krossbow-websocket-core
, or any of the built-in clients directly, simply change your dependency to krossbow-websocket-builtin
instead.StompClient()
constructor without WS client argument (using the default value), add an explicit dependency on krossbow-websocket-builtin
and pass the built-in client explicitly to the constructor: StompClient(WebSocketClient.default())
.Note: the WebSocketClient.default()
function was since renamed WebSocketClient.builtIn()
in newer versions.
If you used other web socket implementations than the built-in ones, you don't have to change anything to your dependencies.
"},{"location":"migration-guides/#from-2x-to-3x","title":"From 2.x to 3.x","text":""},{"location":"migration-guides/#use-durations-instead-of-millis","title":"Use Durations instead of millis","text":"StompConfiguration
no longer uses amounts of milliseconds, but uses the kotlin.time.Duration
API. The -Millis
suffixes for the relevant properties were therefore dropped and the types changed.
Before:
val stomp = StompClient {\n connectionTimeoutMillis = 2000\n receiptTimeoutMillis = 5000\n disconnectTimeoutMillis = 300\n}\n
After:
import kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.Duration.Companion.seconds\n\nval stomp = StompClient {\n connectionTimeout = 2.seconds\n receiptTimeout = 5.seconds\n disconnectTimeout = 300.milliseconds\n}\n
"},{"location":"migration-guides/#flow-instead-of-channel-in-websocketconnection","title":"Flow instead of Channel in WebSocketConnection","text":"If you used the websocket API directly, the incomingFrames
channel is now a Flow
.
Before:
val conn = wsClient.connect(url)\nfor (frame in conn.incomingFrames) {\n // do stuff\n}\n
After:
val conn = wsClient.connect(url)\nconn.incomingFrames.collect {\n // do stuff\n}\n
"},{"location":"migration-guides/#tyrus-no-longer-embedded-in-krossbow-websocket-spring","title":"Tyrus no longer embedded in krossbow-websocket-spring
","text":"krossbow-websocket-spring
no longer transitively brings a dependency on Tyrus.
If you didn't add any JSR-356 implementation manually, you now have to explicitly depend on one. If you want the same behaviour as before, add the Tyrus dependency to your build.gradle.kts
as follows:
dependencies {\n implementation(\"org.glassfish.tyrus.bundles:tyrus-standalone-client-jdk:2.1.5\")\n}\n
"},{"location":"migration-guides/#from-1x-to-2x","title":"From 1.x to 2.x","text":""},{"location":"migration-guides/#stompsessionuse-now-passes-the-session-as-it-not-this","title":"StompSession.use
now passes the session as it
, not this
","text":"In order to align with Closeable.use, the lambda for StompSession.use
now receives the session as an argument (it
) and not receiver (this
).
Before:
StompClient().connect(url).use {\n sendText(\"/dest\", \"message\")\n}\n
After:
StompClient().connect(url).use {\n it.sendText(\"/dest\", \"message\")\n}\n// or\nStompClient().connect(url).use { session ->\n session.sendText(\"/dest\", \"message\")\n}\n
"},{"location":"stomp/advanced-features/","title":"Advanced features","text":""},{"location":"stomp/advanced-features/#receipts-suspension","title":"Receipts & Suspension","text":"The STOMP protocol supports RECEIPT frames, allowing the client to know when the server has received a frame. This only happens if a receipt header is set on the client frame.
If auto-receipt is enabled, a receipt
header is automatically generated and added to all client frames supporting the mechanism, and for which a receipt
header is not already present. If auto-receipt is not enabled, a receipt
header may still be provided manually in the parameters of some overloads.
When a receipt
header is present (automatically added or manually provided), the method that is used to send the frame suspends until the corresponding RECEIPT frame is received from the server. If no RECEIPT frame is received from the server in the configured time limit, a LostReceiptException
is thrown.
If no receipt is provided and auto-receipt is disabled, the method used to send the frame doesn't wait for a RECEIPT frame and never throws LostReceiptException
. Instead, it returns immediately after the underlying web socket implementation is done sending the frame.
When configured, heart beats can be used as a keep-alive to detect if the connection is lost. The heartBeat property should be used to configure heart beats in the StompClient
.
Note that the heart beats for the STOMP session are negotiated with the server. The actual heart beats are defined by the CONNECTED frame received from the server as a result of the negotiation, and may differ from the StompClient
configuration. The negotiation behaviour is defined by the specification.
Sending and checking heart beats is automatically handled by StompSession
implementations, depending on the result of the negotiation with the server. If expected heart beats are not received in time, a MissingHeartBeatException
is thrown and fails active subscriptions.
The graceful disconnect (or graceful shutdown) is a disconnection procedure defined by the STOMP specification to make sure the server gets all the frames before dropping the connection.
If enabled in the config, when disconnecting from the server, the client first sends a DISCONNECT frame with a receipt
header, and then waits for a RECEIPT frame before closing the connection.
If this graceful disconnect is disabled, then calling StompSession.disconnect()
immediately closes the web socket connection. In this case, there is no guarantee that the server received all previous messages.
Not supported in browsers
The browser's WebSocket
API does not support custom headers in the handshake (see this open issue in the web socket standard repo). Because of this, Krossbow cannot support this feature for the JS browser platform. However, the JS web socket client adapter is designed in a way that allows other implementations to support it, such as a Node.js implementation.
Some servers or connection flows may require extra HTTP headers in the web socket handshake. The StompClient.connect()
function doesn't support such headers out of the box, but this function is essentially just a shorthand for connecting at the web socket level, and then connecting at the STOMP level.
In fact, we technically don't need to create and use a StompClient
at all in order to use STOMP with Krossbow. Krossbow provides a WebSocketConnection.stomp() extension function that establishes a STOMP connection from an existing web socket connection.
We can leverage this to customize the web socket connection at will before connecting at STOMP level. For example:
val webSocketClient = WebSocketClient.builtIn() // or another web socket client\n\n// connect at web socket level with custom headers\nval wsSession = webSocketClient.connect(url, headers = mapOf(\"Custom-Header\" to \"custom-value\"))\n\nval config = StompConfig().apply {\n // here you can set up whatever config you would have done in the StompClient { ... } block\n}\n// connect at STOMP level on this open web socket, using the above config\nval stompSession = wsSession.stomp(config)\n
"},{"location":"stomp/config/","title":"Configuration","text":""},{"location":"stomp/config/#configuring-the-stompclient","title":"Configuring the StompClient","text":"The StompClient
can be configured at construction time using a convenient lambda block:
val stompClient = StompClient(WebSocketClient.builtIn()) {\n connectionTimeout = 3.seconds\n gracefulDisconnect = false\n}\n
You can also create the configuration separately and then pass it when constructing the client:
val stompConfig = StompConfig().apply {\n connectionTimeout = 3.seconds\n gracefulDisconnect = false\n}\n\nval stompClient = StompClient(WebSocketClient.builtIn(), stompConfig)\n
"},{"location":"stomp/config/#configuration-options","title":"Configuration options","text":"You can find out about all configuration properties in the StompConfig KDoc.
"},{"location":"stomp/getting-started/","title":"Getting started","text":""},{"location":"stomp/getting-started/#choosing-a-web-socket-implementation","title":"Choosing a web socket implementation","text":"Krossbow uses web sockets as transport for the STOMP communication. Multiple web socket client implementations are supported.
Check out the web socket client table to help you choose a web socket implementation based on the platforms you need to support. You can find more information about each client in their respective section of this doc.
We recommend the built-in client adapters if they cover the Kotlin targets you need to support, in order to limit 3rd party dependencies. Otherwise, Ktor is a good choice if you don't have special needs like SockJS.
"},{"location":"stomp/getting-started/#dependencies-setup","title":"Dependencies setup","text":"For the basic usage of STOMP without serialization, add the krossbow-stomp-core
dependency as well as the web socket module of your choice.
For example to use STOMP with the built-in web socket client:
implementation(\"org.hildan.krossbow:krossbow-stomp-core:7.0.0\")\nimplementation(\"org.hildan.krossbow:krossbow-websocket-builtin:7.0.0\")\n
For other web socket clients, check out their dedicated documentation page to find out which Krossbow dependencies are needed.
The rest of this guide uses the built-in client.
"},{"location":"stomp/getting-started/#basic-usage-without-body-conversions","title":"Basic usage (without body conversions)","text":"This is how to create a STOMP client and interact with it:
val client = StompClient(WebSocketClient.builtIn()) // other config can be passed in here\nval session: StompSession = client.connect(url) // optional login/passcode can be provided here\n\n// Send text messages using this convenience function\nsession.sendText(destination = \"/some/destination\", body = \"Basic text message\")\n\n// Sometimes no message body is necessary\nsession.sendEmptyMsg(destination = \"/some/destination\") \n\n// This subscribe() call triggers a SUBSCRIBE frame\n// and returns the flow of messages for the subscription\nval subscription: Flow<String> = session.subscribeText(\"/some/topic/destination\")\n\n// Use an appropriate coroutine 'scope' to collect the received frames\nval collectorJob = scope.launch {\n subscription.collect { msg ->\n println(\"Received: $msg\")\n }\n}\ndelay(3000)\n// cancelling the flow collector triggers an UNSUBSCRIBE frame\ncollectorJob.cancel()\n\nsession.disconnect()\n
If you want to disconnect automatically in case of exception or normal termination, you can use a try
/finally
block, or use StompSession.use()
, which is similar to Closeable.use()
:
import kotlinx.coroutines.flow.*\nimport org.hildan.krossbow.stomp.*\nimport org.hildan.krossbow.websocket.*\nimport org.hildan.krossbow.websocket.builtin.*\n\nval client = StompClient(WebSocketClient.builtIn()) // other config can be passed in here\nval session: StompSession = client.connect(url) // optional login/passcode can be provided here\n\nsession.use { s ->\n s.sendText(\"/some/destination\", \"Basic text message\") \n\n val subscription: Flow<String> = s.subscribeText(\"/some/topic/destination\")\n\n // terminal operators that finish early (like first) also trigger UNSUBSCRIBE automatically\n val firstMessage: String = subscription.first()\n println(\"Received: $firstMessage\")\n}\n// DISCONNECT frame was automatically sent at the end of the use{...} block\n
"},{"location":"stomp/getting-started/#using-body-conversions","title":"Using body conversions","text":"You can use STOMP with basic text as frame bodies, but it really becomes interesting when you can convert the frame bodies back and forth into Kotlin objects.
Check out the following sections to see how to automatically convert your objects into STOMP frame bodies:
If you want to use your own text conversion, you can implement TextMessageConverter
without any additional module, and use withTextConversions
to wrap your StompSession
into a TypedStompSession
.
Limited JS support
Reflection-based conversions may behave poorly on the JS platform. It is usually safer to rely on Kotlinx Serialization for multiplatform conversions.
val myConverter = object : TextMessageConverter {\n override val mimeType: String = \"application/json;charset=utf-8\"\n\n override fun <T> convertToString(value: T, type: KTypeRef<T>): String {\n TODO(\"your own object -> text conversion\")\n }\n\n override fun <T> convertFromString(text: String, type: KTypeRef<T>): T {\n TODO(\"your own text -> object conversion\")\n }\n}\n\nStompClient(WebSocketClient.builtIn()).connect(url).withTextConversions(myConverter).use { session ->\n session.convertAndSend(\"/some/destination\", MyPojo(\"Custom\", 42)) \n\n val messages = session.subscribe<MyMessage>(\"/some/topic/destination\")\n val firstMessage: MyMessage = messages.first()\n\n println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/jackson/","title":"STOMP with Jackson","text":"The krossbow-stomp-jackson
module is a JVM-only extension of krossbow-stomp-core
that provides new APIs to send and receive properly typed classes, and automatically convert them to/from the JSON bodies of STOMP frames by leveraging Jackson and jackson-module-kotlin.
The main addition is the extension function StompSession.withJackson()
, which turns your StompSession
into a TypedStompSession
. This new session type has additional methods that use Jackson to convert your objects into JSON and back:
StompClient(WebSocketClient.builtIn()).connect(url).withJackson().use { session ->\n session.convertAndSend(\"/some/destination\", Person(\"Bob\", 42)) \n\n val messages: Flow<MyMessage> = session.subscribe<MyMessage>(\"/some/topic/destination\")\n val firstMessage: MyMessage = messages.first()\n\n println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/jackson/#using-a-custom-objectmapper","title":"Using a custom ObjectMapper
","text":"Jackson is highly configurable, and it's often useful to configure the ObjectMapper
manually.
The withJackson()
method takes an optional ObjectMapper
parameter, so you can configure it as you please:
val customObjectMapper: ObjectMapper = jacksonObjectMapper()\n .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)\n .disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)\n .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)\n\nval client = StompClient(WebSocketClient.builtIn()).connect(url)\nval session = client.withJackson(customObjectMapper)\n
"},{"location":"stomp/conversions/jackson/#dependency","title":"Dependency","text":"To use Jackson conversions, add krossbow-stomp-jackson
to your Gradle dependencies (krossbow-stomp-core
is unnecessary because it's transitively brought by this one):
implementation(\"org.hildan.krossbow:krossbow-stomp-jackson:7.0.0\")\n
This dependency transitively brings Jackson 2.17.0 with the Kotlin module.
"},{"location":"stomp/conversions/kx-serialization/","title":"STOMP with Kotlinx Serialization","text":"Kotlinx Serialization is a multiplatform and multi-format serialization library provided by the Kotlin team. It is a popular choice for Kotlin multiplatform libraries especially because of its extensive support of Kotlin features.
The krossbow-stomp-kxserialization
module is an extension of krossbow-stomp-core
that provides new APIs to send and receive properly typed classes, and automatically convert STOMP frame bodies by leveraging Kotlinx Serialization.
Since Kotlinx Serialization supports multiple formats with different dependencies, you normally have to add your own dependency to bring the format of your choice.
However, since JSON is so popular, Krossbow comes with the krossbow-stomp-kxserialization-json
module, which adds dedicated helpers for JSON and the necessary transitive dependency on the JSON format (see dependencies section below).
This module brings the withJsonConversions helper to convert your StompSession
into a StompSessionWithKxSerialization
:
val session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withJsonConversions()\n
This new session type has the additional convertAndSend
method and subscribe
overloads that use Kotlinx Serialization's serializers to convert your payloads using the format of your choice (in this case, JSON):
@Serializable\ndata class Person(val name: String, val age: Int)\n@Serializable\ndata class MyMessage(val timestamp: Long, val author: String, val content: String)\n\njsonStompSession.use { s ->\n s.convertAndSend(\"/some/destination\", Person(\"Bob\", 42), Person.serializer()) \n\n // overloads without explicit serializers exist, but should be avoided if you also target JavaScript\n val messages: Flow<MyMessage> = s.subscribe(\"/some/topic/destination\", MyMessage.serializer())\n\n messages.collect { msg ->\n println(\"Received message from ${msg.author}: ${msg.content}\")\n }\n}\n
"},{"location":"stomp/conversions/kx-serialization/#custom-json-instance","title":"Custom Json
instance","text":"The withJsonConversions()
method takes an optional Json
parameter, so you can configure it as you please:
// custom Json configuration\nval json = Json {\n encodeDefaults = true\n ignoreUnknownKeys = true\n}\n\nval session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withJsonConversions(json)\n
"},{"location":"stomp/conversions/kx-serialization/#usage-with-other-formats","title":"Usage with other formats","text":"For other formats than JSON, use the more general krossbow-stomp-kxserialization
module, and add a dependency on the Kotlinx Serialization format of your choice (see dependencies section below).
This module brings the following extension functions on StompSession
:
withBinaryConversions(format: BinaryFormat, mediaType: String)
withTextConversions(format: StringFormat, mediaType: String)
These helpers are equivalent to withJsonConversions
, but more general, and also turn your StompSession
into a StompSessionWithKxSerialization
. You should provide the media type that you want to set as content-type
header in the messages you send:
val session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withBinaryConversions(Protobuf.Default, \"application/x-protobuf\")\n
You can then use convertAndSend
and subscribe
the same way as in the JSON section above.
Krossbow's base Kotlinx Serialization module is format-agnostic, so you need to add both the krossbow-stomp-kxserialization
dependency and the Kotlinx Serialization dependency for the format you want to use. For instance in the case of protobuf, that would be kotlinx-serialization-protobuf
:
implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization:7.0.0\")\nimplementation(\"org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.6.3\")\n
"},{"location":"stomp/conversions/kx-serialization/#json-format","title":"JSON format","text":"Since JSON is so common, Krossbow provides an all-in-one module with additional helpers for JSON:
implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization-json:7.0.0\")\n
This module brings kotlinx-serialization-json
transitively, so you don't have to add it yourself.
With this setup, krossbow-stomp-core
is unnecessary because it's transitively brought by the krossbow-stomp-kxserialization
modules.
Note that Kotlinx Serialization also requires a compiler plugin to generate serializers for your @Serializable
classes. See the Kotlinx Serialization doc for more information about this.
The krossbow-stomp-moshi
module is a JVM-only extension of krossbow-stomp-core
that provides new APIs to send and receive properly typed classes, and automatically convert them to/from the JSON bodies of STOMP frames by leveraging Moshi.
The main addition is the extension function StompSession.withMoshi()
, which turns your StompSession
into a TypedStompSession
. This new session type has additional methods that use Moshi to convert your objects into JSON and back:
// example Moshi instance that converts Kotlin types using reflection\nval moshi = Moshi.Builder()\n .addLast(KotlinJsonAdapterFactory())\n .build()\nStompClient(WebSocketClient.builtIn()).connect(url).withMoshi(moshi).use { session ->\n session.convertAndSend(\"/some/destination\", Person(\"Bob\", 42)) \n\n val messages: Flow<MyMessage> = session.subscribe<MyMessage>(\"/some/topic/destination\")\n val firstMessage: MyMessage = messages.first()\n\n println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/moshi/#dependency","title":"Dependency","text":"To use Moshi conversions, add krossbow-stomp-moshi
to your Gradle dependencies (krossbow-stomp-core
is unnecessary because it's transitively brought by this one):
implementation(\"org.hildan.krossbow:krossbow-stomp-moshi:7.0.0\")\n
This dependency transitively brings Moshi 1.15.1.
"},{"location":"websocket/builtin/","title":"Krossbow Web Socket Built-in","text":"The krossbow-websocket-builtin
module defines implementations of the WebSocketClient
interface by adapting clients that already exist in each platform (without 3rd-party dependencies).
For convenience in common
source sets, this module also provides the builtIn() factory method, which returns the built-in implementation of the current platform.
On the JVM target, the Jdk11WebSocketClient adapts the built-in HttpClient provided in the JRE since Java 11, and its WebSocket interface.
Android not supported
This adapter requires Java 11's HttpClient
which is not available on Android. If you need to support Android, please use the OkHttp or Ktor adapter instead.
On the JS target, the BrowserWebSocketClient adapts the browser's built-in WebSocket directly.
Also, the JsWebSocketClientAdapter
allows to adapt anything that looks like the browser's WebSocket
.
On all Darwin targets, the DarwinWebSocketClient adapts the Foundation framework's NSURLSessionWebSocketTask.
"},{"location":"websocket/builtin/#dependency-information","title":"Dependency information","text":"To use the built-in web socket clients, add the following to your build.gradle
:
implementation(\"org.hildan.krossbow:krossbow-websocket-builtin:7.0.0\")\n
"},{"location":"websocket/custom/","title":"Implement Krossbow's WebSocketClient yourself","text":"The krossbow-websocket-core
module defines a standard web socket API abstraction that is used by the STOMP artifacts and that you can also use directly if you're only interested in the web socket protocol without STOMP.
Krossbow provides built-in implementations of the web socket interfaces in the krossbow-websocket-builtin
module, but you can of course implement your own.
You can create your own implementation of Krossbow's web socket client by implementing the WebSocketClient interface.
This interface simply has a connect() method returning an instance of WebSocketConnection. The WebSocketConnection
actually contains the bulk of the web socket interactions implementation.
Please follow the KDoc of these interfaces to learn more about the contract that needs to be satisfied for each method.
"},{"location":"websocket/custom/#helpers","title":"Helpers","text":"The krossbow-websocket-core
module doesn't only provide interfaces to implement. It also provides some helper classes that help with most implementations of those interfaces.
The WebSocketListenerFlowAdapter allows to adapt listener-based web socket APIs to Krossbow's Flow
API easily. It takes care of partial message handling automatically, and can provide backpressure on the callback caller thanks to its suspend
callbacks.
The UnboundedWsListenerFlowAdapter also adapts listener-based APIs to Krossbow's flow, but without any backpressure support (functions are not suspend
and return immediately). It adds new messages to an unbounded queue. This is necessary with some APIs like JS browsers WebSocket
API, which cannot apply backpressure in any way on their web socket traffic.
Add the following to your build.gradle(.kts)
in order to get the Krossbow's interfaces and helpers:
implementation(\"org.hildan.krossbow:krossbow-websocket-core:7.0.0\")\n
"},{"location":"websocket/ktor/","title":"Krossbow with Ktor","text":"Krossbow allows you to use Ktor's web socket as transport for STOMP.
Ktor's implementation supports a variety of platforms and is very popular in the Kotlin world, especially in Kotlin multiplatform.
The krossbow-websocket-ktor
module provides the KtorWebSocketClient
, which adapts Ktor 2.3.9's HttpClient
to Krossbow's web socket interface.
To use the KtorWebSocketClient
pass an instance of it when creating your StompClient
:
val client = StompClient(KtorWebSocketClient())\n
You can customize the actual Ktor HTTP client used behind the scenes by passing it to KtorWebSocketClient
:
// You may configure Ktor HTTP client as you please,\n// but make sure at least the websocket feature is installed\nval httpClient = HttpClient {\n install(WebSockets)\n}\nval wsClient = KtorWebSocketClient(httpClient)\nval stompClient = StompClient(wsClient)\n
"},{"location":"websocket/ktor/#dependency-information","title":"Dependency information","text":"You will need to declare the following Gradle dependency to use the KtorWebSocketClient
:
implementation(\"org.hildan.krossbow:krossbow-websocket-ktor:7.0.0\")\n
Ktor uses pluggable engines to perform the platform-specific network operations (just like Krossbow uses different web socket implementations). You need to pick an engine that supports web sockets in order to use Ktor's HttpClient
with web sockets. Follow Ktor's documentation to find out more about how to use engines.
For instance, if you want to use Ktor's CIO engine with Krossbow, you need to declare the following:
implementation(\"org.hildan.krossbow:krossbow-websocket-ktor:7.0.0\")\nimplementation(\"io.ktor:ktor-client-cio:2.3.9\")\n
"},{"location":"websocket/okhttp/","title":"Krossbow with OkHttp","text":"Krossbow allows you to use OkHttp's WebSocket
as transport for STOMP.
OkHttp is very popular on Android, and is already part of many projects as HTTP client of choice.
The krossbow-websocket-okhttp
module provides the OkHttpWebSocketClient
, which adapts OkHttp's WebSocket
to Krossbow's web socket interface.
To use the OkHttpWebSocketClient
pass an instance of it when creating your StompClient
:
val client = StompClient(OkHttpWebSocketClient())\n
You can customize the actual OkHttpClient
used behind the scenes by passing it to OkHttpWebSocketClient()
:
// This allows to configure the underlying OkHttpClient as you please\n// (or use an existing one from your project)\nval okHttpClient = OkHttpClient.Builder()\n .callTimeout(Duration.ofMinutes(1))\n .pingInterval(Duration.ofSeconds(10))\n .build()\nval wsClient = OkHttpWebSocketClient(okHttpClient)\nval stompClient = StompClient(wsClient)\n
"},{"location":"websocket/okhttp/#dependency-information","title":"Dependency information","text":"You will need to declare the following Gradle dependency to use the OkHttpWebSocketClient
:
implementation(\"org.hildan.krossbow:krossbow-websocket-okhttp:7.0.0\")\n
"},{"location":"websocket/sockjs/","title":"Krossbow with SockJS","text":"Krossbow allows you to use SockJS-compatible clients as transport for STOMP.
The krossbow-websocket-sockjs
is a multiplatform facade implementing Krossbow's web socket interface by relying on different SockJS implementations. Here are the backing implementations for the different platforms:
sockjs-client
library (isomorphic)WebSocketClient
(with SockJS enabled), through krossbow-websocket-spring
Using a SockJS client requires a SockJS-enabled server.
"},{"location":"websocket/sockjs/#usage-with-stompclient","title":"Usage with StompClient","text":"To use this client, just call SockJSClient()
and the relevant platform-specific client will be instantiated for you:
val client = StompClient(SockJSClient())\n
"},{"location":"websocket/sockjs/#dependency-information","title":"Dependency information","text":"You will need to declare the following Gradle dependency to use the SockJSClient
:
implementation(\"org.hildan.krossbow:krossbow-websocket-sockjs:7.0.0\")\n
"},{"location":"websocket/spring/","title":"Krossbow with Spring","text":"Krossbow allows you to use Spring's WebSocketClient
as transport for STOMP.
The krossbow-websocket-spring
module provides the .asKrossbowWebSocketClient()
extension, which adapts any of Spring's WebSocketClient
to Krossbow's web socket client interface.
For example, use StandardWebSocketClient().asKrossbowWebSocketClient()
to wrap Spring's standard JSR-356 client.
To use a Spring web socket client with Krossbow's StompClient
, adapt it to a Krossbow WebSocketClient
using .asKrossbowWebSocketClient()
and pass it to the StompClient
constructor:
val stompClient = StompClient(StandardWebSocketClient().asKrossbowWebSocketClient())\n
You can of course further customize your Spring client before adapting it to Krossbow:
// Pure Spring configuration\nval springWsClient = StandardWebSocketClient().apply {\n taskExecutor = SimpleAsyncTaskExecutor(\"my-websocket-threads\")\n userProperties = mapOf(\"my-prop\" to \"someValue\")\n}\n\n// Krossbow adapter\nval stompClient = StompClient(springWsClient.asKrossbowWebSocketClient())\n
Another example of custom client, using Spring's SockJS client:
// Pure Spring configuration\nval transports = listOf(\n WebSocketTransport(StandardWebSocketClient()),\n RestTemplateXhrTransport(myCustomRestTemplate),\n)\nval springSockJsWsClient = SockJsClient(transports)\n\n// Krossbow adapter\nval stompClient = StompClient(springSockJsWsClient.asKrossbowWebSocketClient())\n
"},{"location":"websocket/spring/#dependency-information","title":"Dependency information","text":"You will need to declare the following Gradle dependency to use the Spring adapters:
implementation(\"org.hildan.krossbow:krossbow-websocket-spring:7.0.0\")\n
It transitively depends on spring-websocket
, so you don't need to add it yourself.
Important: if you're using Spring's StandardWebSocketClient
, you'll also need to add a dependency on a JSR-356 implementation, such as the Tyrus reference implementation:
implementation(\"org.glassfish.tyrus.bundles:tyrus-standalone-client-jdk:2.1.5\")\n
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Overview","text":"Krossbow is a Kotlin multiplatform STOMP 1.2 client with a coroutine-based API.
It is built on a web socket client abstraction, and provides a bunch of adapters for popular web socket clients (OkHttp, Ktor, Spring, SockJS...). It also provides out-of-the-box a built-in web socket implementation (without third-party dependencies) for most platforms (see below).
Krossbow can also be used as a multiplatform web socket client without STOMP protocol.
"},{"location":"#features","title":"Features","text":"All the STOMP 1.2 specification is implemented:
ACK
/NACK
and transactionsRECEIPT
frame based on receipt header)Additional features:
receipt
headers to ensure no frame is lost)If you find a bug or a feature that's missing compared to the specification, please open an issue.
"},{"location":"#supported-targets","title":"Supported targets","text":"Krossbow supports most Kotlin targets in its STOMP and web socket API modules: JVM, JS (browser and nodeJS), iOS, watchOS, tvOS, macOSX64, linuxX64, mingwX64.
However, each web socket client implementation has its own subset of supported targets (see below).
Android not tested on CI
Android 5.0+ (API level 21+) is supported by using JVM artifacts (e.g. OkHttp). However, the Android tooling's desugaring is currently not tested as part of the build, so any feedback on this use case is more than welcome. Please upvote the corresponding issue if you'd like to see proper CI or special packaging for the Android target.
"},{"location":"#web-socket-clients-target-support","title":"Web socket clients target support","text":"Krossbow can use built-in web socket implementations without third-party dependencies on some platforms. It also provides adapters for third-party implementations which have different platform support. Here is a summary of the supported platforms by module:
Module Browser NodeJS JVM iOS / tvOS / watchOS macOS / Linux / Windows Transitive dependencies Built-in (JDK\u00a011+) None Ktor Ktor, and the relevant Ktor engine(s) OkHttp OkHttp SockJS sockjs-client (on JS), Spring websocket (on JVM) Spring Spring websocketsupported with actual web socket transport (RFC6455)
supported using SockJS protocol (requires a SockJS server)
"},{"location":"#contribute","title":"Contribute","text":"Don't hesitate to open GitHub issues, even to ask questions or discuss a new feature. Pull-requests are welcome, but please open an issue first so that we can discuss the initial design or fix, which may avoid unnecessary work.
"},{"location":"artifacts/","title":"Artifacts summary","text":"Krossbow offers a lot of possibilities so here is a summary of all available artifacts.
All these artifacts are published to Maven Central under the group ID org.hildan.krossbow
and a common version.
You should pick only one of the krossbow-stomp-*
artifacts, depending on whether you need automatic serialization of frame bodies:
krossbow-stomp-coreThe basic multiplatform STOMP client. It implements the STOMP 1.2 protocol on top of the web socket abstraction defined by the
krossbow-websocket-core
module. krossbow-stomp-jacksonA superset of
krossbow-stomp-core
adding JSON conversion features using Jackson (JVM only) krossbow-stomp-moshiA superset of
krossbow-stomp-core
adding JSON conversion features using Moshi (JVM only) krossbow-stomp-kxserializationA superset of
krossbow-stomp-core
adding conversion features using Kotlinx Serialization library (multiplatform). You can leverage the multi-format capabilities of Kotlinx Serialization (JSON, protobuf, CBOR, ...). krossbow-stomp-kxserialization-jsonA superset of
krossbow-stomp-kxserialization
adding JSON helpers and the JSON format dependency. Then add the dependency of your choice to your Gradle build. For instance, if you intend to use Krossbow with Kotlinx Serialization:
implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization:7.0.0\")\n
Don't need STOMP?
If you're just interested in the web socket client without STOMP protocol, don't declare a STOMP artifact, but instead choose one of the web socket artifacts below.
"},{"location":"artifacts/#web-socket-artifacts","title":"Web Socket artifacts","text":"The STOMP artifacts depend on a web socket API that needs an implementation. Krossbow provides implementations for the built-in web socket API of most platforms, and also adapters for 3rd-party web socket implementations:
Artifact Descriptionkrossbow-websocket-builtinA multiplatform
WebSocketClient
implementation that adapts the built-in client for each supported platform without transitive dependency. krossbow-websocket-ktorA multiplatform
WebSocketClient
implementation based on Ktor 2.3.10's HttpClient
. krossbow-websocket-okhttpA JVM implementation of the web socket API using OkHttp's client.
krossbow-websocket-sockjsA multiplatform
WebSocketClient
implementation for use with SockJS servers. It uses Spring's SockJSClient on JVM, and npm sockjs-client
for JavaScript (NodeJS and browser). krossbow-websocket-springA JVM 8+ implementation of the web socket API using Spring's WebSocketClient. Provides both a normal WebSocket client and a SockJS one.
Peer dependencies
Some Krossbow modules are not opinionated and require some extra third-party peer dependencies. Make sure to read the usage section corresponding to the module of your choice for more details.
"},{"location":"license/","title":"MIT License","text":"Copyright (c) 2019-2023 Joffrey Bion
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"},{"location":"migration-guides/","title":"Migration guides","text":"Here are some details about how to migrate from one major version to another.
"},{"location":"migration-guides/#from-6x-to-7x","title":"From 6.x to 7.x","text":""},{"location":"migration-guides/#web-socket-subprotocol-negotiation","title":"Web socket subprotocol negotiation","text":"As part of issue #493, a separate protocols
parameter was added to WebSocketClient.connect
to enable web socket subprotocol negotiation.
Binary compatibility is preserved through some hidden synthetic functions. However, source compatibility isn't: usages of the connect()
method that passed custom headers without a named headers
parameter will no longer compile. Adding the headers =
parameter name will solve the issue.
For full negotiation support, clients need to be aware of which subprotocol the server chose to speak. This is why the protocol
property was added to WebSocketConnection
(#498). Implementers of this interface must implement this property. There shouldn't be many 3rd party implementations of the connection interface, so binary compatibility should not be a real issue here.
Some servers like ActiveMQ require negotiating the STOMP protocol as a web socket subprotocol during the web socket handshake (see issue #492), and cannot work otherwise.
Breaking change: To make the experience smoother, Krossbow v7.0.0 now automatically sends STOMP subprotocols (in all supported versions) during the web socket handshake via the Sec-WebSocket-Protocol
header.
If your server doesn't support it, you can customize the web socket handshake by manually connecting using WebSocketClient.connect()
with the parameters that suit you best, and then connect at STOMP level using the WebSocketConnection.stomp()
extension (without the need for a StompClient
at all):
val client: WebSocketClient = TODO(\"get some web socket client implementation\")\nval wsConnection = client.connect(url) // without any subprotocols\nval stompConfig = StompConfig().apply { \n // set your config here if needed\n}\nval stompSession = wsConnection.stomp(config)\n
"},{"location":"migration-guides/#no-host-header-sent-for-stomp-10","title":"No host
header sent for STOMP 1.0","text":"Thanks to the aforementioned changes, we can now detect the STOMP protocol version used by the server before sending the first STOMP frame (CONNECT
or STOMP
). If STOMP 1.0 is detected as web socket subprotocol during the web socket handshake, we no longer send by default the host
header which was introduced in 1.1 (and effectively breaks some old servers, see #122). It can still be sent by manually specifying it of course.
The STOMP protocol itself supports negotiation of the version via headers in the CONNECT
(or STOMP
) frame. So far, Krossbow only specified 1.2
as supported version. From now on, all 3 versions 1.0
, 1.1
, and 1.2
are advertised as supported by the client.
If necessary, this behavior can be overridden by sending the accept-version
header manually in customStompConnectHeaders
.
Because the protocol version can be negotiated both via web socket subprotocol and at STOMP level, there could potentially be a mismatch. If this happens, the connect()
call throws an exception. This can be disabled with StompConfig.failOnStompVersionMismatch
.
StompClient.connect()
now throws a different WebSocketConnectionException
","text":"The org.hildan.krossbow.stomp.WebSocketConnectionException
is deprecated in favor of org.hildan.krossbow.websocket.WebSocketConnectionException
. That exception has been around for a few years now and encapsulates all connection failures from different client implementations already, so there is no need for a similar exception at the StompClient
level.
If you used to catch this exception, make sure to update your import (the error-level deprecation should mitigate any risk of missing the new exception).
"},{"location":"migration-guides/#from-5x-to-6x","title":"From 5.x to 6.x","text":""},{"location":"migration-guides/#switch-to-kotlinx-io-and-the-bytestring-type","title":"Switch tokotlinx-io
and the ByteString
type","text":"The kotlinx-io
library has been revamped, with an implementation that closely matches Okio now. Krossbow has now internally switched from Okio to kotlinx-io
as a result, but this part should have no visible effect for the consumers of the library.
However, since kotlinx-io
is a somewhat \"standard\" extension library for Kotlin, Krossbow can now more legitimately use its ByteString
type in public APIs that handled binary data (for both web socket and STOMP sessions). This type represents immutable sequences of bytes, which is more convenient API-wise than byte arrays.
You will have to switch to these types if you used binary-based APIs.
"},{"location":"migration-guides/#deprecations-cleanup","title":"Deprecations cleanup","text":"This is a major version, and therefore we allowed ourselves some cleanup by removing a bunch of deprecated APIs. If you see unresolved references and are not sure how to fix them, please switch back to the Krossbow version 5 and fix deprecation warnings by following the corresponding instructions.
Please check the changelog for the list of removals.
"},{"location":"migration-guides/#from-4x-to-5x","title":"From 4.x to 5.x","text":""},{"location":"migration-guides/#end-of-ktor-1x-support","title":"End of Ktor 1.x support","text":"krossbow-websocket-ktor-legacy
artifact was removed. This means Krossbow no longer works with Ktor 1.x. If you were using this module, please migrate to Ktor 2, and use the non-legacy krossbow-websocket-ktor module.
This update of Krossbow brings Kotlin 1.8, which might bring some incompatible changes to the Kotlin stdlib. Please check the compatibility guide if you were using an older version of Kotlin.
"},{"location":"migration-guides/#from-3x-to-4x","title":"From 3.x to 4.x","text":""},{"location":"migration-guides/#withjsonconversions-moved-to-its-own-module","title":"withJsonConversions moved to its own module","text":"If you were using Krossbow with krossbow-stomp-kxserialization
, the withJsonConversions
helper has moved to a new module called krossbow-stomp-kxserialization-json
. This new module now transitively brings kotlinx-serialization-json
so you don't need to depend on that one explicitly.
StompClient
constructor removed","text":"Up to (and including) version 3.x of Krossbow, the built-in web socket clients for the supported platforms were part of the krossbow-websocket-core
module. This module provided a WebSocketClient.Companion.default()
factory function to provide the built-in web socket implementation of the current platform. Likewise, the krossbow-stomp-core
module provided a StompClient
constructor that used the \"default\" built-in web socket implementation for the current platform.
This approach limited the targets supported by those 2 core modules, even though all of their functionality was target-agnostic. In order to support all Kotlin platforms in pure Kotlin modules, the built-in websocket implementations had to be moved to a separate module, and the constructor without web socket client was moved to a separate module (and later removed completely for simplicity).
Breaking dependency changes, in short:
WebSocketClient.default()
from krossbow-websocket-core
, or any of the built-in clients directly, simply change your dependency to krossbow-websocket-builtin
instead.StompClient()
constructor without WS client argument (using the default value), add an explicit dependency on krossbow-websocket-builtin
and pass the built-in client explicitly to the constructor: StompClient(WebSocketClient.default())
.Note: the WebSocketClient.default()
function was since renamed WebSocketClient.builtIn()
in newer versions.
If you used other web socket implementations than the built-in ones, you don't have to change anything to your dependencies.
"},{"location":"migration-guides/#from-2x-to-3x","title":"From 2.x to 3.x","text":""},{"location":"migration-guides/#use-durations-instead-of-millis","title":"Use Durations instead of millis","text":"StompConfiguration
no longer uses amounts of milliseconds, but uses the kotlin.time.Duration
API. The -Millis
suffixes for the relevant properties were therefore dropped and the types changed.
Before:
val stomp = StompClient {\n connectionTimeoutMillis = 2000\n receiptTimeoutMillis = 5000\n disconnectTimeoutMillis = 300\n}\n
After:
import kotlin.time.Duration.Companion.milliseconds\nimport kotlin.time.Duration.Companion.seconds\n\nval stomp = StompClient {\n connectionTimeout = 2.seconds\n receiptTimeout = 5.seconds\n disconnectTimeout = 300.milliseconds\n}\n
"},{"location":"migration-guides/#flow-instead-of-channel-in-websocketconnection","title":"Flow instead of Channel in WebSocketConnection","text":"If you used the websocket API directly, the incomingFrames
channel is now a Flow
.
Before:
val conn = wsClient.connect(url)\nfor (frame in conn.incomingFrames) {\n // do stuff\n}\n
After:
val conn = wsClient.connect(url)\nconn.incomingFrames.collect {\n // do stuff\n}\n
"},{"location":"migration-guides/#tyrus-no-longer-embedded-in-krossbow-websocket-spring","title":"Tyrus no longer embedded in krossbow-websocket-spring
","text":"krossbow-websocket-spring
no longer transitively brings a dependency on Tyrus.
If you didn't add any JSR-356 implementation manually, you now have to explicitly depend on one. If you want the same behaviour as before, add the Tyrus dependency to your build.gradle.kts
as follows:
dependencies {\n implementation(\"org.glassfish.tyrus.bundles:tyrus-standalone-client-jdk:2.1.5\")\n}\n
"},{"location":"migration-guides/#from-1x-to-2x","title":"From 1.x to 2.x","text":""},{"location":"migration-guides/#stompsessionuse-now-passes-the-session-as-it-not-this","title":"StompSession.use
now passes the session as it
, not this
","text":"In order to align with Closeable.use, the lambda for StompSession.use
now receives the session as an argument (it
) and not receiver (this
).
Before:
StompClient().connect(url).use {\n sendText(\"/dest\", \"message\")\n}\n
After:
StompClient().connect(url).use {\n it.sendText(\"/dest\", \"message\")\n}\n// or\nStompClient().connect(url).use { session ->\n session.sendText(\"/dest\", \"message\")\n}\n
"},{"location":"stomp/advanced-features/","title":"Advanced features","text":""},{"location":"stomp/advanced-features/#receipts-suspension","title":"Receipts & Suspension","text":"The STOMP protocol supports RECEIPT frames, allowing the client to know when the server has received a frame. This only happens if a receipt header is set on the client frame.
If auto-receipt is enabled, a receipt
header is automatically generated and added to all client frames supporting the mechanism, and for which a receipt
header is not already present. If auto-receipt is not enabled, a receipt
header may still be provided manually in the parameters of some overloads.
When a receipt
header is present (automatically added or manually provided), the method that is used to send the frame suspends until the corresponding RECEIPT frame is received from the server. If no RECEIPT frame is received from the server in the configured time limit, a LostReceiptException
is thrown.
If no receipt is provided and auto-receipt is disabled, the method used to send the frame doesn't wait for a RECEIPT frame and never throws LostReceiptException
. Instead, it returns immediately after the underlying web socket implementation is done sending the frame.
When configured, heart beats can be used as a keep-alive to detect if the connection is lost. The heartBeat property should be used to configure heart beats in the StompClient
.
Note that the heart beats for the STOMP session are negotiated with the server. The actual heart beats are defined by the CONNECTED frame received from the server as a result of the negotiation, and may differ from the StompClient
configuration. The negotiation behaviour is defined by the specification.
Sending and checking heart beats is automatically handled by StompSession
implementations, depending on the result of the negotiation with the server. If expected heart beats are not received in time, a MissingHeartBeatException
is thrown and fails active subscriptions.
The graceful disconnect (or graceful shutdown) is a disconnection procedure defined by the STOMP specification to make sure the server gets all the frames before dropping the connection.
If enabled in the config, when disconnecting from the server, the client first sends a DISCONNECT frame with a receipt
header, and then waits for a RECEIPT frame before closing the connection.
If this graceful disconnect is disabled, then calling StompSession.disconnect()
immediately closes the web socket connection. In this case, there is no guarantee that the server received all previous messages.
Not supported in browsers
The browser's WebSocket
API does not support custom headers in the handshake (see this open issue in the web socket standard repo). Because of this, Krossbow cannot support this feature for the JS browser platform. However, the JS web socket client adapter is designed in a way that allows other implementations to support it, such as a Node.js implementation.
Some servers or connection flows may require extra HTTP headers in the web socket handshake. The StompClient.connect()
function doesn't support such headers out of the box, but this function is essentially just a shorthand for connecting at the web socket level, and then connecting at the STOMP level.
In fact, we technically don't need to create and use a StompClient
at all in order to use STOMP with Krossbow. Krossbow provides a WebSocketConnection.stomp() extension function that establishes a STOMP connection from an existing web socket connection.
We can leverage this to customize the web socket connection at will before connecting at STOMP level. For example:
val webSocketClient = WebSocketClient.builtIn() // or another web socket client\n\n// connect at web socket level with custom headers\nval wsSession = webSocketClient.connect(url, headers = mapOf(\"Custom-Header\" to \"custom-value\"))\n\nval config = StompConfig().apply {\n // here you can set up whatever config you would have done in the StompClient { ... } block\n}\n// connect at STOMP level on this open web socket, using the above config\nval stompSession = wsSession.stomp(config)\n
"},{"location":"stomp/config/","title":"Configuration","text":""},{"location":"stomp/config/#configuring-the-stompclient","title":"Configuring the StompClient","text":"The StompClient
can be configured at construction time using a convenient lambda block:
val stompClient = StompClient(WebSocketClient.builtIn()) {\n connectionTimeout = 3.seconds\n gracefulDisconnect = false\n}\n
You can also create the configuration separately and then pass it when constructing the client:
val stompConfig = StompConfig().apply {\n connectionTimeout = 3.seconds\n gracefulDisconnect = false\n}\n\nval stompClient = StompClient(WebSocketClient.builtIn(), stompConfig)\n
"},{"location":"stomp/config/#configuration-options","title":"Configuration options","text":"You can find out about all configuration properties in the StompConfig KDoc.
"},{"location":"stomp/getting-started/","title":"Getting started","text":""},{"location":"stomp/getting-started/#choosing-a-web-socket-implementation","title":"Choosing a web socket implementation","text":"Krossbow uses web sockets as transport for the STOMP communication. Multiple web socket client implementations are supported.
Check out the web socket client table to help you choose a web socket implementation based on the platforms you need to support. You can find more information about each client in their respective section of this doc.
We recommend the built-in client adapters if they cover the Kotlin targets you need to support, in order to limit 3rd party dependencies. Otherwise, Ktor is a good choice if you don't have special needs like SockJS.
"},{"location":"stomp/getting-started/#dependencies-setup","title":"Dependencies setup","text":"For the basic usage of STOMP without serialization, add the krossbow-stomp-core
dependency as well as the web socket module of your choice.
For example to use STOMP with the built-in web socket client:
implementation(\"org.hildan.krossbow:krossbow-stomp-core:7.0.0\")\nimplementation(\"org.hildan.krossbow:krossbow-websocket-builtin:7.0.0\")\n
For other web socket clients, check out their dedicated documentation page to find out which Krossbow dependencies are needed.
The rest of this guide uses the built-in client.
"},{"location":"stomp/getting-started/#basic-usage-without-body-conversions","title":"Basic usage (without body conversions)","text":"This is how to create a STOMP client and interact with it:
val client = StompClient(WebSocketClient.builtIn()) // other config can be passed in here\nval session: StompSession = client.connect(url) // optional login/passcode can be provided here\n\n// Send text messages using this convenience function\nsession.sendText(destination = \"/some/destination\", body = \"Basic text message\")\n\n// Sometimes no message body is necessary\nsession.sendEmptyMsg(destination = \"/some/destination\") \n\n// This subscribe() call triggers a SUBSCRIBE frame\n// and returns the flow of messages for the subscription\nval subscription: Flow<String> = session.subscribeText(\"/some/topic/destination\")\n\n// Use an appropriate coroutine 'scope' to collect the received frames\nval collectorJob = scope.launch {\n subscription.collect { msg ->\n println(\"Received: $msg\")\n }\n}\ndelay(3000)\n// cancelling the flow collector triggers an UNSUBSCRIBE frame\ncollectorJob.cancel()\n\nsession.disconnect()\n
If you want to disconnect automatically in case of exception or normal termination, you can use a try
/finally
block, or use StompSession.use()
, which is similar to Closeable.use()
:
import kotlinx.coroutines.flow.*\nimport org.hildan.krossbow.stomp.*\nimport org.hildan.krossbow.websocket.*\nimport org.hildan.krossbow.websocket.builtin.*\n\nval client = StompClient(WebSocketClient.builtIn()) // other config can be passed in here\nval session: StompSession = client.connect(url) // optional login/passcode can be provided here\n\nsession.use { s ->\n s.sendText(\"/some/destination\", \"Basic text message\") \n\n val subscription: Flow<String> = s.subscribeText(\"/some/topic/destination\")\n\n // terminal operators that finish early (like first) also trigger UNSUBSCRIBE automatically\n val firstMessage: String = subscription.first()\n println(\"Received: $firstMessage\")\n}\n// DISCONNECT frame was automatically sent at the end of the use{...} block\n
"},{"location":"stomp/getting-started/#using-body-conversions","title":"Using body conversions","text":"You can use STOMP with basic text as frame bodies, but it really becomes interesting when you can convert the frame bodies back and forth into Kotlin objects.
Check out the following sections to see how to automatically convert your objects into STOMP frame bodies:
If you want to use your own text conversion, you can implement TextMessageConverter
without any additional module, and use withTextConversions
to wrap your StompSession
into a TypedStompSession
.
Limited JS support
Reflection-based conversions may behave poorly on the JS platform. It is usually safer to rely on Kotlinx Serialization for multiplatform conversions.
val myConverter = object : TextMessageConverter {\n override val mimeType: String = \"application/json;charset=utf-8\"\n\n override fun <T> convertToString(value: T, type: KTypeRef<T>): String {\n TODO(\"your own object -> text conversion\")\n }\n\n override fun <T> convertFromString(text: String, type: KTypeRef<T>): T {\n TODO(\"your own text -> object conversion\")\n }\n}\n\nStompClient(WebSocketClient.builtIn()).connect(url).withTextConversions(myConverter).use { session ->\n session.convertAndSend(\"/some/destination\", MyPojo(\"Custom\", 42)) \n\n val messages = session.subscribe<MyMessage>(\"/some/topic/destination\")\n val firstMessage: MyMessage = messages.first()\n\n println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/jackson/","title":"STOMP with Jackson","text":"The krossbow-stomp-jackson
module is a JVM-only extension of krossbow-stomp-core
that provides new APIs to send and receive properly typed classes, and automatically convert them to/from the JSON bodies of STOMP frames by leveraging Jackson and jackson-module-kotlin.
The main addition is the extension function StompSession.withJackson()
, which turns your StompSession
into a TypedStompSession
. This new session type has additional methods that use Jackson to convert your objects into JSON and back:
StompClient(WebSocketClient.builtIn()).connect(url).withJackson().use { session ->\n session.convertAndSend(\"/some/destination\", Person(\"Bob\", 42)) \n\n val messages: Flow<MyMessage> = session.subscribe<MyMessage>(\"/some/topic/destination\")\n val firstMessage: MyMessage = messages.first()\n\n println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/jackson/#using-a-custom-objectmapper","title":"Using a custom ObjectMapper
","text":"Jackson is highly configurable, and it's often useful to configure the ObjectMapper
manually.
The withJackson()
method takes an optional ObjectMapper
parameter, so you can configure it as you please:
val customObjectMapper: ObjectMapper = jacksonObjectMapper()\n .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)\n .disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)\n .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)\n\nval client = StompClient(WebSocketClient.builtIn()).connect(url)\nval session = client.withJackson(customObjectMapper)\n
"},{"location":"stomp/conversions/jackson/#dependency","title":"Dependency","text":"To use Jackson conversions, add krossbow-stomp-jackson
to your Gradle dependencies (krossbow-stomp-core
is unnecessary because it's transitively brought by this one):
implementation(\"org.hildan.krossbow:krossbow-stomp-jackson:7.0.0\")\n
This dependency transitively brings Jackson 2.17.0 with the Kotlin module.
"},{"location":"stomp/conversions/kx-serialization/","title":"STOMP with Kotlinx Serialization","text":"Kotlinx Serialization is a multiplatform and multi-format serialization library provided by the Kotlin team. It is a popular choice for Kotlin multiplatform libraries especially because of its extensive support of Kotlin features.
The krossbow-stomp-kxserialization
module is an extension of krossbow-stomp-core
that provides new APIs to send and receive properly typed classes, and automatically convert STOMP frame bodies by leveraging Kotlinx Serialization.
Since Kotlinx Serialization supports multiple formats with different dependencies, you normally have to add your own dependency to bring the format of your choice.
However, since JSON is so popular, Krossbow comes with the krossbow-stomp-kxserialization-json
module, which adds dedicated helpers for JSON and the necessary transitive dependency on the JSON format (see dependencies section below).
This module brings the withJsonConversions helper to convert your StompSession
into a StompSessionWithKxSerialization
:
val session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withJsonConversions()\n
This new session type has the additional convertAndSend
method and subscribe
overloads that use Kotlinx Serialization's serializers to convert your payloads using the format of your choice (in this case, JSON):
@Serializable\ndata class Person(val name: String, val age: Int)\n@Serializable\ndata class MyMessage(val timestamp: Long, val author: String, val content: String)\n\njsonStompSession.use { s ->\n s.convertAndSend(\"/some/destination\", Person(\"Bob\", 42), Person.serializer()) \n\n // overloads without explicit serializers exist, but should be avoided if you also target JavaScript\n val messages: Flow<MyMessage> = s.subscribe(\"/some/topic/destination\", MyMessage.serializer())\n\n messages.collect { msg ->\n println(\"Received message from ${msg.author}: ${msg.content}\")\n }\n}\n
"},{"location":"stomp/conversions/kx-serialization/#custom-json-instance","title":"Custom Json
instance","text":"The withJsonConversions()
method takes an optional Json
parameter, so you can configure it as you please:
// custom Json configuration\nval json = Json {\n encodeDefaults = true\n ignoreUnknownKeys = true\n}\n\nval session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withJsonConversions(json)\n
"},{"location":"stomp/conversions/kx-serialization/#usage-with-other-formats","title":"Usage with other formats","text":"For other formats than JSON, use the more general krossbow-stomp-kxserialization
module, and add a dependency on the Kotlinx Serialization format of your choice (see dependencies section below).
This module brings the following extension functions on StompSession
:
withBinaryConversions(format: BinaryFormat, mediaType: String)
withTextConversions(format: StringFormat, mediaType: String)
These helpers are equivalent to withJsonConversions
, but more general, and also turn your StompSession
into a StompSessionWithKxSerialization
. You should provide the media type that you want to set as content-type
header in the messages you send:
val session = StompClient(WebSocketClient.builtIn()).connect(url)\nval jsonStompSession = session.withBinaryConversions(Protobuf.Default, \"application/x-protobuf\")\n
You can then use convertAndSend
and subscribe
the same way as in the JSON section above.
Krossbow's base Kotlinx Serialization module is format-agnostic, so you need to add both the krossbow-stomp-kxserialization
dependency and the Kotlinx Serialization dependency for the format you want to use. For instance in the case of protobuf, that would be kotlinx-serialization-protobuf
:
implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization:7.0.0\")\nimplementation(\"org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.6.3\")\n
"},{"location":"stomp/conversions/kx-serialization/#json-format","title":"JSON format","text":"Since JSON is so common, Krossbow provides an all-in-one module with additional helpers for JSON:
implementation(\"org.hildan.krossbow:krossbow-stomp-kxserialization-json:7.0.0\")\n
This module brings kotlinx-serialization-json
transitively, so you don't have to add it yourself.
With this setup, krossbow-stomp-core
is unnecessary because it's transitively brought by the krossbow-stomp-kxserialization
modules.
Note that Kotlinx Serialization also requires a compiler plugin to generate serializers for your @Serializable
classes. See the Kotlinx Serialization doc for more information about this.
The krossbow-stomp-moshi
module is a JVM-only extension of krossbow-stomp-core
that provides new APIs to send and receive properly typed classes, and automatically convert them to/from the JSON bodies of STOMP frames by leveraging Moshi.
The main addition is the extension function StompSession.withMoshi()
, which turns your StompSession
into a TypedStompSession
. This new session type has additional methods that use Moshi to convert your objects into JSON and back:
// example Moshi instance that converts Kotlin types using reflection\nval moshi = Moshi.Builder()\n .addLast(KotlinJsonAdapterFactory())\n .build()\nStompClient(WebSocketClient.builtIn()).connect(url).withMoshi(moshi).use { session ->\n session.convertAndSend(\"/some/destination\", Person(\"Bob\", 42)) \n\n val messages: Flow<MyMessage> = session.subscribe<MyMessage>(\"/some/topic/destination\")\n val firstMessage: MyMessage = messages.first()\n\n println(\"Received: $firstMessage\")\n}\n
"},{"location":"stomp/conversions/moshi/#dependency","title":"Dependency","text":"To use Moshi conversions, add krossbow-stomp-moshi
to your Gradle dependencies (krossbow-stomp-core
is unnecessary because it's transitively brought by this one):
implementation(\"org.hildan.krossbow:krossbow-stomp-moshi:7.0.0\")\n
This dependency transitively brings Moshi 1.15.1.
"},{"location":"websocket/builtin/","title":"Krossbow Web Socket Built-in","text":"The krossbow-websocket-builtin
module defines implementations of the WebSocketClient
interface by adapting clients that already exist in each platform (without 3rd-party dependencies).
For convenience in common
source sets, this module also provides the builtIn() factory method, which returns the built-in implementation of the current platform.
On the JVM target, the Jdk11WebSocketClient adapts the built-in HttpClient provided in the JRE since Java 11, and its WebSocket interface.
Android not supported
This adapter requires Java 11's HttpClient
which is not available on Android. If you need to support Android, please use the OkHttp or Ktor adapter instead.
On the JS target, the BrowserWebSocketClient adapts the browser's built-in WebSocket directly.
Also, the JsWebSocketClientAdapter
allows to adapt anything that looks like the browser's WebSocket
.
On all Darwin targets, the DarwinWebSocketClient adapts the Foundation framework's NSURLSessionWebSocketTask.
"},{"location":"websocket/builtin/#dependency-information","title":"Dependency information","text":"To use the built-in web socket clients, add the following to your build.gradle
:
implementation(\"org.hildan.krossbow:krossbow-websocket-builtin:7.0.0\")\n
"},{"location":"websocket/custom/","title":"Implement Krossbow's WebSocketClient yourself","text":"The krossbow-websocket-core
module defines a standard web socket API abstraction that is used by the STOMP artifacts and that you can also use directly if you're only interested in the web socket protocol without STOMP.
Krossbow provides built-in implementations of the web socket interfaces in the krossbow-websocket-builtin
module, but you can of course implement your own.
You can create your own implementation of Krossbow's web socket client by implementing the WebSocketClient interface.
This interface simply has a connect() method returning an instance of WebSocketConnection. The WebSocketConnection
actually contains the bulk of the web socket interactions implementation.
Please follow the KDoc of these interfaces to learn more about the contract that needs to be satisfied for each method.
"},{"location":"websocket/custom/#helpers","title":"Helpers","text":"The krossbow-websocket-core
module doesn't only provide interfaces to implement. It also provides some helper classes that help with most implementations of those interfaces.
The WebSocketListenerFlowAdapter allows to adapt listener-based web socket APIs to Krossbow's Flow
API easily. It takes care of partial message handling automatically, and can provide backpressure on the callback caller thanks to its suspend
callbacks.
The UnboundedWsListenerFlowAdapter also adapts listener-based APIs to Krossbow's flow, but without any backpressure support (functions are not suspend
and return immediately). It adds new messages to an unbounded queue. This is necessary with some APIs like JS browsers WebSocket
API, which cannot apply backpressure in any way on their web socket traffic.
Add the following to your build.gradle(.kts)
in order to get the Krossbow's interfaces and helpers:
implementation(\"org.hildan.krossbow:krossbow-websocket-core:7.0.0\")\n
"},{"location":"websocket/ktor/","title":"Krossbow with Ktor","text":"Krossbow allows you to use Ktor's web socket as transport for STOMP.
Ktor's implementation supports a variety of platforms and is very popular in the Kotlin world, especially in Kotlin multiplatform.
The krossbow-websocket-ktor
module provides the KtorWebSocketClient
, which adapts Ktor 2.3.10's HttpClient
to Krossbow's web socket interface.
To use the KtorWebSocketClient
pass an instance of it when creating your StompClient
:
val client = StompClient(KtorWebSocketClient())\n
You can customize the actual Ktor HTTP client used behind the scenes by passing it to KtorWebSocketClient
:
// You may configure Ktor HTTP client as you please,\n// but make sure at least the websocket feature is installed\nval httpClient = HttpClient {\n install(WebSockets)\n}\nval wsClient = KtorWebSocketClient(httpClient)\nval stompClient = StompClient(wsClient)\n
"},{"location":"websocket/ktor/#dependency-information","title":"Dependency information","text":"You will need to declare the following Gradle dependency to use the KtorWebSocketClient
:
implementation(\"org.hildan.krossbow:krossbow-websocket-ktor:7.0.0\")\n
Ktor uses pluggable engines to perform the platform-specific network operations (just like Krossbow uses different web socket implementations). You need to pick an engine that supports web sockets in order to use Ktor's HttpClient
with web sockets. Follow Ktor's documentation to find out more about how to use engines.
For instance, if you want to use Ktor's CIO engine with Krossbow, you need to declare the following:
implementation(\"org.hildan.krossbow:krossbow-websocket-ktor:7.0.0\")\nimplementation(\"io.ktor:ktor-client-cio:2.3.10\")\n
"},{"location":"websocket/okhttp/","title":"Krossbow with OkHttp","text":"Krossbow allows you to use OkHttp's WebSocket
as transport for STOMP.
OkHttp is very popular on Android, and is already part of many projects as HTTP client of choice.
The krossbow-websocket-okhttp
module provides the OkHttpWebSocketClient
, which adapts OkHttp's WebSocket
to Krossbow's web socket interface.
To use the OkHttpWebSocketClient
pass an instance of it when creating your StompClient
:
val client = StompClient(OkHttpWebSocketClient())\n
You can customize the actual OkHttpClient
used behind the scenes by passing it to OkHttpWebSocketClient()
:
// This allows to configure the underlying OkHttpClient as you please\n// (or use an existing one from your project)\nval okHttpClient = OkHttpClient.Builder()\n .callTimeout(Duration.ofMinutes(1))\n .pingInterval(Duration.ofSeconds(10))\n .build()\nval wsClient = OkHttpWebSocketClient(okHttpClient)\nval stompClient = StompClient(wsClient)\n
"},{"location":"websocket/okhttp/#dependency-information","title":"Dependency information","text":"You will need to declare the following Gradle dependency to use the OkHttpWebSocketClient
:
implementation(\"org.hildan.krossbow:krossbow-websocket-okhttp:7.0.0\")\n
"},{"location":"websocket/sockjs/","title":"Krossbow with SockJS","text":"Krossbow allows you to use SockJS-compatible clients as transport for STOMP.
The krossbow-websocket-sockjs
is a multiplatform facade implementing Krossbow's web socket interface by relying on different SockJS implementations. Here are the backing implementations for the different platforms:
sockjs-client
library (isomorphic)WebSocketClient
(with SockJS enabled), through krossbow-websocket-spring
Using a SockJS client requires a SockJS-enabled server.
"},{"location":"websocket/sockjs/#usage-with-stompclient","title":"Usage with StompClient","text":"To use this client, just call SockJSClient()
and the relevant platform-specific client will be instantiated for you:
val client = StompClient(SockJSClient())\n
"},{"location":"websocket/sockjs/#dependency-information","title":"Dependency information","text":"You will need to declare the following Gradle dependency to use the SockJSClient
:
implementation(\"org.hildan.krossbow:krossbow-websocket-sockjs:7.0.0\")\n
"},{"location":"websocket/spring/","title":"Krossbow with Spring","text":"Krossbow allows you to use Spring's WebSocketClient
as transport for STOMP.
The krossbow-websocket-spring
module provides the .asKrossbowWebSocketClient()
extension, which adapts any of Spring's WebSocketClient
to Krossbow's web socket client interface.
For example, use StandardWebSocketClient().asKrossbowWebSocketClient()
to wrap Spring's standard JSR-356 client.
To use a Spring web socket client with Krossbow's StompClient
, adapt it to a Krossbow WebSocketClient
using .asKrossbowWebSocketClient()
and pass it to the StompClient
constructor:
val stompClient = StompClient(StandardWebSocketClient().asKrossbowWebSocketClient())\n
You can of course further customize your Spring client before adapting it to Krossbow:
// Pure Spring configuration\nval springWsClient = StandardWebSocketClient().apply {\n taskExecutor = SimpleAsyncTaskExecutor(\"my-websocket-threads\")\n userProperties = mapOf(\"my-prop\" to \"someValue\")\n}\n\n// Krossbow adapter\nval stompClient = StompClient(springWsClient.asKrossbowWebSocketClient())\n
Another example of custom client, using Spring's SockJS client:
// Pure Spring configuration\nval transports = listOf(\n WebSocketTransport(StandardWebSocketClient()),\n RestTemplateXhrTransport(myCustomRestTemplate),\n)\nval springSockJsWsClient = SockJsClient(transports)\n\n// Krossbow adapter\nval stompClient = StompClient(springSockJsWsClient.asKrossbowWebSocketClient())\n
"},{"location":"websocket/spring/#dependency-information","title":"Dependency information","text":"You will need to declare the following Gradle dependency to use the Spring adapters:
implementation(\"org.hildan.krossbow:krossbow-websocket-spring:7.0.0\")\n
It transitively depends on spring-websocket
, so you don't need to add it yourself.
Important: if you're using Spring's StandardWebSocketClient
, you'll also need to add a dependency on a JSR-356 implementation, such as the Tyrus reference implementation:
implementation(\"org.glassfish.tyrus.bundles:tyrus-standalone-client-jdk:2.1.5\")\n
"}]}
\ No newline at end of file
diff --git a/websocket/ktor/index.html b/websocket/ktor/index.html
index c7d970d84..206830cef 100644
--- a/websocket/ktor/index.html
+++ b/websocket/ktor/index.html
@@ -899,7 +899,7 @@
Krossbow allows you to use Ktor's web socket as transport for STOMP.
Ktor's implementation supports a variety of platforms and is very popular in the Kotlin world, especially in Kotlin multiplatform.
-The krossbow-websocket-ktor
module provides the KtorWebSocketClient
, which adapts Ktor 2.3.9's
+
The krossbow-websocket-ktor
module provides the KtorWebSocketClient
, which adapts Ktor 2.3.10's
HttpClient
to Krossbow's web socket interface.
To use the KtorWebSocketClient
pass an instance of it when creating your StompClient
:
For instance, if you want to use Ktor's CIO engine with Krossbow, you need to declare the following:
implementation("org.hildan.krossbow:krossbow-websocket-ktor:7.0.0")
-implementation("io.ktor:ktor-client-cio:2.3.9")
+implementation("io.ktor:ktor-client-cio:2.3.10")