Bläddra i källkod

Update README.*. Still requires build fix and version updates.

Craig Walls 11 år sedan
förälder
incheckning
92e714f726

+ 42 - 32
README.ftl.md

@@ -11,7 +11,6 @@ What you'll need
 ----------------
 
  - About 15 minutes
- - Tomcat 8 ([Tomcat 8.0.0-RC1][tomcat8] is available)
  - <@prereq_editor_jdk_buildtools/>
 
 
@@ -26,9 +25,10 @@ Set up the project
 
 <@create_directory_structure_hello/>
 
-### Create a Maven POM
 
-    <@snippet path="pom.xml" prefix="initial"/>
+<@create_both_builds/>
+
+<@bootstrap_starter_pom_disclaimer/>
 
 
 <a name="initial"></a>
@@ -59,9 +59,9 @@ To model the greeting representation, you add another plain old Java object with
 
     <@snippet path="src/main/java/hello/Greeting.java" prefix="complete"/>
 
-> **Note:** As you see in steps below, Spring uses the [Jackson JSON][jackson] library to automatically marshal instances of type `Greeting` into JSON.
+Spring will use the [Jackson JSON][jackson] library to automatically marshal instances of type `Greeting` into JSON.
 
-Next, you'll create the controller to receive the hello message and send a greeting message.
+Next, you'll create a controller to receive the hello message and send a greeting message.
 
 Create a message-handling controller
 ------------------------------------
@@ -72,9 +72,9 @@ In Spring's approach to working with STOMP messaging, STOMP messages can be hand
 
 This controller is concise and simple, but there's plenty going on. Let's break it down step by step.
 
-The `@MessageMapping` annotation ensures that if a message is published on the "/app/hello" destination, then the `greeting()` method is called.
+The [`@MessageMapping`][AtMessageMapping] annotation ensures that if a message is published on the "/app/hello" destination, then the `greeting()` method is called.
 
-`@RequestBody` binds the payload of the message to a `HelloMessage` object which is passed into `greeting()`. 
+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.
 
@@ -85,11 +85,22 @@ The `Greeting` object must be converted to JSON. Thanks to Spring's HTTP message
 Configure Spring for STOMP messaging
 ------------------------------------
 
-TODO: What follows is the configuration for the STOMP/WebSocket-specific piece.
+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:
+
+    <@snippet path="src/main/java/hello/WebSocketConfig.java" prefix="complete"/>
 
-    <@snippet path="src/main/java/hello/StompConfig.java" prefix="complete"/>
+`WebSocketConfig` is annotated with `@Configuration` to indicate that it is a Spring configuration class.
+It is also annotated [`@EnableWebSocketMessageBroker`][AtEnableWebSocketMessageBroker].
+As its name suggests, `@EnableWebSocketMessageBroker` enables a WebSocket message handling, backed by a message broker.
 
-TODO: This is extremely ugly at the moment, with many beans in play. Rossen says that SPR-10835 will be resolved in time for RC1. There's no need to write this section to describe all of these beans now. It's better to hold off and wait for SPR-10835 to be resolved and then adjust the configuration accordingly and write about that. At that time, assuming the solution is simple enough, the configuration in StompConfig.java can be merged back into WebConfig.java to have only a single configuration class.
+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
 -----------------------
@@ -98,7 +109,7 @@ With the server side pieces in place, now let's turn our attention to the JavaSc
 
 Create an index.html file that looks like this:
 
-    <@snippet path="src/main/webapp/index.html" prefix="complete"/>
+    <@snippet path="src/main/resources/static/index.html" prefix="complete"/>
 
 The main piece of this HTML file to pay attention to is the JavaScript code in the `connect()` and `sendName()` functions.
 
@@ -109,36 +120,32 @@ The `sendName()` function retrieves the name entered by the user and uses the ST
 Make the application executable
 -------------------------------
 
