README.adoc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. :spring_version: current
  2. :jackson: https://wiki.fasterxml.com/JacksonHome
  3. :AtMessageMapping: https://docs.spring.io/spring/docs/{spring_version}/javadoc-api/org/springframework/messaging/handler/annotation/MessageMapping.html
  4. :AtController: https://docs.spring.io/spring/docs/{spring_version}/javadoc-api/org/springframework/stereotype/Controller.html
  5. :AtEnableWebSocketMessageBroker: https://docs.spring.io/spring/docs/{spring_version}/javadoc-api/org/springframework/messaging/simp/config/EnableWebSocketMessageBroker.html
  6. :Stomp_JS: http://jmesnil.net/stomp-websocket/doc/
  7. :AtSendTo: https://docs.spring.io/spring/docs/{spring_version}/javadoc-api/org/springframework/messaging/handler/annotation/SendTo.html
  8. :toc:
  9. :icons: font
  10. :source-highlighter: prettify
  11. :project_id: gs-messaging-stomp-websocket
  12. This guide walks you through the process of creating a "`Hello, world`" application that
  13. sends messages back and forth between a browser and a server. WebSocket is a thin,
  14. lightweight layer above TCP. This makes it suitable for using "`subprotocols`" to embed
  15. messages. In this guide, we use
  16. http://en.wikipedia.org/wiki/Streaming_Text_Oriented_Messaging_Protocol[STOMP] messaging
  17. with Spring to create an interactive web application. STOMP is a subprotocol operating
  18. on top of the lower-level WebSocket.
  19. == What You Will build
  20. You will build a server that accepts a message that carries a user's name. In response,
  21. the server will push a greeting into a queue to which the client is subscribed.
  22. == What You Need
  23. :java_version: 1.8
  24. include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/prereq_editor_jdk_buildtools.adoc[]
  25. include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/how_to_complete_this_guide.adoc[]
  26. [[scratch]]
  27. == Starting with Spring Initializr
  28. If you use Maven, visit the https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.4.3.RELEASE&packaging=jar&jvmVersion=1.8&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[Spring Initializr] to generate a new project with the required dependency (Websocket).
  29. The following listing shows the `pom.xml` file that is created when you choose Maven:
  30. ====
  31. [src,xml]
  32. ----
  33. include::initial/pom.xml[]
  34. ----
  35. ====
  36. If you use Gradle, visit the https://start.spring.io/#!type=gradle-project&language=java&platformVersion=2.4.3.RELEASE&packaging=jar&jvmVersion=1.8&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[Spring Initializr] to generate a new project with the required dependency (Websocket).
  37. The following listing shows the `build.gradle` file that is created when you choose Gradle:
  38. ====
  39. [src,java]
  40. ----
  41. include::initial/build.gradle[]
  42. ----
  43. ====
  44. === Manual Initialization (optional)
  45. If you want to initialize the project manually rather than use the links shown earlier, follow the steps given below:
  46. . Navigate to https://start.spring.io.
  47. This service pulls in all the dependencies you need for an application and does most of the setup for you.
  48. . Choose either Gradle or Maven and the language you want to use. This guide assumes that you chose Java.
  49. . Click *Dependencies* and select *Websocket*.
  50. . Click *Generate*.
  51. . Download the resulting ZIP file, which is an archive of a web application that is configured with your choices.
  52. NOTE: If your IDE has the Spring Initializr integration, you can complete this process from your IDE.
  53. == Adding Dependencies
  54. The Spring Initializr does not provide everything you need in this case. For Maven, you
  55. need to add the following dependencies:
  56. ====
  57. [source,xml]
  58. ----
  59. <dependency>
  60. <groupId>org.webjars</groupId>
  61. <artifactId>webjars-locator-core</artifactId>
  62. </dependency>
  63. <dependency>
  64. <groupId>org.webjars</groupId>
  65. <artifactId>sockjs-client</artifactId>
  66. <version>1.0.2</version>
  67. </dependency>
  68. <dependency>
  69. <groupId>org.webjars</groupId>
  70. <artifactId>stomp-websocket</artifactId>
  71. <version>2.3.3</version>
  72. </dependency>
  73. <dependency>
  74. <groupId>org.webjars</groupId>
  75. <artifactId>bootstrap</artifactId>
  76. <version>3.3.7</version>
  77. </dependency>
  78. <dependency>
  79. <groupId>org.webjars</groupId>
  80. <artifactId>jquery</artifactId>
  81. <version>3.1.1-1</version>
  82. </dependency>
  83. ----
  84. ====
  85. The following listing shows the finished `pom.xml` file:
  86. ====
  87. [src,xml]
  88. ----
  89. include::complete/pom.xml[]
  90. ----
  91. ====
  92. If you use Gradle, you need to add the following dependencies:
  93. ====
  94. [source,java]
  95. ----
  96. implementation 'org.webjars:webjars-locator-core'
  97. implementation 'org.webjars:sockjs-client:1.0.2'
  98. implementation 'org.webjars:stomp-websocket:2.3.3'
  99. implementation 'org.webjars:bootstrap:3.3.7'
  100. implementation 'org.webjars:jquery:3.1.1-1'
  101. ----
  102. ====
  103. The following listing shows the finished `build.gradle` file:
  104. ====
  105. [src,java]
  106. ----
  107. include::complete/build.gradle[]
  108. ----
  109. ====
  110. [[initial]]
  111. == Create a Resource Representation Class
  112. Now that you have set up the project and build system, you can create your STOMP message
  113. service.
  114. Begin the process by thinking about service interactions.
  115. The service will accept messages that contain a name in a STOMP message whose body is a
  116. JSON object. If the name is `Fred`, the message might resemble the following:
  117. ====
  118. [source,json]
  119. ----
  120. {
  121. "name": "Fred"
  122. }
  123. ----
  124. ====
  125. To model the message that carries the name, you can create a plain old Java object with a
  126. `name` property and a corresponding `getName()` method, as the following listing (from
  127. `src/main/java/com/example/messagingstompwebsocket/HelloMessage.java`) shows:
  128. ====
  129. [source,java,tabsize=2]
  130. ----
  131. include::complete/src/main/java/com/example/messagingstompwebsocket/HelloMessage.java[]
  132. ----
  133. ====
  134. Upon receiving the message and extracting the name, the service will process it by
  135. creating a greeting and publishing that greeting on a separate queue to which the client
  136. is subscribed. The greeting will also be a JSON object, which as the following listing
  137. shows:
  138. ====
  139. [source,json]
  140. ----
  141. {
  142. "content": "Hello, Fred!"
  143. }
  144. ----
  145. ====
  146. To model the greeting representation, add another plain old Java object with a `content`
  147. property and a corresponding `getContent()` method, as the following listing (from
  148. `src/main/java/com/example/messagingstompwebsocket/Greeting.java`) shows:
  149. ====
  150. [source,java,tabsize=2]
  151. ----
  152. include::complete/src/main/java/com/example/messagingstompwebsocket/Greeting.java[]
  153. ----
  154. ====
  155. Spring will use the {jackson}[Jackson JSON] library to automatically marshal instances of
  156. type `Greeting` into JSON.
  157. Next, you will create a controller to receive the hello message and send a greeting
  158. message.
  159. == Create a Message-handling Controller
  160. In Spring's approach to working with STOMP messaging, STOMP messages can be routed to
  161. {AtController}[`@Controller`] classes. For example, the `GreetingController` (from
  162. `src/main/java/com/example/messagingstompwebsocket/GreetingController.java`) is mapped to
  163. handle messages to the `/hello` destination, as the following listing shows:
  164. ====
  165. [source,java,tabsize=2]
  166. ----
  167. include::complete/src/main/java/com/example/messagingstompwebsocket/GreetingController.java[]
  168. ----
  169. ====
  170. This controller is concise and simple, but plenty is going on. We break it down step by
  171. step.
  172. The {AtMessageMapping}[`@MessageMapping`] annotation ensures that, if a message is sent to
  173. the `/hello` destination, the `greeting()` method is called.
  174. The payload of the message is bound to a `HelloMessage` object, which is passed into
  175. `greeting()`.
  176. Internally, the implementation of the method simulates a processing delay by causing the
  177. thread to sleep for one second. This is to demonstrate that, after the client sends a
  178. message, the server can take as long as it needs to asynchronously process the message.
  179. The client can continue with whatever work it needs to do without waiting for the
  180. response.
  181. After the one-second delay, the `greeting()` method creates a `Greeting` object and
  182. returns it. The return value is broadcast to all subscribers of `/topic/greetings`, as
  183. specified in the {AtSendTo}[`@SendTo`] annotation. Note that the name from the input
  184. message is sanitized, since, in this case, it will be echoed back and re-rendered in the
  185. browser DOM on the client side.
  186. == Configure Spring for STOMP messaging
  187. Now that the essential components of the service are created, you can configure Spring to
  188. enable WebSocket and STOMP messaging.
  189. Create a Java class named `WebSocketConfig` that resembles the following listing (from
  190. `src/main/java/com/example/messagingstompwebsocket/WebSocketConfig.java`):
  191. ====
  192. [source,java,tabsize=2]
  193. ----
  194. include::complete/src/main/java/com/example/messagingstompwebsocket/WebSocketConfig.java[]
  195. ----
  196. ====
  197. `WebSocketConfig` is annotated with `@Configuration` to indicate that it is a Spring
  198. configuration class. It is also annotated with
  199. {AtEnableWebSocketMessageBroker}[`@EnableWebSocketMessageBroker`]. As its name suggests,
  200. `@EnableWebSocketMessageBroker` enables WebSocket message handling, backed by a message
  201. broker.
  202. The `configureMessageBroker()` method implements the default method in
  203. `WebSocketMessageBrokerConfigurer` to configure the message broker. It starts by calling
  204. `enableSimpleBroker()` to enable a simple memory-based message broker to carry the
  205. greeting messages back to the client on destinations prefixed with `/topic`. It also
  206. designates the `/app` prefix for messages that are bound for methods annotated with
  207. `@MessageMapping`. This prefix will be used to define all the message mappings. For
  208. example, `/app/hello` is the endpoint that the `GreetingController.greeting()` method is
  209. mapped to handle.
  210. The `registerStompEndpoints()` method registers the `/gs-guide-websocket` endpoint,
  211. enabling SockJS fallback options so that alternate transports can be used if WebSocket is
  212. not available. The SockJS client will attempt to connect to `/gs-guide-websocket` and use
  213. the best available transport (websocket, xhr-streaming, xhr-polling, and so on).
  214. == Create a Browser Client
  215. With the server-side pieces in place, you can turn your attention to the JavaScript client
  216. that will send messages to and receive messages from the server side.
  217. Create an `index.html` file similar to the following listing (from
  218. `src/main/resources/static/index.html`):
  219. ====
  220. [source,html]
  221. ----
  222. include::complete/src/main/resources/static/index.html[]
  223. ----
  224. ====
  225. This HTML file imports the `SockJS` and `STOMP` javascript libraries that will be used to
  226. communicate with our server through STOMP over websocket. We also import `app.js`, which
  227. contains the logic of our client application. The following listing (from
  228. `src/main/resources/static/app.js`) shows that file:
  229. ====
  230. [source,javascript,tabsize=2]
  231. ----
  232. include::complete/src/main/resources/static/app.js[]
  233. ----
  234. ====
  235. The main pieces of this JavaScript file to understand are the `connect()` and `sendName()`
  236. functions.
  237. The `connect()` function uses https://github.com/sockjs[SockJS] and {Stomp_JS}[stomp.js]
  238. to open a connection to `/gs-guide-websocket`, which is where our SockJS server waits for
  239. connections. Upon a successful connection, the client subscribes to the `/topic/greetings`
  240. destination, where the server will publish greeting messages. When a greeting is received
  241. on that destination, it will append a paragraph element to the DOM to display the greeting
  242. message.
  243. The `sendName()` function retrieves the name entered by the user and uses the STOMP client
  244. to send it to the `/app/hello` destination (where `GreetingController.greeting()` will
  245. receive it).
  246. The `main.css` can be omitted if you like, or you can create an empty
  247. one, just so the `<link>` can be resolved.
  248. == Make the Application Executable
  249. Spring Boot creates an application class for you. In this case, it needs no further
  250. modification. You can use it to run this application. The following listing (from
  251. `src/main/java/com/example/messagingstompwebsocket/MessagingStompWebsocketApplication.java`)
  252. shows the application class:
  253. ====
  254. [source,java,tabsize=2]
  255. ----
  256. include::complete/src/main/java/com/example/messagingstompwebsocket/MessagingStompWebsocketApplication.java[]
  257. ----
  258. ====
  259. include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/spring-boot-application-new-path.adoc[]
  260. include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/build_an_executable_jar_subhead.adoc[]
  261. include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/build_an_executable_jar_with_both.adoc[]
  262. Logging output is displayed. The service should be up and running within a few seconds.
  263. == Test the service
  264. Now that the service is running, point your browser at http://localhost:8080 and click the *Connect* button.
  265. Upon opening a connection, you are asked for your name. Enter your name and click *Send*.
  266. Your name is sent to the server as a JSON message over STOMP. After a one-second simulated
  267. delay, the server sends a message back with a "`Hello`" greeting that is displayed on the
  268. page. At this point, you can send another name or you can click the *Disconnect* button to
  269. close the connection.
  270. == Summary
  271. Congratulations! You have just developed a STOMP-based messaging service with Spring.
  272. == See Also
  273. The following guides may also be helpful:
  274. * https://spring.io/guides/gs/serving-web-content/[Serving Web Content with Spring MVC]
  275. * https://spring.io/guides/gs/spring-boot/[Building an Application with Spring Boot]
  276. include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/footer.adoc[]