This guide walks you through the process of creating a “Hello, world” application that sends messages back and forth between a browser and a server. WebSocket is a thin, lightweight layer above TCP. This makes it suitable for using “subprotocols” to embed messages. In this guide, we use STOMP messaging with Spring to create an interactive web application.
You will build a server that accepts a message that carries a user’s name. In response, the server will push a greeting into a queue to which the client is subscribed.
For all Spring applications, you should start with the Spring Initializr. The Initializr offers a fast way to pull in all the dependencies you need for an application and does a lot of the set up for you. This example needs only the Websocket dependency. The following image shows the Initializr set up for this sample project:
Note
|
The preceding image shows the Initializr with Maven chosen as the build tool. You
can also use Gradle. It also shows values of com.example and messaging-stomp-websocket
as the Group and Artifact, respectively. You will use those values throughout the rest of
this sample.
|
The following listing shows the pom.xml
file that is created when you choose Maven:
link:initial/pom.xml[role=include]
The following listing shows the build.gradle
file that is created when you choose Gradle:
link:initial/build.gradle[role=include]
The Spring Initializr does not provide everything you need in this case. For Maven, you need to add the following dependencies:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.1-1</version>
</dependency>
The following listing shows the finished pom.xml
file:
link:initial/pom.xml[role=include]
If you use Gradle, you need to add the following dependencies:
implementation 'org.webjars:webjars-locator-core'
implementation 'org.webjars:sockjs-client:1.0.2'
implementation 'org.webjars:stomp-websocket:2.3.3'
implementation 'org.webjars:bootstrap:3.3.7'
implementation 'org.webjars:jquery:3.1.1-1'
The following listing shows the finished build.gradle
file:
link:initial/build.gradle[role=include]
Now that you have set up the project and build system, you can create your STOMP message service.
Begin the process by thinking about service interactions.
The service will accept messages that contain a name in a STOMP message whose body is a
JSON object. If the name is Fred
, the message might resemble the following:
{
"name": "Fred"
}
To model the message that carries the name, you can create a plain old Java object with a
name
property and a corresponding getName()
method, as the following listing (from
src/main/java/com/example/messagingstompwebsocket/HelloMessage.java
) shows:
link:complete/src/main/java/com/example/messagingstompwebsocket/HelloMessage.java[role=include]
Upon receiving the message and extracting the name, the service will process it by creating a greeting and publishing that greeting on a separate queue to which the client is subscribed. The greeting will also be a JSON object, which as the following listing shows:
{
"content": "Hello, Fred!"
}
To model the greeting representation, add another plain old Java object with a content
property and a corresponding getContent()
method, as the following listing (from
src/main/java/com/example/messagingstompwebsocket/Greeting.java
) shows:
link:complete/src/main/java/com/example/messagingstompwebsocket/Greeting.java[role=include]
Spring will use the Jackson JSON library to automatically marshal instances of
type Greeting
into JSON.
Next, you will create a controller to receive the hello message and send a greeting message.
In Spring’s approach to working with STOMP messaging, STOMP messages can be routed to
@Controller
classes. For example, the GreetingController
(from
src/main/java/com/example/messagingstompwebsocket/GreetingController.java
) is mapped to
handle messages to the /hello
destination, as the following listing shows:
link:complete/src/main/java/com/example/messagingstompwebsocket/GreetingController.java[role=include]
This controller is concise and simple, but plenty is going on. We break it down step by step.
The @MessageMapping
annotation ensures that, if a message is sent to
the /hello
destination, the greeting()
method is called.
The payload of the message is bound to a HelloMessage
object, which is passed into
greeting()
.
Internally, the implementation of the method simulates a processing delay by causing the thread to sleep for one second. This is to demonstrate that, after the client sends a message, the server can take as long as it needs to asynchronously process the message. The client can continue with whatever work it needs to do without waiting for the response.
After the one-second delay, the greeting()
method creates a Greeting
object and
returns it. The return value is broadcast to all subscribers of /topic/greetings
, as
specified in the @SendTo
annotation. Note that the name from the input
message is sanitized, since, in this case, it will be echoed back and re-rendered in the
browser DOM on the client side.
Now that the essential components of the service are created, you can configure Spring to enable WebSocket and STOMP messaging.
Create a Java class named WebSocketConfig
that resembles the following listing (from
src/main/java/com/example/messagingstompwebsocket/WebSocketConfig.java
):
link:complete/src/main/java/com/example/messagingstompwebsocket/WebSocketConfig.java[role=include]
WebSocketConfig
is annotated with @Configuration
to indicate that it is a Spring
configuration class. It is also annotated with
@EnableWebSocketMessageBroker
. As its name suggests,
@EnableWebSocketMessageBroker
enables WebSocket message handling, backed by a message
broker.
The configureMessageBroker()
method implements the default method in
WebSocketMessageBrokerConfigurer
to configure the message broker. It starts by calling
enableSimpleBroker()
to enable a simple memory-based message broker to carry the
greeting messages back to the client on destinations prefixed with /topic
. It also
designates the /app
prefix for messages that are bound for methods annotated with
@MessageMapping
. This prefix will be used to define all the message mappings. For
example, /app/hello
is the endpoint that the GreetingController.greeting()
method is
mapped to handle.
The registerStompEndpoints()
method registers the /gs-guide-websocket
endpoint,
enabling SockJS fallback options so that alternate transports can be used if WebSocket is
not available. The SockJS client will attempt to connect to /gs-guide-websocket
and use
the best available transport (websocket, xhr-streaming, xhr-polling, and so on).
With the server-side pieces in place, you can turn your attention to the JavaScript client that will send messages to and receive messages from the server side.
Create an index.html
file similar to the following listing (from
src/main/resources/static/index.html
):
link:complete/src/main/resources/static/index.html[role=include]
This HTML file imports the SockJS
and STOMP
javascript libraries that will be used to
communicate with our server through STOMP over websocket. We also import app.js
, which
contains the logic of our client application. The following listing (from
src/main/resources/static/app.js
) shows that file:
link:complete/src/main/resources/static/app.js[role=include]
The main pieces of this JavaScript file to understand are the connect()
and sendName()
functions.
The connect()
function uses SockJS and stomp.js
to open a connection to /gs-guide-websocket
, which is where our SockJS server waits for
connections. Upon a successful connection, the client subscribes to the /topic/greetings
destination, where the server will publish greeting messages. When a greeting is received
on that destination, it will append a paragraph element to the DOM to display the greeting
message.
The sendName()
function retrieves the name entered by the user and uses the STOMP client
to send it to the /app/hello
destination (where GreetingController.greeting()
will
receive it).
Spring Boot creates an application class for you. In this case, it needs no further
modification. You can use it to run this application. The following listing (from
src/main/java/com/example/messagingstompwebsocket/MessagingStompWebsocketApplication.java
)
shows the application class:
link:complete/src/main/java/com/example/messagingstompwebsocket/MessagingStompWebsocketApplication.java[role=include]
Logging output is displayed. The service should be up and running within a few seconds.
Now that the service is running, point your browser at http://localhost:8080 and click the Connect button.
Upon opening a connection, you are asked for your name. Enter your name and click Send. Your name is sent to the server as a JSON message over STOMP. After a one-second simulated delay, the server sends a message back with a “Hello” greeting that is displayed on the page. At this point, you can send another name or you can click the Disconnect button to close the connection.
The following guides may also be helpful: