Merge branch '1.1.x'

Conflicts:
	spring-boot-dependencies/pom.xml
pull/1862/merge
Andy Wilkinson 10 years ago
commit 150b85e10d

@ -617,6 +617,26 @@ change the version properties, e.g. for a simple webapp or service:
[[howto-create-websocket-endpoints-using-serverendpoint]]
=== Create WebSocket endpoints using @ServerEndpoint
If you want to use `@ServerEndpoint` in a Spring Boot application that used an embedded
container, you must declare a single `ServerEndpointExporter` `@Bean`:
[source,java,indent=0,subs="verbatim,quotes,attributes"]
----
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
----
This bean will register any `@ServerEndpoint` annotated beans with the underlying
WebSocket container. When deployed to a standalone servlet container this role is
performed by a servlet container initializer and the `ServerEndpointExporter` bean is
not required.
[[howto-spring-mvc]] [[howto-spring-mvc]]
== Spring MVC == Spring MVC

@ -17,6 +17,7 @@
package samples.websocket.client; package samples.websocket.client;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -33,11 +34,14 @@ public class SimpleClientWebSocketHandler extends TextWebSocketHandler {
private final CountDownLatch latch; private final CountDownLatch latch;
private final AtomicReference<String> messagePayload;
@Autowired @Autowired
public SimpleClientWebSocketHandler(GreetingService greetingService, public SimpleClientWebSocketHandler(GreetingService greetingService,
CountDownLatch latch) { CountDownLatch latch, AtomicReference<String> message) {
this.greetingService = greetingService; this.greetingService = greetingService;
this.latch = latch; this.latch = latch;
this.messagePayload = message;
} }
@Override @Override
@ -51,6 +55,7 @@ public class SimpleClientWebSocketHandler extends TextWebSocketHandler {
throws Exception { throws Exception {
this.logger.info("Received: " + message + " (" + this.latch.getCount() + ")"); this.logger.info("Received: " + message + " (" + this.latch.getCount() + ")");
session.close(); session.close();
this.messagePayload.set(message.getPayload());
this.latch.countDown(); this.latch.countDown();
} }

@ -26,12 +26,14 @@ import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.PerConnectionWebSocketHandler; import org.springframework.web.socket.handler.PerConnectionWebSocketHandler;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import samples.websocket.client.GreetingService; import samples.websocket.client.GreetingService;
import samples.websocket.client.SimpleGreetingService; import samples.websocket.client.SimpleGreetingService;
import samples.websocket.echo.DefaultEchoService; import samples.websocket.echo.DefaultEchoService;
import samples.websocket.echo.EchoService; import samples.websocket.echo.EchoService;
import samples.websocket.echo.EchoWebSocketHandler; import samples.websocket.echo.EchoWebSocketHandler;
import samples.websocket.reverse.ReverseWebSocketEndpoint;
import samples.websocket.snake.SnakeWebSocketHandler; import samples.websocket.snake.SnakeWebSocketHandler;
@SpringBootApplication @SpringBootApplication
@ -74,4 +76,14 @@ public class SampleWebSocketsApplication extends SpringBootServletInitializer im
return new PerConnectionWebSocketHandler(SnakeWebSocketHandler.class); return new PerConnectionWebSocketHandler(SnakeWebSocketHandler.class);
} }
@Bean
public ReverseWebSocketEndpoint reverseWebSocketEndpoint() {
return new ReverseWebSocketEndpoint();
}
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
} }

@ -0,0 +1,33 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package samples.websocket.reverse;
import java.io.IOException;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/reverse")
public class ReverseWebSocketEndpoint {
@OnMessage
public void handleMessage(Session session, String message) throws IOException {
session.getBasicRemote().sendText(
"Reversed: " + new StringBuilder(message).reverse());
}
}

@ -25,6 +25,7 @@
<p>Please select the sample you would like to try.</p> <p>Please select the sample you would like to try.</p>
<ul> <ul>
<li><a href="./echo.html">Echo</a></li> <li><a href="./echo.html">Echo</a></li>
<li><a href="./reverse.html">Reverse</a></li>
<li><a href="./snake.html">Snake</a></li> <li><a href="./snake.html">Snake</a></li>
</ul> </ul>
</body> </body>

