123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- :spring_version: current
- :jackson: https://wiki.fasterxml.com/JacksonHome
- :AtMessageMapping: https://docs.spring.io/spring/docs/{spring_version}/javadoc-api/org/springframework/messaging/handler/annotation/MessageMapping.html
- :AtController: https://docs.spring.io/spring/docs/{spring_version}/javadoc-api/org/springframework/stereotype/Controller.html
- :AtEnableWebSocketMessageBroker: https://docs.spring.io/spring/docs/{spring_version}/javadoc-api/org/springframework/messaging/simp/config/EnableWebSocketMessageBroker.html
- :Stomp_JS: http://jmesnil.net/stomp-websocket/doc/
- :AtSendTo: https://docs.spring.io/spring/docs/{spring_version}/javadoc-api/org/springframework/messaging/handler/annotation/SendTo.html
- :toc:
- :icons: font
- :source-highlighter: prettify
- :project_id: gs-messaging-stomp-websocket
- 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
- http://en.wikipedia.org/wiki/Streaming_Text_Oriented_Messaging_Protocol[STOMP] messaging
- with Spring to create an interactive web application. STOMP is a subprotocol operating
- on top of the lower-level WebSocket.
- == What You Will build
- 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.
- == What You Need
- include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/prereq_editor_jdk_buildtools.adoc[]
- include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/how_to_complete_this_guide.adoc[]
- [[scratch]]
- == Starting with Spring Initializr
- You can use this https://start.spring.io/#!type=maven-project&groupId=com.example&artifactId=messaging-stomp-websocket&name=messaging-stomp-websocket&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.messaging-stomp-websocket&dependencies=websocket[pre-initialized project] and click Generate to download a ZIP file. This project is configured to fit the examples in this tutorial.
- To manually initialize the project:
- . Navigate to https://start.spring.io.
- This service pulls in all the dependencies you need for an application and does most of the setup for you.
- . Choose either Gradle or Maven and the language you want to use. This guide assumes that you chose Java.
- . Click *Dependencies* and select *Websocket*.
- . Click *Generate*.
- . Download the resulting ZIP file, which is an archive of a web application that is configured with your choices.
- NOTE: If your IDE has the Spring Initializr integration, you can complete this process from your IDE.
- NOTE: You can also fork the project from Github and open it in your IDE or other editor.
- ===
- [[initial]]
- == Create a Resource Representation Class
- 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:
- ====
- [source,json]
- ----
- {
- "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:
- ====
- [source,java,tabsize=2]
- ----
- include::complete/src/main/java/com/example/messagingstompwebsocket/HelloMessage.java[]
- ----
- ====
- 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:
- ====
- [source,json]
- ----
- {
- "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:
- ====
- [source,java,tabsize=2]
- ----
- include::complete/src/main/java/com/example/messagingstompwebsocket/Greeting.java[]
- ----
- ====
- Spring will use the {jackson}[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.
- == Create a Message-handling Controller
- In Spring's approach to working with STOMP messaging, STOMP messages can be routed to
- {AtController}[`@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:
- ====
- [source,java,tabsize=2]
- ----
- include::complete/src/main/java/com/example/messagingstompwebsocket/GreetingController.java[]
- ----
- ====
- This controller is concise and simple, but plenty is going on. We break it down step by
- step.
- The {AtMessageMapping}[`@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 {AtSendTo}[`@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.
- == Configure Spring for STOMP messaging
- 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`):
- ====
- [source,java,tabsize=2]
- ----
- include::complete/src/main/java/com/example/messagingstompwebsocket/WebSocketConfig.java[]
- ----
- ====
- `WebSocketConfig` is annotated with `@Configuration` to indicate that it is a Spring
- configuration class. It is also annotated with
- {AtEnableWebSocketMessageBroker}[`@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 for websocket connections.
- == Create a Browser Client
- 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`):
- ====
- [source,html]
- ----
- include::complete/src/main/resources/static/index.html[]
- ----
- ====
- This HTML file imports the https://stomp-js.github.io/[`StompJS`] javascript library 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:
- ====
- [source,javascript,tabsize=2]
- ----
- include::complete/src/main/resources/static/app.js[]
- ----
- ====
- The main pieces of this JavaScript file to understand are the `stompClient.onConnect` and `sendName`
- functions.
- `stompClient` is initialized with `brokerURL` referring to path `/gs-guide-websocket`,
- which is where our websockets 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).
- The `main.css` can be omitted if you like, or you can create an empty
- one, just so the `<link>` can be resolved.
- == Make the Application Executable
- 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:
- ====
- [source,java,tabsize=2]
- ----
- include::complete/src/main/java/com/example/messagingstompwebsocket/MessagingStompWebsocketApplication.java[]
- ----
- ====
- include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/spring-boot-application-new-path.adoc[]
- include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/build_an_executable_jar_subhead.adoc[]
- include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/build_an_executable_jar_with_both.adoc[]
- Logging output is displayed. The service should be up and running within a few seconds.
- == Test the service
- 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.
- == Summary
- Congratulations! You have just developed a STOMP-based messaging service with Spring.
- == See Also
- The following guides may also be helpful:
- * https://stomp-js.github.io/[StompJS client library docs]
- * https://spring.io/guides/gs/serving-web-content/[Serving Web Content with Spring MVC]
- * https://spring.io/guides/gs/spring-boot/[Building an Application with Spring Boot]
- include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/footer.adoc[]
|