A lightweight implementation of the STOMP (1.2↓) protocol for those using or transferring over to Spring WebFlux
With the addition of Spring's Reactive framework, dubbed WebFlux, many Spring MVC features have either not yet been implemented or do not plan to get implemented. This leaves a Flux<Void> for some who intend to upgrade their backend architecture without having to restructure their frontend.
This repository features a very lightweight implementation of a server-side STOMP WebSocket handler (designed around the spec outlined here) written using Spring WebFlux. This implementation checks all the required boxes within the spec, but does not fully support every optional feature (i.e. heartbeat-ing).
This STOMP implementation is achieved using five general classes:
StompConfig.java
- A basic Spring Configuration class used to configure multiple backend handlers, each with a different path.StompFrame.java
- A bare-bones immutable frame object used to organize the different components of standard STOMP frames and handle the conversion to and from Spring WebFlux'sWebSocketMessage
.StompHandler.java
- The backbone class that defines the core functionality, logic, and implementation of a server-side STOMP handler.StompServer.java
- A minimalistic interface that provides methods to define side effects upon receiving specific client frames. The methods allow users to have a final look at the responding frames and, while not suggested, alter them depending on applicable business logic.StompUtils.java
- A utility class intended to supply convenient functions to construct server-side STOMP frames.
Each class provided in this repository was designed with generality in mind. This is not to say that this
implementation is complete or flawless, however, changes are not intended to be made to these five files. Instead,
the StompServer
should be implemented as many times as applicable for the specific server in mind. The
CountingServer.java
and
HelloWorldServer.java
demonstrate the
implementations of potentially tailored business logic.
The StompUtils
provides multiple utility functions to formulate outbound STOMP frames (listed below),
which are recommended to be used instead of directly constructing a StompMessage
. Keep in mind that
ERROR
frames resulting from a non-compliance with the spec and RECEIPT
frames expected in compliance
with the spec are already generated when appropriate before methods in the StompServer
implementations are
invoked.
To create MESSAGE
frames:
StompUtils#makeMessage(String destination, String subscription, String body)
StompUtils#makeMessage(String destination, String subscription, MimeType contentType, byte[] body)
StompUtils#makeMessage(String destination, String subscription, Map<String, List<String>> userDefinedHeaders)
StompUtils#makeMessage(String destination, String subscription, MultiValueMap<String, String> userDefinedHeaders)
StompUtils#makeMessage(String destination, String subscription, Map<String, List<String>> userDefinedHeaders, String body)
StompUtils#makeMessage(String destination, String subscription, MultiValueMap<String, String> userDefinedHeaders, String body)
StompUtils#makeMessage(String destination, String subscription, Map<String, List<String>> userDefinedHeaders, MimeType contentType, byte[] body)
StompUtils#makeMessage(String destination, String subscription, MultiValueMap<String, String> userDefinedHeaders, MimeType contentType, byte[] body)
To create RECEIPT
frames:
StompUtils#makeReceipt(StompFrame inbound)
To create ERROR
frames:
StompUtils#makeError(StompFrame inbound, String errorHeader)
StompUtils#makeError(StompFrame inbound, String errorHeader, String body)
StompUtils#makeError(StompFrame inbound, String errorHeader, MimeType contentType, byte[] body)
StompUtils#makeError(StompFrame inbound, String errorHeader, Map<String, List<String>> userDefinedHeaders)
StompUtils#makeError(StompFrame inbound, String errorHeader, MultiValueMap<String, String> userDefinedHeaders)
StompUtils#makeError(StompFrame inbound, String errorHeader, Map<String, List<String>> userDefinedHeaders, String body)
StompUtils#makeError(StompFrame inbound, String errorHeader, MultiValueMap<String, String> userDefinedHeaders, String body)
StompUtils#makeError(StompFrame inbound, String errorHeader, Map<String, List<String>> userDefinedHeaders, MimeType contentType, byte[] body)
StompUtils#makeError(StompFrame inbound, String errorHeader, MultiValueMap<String, String> userDefinedHeaders, MimeType contentType, byte[] body)
Some of the methods defined in the StompServer
are guaranteed to be provided with a non-null outbound frame
(demonstrated by the default implementation using Mono#just(T data)
as opposed to Mono#justOrEmpty(T data)
).
However, all the methods (except
for StompServer#onError(WebSocketSession session, StompFrame inbound, StompFrame outbound, Map<String, ConcurrentLinkedQueue<String>> messagesQueueBySubscription, Map<String, StompFrame> messagesCache)
of course) are guaranteed to be provided with either a non-ERROR
or a null frame, thus eliminating the need to check for errors.
This project can be built by running the following from the repository root directory:
./gradlew clean build
This repository contains an example of an application based on the STOMP protocol.
To interact with this example, run the sample.feature
Cucumber
Scenario and open websocket_count.html
or
websocket_hello_world.html
.
This software is not complete and designed by a 3rd party source. While it is currently in use in a production environment, users should utilize at their own risk.
For those designing new architectures, the Spring Team recommends the use of the newly created
RSocket
seeing as this is
a 1st party feature of the Spring Framework.
- Implement heartbeat functionality
- Include unit tests