@ -0,0 +1,140 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Examples: Reverse</title>
<style type="text/css">
#connect-container {
float: left;
width: 400px
}
#connect-container div {
padding: 5px;
}
#console-container {
float: left;
margin-left: 15px;
width: 400px;
}
#console {
border: 1px solid #CCCCCC;
border-right-color: #999999;
border-bottom-color: #999999;
height: 170px;
overflow-y: scroll;
padding: 5px;
width: 100%;
}
#console p {
padding: 0;
margin: 0;
}
</style>
<script type="text/javascript">
var ws = null;
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('reverse').disabled = !connected;
}
function connect() {
var target = document.getElementById('target').value;
ws = new WebSocket(target);
ws.onopen = function () {
setConnected(true);
log('Info: WebSocket connection opened.');
};
ws.onmessage = function (event) {
log('Received: ' + event.data);
};
ws.onclose = function () {
setConnected(false);
log('Info: WebSocket connection closed.');
};
}
function updateTarget() {
if (window.location.protocol == 'http:') {
document.getElementById('target').value = 'ws://' + window.location.host + document.getElementById('target').value;
} else {
document.getElementById('target').value = 'wss://' + window.location.host + document.getElementById('target').value;
}
}
function disconnect() {
if (ws != null) {
ws.close();
ws = null;
}
setConnected(false);
}
function reverse() {
if (ws != null) {
var message = document.getElementById('message').value;
log('Sent: ' + message);
ws.send(message);
} else {
alert('WebSocket connection not established, please connect.');
}
}
function log(message) {
var console = document.getElementById('console');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(message));
console.appendChild(p);
while (console.childNodes.length > 25) {
console.removeChild(console.firstChild);
}
console.scrollTop = console.scrollHeight;
}
</script>
</head>
<body onload="updateTarget()">
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable
Javascript and reload this page!</h2></noscript>
<div>
<div id="connect-container">
<div>
<input id="target" type="text" size="40" style="width: 350px" value="/reverse"/>
</div>
<div>
<button id="connect" onclick="connect();">Connect</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
</div>
<div>
<textarea id="message" style="width: 350px">Here is a message!</textarea>
</div>
<div>
<button id="reverse" onclick="reverse();" disabled="disabled">Reverse message</button>
</div>
</div>
<div id="console-container">
<div id="console"></div>
</div>
</div>
</body>
</html>