-In order to deploy the application to Tomcat, you'll need to add a bit more configuration.
+Although it is possible to package this service as a traditional [WAR][u-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 [Tomcat][u-tomcat] servlet container as the HTTP runtime, instead of deploying to an external instance.
 
-First, configure Spring's [`DispatcherServlet`][DispatcherServlet] to serve static resources so that it will serve index.html. You do this by creating a configuration class that overrides the `configureDefaultServletHandling()` method of `WebMvcConfigurerAdapter` and calls `enable()` on the given `DefaultServletHandlerConfigurer`:
+### Create an Application class
 
-    <@snippet path="src/main/java/hello/WebConfig.java" prefix="complete"/>
+    <@snippet path="src/main/java/hello/Application.java" prefix="complete"/>
 
-You'll also need to configure `DispatcherServlet`. This is most easily done by creating a class that extends `AbstractAnnotationConfigDispatcherServletInitializer`:
+The `main()` method defers to the [`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 [Spring application context][u-application-context].
 
-    <@snippet path="src/main/java/hello/HelloServletInitializer.java" prefix="complete"/>
+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`][] 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.
 
-Here, `DispatcherServlet` is mapped to "/". Also, `WebConfig` and `EndpointConfig` are specified, respectively, as the servlet and root configuration classes.
+The [`@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`][] is configured and registered for you — no `web.xml` necessary! Auto-configuration is a powerful, flexible mechanism. See the [API documentation][`@EnableAutoConfiguration`] for further details.
 
-Now you're ready to build and deploy the application to Tomcat 8. Start by building the WAR file:
+<@build_an_executable_jar_subhead/>
 
-```sh
-mvn package
-```
+<@build_an_executable_jar_with_both/>
 
-Then copy the WAR file to Tomcat 8's `trunk/output/webapps` directory. 
+<@run_the_application_with_both module="service"/>
 
-Finally, restart Tomcat 8:
+Logging output is displayed. The service should be up and running within a few seconds.
 
-```sh
-output/bin/shutdown.sh
-output/bin/startup.sh
-```
+Test the service
+----------------
 
-After the application starts, point your browser at http://localhost:8080/gs-messaging-stomp-websocket and click the "Connect" button.
+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. 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.
+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
@@ -147,13 +154,16 @@ Summary
 Congratulations! You've just developed a STOMP-based messaging service with Spring. 
 
 
-[tomcat8]: http://tomcat.apache.org/download-80.cgi
 [u-rest]: /understanding/rest
 [u-json]: /understanding/json
 [jackson]: http://wiki.fasterxml.com/JacksonHome
 [MappingJackson2MessageConverter]: http://static.springsource.org/spring/docs/4.0.x/javadoc-api/org/springframework/messaging/support/converter/MappingJackson2MessageConverter.html
-[`AtController`]: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/stereotype/Controller.html
-[DispatcherServlet]: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/web/servlet/DispatcherServlet.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
+[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
+.html
 [SockJS]: https://github.com/sockjs
 [Stomp_JS]: http://jmesnil.net/stomp-websocket/doc/
 

+ 157 - 352
README.md

@@ -1,24 +1,24 @@
 
-# Getting Started: Messaging with WebSocket and STOMP
-
-This guide walks you through creating a "hello world" STOMP messaging server with Spring. 
+This guide walks you through the process of creating a "hello world" STOMP messaging server with Spring. 
 
 What you'll build
 -----------------
 
-The server will accept a message carying the user's name. In response, it will push a greeting into a queue that the client is subscribed to.
+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
 ----------------
 
  - About 15 minutes
- - Tomcat 8 ([Tomcat 8.0.0-RC1][tomcat8] is available)
  - A favorite text editor or IDE
  - [JDK 6][jdk] or later
- - [Maven 3.0][mvn] or later
+ - [Gradle 1.8+][gradle] or [Maven 3.0+][mvn]
+ - You can also import the code from this guide as well as view the web page directly into [Spring Tool Suite (STS)][gs-sts] and work your way through it from there.
 
 [jdk]: http://www.oracle.com/technetwork/java/javase/downloads/index.html
+[gradle]: http://www.gradle.org/
 [mvn]: http://maven.apache.org/download.cgi
+[gs-sts]: /guides/gs/sts
 
 
 How to complete this guide
@@ -31,12 +31,12 @@ To **start from scratch**, move on to [Set up the project](#scratch).
 To **skip the basics**, do the following:
 
  - [Download][zip] and unzip the source repository for this guide, or clone it using [Git][u-git]:
-`git clone https://github.com/spring-guides/gs-rest-service.git`
- - cd into `gs-rest-service/initial`.
+`git clone https://github.com/spring-guides/gs-messaging-stomp-websocket.git`
+ - cd into `gs-messaging-stomp-websocket/initial`.
  - Jump ahead to [Create a resource representation class](#initial).
 
-**When you're finished**, you can check your results against the code in `gs-rest-service/complete`.
-[zip]: https://github.com/spring-guides/gs-rest-service/archive/master.zip
+**When you're finished**, you can check your results against the code in `gs-messaging-stomp-websocket/complete`.
+[zip]: https://github.com/spring-guides/gs-messaging-stomp-websocket/archive/master.zip
 [u-git]: /understanding/Git
 
 
@@ -44,7 +44,7 @@ To **skip the basics**, do the following:
 Set up the project
 ------------------
 
-First you set up a basic build script. You can use any build system you like when building apps with Spring, but the code you need to work with [Maven](https://maven.apache.org) and [Gradle](http://gradle.org) is included here. If you're not familiar with either, refer to [Building Java Projects with Maven](/guides/gs/maven) or [Building Java Projects with Gradle](/guides/gs/gradle/).
+First you set up a basic build script. You can use any build system you like when building apps with Spring, but the code you need to work with [Gradle](http://gradle.org) and [Maven](https://maven.apache.org) is included here. If you're not familiar with either, refer to [Building Java Projects with Gradle](/guides/gs/gradle/) or [Building Java Projects with Maven](/guides/gs/maven).
 
 ### Create the directory structure
 
@@ -55,151 +55,43 @@ In a project directory of your choosing, create the following subdirectory struc
             └── java
                 └── hello
 
-### Create a Maven POM
-
-`pom.xml`
-```xml
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>org.springframework.samples</groupId>
-    <artifactId>gs-messaging-stomp-websocket-initial</artifactId>
-    <packaging>war</packaging>
-    <version>0.1.0</version>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-webmvc</artifactId>
-            <version>4.0.0.BUILD-SNAPSHOT</version>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-messaging</artifactId>
-            <version>4.0.0.BUILD-SNAPSHOT</version>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-websocket</artifactId>
-            <version>4.0.0.BUILD-SNAPSHOT</version>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
-            <version>2.2.2</version>
-        </dependency>
-        <dependency>
-            <groupId>javax.websocket</groupId>
-            <artifactId>javax.websocket-api</artifactId>
-            <version>1.0-rc5</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
-            <version>3.1-b09</version>
-            <scope>provided</scope>
-        </dependency>
-        
-        
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-            <version>1.6.4</version>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <version>1.6.4</version>
-            <scope>runtime</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <version>1.6.4</version>
-            <scope>runtime</scope>
-        </dependency>
-        <dependency>
-            <groupId>log4j</groupId>
-            <artifactId>log4j</artifactId>
-            <version>1.2.16</version>
-            <scope>runtime</scope>
-        </dependency>
-        
-        <dependency>
-            <groupId>org.projectreactor</groupId>
-            <artifactId>reactor-core</artifactId>
-            <version>1.0.0.M1</version>
-        </dependency>
-
-        <!-- Required when the "stomp-broker-relay" profile is enabled -->
-        <dependency>
-            <groupId>org.projectreactor</groupId>
-            <artifactId>reactor-tcp</artifactId>
-            <version>1.0.0.M1</version>
-        </dependency>
-        
 
-        <dependency>
-            <groupId>commons-logging</groupId>
-            <artifactId>commons-logging</artifactId>
-            <version>1.1</version>
-            <exclusions>
-                <exclusion>
-                    <groupId>javax.servlet</groupId>
-                    <artifactId>servlet-api</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        
-    </dependencies>
-
-    <repositories>
-        <repository>
-            <id>spring-snapshots</id>
-            <url>http://repo.springsource.org/libs-snapshot</url>
-            <snapshots>
-                <enabled>true</enabled>
-            </snapshots>
-        </repository>
-        <repository>
-            <id>tomcat-snapshots</id>
-            <url>https://repository.apache.org/content/repositories/snapshots</url>
-            <snapshots><enabled>true</enabled></snapshots>
-            <releases><enabled>false</enabled></releases>
-        </repository>
-        <repository>
-            <id>java-net-snapshots</id>
-            <url>https://maven.java.net/content/repositories/snapshots</url>
-            <snapshots><enabled>true</enabled></snapshots>
-            <releases><enabled>false</enabled></releases>
-        </repository>
-    </repositories>
-
-    <build>
-        <finalName>${project.artifactId}</finalName>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>2.3.2</version>
-                <configuration>
-                    <source>1.7</source>
-                    <target>1.7</target>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-war-plugin</artifactId>
-                <version>2.2</version>
-                <configuration>
-                    <failOnMissingWebXml>false</failOnMissingWebXml>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-</project>
+### Create a Gradle build file
+Below is the [initial Gradle build file](https://github.com/spring-guides/gs-messaging-stomp-websocket/blob/master/initial/build.gradle). But you can also use Maven. The pom.xml file is included [right here](https://github.com/spring-guides/gs-messaging-stomp-websocket/blob/master/initial/pom.xml). If you are using [Spring Tool Suite (STS)][gs-sts], you can import the guide directly.
+
+`build.gradle`
+```gradle
+apply plugin: 'java'
+apply plugin: 'war'
+apply plugin: 'eclipse'
+apply plugin: 'idea'
+
+war {
+    baseName = 'gs-messaging-websocket'
+    version =  '0.1.0'
+}
+
+repositories {
+    mavenCentral()
+    maven { url "http://repo.springsource.org/libs-snapshot" }
+}
+
+dependencies {
+    compile("org.springframework:spring-webmvc:4.0.0.BUILD-SNAPSHOT")
+    compile("org.springframework:spring-websocket:4.0.0.BUILD-SNAPSHOT")
+    providedCompile("javax.websocket:javax.websocket-api:1.0-rc5")
+    providedCompile("javax.servlet:javax.servlet-api:3.1-b09")
+    testCompile("junit:junit:4.11")
+}
+
+task wrapper(type: Wrapper) {
+    gradleVersion = '1.6'
+}
 ```
+    
+[gs-sts]: /guides/gs/sts    
+
+> **Note:** This guide is using [Spring Boot](/guides/gs/spring-boot/).
 
 
 <a name="initial"></a>
@@ -239,7 +131,7 @@ Upon receiving the message and extracting the name, the service will process it
         "content": "Hello, Fred!"
     }
 
-To model the greeting representation, you another plain old Java object with a `content` property and corresponding `getContent()` method:
+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`
 ```java
@@ -260,9 +152,9 @@ public class Greeting {
 }
 ```
 
-> **Note:** As you see in steps below, Spring uses the [Jackson JSON][jackson] library to automatically marshal instances of type `Greeting` into JSON.
+Spring will use the [Jackson JSON][jackson] library to automatically marshal instances of type `Greeting` into JSON.
 
-Next, you'll create the controller to receive the hello message and send a greeting message.
+Next, you'll create a controller to receive the hello message and send a greeting message.
 
 Create a message-handling controller
 ------------------------------------
@@ -279,6 +171,7 @@ import org.springframework.messaging.simp.SimpMessageSendingOperations;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RequestBody;
 
+
 @Controller
 public class GreetingController {
 
@@ -295,12 +188,13 @@ public class GreetingController {
         Greeting greeting = new Greeting("Hello, " + message.getName() + "!");
         messagingTemplate.convertAndSend("/queue/greetings", greeting);
     }
+
 }
 ```
 
-This controller is concise and simple, but there's plenty going on under the hood. Let's break it down step by step.
+This controller is concise and simple, but there's plenty going on. Let's break it down step by step.
 
-The `@MessageMapping` annotation ensures that if a message is published on the "/app/hello" destination, then the `greeting()` method will called.
+The [`@MessageMapping`][AtMessageMapping] annotation ensures that if a message is published on the "/app/hello" destination, then the `greeting()` method is called.
 
 `@RequestBody` binds the payload of the message to a `HelloMessage` object which is passed into `greeting()`. 
 
@@ -313,164 +207,58 @@ The `Greeting` object must be converted to JSON. Thanks to Spring's HTTP message
 Configure Spring for STOMP messaging
 ------------------------------------
 
-TODO: What follows is the configuration for the STOMP/WebSocket-specific piece.
+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/StompConfig.java`
+`src/main/java/hello/WebSocketConfig.java`
 ```java
 package hello;
 
-import java.util.Arrays;
-import java.util.Collections;
-
-import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.messaging.SubscribableChannel;
-import org.springframework.messaging.handler.websocket.SubProtocolWebSocketHandler;
-import org.springframework.messaging.simp.SimpMessageSendingOperations;
-import org.springframework.messaging.simp.SimpMessagingTemplate;
-import org.springframework.messaging.simp.handler.AnnotationMethodMessageHandler;
-import org.springframework.messaging.simp.handler.SimpleBrokerMessageHandler;
-import org.springframework.messaging.simp.handler.SimpleUserQueueSuffixResolver;
-import org.springframework.messaging.simp.handler.UserDestinationMessageHandler;
-import org.springframework.messaging.simp.stomp.StompProtocolHandler;
-import org.springframework.messaging.support.channel.ExecutorSubscribableChannel;
-import org.springframework.messaging.support.converter.MappingJackson2MessageConverter;
-import org.springframework.messaging.support.converter.MessageConverter;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
-import org.springframework.web.HttpRequestHandler;
-import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
-import org.springframework.web.socket.WebSocketHandler;
-import org.springframework.web.socket.sockjs.SockJsHttpRequestHandler;
-import org.springframework.web.socket.sockjs.SockJsService;
-import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService;
+import org.springframework.messaging.simp.config.EnableWebSocketMessageBroker;
+import org.springframework.messaging.simp.config.MessageBrokerConfigurer;
+import org.springframework.messaging.simp.config.StompEndpointRegistry;
+import org.springframework.messaging.simp.config.WebSocketMessageBrokerConfigurer;
 
 @Configuration
-public class StompConfig {
-    private final MessageConverter<?> messageConverter = new MappingJackson2MessageConverter();
-
-    private final SimpleUserQueueSuffixResolver userQueueSuffixResolver = new SimpleUserQueueSuffixResolver();
-
-
-    @Bean
-    public SimpleUrlHandlerMapping handlerMapping() {
-
-        SockJsService sockJsService = new DefaultSockJsService(taskScheduler());
-        HttpRequestHandler requestHandler = new SockJsHttpRequestHandler(sockJsService, webSocketHandler());
-
-        SimpleUrlHandlerMapping hm = new SimpleUrlHandlerMapping();
-        hm.setOrder(-1);
-        hm.setUrlMap(Collections.singletonMap("/hello/**", requestHandler));
-        return hm;
-    }
-
-    // WebSocketHandler supporting STOMP messages
-
-    @Bean
-    public WebSocketHandler webSocketHandler() {
-
-        StompProtocolHandler stompHandler = new StompProtocolHandler();
-        stompHandler.setUserQueueSuffixResolver(this.userQueueSuffixResolver);
-
-        SubProtocolWebSocketHandler webSocketHandler = new SubProtocolWebSocketHandler(dispatchChannel());
-        webSocketHandler.setDefaultProtocolHandler(stompHandler);
-        webSocketHandlerChannel().subscribe(webSocketHandler);
-
-        return webSocketHandler;
-    }
-
-    // MessageHandler for processing messages by delegating to @Controller annotated methods
-
-    @Bean
-    public AnnotationMethodMessageHandler annotationMessageHandler() {
-
-        AnnotationMethodMessageHandler handler =
-                new AnnotationMethodMessageHandler(dispatchMessagingTemplate(), webSocketHandlerChannel());
-
-        handler.setDestinationPrefixes(Arrays.asList("/app/"));
-        handler.setMessageConverter(this.messageConverter);
-        dispatchChannel().subscribe(handler);
-        return handler;
-    }
-
-    // MessageHandler that acts as a "simple" message broker
-    // See DispatcherServletInitializer for enabling/disabling the "simple-broker" profile
-
-    @Bean
-    public SimpleBrokerMessageHandler simpleBrokerMessageHandler() {
-        SimpleBrokerMessageHandler handler = new SimpleBrokerMessageHandler(webSocketHandlerChannel());
-        handler.setDestinationPrefixes(Arrays.asList("/topic/", "/queue/"));
-        dispatchChannel().subscribe(handler);
-        return handler;
-    }
-
-    // MessageHandler that resolves destinations prefixed with "/user/{user}"
-    // See the Javadoc of UserDestinationMessageHandler for details
-
-    @Bean
-    public UserDestinationMessageHandler userMessageHandler() {
-        UserDestinationMessageHandler handler = new UserDestinationMessageHandler(
-                dispatchMessagingTemplate(), this.userQueueSuffixResolver);
-        dispatchChannel().subscribe(handler);
-        return handler;
-    }
-
-    // MessagingTemplate (and MessageChannel) to dispatch messages to for further processing
-    // All MessageHandler beans above subscribe to this channel
-
-    @Bean
-    public SimpMessageSendingOperations dispatchMessagingTemplate() {
-        SimpMessagingTemplate template = new SimpMessagingTemplate(dispatchChannel());
-        template.setMessageConverter(this.messageConverter);
-        return template;
-    }
-
-    @Bean
-    public SubscribableChannel dispatchChannel() {
-        return new ExecutorSubscribableChannel(asyncExecutor());
-    }
+@EnableWebSocketMessageBroker
+public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
+
+	@Override
+	public void configureMessageBroker(MessageBrokerConfigurer config) {
+		config.enableSimpleBroker("/queue");
+		config.setAnnotationMethodDestinationPrefixes("/app");
+	}
+	
+	@Override
+	public void registerStompEndpoints(StompEndpointRegistry registry) {
+		registry.addEndpoint("/hello").withSockJS();
+	}
 
-    // Channel for sending STOMP messages to connected WebSocket sessions (mostly for internal use)
-
-    @Bean
-    public SubscribableChannel webSocketHandlerChannel() {
-        return new ExecutorSubscribableChannel(asyncExecutor());
-    }
-
-    // Executor for message passing via MessageChannel
-
-    @Bean
-    public ThreadPoolTaskExecutor asyncExecutor() {
-        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
-        executor.setCorePoolSize(4);
-        executor.setCorePoolSize(8);
-        executor.setThreadNamePrefix("MessageChannel-");
-        return executor;
-    }
+}
+```
 
-    // Task executor for use in SockJS (heartbeat frames, session timeouts)
+`WebSocketConfig` is annotated with `@Configuration` to indicate that it is a Spring configuration class.
+It is also annotated [`@EnableWebSocketMessageBroker`][AtEnableWebSocketMessageBroker].
+As its name suggests, `@EnableWebSocketMessageBroker` enables a WebSocket message handling, backed by a message broker.
 
-    @Bean
-    public ThreadPoolTaskScheduler taskScheduler() {
-        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
-        taskScheduler.setThreadNamePrefix("SockJS-");
-        taskScheduler.setPoolSize(4);
-        return taskScheduler;
-    }
+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 (which, along with the "/app" prefix, is handled by `GreetingController`'s `greeting()` method). 
+In doing so, it enables SockJS fallback options on that endpoint so that alternative messaging options may be used if WebSocket is not available.
 
-TODO: This is extremely ugly at the moment, with many beans in play. Rossen says that SPR-10835 will be resolved in time for RC1. There's no need to write this section to describe all of these beans now. It's better to hold off and wait for SPR-10835 to be resolved and then adjust the configuration accordingly and write about that. At that time, assuming the solution is simple enough, the configuration in StompConfig.java can be merged back into WebConfig.java to have only a single configuration class.
 
-Create a Browser Client
+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/webapp/index.html`
+`src/main/resources/static/index.html`
 ```html
 <!DOCTYPE html>
 <html>
@@ -489,7 +277,7 @@ Create an index.html file that looks like this:
         }
         
         function connect() {
-            var socket = new SockJS('/gs-messaging-stomp-websocket/hello');
+            var socket = new SockJS('/hello');
             stompClient = Stomp.over(socket);            
             stompClient.connect('', '', function(frame) {
                 setConnected(true);
@@ -547,91 +335,105 @@ The `sendName()` function retrieves the name entered by the user and uses the ST
 Make the application executable
 -------------------------------
 
-In order to deploy the application to Tomcat, you'll need to add a bit more configuration.
+Although it is possible to package this service as a traditional [WAR][u-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 [Tomcat][u-tomcat] servlet container as the HTTP runtime, instead of deploying to an external instance.
 
-First, you'll need to configure Spring's [`DispatcherServlet`][DispatcherServlet] to serve static resources so that it will serve index.html. This can be done by creating a configuration class that overrides the `configureDefaultServletHandling()` method of `WebMvcConfigurerAdapter` and calls `enable()` on the given `DefaultServletHandlerConfigurer`:
+### Create an Application class
 
-`src/main/java/hello/WebConfig.java`
+`src/main/java/hello/Application.java`
 ```java
 package hello;
 
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.SpringApplication;
 import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Import;
-import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
-import org.springframework.web.servlet.config.annotation.EnableWebMvc;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
-@Configuration
-@EnableWebMvc
 @ComponentScan
-@Import(StompConfig.class)
-public class WebConfig extends WebMvcConfigurerAdapter {
-    
-    // Allow serving HTML files through the default Servlet
-    @Override
-    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
-        configurer.enable();
-    }
+@EnableAutoConfiguration
+public class Application {
 
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
 }
 ```
 
-You'll also need to configure `DispatcherServlet`. This is most easily done by creating a class that extends `AbstractAnnotationConfigDispatcherServletInitializer`:
+The `main()` method defers to the [`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 [Spring application context][u-application-context].
 
-`src/main/java/hello/HelloServletInitializer.java`
-```java
-package hello;
+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`][] 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.
 
-import javax.servlet.ServletRegistration.Dynamic;
+The [`@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`][] is configured and registered for you — no `web.xml` necessary! Auto-configuration is a powerful, flexible mechanism. See the [API documentation][`@EnableAutoConfiguration`] for further details.
 
-import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
+### Build an executable JAR
 
-public class HelloServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
+Now that your `Application` class is ready, you simply instruct the build system to create a single, executable jar containing everything. This makes it easy to ship, version, and deploy the service as an application throughout the development lifecycle, across different environments, and so forth.
 
-    @Override
-    protected Class<?>[] getRootConfigClasses() {
-        return new Class<?>[] {};
-    }
+Below are the Gradle steps, but if you are using Maven, you can find the updated pom.xml [right here](https://github.com/spring-guides/gs-messaging-stomp-websocket/blob/master/complete/pom.xml) and build it by typing `mvn clean package`.
 
-    @Override
-    protected Class<?>[] getServletConfigClasses() {
-        return new Class<?>[] { WebConfig.class };
-    }
+Update your Gradle `build.gradle` file's `buildscript` section, so that it looks like this:
 
-    @Override
-    protected String[] getServletMappings() {
-        return new String[] { "/" };
+```groovy
+buildscript {
+    repositories {
+        maven { url "http://repo.spring.io/libs-snapshot" }
+        mavenLocal()
     }
-
-    @Override
-    protected void customizeRegistration(Dynamic registration) {
-        registration.setInitParameter("dispatchOptionsRequest", "true");
+    dependencies {
+        classpath("org.springframework.boot:spring-boot-gradle-plugin:0.5.0.M6")
     }
-
 }
 ```
 
-Here, `DispatcherServlet` is mapped to "/". Also, `WebConfig` and `EndpointConfig` are specified, respectively, as the servlet and root configuration classes.
+Further down inside `build.gradle`, add the following to the list of applied plugins:
+
+```groovy
+apply plugin: 'spring-boot'
+```
+You can see the final version of `build.gradle` [right here]((https://github.com/spring-guides/gs-messaging-stomp-websocket/blob/master/complete/build.gradle).
+
+The [Spring Boot gradle plugin][spring-boot-gradle-plugin] collects all the jars on the classpath and builds a single "über-jar", which makes it more convenient to execute and transport your service.
+It also searches for the `public static void main()` method to flag as a runnable class.
+
+Now run the following command to produce a single executable JAR file containing all necessary dependency classes and resources:
+
+```sh
+$ ./gradlew build
+```
+
+If you are using Gradle, you can run the JAR by typing:
+
+```sh
+$ java -jar build/libs/gs-messaging-stomp-websocket-0.1.0.jar
+```
 
-Now you're ready to build and deploy the application to Tomcat 8. Start by building the WAR file:
+If you are using Maven, you can run the JAR by typing:
 
 ```sh
-mvn package
+$ java -jar target/gs-messaging-stomp-websocket-0.1.0.jar
 ```
 
-Then copy the WAR file to Tomcat 8's `trunk/output/webapps` directory. 
+[spring-boot-gradle-plugin]: https://github.com/spring-projects/spring-boot/tree/master/spring-boot-tools/spring-boot-gradle-plugin
+
+> **Note:** The procedure above will create a runnable JAR. You can also opt to [build a classic WAR file](/guides/gs/convert-jar-to-war/) instead.
 
-Finally, restart Tomcat 8:
+Run the service
+-------------------
+If you are using Gradle, you can run your service at the command line this way:
 
 ```sh
-output/bin/shutdown.sh
-output/bin/startup.sh
+$ ./gradlew clean build && java -jar build/libs/gs-messaging-stomp-websocket-0.1.0.jar
 ```
 
-After the application starts, point your browser at http://localhost:8080/gs-messaging-stomp-websocket and click the "Connect" button.
+> **Note:** If you are using Maven, you can run your service by typing `mvn clean package && java -jar target/gs-messaging-stomp-websocket-0.1.0.jar`.
+
+
+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 will be asked for your name. Enter your name and click "Send". Your name will be sent to the server as a JSON message over STOMP. The server will send a message back with a "Hello" greeting that will be displayed on the page. At this point, you may choose to send another name or you can click the "Disconnect" button to close the connection.
+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
@@ -640,13 +442,16 @@ Summary
 Congratulations! You've just developed a STOMP-based messaging service with Spring. 
 
 
-[tomcat8]: http://tomcat.apache.org/download-80.cgi
 [u-rest]: /understanding/rest
 [u-json]: /understanding/json
 [jackson]: http://wiki.fasterxml.com/JacksonHome
 [MappingJackson2MessageConverter]: http://static.springsource.org/spring/docs/4.0.x/javadoc-api/org/springframework/messaging/support/converter/MappingJackson2MessageConverter.html
-[`AtController`]: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/stereotype/Controller.html
-[DispatcherServlet]: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/web/servlet/DispatcherServlet.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
+[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
+.html
 [SockJS]: https://github.com/sockjs
 [Stomp_JS]: http://jmesnil.net/stomp-websocket/doc/
 

+ 2 - 0
complete/.gitignore

@@ -0,0 +1,2 @@
+build
+target

BIN
complete/.gradle/1.8/taskArtifacts/fileHashes.bin


BIN
complete/.gradle/1.8/taskArtifacts/fileSnapshots.bin


BIN
complete/.gradle/1.8/taskArtifacts/outputFileStates.bin


BIN
complete/.gradle/1.8/taskArtifacts/taskArtifacts.bin


+ 5 - 7
complete/build.gradle

@@ -4,19 +4,17 @@ buildscript {
         mavenLocal()
     }
     dependencies {
-        classpath("org.springframework.boot:spring-boot-gradle-plugin:0.5.0.M5")
+        classpath("org.springframework.boot:spring-boot-gradle-plugin:0.5.0.M6")
     }
 }
 
 apply plugin: 'java'
 apply plugin: 'eclipse'
 apply plugin: 'idea'
-apply plugin: 'war'
-apply plugin: 'eclipse-wtp'
 apply plugin: 'spring-boot'
 
 jar {
-    baseName = 'gs-rest-service'
+    baseName = 'gs-messaging-stomp-websocket'
     version =  '0.1.0'
 }
 
@@ -26,10 +24,10 @@ repositories {
 }
 
 dependencies {
-    compile("org.springframework.boot:spring-boot-starter-web:0.5.0.M5")
-    compile("org.springframework.boot:spring-boot-starter-websocket:0.5.0.M5")
+    compile("org.springframework.boot:spring-boot-starter-web:0.5.0.M6")
+    compile("org.springframework.boot:spring-boot-starter-websocket:0.5.0.M6")
     compile("org.springframework:spring-messaging:4.0.0.M3")
-    compile("org.projectreactor:reactor-tcp:1.0.0.BUILD-SNAPSHOT")
+    compile("org.projectreactor:reactor-tcp:1.0.0.RC1")
     compile("com.fasterxml.jackson.core:jackson-databind")
     testCompile("junit:junit:4.11")
 }

+ 15 - 2
complete/pom.xml

@@ -4,13 +4,13 @@
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>org.springframework</groupId>
-    <artifactId>gs-rest-service</artifactId>
+    <artifactId>gs-messaging-stomp-websocket</artifactId>
     <version>0.1.0</version>
 
     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
-        <version>0.5.0.M5</version>
+        <version>0.5.0.M6</version>
     </parent>
 
     <dependencies>
@@ -18,6 +18,19 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-messaging</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectreactor</groupId>
+            <artifactId>reactor-tcp</artifactId>
+            <version>1.0.0.RC1</version>
+        </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>

+ 4 - 1
complete/src/main/java/hello/GreetingController.java

@@ -18,10 +18,13 @@ public class GreetingController {
     }
     
     @MessageMapping("/app/hello")
-    public void greeting(@RequestBody HelloMessage message) throws Exception {
+    public void greeting(HelloMessage message) throws Exception {
+        System.out.println("--------> Got the message");
         Thread.sleep(3000); // simulated delay
         Greeting greeting = new Greeting("Hello, " + message.getName() + "!");
+        System.out.println("--------> Sending a response");
         messagingTemplate.convertAndSend("/queue/greetings", greeting);
+        System.out.println("--------> Sent the response");
     }
 
 }

+ 6 - 8
complete/src/main/java/hello/WebSocketConfig.java

@@ -5,22 +5,20 @@ import org.springframework.messaging.simp.config.EnableWebSocketMessageBroker;
 import org.springframework.messaging.simp.config.MessageBrokerConfigurer;
 import org.springframework.messaging.simp.config.StompEndpointRegistry;
 import org.springframework.messaging.simp.config.WebSocketMessageBrokerConfigurer;
-import org.springframework.web.socket.server.config.EnableWebSocket;
 
 @Configuration
-@EnableWebSocket
 @EnableWebSocketMessageBroker
 public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
 
-	@Override
-	public void registerStompEndpoints(StompEndpointRegistry registry) {
-		registry.addEndpoint("/hello").withSockJS();
-	}
-
 	@Override
 	public void configureMessageBroker(MessageBrokerConfigurer config) {
-		config.enableSimpleBroker("/queue/", "/topic/");
+		config.enableSimpleBroker("/queue");
 		config.setAnnotationMethodDestinationPrefixes("/app");
 	}
+	
+	@Override
+	public void registerStompEndpoints(StompEndpointRegistry registry) {
+		registry.addEndpoint("/hello").withSockJS();
+	}
 
 }