123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- :spring_boot_version: 0.5.0.M6
- :MappingJackson2MessageConverter: http://static.springsource.org/spring/docs/4.0.x/javadoc-api/org/springframework/messaging/support/converter/MappingJackson2MessageConverter.html
- :Component: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Component.html
- :jackson: http://wiki.fasterxml.com/JacksonHome
- :AtMessageMapping: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/messaging/handler/annotation/MessageMapping.html
- :AtController: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/stereotype/Controller.html
- :AtEnableWebSocket: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/web/socket/server/config/EnableWebSocket.html
- :AtEnableWebSocketMessageBroker: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/messaging/simp/config/EnableWebSocketMessageBroker.html
- :SpringApplication: http://docs.spring.io/spring-boot/docs/{spring_boot_version}/api/org/springframework/boot/SpringApplication.html
- :Stomp_JS: http://jmesnil.net/stomp-websocket/doc/
- :DispatcherServlet: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/DispatcherServlet.html
- :EnableAutoConfiguration: http://docs.spring.io/spring-boot/docs/{spring_boot_version}/api/org/springframework/boot/autoconfigure/EnableAutoConfiguration.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" STOMP messaging server with Spring.
- == What you'll build
- You'll build a server that will accept a message carrying a user's name. In response, it will push a greeting into a queue that the client is subscribed to.
- == What you'll need
- include::https://raw.github.com/spring-guides/getting-started-macros/master/prereq_editor_jdk_buildtools.adoc[]
- include::https://raw.github.com/spring-guides/getting-started-macros/master/how_to_complete_this_guide.adoc[]
- [[scratch]]
- == Set up the project
- include::https://raw.github.com/spring-guides/getting-started-macros/master/build_system_intro.adoc[]
- include::https://raw.github.com/spring-guides/getting-started-macros/master/create_directory_structure_hello.adoc[]
- include::https://raw.github.com/spring-guides/getting-started-macros/master/create_both_builds.adoc[]
- `build.gradle`
- // AsciiDoc source formatting doesn't support groovy, so using java instead
- [source,java]
- ----
- include::initial/build.gradle[]
- ----
- include::https://raw.github.com/spring-guides/getting-started-macros/master/bootstrap_starter_pom_disclaimer.adoc[]
- [[initial]]
- == Create a resource representation class
- Now that you've 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 containing a name in a STOMP message whose body is a link:/understanding/JSON[JSON] object. If the name given is "Fred", then the message might look something like this:
- [source,json]
- ----
- {
- "name": "Fred"
- }
- ----
- To model the message carrying the name, you can create a plain old Java object with a `name` property and a corresponding `getName()` method:
- `src/main/java/hello/HelloMessage.java`
- [source,java]
- ----
- include::complete/src/main/java/hello/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 that the client is subscribed to. The greeting will also be a JSON object, which might look something like this:
- [source,json]
- ----
- {
- "content": "Hello, Fred!"
- }
- ----
- To model the greeting representation, you add another plain old Java object with a `content` property and corresponding `getContent()` method:
- `src/main/java/hello/Greeting.java`
- [source,java]
- ----
- include::complete/src/main/java/hello/Greeting.java[]
- ----
- Spring will use the {jackson}[Jackson JSON] library to automatically marshal instances of type `Greeting` into JSON.
- Next, you'll 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 handled by a controller. These components are easily identified by the {AtController}[`@Controller`] annotation, and the `GreetingController` below is mapped to handle messages published on the "/hello" destination.
- `src/main/java/hello/GreetingController.java`
- [source,java]
- ----
- include::complete/src/main/java/hello/GreetingController.java[]
- ----
- This controller is concise and simple, but there's plenty going on. Let's break it down step by step.
- The {AtMessageMapping}[`@MessageMapping`] annotation ensures that if a message is published on the "/hello" destination, then 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 3 seconds. This is to demonstrate that after the client sends a message, the server can take as long as it needs to process the message asynchronously. The client may continue with whatever work it needs to do without waiting on the response.
- After the 3 second delay, the `greeting()` method creates a new `Greeting` object, setting its content to say "Hello" to the name from the `HelloMessage`. It then calls `convertAndSend()` on the injected `SimpMessageSendingOperations` to send the `Greeting` on the "/queue/greetings" destination.
- The `Greeting` object must be converted to JSON. Thanks to Spring's HTTP message converter support, you don't need to do this conversion manually. When you configure Spring for STOMP messaging, you'll inject `SimpMessagingTemplate` with an instance of {MappingJackson2MessageConverter}[`MappingJackson2MessageConverter`]. It will be used to convert the `Greeting` instance to JSON.
- == 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 looks like this:
- `src/main/java/hello/WebSocketConfig.java`
- [source,java]
- ----
- include::complete/src/main/java/hello/WebSocketConfig.java[]
- ----
- `WebSocketConfig` is annotated with `@Configuration` to indicate that it is a Spring configuration class.
- It is also annotated {AtEnableWebSocketMessageBroker}[`@EnableWebSocketMessageBroker`].
- As its name suggests, `@EnableWebSocketMessageBroker` enables WebSocket message handling, backed by a message broker.
- The `configureMessageBroker()` method overrides 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 "/queue".
- It also designates the "/app" prefix for messages that are bound for `@MessageMapping`-annotated methods.
- The `registerStompEndpoints()` method registers the "/hello" endpoint, enabling SockJS fallback options so that alternative messaging options may be used if WebSocket is not available.
- This endpoint, when prefixed with "/app", is the endpoint that the `GreetingController.greeting()` method is mapped to handle.
- == Create a browser client
- With the server side pieces in place, now let's turn our attention to the JavaScript client that will send messages to and receive messages from the server side.
- Create an index.html file that looks like this:
- `src/main/resources/static/index.html`
- [source,html]
- ----
- include::complete/src/main/resources/static/index.html[]
- ----
- The main piece of this HTML file to pay attention to is the JavaScript code in the `connect()` and `sendName()` functions.
- The `connect()` function uses https://github.com/sockjs[SockJS] and {Stomp_JS}[stomp.js] to open a connection to "/gs-messaging-stomp-websocket/hello", which is where `GreetingController` is waiting for connections. Upon a successful connection, it subscribes to the "/queue/greetings" destination, where the server will publish greeting messages. When a greeting appears on that queue, 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).
- == Make the application executable
- Although it is possible to package this service as a traditional link:/understanding/WAR[WAR] file for deployment to an external application server, the simpler approach demonstrated below creates a standalone application. You package everything in a single, executable JAR file, driven by a good old Java `main()` method. Along the way, you use Spring's support for embedding the link:/understanding/Tomcat[Tomcat] servlet container as the HTTP runtime, instead of deploying to an external instance.
- `src/main/java/hello/Application.java`
- [source,java]
- ----
- include::complete/src/main/java/hello/Application.java[]
- ----
- The `main()` method defers to the {SpringApplication}[`SpringApplication`] helper class, providing `Application.class` as an argument to its `run()` method. This tells Spring to read the annotation metadata from `Application` and to manage it as a component in the link:/understanding/application-context[Spring application context].
- The `@ComponentScan` annotation tells Spring to search recursively through the `hello` package and its children for classes marked directly or indirectly with Spring's {Component}[`@Component`] annotation. This directive ensures that Spring finds and registers the `GreetingController`, because it is marked with `@Controller`, which in turn is a kind of `@Component` annotation.
- The {EnableAutoConfiguration}[`@EnableAutoConfiguration`] annotation switches on reasonable default behaviors based on the content of your classpath. For example, because the application depends on the embeddable version of Tomcat (tomcat-embed-core.jar), a Tomcat server is set up and configured with reasonable defaults on your behalf. And because the application also depends on Spring MVC (spring-webmvc.jar), a Spring MVC {DispatcherServlet}[`DispatcherServlet`] is configured and registered for you — no `web.xml` necessary! Auto-configuration is a powerful, flexible mechanism. See the {EnableAutoConfiguration}[API documentation] for further details.
- include::https://raw.github.com/spring-guides/getting-started-macros/master/build_an_executable_jar_subhead.adoc[]
- include::https://raw.github.com/spring-guides/getting-started-macros/master/build_an_executable_jar_with_both.adoc[]
- :module: service
- include::https://raw.github.com/spring-guides/getting-started-macros/master/run_the_application_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 3-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've just developed a STOMP-based messaging service with Spring.
|