@ -14,19 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
package samples.websocket.echo; package samples.websocket;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
@ -54,42 +55,64 @@ public class SampleWebSocketsApplicationTests {
private static Log logger = LogFactory.getLog(SampleWebSocketsApplicationTests.class); private static Log logger = LogFactory.getLog(SampleWebSocketsApplicationTests.class);
private static String WS_URI;
@Value("${local.server.port}") @Value("${local.server.port}")
private int port; private int port = 1234;
@Before @Test
public void init() { public void echoEndpoint() throws Exception {
WS_URI = "ws://localhost:" + this.port + "/echo/websocket"; ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties(
"websocket.uri:ws://localhost:" + this.port + "/echo/websocket")
.run("--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount();
AtomicReference<String> messagePayloadReference = context
.getBean(ClientConfiguration.class).messagePayload;
context.close();
assertEquals(0, count);
assertEquals("Did you say \"Hello world!\"?", messagePayloadReference.get());
} }
@Test @Test
public void runAndWait() throws Exception { public void reverseEndpoint() throws Exception {
ConfigurableApplicationContext context = SpringApplication.run( ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, "--spring.main.web_environment=false"); ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties("websocket.uri:ws://localhost:" + this.port + "/reverse")
.run("--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount(); long count = context.getBean(ClientConfiguration.class).latch.getCount();
AtomicReference<String> messagePayloadReference = context
.getBean(ClientConfiguration.class).messagePayload;
context.close(); context.close();
assertEquals(0, count); assertEquals(0, count);
assertEquals("Reversed: !dlrow olleH", messagePayloadReference.get());
} }
@Configuration @Configuration
static class ClientConfiguration implements CommandLineRunner { static class ClientConfiguration implements CommandLineRunner {
@Value("${websocket.uri}")
private String webSocketUri;
private final CountDownLatch latch = new CountDownLatch(1); private final CountDownLatch latch = new CountDownLatch(1);
private final AtomicReference<String> messagePayload = new AtomicReference<String>();
@Override @Override
public void run(String... args) throws Exception { public void run(String... args) throws Exception {
logger.info("Waiting for response: latch=" + this.latch.getCount()); logger.info("Waiting for response: latch=" + this.latch.getCount());
this.latch.await(10, TimeUnit.SECONDS); if (this.latch.await(10, TimeUnit.SECONDS)) {
logger.info("Got response: latch=" + this.latch.getCount()); logger.info("Got response: " + this.messagePayload.get());
}
else {
logger.info("Response not received: latch=" + this.latch.getCount());
}
} }
@Bean @Bean
public WebSocketConnectionManager wsConnectionManager() { public WebSocketConnectionManager wsConnectionManager() {
WebSocketConnectionManager manager = new WebSocketConnectionManager(client(), WebSocketConnectionManager manager = new WebSocketConnectionManager(client(),
handler(), WS_URI); handler(), this.webSocketUri);
manager.setAutoStartup(true); manager.setAutoStartup(true);
return manager; return manager;
@ -102,7 +125,8 @@ public class SampleWebSocketsApplicationTests {
@Bean @Bean
public SimpleClientWebSocketHandler handler() { public SimpleClientWebSocketHandler handler() {
return new SimpleClientWebSocketHandler(greetingService(), this.latch); return new SimpleClientWebSocketHandler(greetingService(), this.latch,
this.messagePayload);
} }
@Bean @Bean

@ -18,13 +18,16 @@ package samples.websocket.echo;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.IntegrationTest;
@ -60,8 +63,6 @@ public class CustomContainerWebSocketsApplicationTests {
private static int PORT = SocketUtils.findAvailableTcpPort(); private static int PORT = SocketUtils.findAvailableTcpPort();
private static final String WS_URI = "ws://localhost:" + PORT + "/ws/echo/websocket";
@Configuration @Configuration
protected static class CustomContainerConfiguration { protected static class CustomContainerConfiguration {
@Bean @Bean
@ -71,31 +72,59 @@ public class CustomContainerWebSocketsApplicationTests {
} }
@Test @Test
public void runAndWait() throws Exception { public void echoEndpoint() throws Exception {
ConfigurableApplicationContext context = SpringApplication.run( ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, "--spring.main.web_environment=false"); ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties("websocket.uri:ws://localhost:" + PORT + "/ws/echo/websocket")
.run("--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount(); long count = context.getBean(ClientConfiguration.class).latch.getCount();
AtomicReference<String> messagePayloadReference = context
.getBean(ClientConfiguration.class).messagePayload;
context.close(); context.close();
assertEquals(0, count); assertEquals(0, count);
assertEquals("Did you say \"Hello world!\"?", messagePayloadReference.get());
}
@Test
public void reverseEndpoint() throws Exception {
ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties("websocket.uri:ws://localhost:" + PORT + "/ws/reverse").run(
"--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount();
AtomicReference<String> messagePayloadReference = context
.getBean(ClientConfiguration.class).messagePayload;
context.close();
assertEquals(0, count);
assertEquals("Reversed: !dlrow olleH", messagePayloadReference.get());
} }
@Configuration @Configuration
static class ClientConfiguration implements CommandLineRunner { static class ClientConfiguration implements CommandLineRunner {
@Value("${websocket.uri}")
private String webSocketUri;
private final CountDownLatch latch = new CountDownLatch(1); private final CountDownLatch latch = new CountDownLatch(1);
private final AtomicReference<String> messagePayload = new AtomicReference<String>();
@Override @Override
public void run(String... args) throws Exception { public void run(String... args) throws Exception {
logger.info("Waiting for response: latch=" + this.latch.getCount()); logger.info("Waiting for response: latch=" + this.latch.getCount());
this.latch.await(10, TimeUnit.SECONDS); if (this.latch.await(10, TimeUnit.SECONDS)) {
logger.info("Got response: latch=" + this.latch.getCount()); logger.info("Got response: " + this.messagePayload.get());
}
else {
logger.info("Response not received: latch=" + this.latch.getCount());
}
} }
@Bean @Bean
public WebSocketConnectionManager wsConnectionManager() { public WebSocketConnectionManager wsConnectionManager() {
WebSocketConnectionManager manager = new WebSocketConnectionManager(client(), WebSocketConnectionManager manager = new WebSocketConnectionManager(client(),
handler(), WS_URI); handler(), this.webSocketUri);
manager.setAutoStartup(true); manager.setAutoStartup(true);
return manager; return manager;
@ -108,7 +137,8 @@ public class CustomContainerWebSocketsApplicationTests {
@Bean @Bean
public SimpleClientWebSocketHandler handler() { public SimpleClientWebSocketHandler handler() {
return new SimpleClientWebSocketHandler(greetingService(), this.latch); return new SimpleClientWebSocketHandler(greetingService(), this.latch,
this.messagePayload);
} }
@Bean @Bean

Loading…
Cancel
Save