Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
90af8bf5
Commit
90af8bf5
authored
Nov 19, 2014
by
Andy Wilkinson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add auto-configuration for Jetty 9's WebSocket support
Closes gh-1269
parent
0757d24d
Changes
35
Show whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
2128 additions
and
60 deletions
+2128
-60
pom.xml
spring-boot-autoconfigure/pom.xml
+10
-0
WebSocketAutoConfiguration.java
...t/autoconfigure/websocket/WebSocketAutoConfiguration.java
+119
-56
ConditionalOnJndiTests.java
.../boot/autoconfigure/condition/ConditionalOnJndiTests.java
+39
-0
WebSocketAutoConfigurationTests.java
...oconfigure/websocket/WebSocketAutoConfigurationTests.java
+108
-0
pom.xml
spring-boot-dependencies/pom.xml
+10
-0
spring-boot-cli.adoc
spring-boot-docs/src/main/asciidoc/spring-boot-cli.adoc
+1
-1
spring-boot-features.adoc
spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
+12
-0
using-spring-boot.adoc
spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc
+1
-1
WarPackagingTests.java
...va/org/springframework/boot/gradle/WarPackagingTests.java
+7
-1
pom.xml
spring-boot-samples/pom.xml
+2
-1
pom.xml
spring-boot-samples/spring-boot-sample-jetty8-ssl/pom.xml
+6
-0
pom.xml
spring-boot-samples/spring-boot-sample-jetty8/pom.xml
+6
-0
pom.xml
...g-boot-samples/spring-boot-sample-websocket-jetty/pom.xml
+55
-0
GreetingService.java
...c/main/java/samples/websocket/client/GreetingService.java
+23
-0
SimpleClientWebSocketHandler.java
...amples/websocket/client/SimpleClientWebSocketHandler.java
+62
-0
SimpleGreetingService.java
.../java/samples/websocket/client/SimpleGreetingService.java
+26
-0
SampleWebSocketsApplication.java
...samples/websocket/config/SampleWebSocketsApplication.java
+89
-0
DefaultEchoService.java
.../main/java/samples/websocket/echo/DefaultEchoService.java
+32
-0
EchoService.java
...tty/src/main/java/samples/websocket/echo/EchoService.java
+23
-0
EchoWebSocketHandler.java
...ain/java/samples/websocket/echo/EchoWebSocketHandler.java
+61
-0
ReverseWebSocketEndpoint.java
...a/samples/websocket/reverse/ReverseWebSocketEndpoint.java
+33
-0
Direction.java
...etty/src/main/java/samples/websocket/snake/Direction.java
+22
-0
Location.java
...jetty/src/main/java/samples/websocket/snake/Location.java
+77
-0
Snake.java
...et-jetty/src/main/java/samples/websocket/snake/Snake.java
+139
-0
SnakeTimer.java
...tty/src/main/java/samples/websocket/snake/SnakeTimer.java
+109
-0
SnakeUtils.java
...tty/src/main/java/samples/websocket/snake/SnakeUtils.java
+54
-0
SnakeWebSocketHandler.java
...n/java/samples/websocket/snake/SnakeWebSocketHandler.java
+112
-0
echo.html
...ample-websocket-jetty/src/main/resources/static/echo.html
+133
-0
index.html
...mple-websocket-jetty/src/main/resources/static/index.html
+32
-0
reverse.html
...le-websocket-jetty/src/main/resources/static/reverse.html
+140
-0
snake.html
...mple-websocket-jetty/src/main/resources/static/snake.html
+249
-0
SampleWebSocketsApplicationTests.java
...a/samples/websocket/SampleWebSocketsApplicationTests.java
+138
-0
CustomContainerWebSocketsApplicationTests.java
...ocket/echo/CustomContainerWebSocketsApplicationTests.java
+150
-0
SnakeTimerTests.java
...rc/test/java/samples/websocket/snake/SnakeTimerTests.java
+40
-0
pom.xml
spring-boot-starters/spring-boot-starter-jetty/pom.xml
+8
-0
No files found.
spring-boot-autoconfigure/pom.xml
View file @
90af8bf5
...
@@ -160,6 +160,11 @@
...
@@ -160,6 +160,11 @@
<artifactId>
jetty-webapp
</artifactId>
<artifactId>
jetty-webapp
</artifactId>
<optional>
true
</optional>
<optional>
true
</optional>
</dependency>
</dependency>
<dependency>
<groupId>
org.eclipse.jetty.websocket
</groupId>
<artifactId>
javax-websocket-server-impl
</artifactId>
<optional>
true
</optional>
</dependency>
<dependency>
<dependency>
<groupId>
io.undertow
</groupId>
<groupId>
io.undertow
</groupId>
<artifactId>
undertow-servlet
</artifactId>
<artifactId>
undertow-servlet
</artifactId>
...
@@ -403,6 +408,11 @@
...
@@ -403,6 +408,11 @@
<type>
test-jar
</type>
<type>
test-jar
</type>
<scope>
test
</scope>
<scope>
test
</scope>
</dependency>
</dependency>
<dependency>
<groupId>
org.apache.tomcat.embed
</groupId>
<artifactId>
tomcat-embed-websocket
</artifactId>
<scope>
test
</scope>
</dependency>
<dependency>
<dependency>
<groupId>
org.hsqldb
</groupId>
<groupId>
org.hsqldb
</groupId>
<artifactId>
hsqldb
</artifactId>
<artifactId>
hsqldb
</artifactId>
...
...
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfiguration.java
View file @
90af8bf5
...
@@ -22,6 +22,9 @@ import org.apache.catalina.Context;
...
@@ -22,6 +22,9 @@ import org.apache.catalina.Context;
import
org.apache.catalina.startup.Tomcat
;
import
org.apache.catalina.startup.Tomcat
;
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.eclipse.jetty.webapp.AbstractConfiguration
;
import
org.eclipse.jetty.webapp.WebAppContext
;
import
org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.boot.autoconfigure.AutoConfigureBefore
;
import
org.springframework.boot.autoconfigure.AutoConfigureBefore
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnClass
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnClass
;
...
@@ -29,55 +32,57 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
...
@@ -29,55 +32,57 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration
;
import
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration
;
import
org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer
;
import
org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer
;
import
org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer
;
import
org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer
;
import
org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory
;
import
org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer
;
import
org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer
;
import
org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory
;
import
org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory
;
import
org.springframework.boot.context.web.NonEmbeddedServletContainerFactory
;
import
org.springframework.boot.context.web.NonEmbeddedServletContainerFactory
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.util.Assert
;
import
org.springframework.util.ClassUtils
;
import
org.springframework.util.ClassUtils
;
import
org.springframework.util.ReflectionUtils
;
import
org.springframework.util.ReflectionUtils
;
import
org.springframework.web.socket.WebSocketHandler
;
import
org.springframework.web.socket.WebSocketHandler
;
/**
/**
* Auto configuration for websocket server in embedded Tomcat. If
* Auto configuration for websocket server in embedded Tomcat or Jetty. Requires
* <code>spring-websocket</code> is detected on the classpath then we add a listener that
* <code>spring-websocket</code> and either Tomcat or Jetty with their WebSocket modules
* to be on the classpath.
* <p/>
* If Tomcat's WebSocket support is detected on the classpath we add a listener that
* installs the Tomcat Websocket initializer. In a non-embedded container it should
* installs the Tomcat Websocket initializer. In a non-embedded container it should
* already be there.
* already be there.
* <p/>
* If Jetty's WebSocket support is detected on the classpath we add a configuration that
* configures the context with WebSocket support. In a non-embedded container it should
* already be there.
*
*
* @author Dave Syer
* @author Dave Syer
* @author Phillip Webb
* @author Phillip Webb
* @author Andy Wilkinson
*/
*/
@Configuration
@Configuration
@ConditionalOnClass
(
name
=
"org.apache.tomcat.websocket.server.WsSci"
,
value
=
{
@ConditionalOnClass
({
Servlet
.
class
,
WebSocketHandler
.
class
})
Servlet
.
class
,
Tomcat
.
class
,
WebSocketHandler
.
class
})
@AutoConfigureBefore
(
EmbeddedServletContainerAutoConfiguration
.
class
)
@AutoConfigureBefore
(
EmbeddedServletContainerAutoConfiguration
.
class
)
public
class
WebSocketAutoConfiguration
{
public
class
WebSocketAutoConfiguration
{
@Configuration
@ConditionalOnClass
(
name
=
"org.apache.tomcat.websocket.server.WsSci"
,
value
=
Tomcat
.
class
)
static
class
TomcatWebSocketConfiguration
{
private
static
final
String
TOMCAT_7_LISTENER_TYPE
=
"org.apache.catalina.deploy.ApplicationListener"
;
private
static
final
String
TOMCAT_7_LISTENER_TYPE
=
"org.apache.catalina.deploy.ApplicationListener"
;
private
static
final
String
TOMCAT_8_LISTENER_TYPE
=
"org.apache.tomcat.util.descriptor.web.ApplicationListener"
;
private
static
final
String
TOMCAT_8_LISTENER_TYPE
=
"org.apache.tomcat.util.descriptor.web.ApplicationListener"
;
private
static
final
String
WS_LISTENER
=
"org.apache.tomcat.websocket.server.WsContextListener"
;
private
static
final
String
WS_LISTENER
=
"org.apache.tomcat.websocket.server.WsContextListener"
;
private
static
Log
logger
=
LogFactory
.
getLog
(
WebSocketAutoConfiguration
.
class
);
@Bean
@Bean
@ConditionalOnMissingBean
(
name
=
"websocketContainerCustomizer"
)
@ConditionalOnMissingBean
(
name
=
"websocketContainerCustomizer"
)
public
EmbeddedServletContainerCustomizer
websocketContainerCustomizer
()
{
public
EmbeddedServletContainerCustomizer
websocketContainerCustomizer
()
{
return
new
EmbeddedServletContainerCustomizer
()
{
return
new
WebSocketContainerCustomizer
<
TomcatEmbeddedServletContainerFactory
>(
TomcatEmbeddedServletContainerFactory
.
class
)
{
@Override
@Override
public
void
customize
(
ConfigurableEmbeddedServletContainer
container
)
{
public
void
doCustomize
(
if
(
container
instanceof
NonEmbeddedServletContainerFactory
)
{
TomcatEmbeddedServletContainerFactory
tomcatContainer
)
{
logger
.
info
(
"NonEmbeddedServletContainerFactory detected. Websockets "
+
"support should be native so this normally is not a problem."
);
return
;
}
Assert
.
state
(
container
instanceof
TomcatEmbeddedServletContainerFactory
,
"Websockets are currently only supported in Tomcat (found "
+
container
.
getClass
()
+
"). "
);
TomcatEmbeddedServletContainerFactory
tomcatContainer
=
(
TomcatEmbeddedServletContainerFactory
)
container
;
tomcatContainer
.
addContextCustomizers
(
new
TomcatContextCustomizer
()
{
tomcatContainer
.
addContextCustomizers
(
new
TomcatContextCustomizer
()
{
@Override
@Override
public
void
customize
(
Context
context
)
{
public
void
customize
(
Context
context
)
{
...
@@ -101,10 +106,10 @@ public class WebSocketAutoConfiguration {
...
@@ -101,10 +106,10 @@ public class WebSocketAutoConfiguration {
}
}
/**
/**
* Instead of registering the WsSci directly as a ServletContainerInitializer, we us
e
* Instead of registering the WsSci directly as a ServletContainerInitializer, w
e
* the ApplicationListener provided by Tomcat. Unfortunately the ApplicationListener
* use the ApplicationListener provided by Tomcat. Unfortunately the
* class moved packages in Tomcat 8 and been deleted in 8.0.8 so we have to use
* ApplicationListener class moved packages in Tomcat 8 and been deleted in 8.0.8
*
reflection.
* so we have to use
reflection.
* @param context the current context
* @param context the current context
* @param listenerType the type of listener to add
* @param listenerType the type of listener to add
*/
*/
...
@@ -115,11 +120,69 @@ public class WebSocketAutoConfiguration {
...
@@ -115,11 +120,69 @@ public class WebSocketAutoConfiguration {
}
}
else
{
else
{
Object
instance
=
BeanUtils
.
instantiateClass
(
Object
instance
=
BeanUtils
.
instantiateClass
(
ClassUtils
ClassUtils
.
getConstructorIfAvailable
(
listenerType
,
String
.
class
,
.
getConstructorIfAvailable
(
listenerType
,
String
.
class
,
boolean
.
class
),
WS_LISTENER
,
false
);
boolean
.
class
),
WS_LISTENER
,
false
);
ReflectionUtils
.
invokeMethod
(
ClassUtils
.
getMethod
(
context
.
getClass
(),
ReflectionUtils
.
invokeMethod
(
ClassUtils
.
getMethod
(
context
.
getClass
(),
"addApplicationListener"
,
listenerType
),
context
,
instance
);
"addApplicationListener"
,
listenerType
),
context
,
instance
);
}
}
}
}
}
@Configuration
@ConditionalOnClass
(
WebSocketServerContainerInitializer
.
class
)
static
class
JettyWebSocketConfiguration
{
@Bean
@ConditionalOnMissingBean
(
name
=
"websocketContainerCustomizer"
)
public
EmbeddedServletContainerCustomizer
websocketContainerCustomizer
()
{
return
new
WebSocketContainerCustomizer
<
JettyEmbeddedServletContainerFactory
>(
JettyEmbeddedServletContainerFactory
.
class
)
{
@Override
protected
void
doCustomize
(
JettyEmbeddedServletContainerFactory
container
)
{
container
.
addConfigurations
(
new
AbstractConfiguration
()
{
@Override
public
void
configure
(
WebAppContext
context
)
throws
Exception
{
WebSocketServerContainerInitializer
.
configureContext
(
context
);
}
});
}
};
}
}
abstract
static
class
WebSocketContainerCustomizer
<
T
extends
ConfigurableEmbeddedServletContainer
>
implements
EmbeddedServletContainerCustomizer
{
private
Log
logger
=
LogFactory
.
getLog
(
getClass
());
private
final
Class
<
T
>
containerType
;
protected
WebSocketContainerCustomizer
(
Class
<
T
>
containerType
)
{
this
.
containerType
=
containerType
;
}
@SuppressWarnings
(
"unchecked"
)
@Override
public
void
customize
(
ConfigurableEmbeddedServletContainer
container
)
{
if
(
container
instanceof
NonEmbeddedServletContainerFactory
)
{
this
.
logger
.
info
(
"NonEmbeddedServletContainerFactory detected. Websockets "
+
"support should be native so this normally is not a problem."
);
return
;
}
if
(
this
.
containerType
.
isAssignableFrom
(
container
.
getClass
()))
{
doCustomize
((
T
)
container
);
}
}
protected
abstract
void
doCustomize
(
T
container
);
}
}
}
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJndiTests.java
View file @
90af8bf5
...
@@ -16,6 +16,10 @@
...
@@ -16,6 +16,10 @@
package
org
.
springframework
.
boot
.
autoconfigure
.
condition
;
package
org
.
springframework
.
boot
.
autoconfigure
.
condition
;
import
java.io.IOException
;
import
java.net.URL
;
import
java.util.Collections
;
import
java.util.Enumeration
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.Hashtable
;
import
java.util.Hashtable
;
import
java.util.Map
;
import
java.util.Map
;
...
@@ -27,6 +31,7 @@ import javax.naming.spi.InitialContextFactory;
...
@@ -27,6 +31,7 @@ import javax.naming.spi.InitialContextFactory;
import
org.hamcrest.Matcher
;
import
org.hamcrest.Matcher
;
import
org.junit.After
;
import
org.junit.After
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.junit.Test
;
import
org.springframework.boot.test.EnvironmentTestUtils
;
import
org.springframework.boot.test.EnvironmentTestUtils
;
import
org.springframework.context.ConfigurableApplicationContext
;
import
org.springframework.context.ConfigurableApplicationContext
;
...
@@ -47,15 +52,25 @@ import static org.mockito.Mockito.mock;
...
@@ -47,15 +52,25 @@ import static org.mockito.Mockito.mock;
*
*
* @author Stephane Nicoll
* @author Stephane Nicoll
* @author Phillip Webb
* @author Phillip Webb
* @author Andy Wilkinson
*/
*/
public
class
ConditionalOnJndiTests
{
public
class
ConditionalOnJndiTests
{
private
ClassLoader
threadContextClassLoader
;
private
String
initialContextFactory
;
private
String
initialContextFactory
;
private
ConfigurableApplicationContext
context
;
private
ConfigurableApplicationContext
context
;
private
MockableOnJndi
condition
=
new
MockableOnJndi
();
private
MockableOnJndi
condition
=
new
MockableOnJndi
();
@Before
public
void
setupThreadContextClassLoader
()
{
this
.
threadContextClassLoader
=
Thread
.
currentThread
().
getContextClassLoader
();
Thread
.
currentThread
().
setContextClassLoader
(
new
JndiPropertiesHidingClassLoader
(
getClass
().
getClassLoader
()));
}
@After
@After
public
void
close
()
{
public
void
close
()
{
TestableInitialContextFactory
.
clearAll
();
TestableInitialContextFactory
.
clearAll
();
...
@@ -69,6 +84,7 @@ public class ConditionalOnJndiTests {
...
@@ -69,6 +84,7 @@ public class ConditionalOnJndiTests {
if
(
this
.
context
!=
null
)
{
if
(
this
.
context
!=
null
)
{
this
.
context
.
close
();
this
.
context
.
close
();
}
}
Thread
.
currentThread
().
setContextClassLoader
(
this
.
threadContextClassLoader
);
}
}
@Test
@Test
...
@@ -255,4 +271,27 @@ public class ConditionalOnJndiTests {
...
@@ -255,4 +271,27 @@ public class ConditionalOnJndiTests {
}
}
}
}
/**
* Used as the thread context classloader to prevent jndi.properties resources found
* on the classpath from triggering configuration of an InitialContextFactory that is
* outside the control of these tests.
*/
private
static
class
JndiPropertiesHidingClassLoader
extends
ClassLoader
{
public
JndiPropertiesHidingClassLoader
(
ClassLoader
parent
)
{
super
(
parent
);
}
@Override
public
Enumeration
<
URL
>
getResources
(
String
name
)
throws
IOException
{
if
(
"jndi.properties"
.
equals
(
name
))
{
return
Collections
.
enumeration
(
Collections
.<
URL
>
emptyList
());
}
else
{
return
super
.
getResources
(
name
);
}
}
}
}
}
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfigurationTests.java
0 → 100644
View file @
90af8bf5
/*
* 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
org
.
springframework
.
boot
.
autoconfigure
.
websocket
;
import
javax.websocket.server.ServerContainer
;
import
org.junit.After
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
;
import
org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor
;
import
org.springframework.boot.context.embedded.EmbeddedServletContainerFactory
;
import
org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory
;
import
org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
static
org
.
hamcrest
.
Matchers
.
instanceOf
;
import
static
org
.
hamcrest
.
Matchers
.
is
;
import
static
org
.
junit
.
Assert
.
assertThat
;
/**
* Tests for {@link WebSocketAutoConfiguration}
*
* @author Andy Wilkinson
*/
public
class
WebSocketAutoConfigurationTests
{
private
AnnotationConfigEmbeddedWebApplicationContext
context
;
@Before
public
void
createContext
()
{
this
.
context
=
new
AnnotationConfigEmbeddedWebApplicationContext
();
}
@After
public
void
close
()
{
if
(
this
.
context
!=
null
)
{
this
.
context
.
close
();
}
}
@Test
public
void
tomcatServerContainerIsAvailableFromTheServletContext
()
{
serverContainerIsAvailableFromTheServletContext
(
TomcatConfiguration
.
class
,
WebSocketAutoConfiguration
.
TomcatWebSocketConfiguration
.
class
);
}
@Test
public
void
jettyServerContainerIsAvailableFromTheServletContext
()
{
serverContainerIsAvailableFromTheServletContext
(
JettyConfiguration
.
class
,
WebSocketAutoConfiguration
.
JettyWebSocketConfiguration
.
class
);
}
private
void
serverContainerIsAvailableFromTheServletContext
(
Class
<?>...
configuration
)
{
this
.
context
.
register
(
configuration
);
this
.
context
.
refresh
();
Object
serverContainer
=
this
.
context
.
getServletContext
().
getAttribute
(
"javax.websocket.server.ServerContainer"
);
assertThat
(
serverContainer
,
is
(
instanceOf
(
ServerContainer
.
class
)));
}
static
class
CommonConfiguration
{
@Bean
public
EmbeddedServletContainerCustomizerBeanPostProcessor
embeddedServletContainerCustomizerBeanPostProcessor
()
{
return
new
EmbeddedServletContainerCustomizerBeanPostProcessor
();
}
}
@Configuration
static
class
TomcatConfiguration
extends
CommonConfiguration
{
@Bean
public
EmbeddedServletContainerFactory
servletContainerFactory
()
{
return
new
TomcatEmbeddedServletContainerFactory
();
}
}
@Configuration
static
class
JettyConfiguration
extends
CommonConfiguration
{
@Bean
public
EmbeddedServletContainerFactory
servletContainerFactory
()
{
return
new
JettyEmbeddedServletContainerFactory
();
}
}
}
spring-boot-dependencies/pom.xml
View file @
90af8bf5
...
@@ -925,6 +925,16 @@
...
@@ -925,6 +925,16 @@
<artifactId>
jetty-webapp
</artifactId>
<artifactId>
jetty-webapp
</artifactId>
<version>
${jetty.version}
</version>
<version>
${jetty.version}
</version>
</dependency>
</dependency>
<dependency>
<groupId>
org.eclipse.jetty.websocket
</groupId>
<artifactId>
javax-websocket-server-impl
</artifactId>
<version>
${jetty.version}
</version>
</dependency>
<dependency>
<groupId>
org.eclipse.jetty.websocket
</groupId>
<artifactId>
websocket-server
</artifactId>
<version>
${jetty.version}
</version>
</dependency>
<dependency>
<dependency>
<groupId>
org.flywaydb
</groupId>
<groupId>
org.flywaydb
</groupId>
<artifactId>
flyway-core
</artifactId>
<artifactId>
flyway-core
</artifactId>
...
...
spring-boot-docs/src/main/asciidoc/spring-boot-cli.adoc
View file @
90af8bf5
...
@@ -381,7 +381,7 @@ capabilities of the service using the `--list` flag
...
@@ -381,7 +381,7 @@ capabilities of the service using the `--list` flag
actuator - Actuator: Production ready features to help you monitor and manage your application
actuator - Actuator: Production ready features to help you monitor and manage your application
...
...
web - Web: Support for full-stack web development, including Tomcat and spring-webmvc
web - Web: Support for full-stack web development, including Tomcat and spring-webmvc
websocket - Websocket: Support for
websocket development with Tomca
t
websocket - Websocket: Support for
WebSocket developmen
t
ws - WS: Support for Spring Web Services
ws - WS: Support for Spring Web Services
Available project types:
Available project types:
...
...
spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
View file @
90af8bf5
...
@@ -2550,6 +2550,18 @@ result of a {spring-reference}/#expressions[SpEL expression].
...
@@ -2550,6 +2550,18 @@ result of a {spring-reference}/#expressions[SpEL expression].
[[boot-features-websockets]]
== WebSockets
Spring Boot provides WebSockets auto-configuration for embedded Tomcat (7 and 8) and
embedded Jetty 9. If you're deploying a war file to a standalone container, Spring Boot
assumes that the container will be responsible for the configuration of its WebSocket
support.
Spring Framework provides {spring-reference}/#websocket[rich WebSocket support] that can
be easily accessed via the `spring-boot-starter-websocket` module.
[[boot-features-whats-next]]
[[boot-features-whats-next]]
== What to read next
== What to read next
If you want to learn more about any of the classes discussed in this section you can
If you want to learn more about any of the classes discussed in this section you can
...
...
spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc
View file @
90af8bf5
...
@@ -310,7 +310,7 @@ and Hibernate.
...
@@ -310,7 +310,7 @@ and Hibernate.
|Support for full-stack web development, including Tomcat and `spring-webmvc`.
|Support for full-stack web development, including Tomcat and `spring-webmvc`.
|`spring-boot-starter-websocket`
|`spring-boot-starter-websocket`
|Support for
websocket development with Tomca
t.
|Support for
WebSocket developmen
t.
|`spring-boot-starter-ws`
|`spring-boot-starter-ws`
|Support for Spring Web Services
|Support for Spring Web Services
...
...
spring-boot-integration-tests/src/test/java/org/springframework/boot/gradle/WarPackagingTests.java
View file @
90af8bf5
...
@@ -57,7 +57,13 @@ public class WarPackagingTests {
...
@@ -57,7 +57,13 @@ public class WarPackagingTests {
"jetty-server-"
,
"jetty-security-"
,
"jetty-servlet-"
,
"jetty-server-"
,
"jetty-security-"
,
"jetty-servlet-"
,
"jetty-webapp-"
,
"javax.servlet.jsp-2"
,
"javax.servlet.jsp-api-"
,
"jetty-webapp-"
,
"javax.servlet.jsp-2"
,
"javax.servlet.jsp-api-"
,
"javax.servlet.jsp.jstl-1.2.2"
,
"javax.servlet.jsp.jstl-1.2.0"
,
"javax.servlet.jsp.jstl-1.2.2"
,
"javax.servlet.jsp.jstl-1.2.0"
,
"javax.el-"
,
"org.eclipse.jdt.core-"
,
"jetty-jsp-"
));
"javax.el-"
,
"org.eclipse.jdt.core-"
,
"jetty-jsp-"
,
"websocket-api"
,
"javax.annotation-api"
,
"jetty-plus"
,
"javax-websocket-server-impl-"
,
"asm-"
,
"javax.websocket-api-"
,
"asm-tree-"
,
"asm-commons-"
,
"websocket-common-"
,
"jetty-annotations-"
,
"javax-websocket-client-impl-"
,
"websocket-client-"
,
"websocket-server-"
,
"jetty-jndi-"
,
"jetty-xml-"
,
"websocket-servlet-"
));
private
static
final
String
BOOT_VERSION
=
ManagedDependencies
.
get
()
private
static
final
String
BOOT_VERSION
=
ManagedDependencies
.
get
()
.
find
(
"spring-boot"
).
getVersion
();
.
find
(
"spring-boot"
).
getVersion
();
...
...
spring-boot-samples/pom.xml
View file @
90af8bf5
...
@@ -73,6 +73,7 @@
...
@@ -73,6 +73,7 @@
<module>
spring-boot-sample-web-ui
</module>
<module>
spring-boot-sample-web-ui
</module>
<module>
spring-boot-sample-web-velocity
</module>
<module>
spring-boot-sample-web-velocity
</module>
<module>
spring-boot-sample-websocket
</module>
<module>
spring-boot-sample-websocket
</module>
<module>
spring-boot-sample-websocket-jetty
</module>
<module>
spring-boot-sample-ws
</module>
<module>
spring-boot-sample-ws
</module>
<module>
spring-boot-sample-xml
</module>
<module>
spring-boot-sample-xml
</module>
</modules>
</modules>
...
...
spring-boot-samples/spring-boot-sample-jetty8-ssl/pom.xml
View file @
90af8bf5
...
@@ -29,6 +29,12 @@
...
@@ -29,6 +29,12 @@
<dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-jetty
</artifactId>
<artifactId>
spring-boot-starter-jetty
</artifactId>
<exclusions>
<exclusion>
<groupId>
org.eclipse.jetty.websocket
</groupId>
<artifactId>
*
</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>
org.springframework
</groupId>
<groupId>
org.springframework
</groupId>
...
...
spring-boot-samples/spring-boot-sample-jetty8/pom.xml
View file @
90af8bf5
...
@@ -29,6 +29,12 @@
...
@@ -29,6 +29,12 @@
<dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-jetty
</artifactId>
<artifactId>
spring-boot-starter-jetty
</artifactId>
<exclusions>
<exclusion>
<groupId>
org.eclipse.jetty.websocket
</groupId>
<artifactId>
*
</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>
org.springframework
</groupId>
<groupId>
org.springframework
</groupId>
...
...
spring-boot-samples/spring-boot-sample-websocket-jetty/pom.xml
0 → 100755
View file @
90af8bf5
<?xml version="1.0" encoding="UTF-8"?>
<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>
<artifactId>
spring-boot-sample-websocket-jetty
</artifactId>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-samples
</artifactId>
<version>
1.2.0.BUILD-SNAPSHOT
</version>
</parent>
<name>
Spring Boot WebSocket Jetty Sample
</name>
<description>
Spring Boot WebSocket Jetty Sample
</description>
<url>
http://projects.spring.io/spring-boot/
</url>
<organization>
<name>
Pivotal Software, Inc.
</name>
<url>
http://www.spring.io
</url>
</organization>
<properties>
<main.basedir>
${basedir}/../..
</main.basedir>
<java.version>
1.7
</java.version>
</properties>
<dependencies>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-websocket
</artifactId>
<exclusions>
<exclusion>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-tomcat
</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-jetty
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-actuator
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-test
</artifactId>
<scope>
test
</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
</plugins>
</build>
</project>
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/GreetingService.java
0 → 100644
View file @
90af8bf5
/*
* Copyright 2012-2013 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
.
client
;
public
interface
GreetingService
{
String
getGreeting
();
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/SimpleClientWebSocketHandler.java
0 → 100644
View file @
90af8bf5
/*
* Copyright 2012-2013 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
.
client
;
import
java.util.concurrent.CountDownLatch
;
import
java.util.concurrent.atomic.AtomicReference
;
import
org.apache.commons.logging.Log
;
import
org.apache.commons.logging.LogFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.socket.TextMessage
;
import
org.springframework.web.socket.WebSocketSession
;
import
org.springframework.web.socket.handler.TextWebSocketHandler
;
public
class
SimpleClientWebSocketHandler
extends
TextWebSocketHandler
{
protected
Log
logger
=
LogFactory
.
getLog
(
SimpleClientWebSocketHandler
.
class
);
private
final
GreetingService
greetingService
;
private
final
CountDownLatch
latch
;
private
final
AtomicReference
<
String
>
messagePayload
;
@Autowired
public
SimpleClientWebSocketHandler
(
GreetingService
greetingService
,
CountDownLatch
latch
,
AtomicReference
<
String
>
message
)
{
this
.
greetingService
=
greetingService
;
this
.
latch
=
latch
;
this
.
messagePayload
=
message
;
}
@Override
public
void
afterConnectionEstablished
(
WebSocketSession
session
)
throws
Exception
{
TextMessage
message
=
new
TextMessage
(
this
.
greetingService
.
getGreeting
());
session
.
sendMessage
(
message
);
}
@Override
public
void
handleTextMessage
(
WebSocketSession
session
,
TextMessage
message
)
throws
Exception
{
this
.
logger
.
info
(
"Received: "
+
message
+
" ("
+
this
.
latch
.
getCount
()
+
")"
);
session
.
close
();
this
.
messagePayload
.
set
(
message
.
getPayload
());
this
.
latch
.
countDown
();
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/SimpleGreetingService.java
0 → 100644
View file @
90af8bf5
/*
* Copyright 2012-2013 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
.
client
;
public
class
SimpleGreetingService
implements
GreetingService
{
@Override
public
String
getGreeting
()
{
return
"Hello world!"
;
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/config/SampleWebSocketsApplication.java
0 → 100644
View file @
90af8bf5
/*
* Copyright 2012-2013 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
.
config
;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.boot.builder.SpringApplicationBuilder
;
import
org.springframework.boot.context.web.SpringBootServletInitializer
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.web.socket.WebSocketHandler
;
import
org.springframework.web.socket.config.annotation.EnableWebSocket
;
import
org.springframework.web.socket.config.annotation.WebSocketConfigurer
;
import
org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry
;
import
org.springframework.web.socket.handler.PerConnectionWebSocketHandler
;
import
org.springframework.web.socket.server.standard.ServerEndpointExporter
;
import
samples.websocket.client.GreetingService
;
import
samples.websocket.client.SimpleGreetingService
;
import
samples.websocket.echo.DefaultEchoService
;
import
samples.websocket.echo.EchoService
;
import
samples.websocket.echo.EchoWebSocketHandler
;
import
samples.websocket.reverse.ReverseWebSocketEndpoint
;
import
samples.websocket.snake.SnakeWebSocketHandler
;
@SpringBootApplication
@EnableWebSocket
public
class
SampleWebSocketsApplication
extends
SpringBootServletInitializer
implements
WebSocketConfigurer
{
@Override
public
void
registerWebSocketHandlers
(
WebSocketHandlerRegistry
registry
)
{
registry
.
addHandler
(
echoWebSocketHandler
(),
"/echo"
).
withSockJS
();
registry
.
addHandler
(
snakeWebSocketHandler
(),
"/snake"
).
withSockJS
();
}
@Override
protected
SpringApplicationBuilder
configure
(
SpringApplicationBuilder
application
)
{
return
application
.
sources
(
SampleWebSocketsApplication
.
class
);
}
public
static
void
main
(
String
[]
args
)
{
SpringApplication
.
run
(
SampleWebSocketsApplication
.
class
,
args
);
}
@Bean
public
EchoService
echoService
()
{
return
new
DefaultEchoService
(
"Did you say \"%s\"?"
);
}
@Bean
public
GreetingService
greetingService
()
{
return
new
SimpleGreetingService
();
}
@Bean
public
WebSocketHandler
echoWebSocketHandler
()
{
return
new
EchoWebSocketHandler
(
echoService
());
}
@Bean
public
WebSocketHandler
snakeWebSocketHandler
()
{
return
new
PerConnectionWebSocketHandler
(
SnakeWebSocketHandler
.
class
);
}
@Bean
public
ReverseWebSocketEndpoint
reverseWebSocketEndpoint
()
{
return
new
ReverseWebSocketEndpoint
();
}
@Bean
public
ServerEndpointExporter
serverEndpointExporter
()
{
return
new
ServerEndpointExporter
();
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/DefaultEchoService.java
0 → 100644
View file @
90af8bf5
/*
* Copyright 2012-2013 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
.
echo
;
public
class
DefaultEchoService
implements
EchoService
{
private
final
String
echoFormat
;
public
DefaultEchoService
(
String
echoFormat
)
{
this
.
echoFormat
=
(
echoFormat
!=
null
)
?
echoFormat
:
"%s"
;
}
@Override
public
String
getMessage
(
String
message
)
{
return
String
.
format
(
this
.
echoFormat
,
message
);
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/EchoService.java
0 → 100644
View file @
90af8bf5
/*
* Copyright 2012-2013 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
.
echo
;
public
interface
EchoService
{
String
getMessage
(
String
message
);
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/EchoWebSocketHandler.java
0 → 100644
View file @
90af8bf5
/*
* Copyright 2012-2013 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
.
echo
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.socket.CloseStatus
;
import
org.springframework.web.socket.TextMessage
;
import
org.springframework.web.socket.WebSocketHandler
;
import
org.springframework.web.socket.WebSocketSession
;
import
org.springframework.web.socket.handler.TextWebSocketHandler
;
/**
* Echo messages by implementing a Spring {@link WebSocketHandler} abstraction.
*/
public
class
EchoWebSocketHandler
extends
TextWebSocketHandler
{
private
static
Logger
logger
=
LoggerFactory
.
getLogger
(
EchoWebSocketHandler
.
class
);
private
final
EchoService
echoService
;
@Autowired
public
EchoWebSocketHandler
(
EchoService
echoService
)
{
this
.
echoService
=
echoService
;
}
@Override
public
void
afterConnectionEstablished
(
WebSocketSession
session
)
{
logger
.
debug
(
"Opened new session in instance "
+
this
);
}
@Override
public
void
handleTextMessage
(
WebSocketSession
session
,
TextMessage
message
)
throws
Exception
{
String
echoMessage
=
this
.
echoService
.
getMessage
(
message
.
getPayload
());
logger
.
debug
(
echoMessage
);
session
.
sendMessage
(
new
TextMessage
(
echoMessage
));
}
@Override
public
void
handleTransportError
(
WebSocketSession
session
,
Throwable
exception
)
throws
Exception
{
session
.
close
(
CloseStatus
.
SERVER_ERROR
);
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/reverse/ReverseWebSocketEndpoint.java
0 → 100644
View file @
90af8bf5
/*
* 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
());
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/snake/Direction.java
0 → 100644
View file @
90af8bf5
/*
* 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.
*/
package
samples
.
websocket
.
snake
;
public
enum
Direction
{
NONE
,
NORTH
,
SOUTH
,
EAST
,
WEST
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/snake/Location.java
0 → 100644
View file @
90af8bf5
/*
* 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.
*/
package
samples
.
websocket
.
snake
;
public
class
Location
{
public
int
x
;
public
int
y
;
public
static
final
int
GRID_SIZE
=
10
;
public
static
final
int
PLAYFIELD_HEIGHT
=
480
;
public
static
final
int
PLAYFIELD_WIDTH
=
640
;
public
Location
(
int
x
,
int
y
)
{
this
.
x
=
x
;
this
.
y
=
y
;
}
public
Location
getAdjacentLocation
(
Direction
direction
)
{
switch
(
direction
)
{
case
NORTH:
return
new
Location
(
this
.
x
,
this
.
y
-
Location
.
GRID_SIZE
);
case
SOUTH:
return
new
Location
(
this
.
x
,
this
.
y
+
Location
.
GRID_SIZE
);
case
EAST:
return
new
Location
(
this
.
x
+
Location
.
GRID_SIZE
,
this
.
y
);
case
WEST:
return
new
Location
(
this
.
x
-
Location
.
GRID_SIZE
,
this
.
y
);
case
NONE:
// fall through
default
:
return
this
;
}
}
@Override
public
boolean
equals
(
Object
o
)
{
if
(
this
==
o
)
{
return
true
;
}
if
(
o
==
null
||
getClass
()
!=
o
.
getClass
())
{
return
false
;
}
Location
location
=
(
Location
)
o
;
if
(
this
.
x
!=
location
.
x
)
{
return
false
;
}
if
(
this
.
y
!=
location
.
y
)
{
return
false
;
}
return
true
;
}
@Override
public
int
hashCode
()
{
int
result
=
this
.
x
;
result
=
31
*
result
+
this
.
y
;
return
result
;
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/snake/Snake.java
0 → 100644
View file @
90af8bf5
/*
* 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.
*/
package
samples
.
websocket
.
snake
;
import
java.util.ArrayDeque
;
import
java.util.Collection
;
import
java.util.Deque
;
import
org.springframework.web.socket.TextMessage
;
import
org.springframework.web.socket.WebSocketSession
;
public
class
Snake
{
private
static
final
int
DEFAULT_LENGTH
=
5
;
private
final
int
id
;
private
final
WebSocketSession
session
;
private
Direction
direction
;
private
int
length
=
DEFAULT_LENGTH
;
private
Location
head
;
private
final
Deque
<
Location
>
tail
=
new
ArrayDeque
<
Location
>();
private
final
String
hexColor
;
public
Snake
(
int
id
,
WebSocketSession
session
)
{
this
.
id
=
id
;
this
.
session
=
session
;
this
.
hexColor
=
SnakeUtils
.
getRandomHexColor
();
resetState
();
}
private
void
resetState
()
{
this
.
direction
=
Direction
.
NONE
;
this
.
head
=
SnakeUtils
.
getRandomLocation
();
this
.
tail
.
clear
();
this
.
length
=
DEFAULT_LENGTH
;
}
private
synchronized
void
kill
()
throws
Exception
{
resetState
();
sendMessage
(
"{'type': 'dead'}"
);
}
private
synchronized
void
reward
()
throws
Exception
{
this
.
length
++;
sendMessage
(
"{'type': 'kill'}"
);
}
protected
void
sendMessage
(
String
msg
)
throws
Exception
{
this
.
session
.
sendMessage
(
new
TextMessage
(
msg
));
}
public
synchronized
void
update
(
Collection
<
Snake
>
snakes
)
throws
Exception
{
Location
nextLocation
=
this
.
head
.
getAdjacentLocation
(
this
.
direction
);
if
(
nextLocation
.
x
>=
SnakeUtils
.
PLAYFIELD_WIDTH
)
{
nextLocation
.
x
=
0
;
}
if
(
nextLocation
.
y
>=
SnakeUtils
.
PLAYFIELD_HEIGHT
)
{
nextLocation
.
y
=
0
;
}
if
(
nextLocation
.
x
<
0
)
{
nextLocation
.
x
=
SnakeUtils
.
PLAYFIELD_WIDTH
;
}
if
(
nextLocation
.
y
<
0
)
{
nextLocation
.
y
=
SnakeUtils
.
PLAYFIELD_HEIGHT
;
}
if
(
this
.
direction
!=
Direction
.
NONE
)
{
this
.
tail
.
addFirst
(
this
.
head
);
if
(
this
.
tail
.
size
()
>
this
.
length
)
{
this
.
tail
.
removeLast
();
}
this
.
head
=
nextLocation
;
}
handleCollisions
(
snakes
);
}
private
void
handleCollisions
(
Collection
<
Snake
>
snakes
)
throws
Exception
{
for
(
Snake
snake
:
snakes
)
{
boolean
headCollision
=
this
.
id
!=
snake
.
id
&&
snake
.
getHead
().
equals
(
this
.
head
);
boolean
tailCollision
=
snake
.
getTail
().
contains
(
this
.
head
);
if
(
headCollision
||
tailCollision
)
{
kill
();
if
(
this
.
id
!=
snake
.
id
)
{
snake
.
reward
();
}
}
}
}
public
synchronized
Location
getHead
()
{
return
this
.
head
;
}
public
synchronized
Collection
<
Location
>
getTail
()
{
return
this
.
tail
;
}
public
synchronized
void
setDirection
(
Direction
direction
)
{
this
.
direction
=
direction
;
}
public
synchronized
String
getLocationsJson
()
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
String
.
format
(
"{x: %d, y: %d}"
,
Integer
.
valueOf
(
this
.
head
.
x
),
Integer
.
valueOf
(
this
.
head
.
y
)));
for
(
Location
location
:
this
.
tail
)
{
sb
.
append
(
','
);
sb
.
append
(
String
.
format
(
"{x: %d, y: %d}"
,
Integer
.
valueOf
(
location
.
x
),
Integer
.
valueOf
(
location
.
y
)));
}
return
String
.
format
(
"{'id':%d,'body':[%s]}"
,
Integer
.
valueOf
(
this
.
id
),
sb
.
toString
());
}
public
int
getId
()
{
return
this
.
id
;
}
public
String
getHexColor
()
{
return
this
.
hexColor
;
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/snake/SnakeTimer.java
0 → 100644
View file @
90af8bf5
/*
* 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.
*/
package
samples
.
websocket
.
snake
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Iterator
;
import
java.util.Timer
;
import
java.util.TimerTask
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.CopyOnWriteArrayList
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
/**
* Sets up the timer for the multi-player snake game WebSocket example.
*/
public
class
SnakeTimer
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
SnakeTimer
.
class
);
private
static
Timer
gameTimer
=
null
;
private
static
final
long
TICK_DELAY
=
100
;
private
static
final
ConcurrentHashMap
<
Integer
,
Snake
>
snakes
=
new
ConcurrentHashMap
<
Integer
,
Snake
>();
public
static
synchronized
void
addSnake
(
Snake
snake
)
{
if
(
snakes
.
size
()
==
0
)
{
startTimer
();
}
snakes
.
put
(
Integer
.
valueOf
(
snake
.
getId
()),
snake
);
}
public
static
Collection
<
Snake
>
getSnakes
()
{
return
Collections
.
unmodifiableCollection
(
snakes
.
values
());
}
public
static
synchronized
void
removeSnake
(
Snake
snake
)
{
snakes
.
remove
(
Integer
.
valueOf
(
snake
.
getId
()));
if
(
snakes
.
size
()
==
0
)
{
stopTimer
();
}
}
public
static
void
tick
()
throws
Exception
{
StringBuilder
sb
=
new
StringBuilder
();
for
(
Iterator
<
Snake
>
iterator
=
SnakeTimer
.
getSnakes
().
iterator
();
iterator
.
hasNext
();)
{
Snake
snake
=
iterator
.
next
();
snake
.
update
(
SnakeTimer
.
getSnakes
());
sb
.
append
(
snake
.
getLocationsJson
());
if
(
iterator
.
hasNext
())
{
sb
.
append
(
','
);
}
}
broadcast
(
String
.
format
(
"{'type': 'update', 'data' : [%s]}"
,
sb
.
toString
()));
}
public
static
void
broadcast
(
String
message
)
throws
Exception
{
Collection
<
Snake
>
snakes
=
new
CopyOnWriteArrayList
<>(
SnakeTimer
.
getSnakes
());
for
(
Snake
snake
:
snakes
)
{
try
{
snake
.
sendMessage
(
message
);
}
catch
(
Throwable
ex
)
{
// if Snake#sendMessage fails the client is removed
removeSnake
(
snake
);
}
}
}
public
static
void
startTimer
()
{
gameTimer
=
new
Timer
(
SnakeTimer
.
class
.
getSimpleName
()
+
" Timer"
);
gameTimer
.
scheduleAtFixedRate
(
new
TimerTask
()
{
@Override
public
void
run
()
{
try
{
tick
();
}
catch
(
Throwable
ex
)
{
log
.
error
(
"Caught to prevent timer from shutting down"
,
ex
);
}
}
},
TICK_DELAY
,
TICK_DELAY
);
}
public
static
void
stopTimer
()
{
if
(
gameTimer
!=
null
)
{
gameTimer
.
cancel
();
}
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/snake/SnakeUtils.java
0 → 100644
View file @
90af8bf5
/*
* 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.
*/
package
samples
.
websocket
.
snake
;
import
java.awt.Color
;
import
java.util.Random
;
public
class
SnakeUtils
{
public
static
final
int
PLAYFIELD_WIDTH
=
640
;
public
static
final
int
PLAYFIELD_HEIGHT
=
480
;
public
static
final
int
GRID_SIZE
=
10
;
private
static
final
Random
random
=
new
Random
();
public
static
String
getRandomHexColor
()
{
float
hue
=
random
.
nextFloat
();
// sat between 0.1 and 0.3
float
saturation
=
(
random
.
nextInt
(
2000
)
+
1000
)
/
10000
f
;
float
luminance
=
0.9f
;
Color
color
=
Color
.
getHSBColor
(
hue
,
saturation
,
luminance
);
return
'#'
+
Integer
.
toHexString
((
color
.
getRGB
()
&
0xffffff
)
|
0x1000000
)
.
substring
(
1
);
}
public
static
Location
getRandomLocation
()
{
int
x
=
roundByGridSize
(
random
.
nextInt
(
PLAYFIELD_WIDTH
));
int
y
=
roundByGridSize
(
random
.
nextInt
(
PLAYFIELD_HEIGHT
));
return
new
Location
(
x
,
y
);
}
private
static
int
roundByGridSize
(
int
value
)
{
value
=
value
+
(
GRID_SIZE
/
2
);
value
=
value
/
GRID_SIZE
;
value
=
value
*
GRID_SIZE
;
return
value
;
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/snake/SnakeWebSocketHandler.java
0 → 100644
View file @
90af8bf5
/*
* 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.
*/
package
samples
.
websocket
.
snake
;
import
java.awt.Color
;
import
java.util.Iterator
;
import
java.util.Random
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
org.springframework.web.socket.CloseStatus
;
import
org.springframework.web.socket.TextMessage
;
import
org.springframework.web.socket.WebSocketSession
;
import
org.springframework.web.socket.handler.TextWebSocketHandler
;
public
class
SnakeWebSocketHandler
extends
TextWebSocketHandler
{
public
static
final
int
PLAYFIELD_WIDTH
=
640
;
public
static
final
int
PLAYFIELD_HEIGHT
=
480
;
public
static
final
int
GRID_SIZE
=
10
;
private
static
final
AtomicInteger
snakeIds
=
new
AtomicInteger
(
0
);
private
static
final
Random
random
=
new
Random
();
private
final
int
id
;
private
Snake
snake
;
public
static
String
getRandomHexColor
()
{
float
hue
=
random
.
nextFloat
();
// sat between 0.1 and 0.3
float
saturation
=
(
random
.
nextInt
(
2000
)
+
1000
)
/
10000
f
;
float
luminance
=
0.9f
;
Color
color
=
Color
.
getHSBColor
(
hue
,
saturation
,
luminance
);
return
'#'
+
Integer
.
toHexString
((
color
.
getRGB
()
&
0xffffff
)
|
0x1000000
)
.
substring
(
1
);
}
public
static
Location
getRandomLocation
()
{
int
x
=
roundByGridSize
(
random
.
nextInt
(
PLAYFIELD_WIDTH
));
int
y
=
roundByGridSize
(
random
.
nextInt
(
PLAYFIELD_HEIGHT
));
return
new
Location
(
x
,
y
);
}
private
static
int
roundByGridSize
(
int
value
)
{
value
=
value
+
(
GRID_SIZE
/
2
);
value
=
value
/
GRID_SIZE
;
value
=
value
*
GRID_SIZE
;
return
value
;
}
public
SnakeWebSocketHandler
()
{
this
.
id
=
snakeIds
.
getAndIncrement
();
}
@Override
public
void
afterConnectionEstablished
(
WebSocketSession
session
)
throws
Exception
{
this
.
snake
=
new
Snake
(
this
.
id
,
session
);
SnakeTimer
.
addSnake
(
this
.
snake
);
StringBuilder
sb
=
new
StringBuilder
();
for
(
Iterator
<
Snake
>
iterator
=
SnakeTimer
.
getSnakes
().
iterator
();
iterator
.
hasNext
();)
{
Snake
snake
=
iterator
.
next
();
sb
.
append
(
String
.
format
(
"{id: %d, color: '%s'}"
,
Integer
.
valueOf
(
snake
.
getId
()),
snake
.
getHexColor
()));
if
(
iterator
.
hasNext
())
{
sb
.
append
(
','
);
}
}
SnakeTimer
.
broadcast
(
String
.
format
(
"{'type': 'join','data':[%s]}"
,
sb
.
toString
()));
}
@Override
protected
void
handleTextMessage
(
WebSocketSession
session
,
TextMessage
message
)
throws
Exception
{
String
payload
=
message
.
getPayload
();
if
(
"west"
.
equals
(
payload
))
{
this
.
snake
.
setDirection
(
Direction
.
WEST
);
}
else
if
(
"north"
.
equals
(
payload
))
{
this
.
snake
.
setDirection
(
Direction
.
NORTH
);
}
else
if
(
"east"
.
equals
(
payload
))
{
this
.
snake
.
setDirection
(
Direction
.
EAST
);
}
else
if
(
"south"
.
equals
(
payload
))
{
this
.
snake
.
setDirection
(
Direction
.
SOUTH
);
}
}
@Override
public
void
afterConnectionClosed
(
WebSocketSession
session
,
CloseStatus
status
)
throws
Exception
{
SnakeTimer
.
removeSnake
(
this
.
snake
);
SnakeTimer
.
broadcast
(
String
.
format
(
"{'type': 'leave', 'id': %d}"
,
Integer
.
valueOf
(
this
.
id
)));
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/resources/static/echo.html
0 → 100644
View file @
90af8bf5
<!--
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>
Apache Tomcat WebSocket Examples: Echo
</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
src=
"https://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"
></script>
<script
type=
"text/javascript"
>
var
ws
=
null
;
function
setConnected
(
connected
)
{
document
.
getElementById
(
'connect'
).
disabled
=
connected
;
document
.
getElementById
(
'disconnect'
).
disabled
=
!
connected
;
document
.
getElementById
(
'echo'
).
disabled
=
!
connected
;
}
function
connect
()
{
var
target
=
document
.
getElementById
(
'target'
).
value
;
ws
=
new
SockJS
(
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
disconnect
()
{
if
(
ws
!=
null
)
{
ws
.
close
();
ws
=
null
;
}
setConnected
(
false
);
}
function
echo
()
{
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>
<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=
"/echo"
/>
</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=
"echo"
onclick=
"echo();"
disabled=
"disabled"
>
Echo message
</button>
</div>
</div>
<div
id=
"console-container"
>
<div
id=
"console"
></div>
</div>
</div>
</body>
</html>
\ No newline at end of file
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/resources/static/index.html
0 → 100644
View file @
90af8bf5
<!--
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>
Apache Tomcat WebSocket Examples: Index
</title>
</head>
<body>
<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>
<p>
Please select the sample you would like to try.
</p>
<ul>
<li><a
href=
"./echo.html"
>
Echo
</a></li>
<li><a
href=
"./reverse.html"
>
Reverse
</a></li>
<li><a
href=
"./snake.html"
>
Snake
</a></li>
</ul>
</body>
</html>
\ No newline at end of file
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/resources/static/reverse.html
0 → 100644
View file @
90af8bf5
<!--
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>
\ No newline at end of file
spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/resources/static/snake.html
0 → 100644
View file @
90af8bf5
<!--
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 PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns=
"http://www.w3.org/1999/xhtml"
lang=
"en"
xml:lang=
"en"
>
<head>
<meta
http-equiv=
"Content-Type"
content=
"text/html; charset=utf-8"
/>
<title>
Apache Tomcat WebSocket Examples: Multiplayer Snake
</title>
<style
type=
"text/css"
>
#playground
{
width
:
640px
;
height
:
480px
;
background-color
:
#000
;
}
#console-container
{
float
:
left
;
margin-left
:
15px
;
width
:
300px
;
}
#console
{
border
:
1px
solid
#CCCCCC
;
border-right-color
:
#999999
;
border-bottom-color
:
#999999
;
height
:
480px
;
overflow-y
:
scroll
;
padding-left
:
5px
;
padding-right
:
5px
;
width
:
100%
;
}
#console
p
{
padding
:
0
;
margin
:
0
;
}
</style>
<script
src=
"https://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"
></script>
</head>
<body>
<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
style=
"float: left"
>
<canvas
id=
"playground"
width=
"640"
height=
"480"
></canvas>
</div>
<div
id=
"console-container"
>
<div
id=
"console"
></div>
</div>
<script
type=
"text/javascript"
>
var
Game
=
{};
Game
.
fps
=
30
;
Game
.
socket
=
null
;
Game
.
nextFrame
=
null
;
Game
.
interval
=
null
;
Game
.
direction
=
'none'
;
Game
.
gridSize
=
10
;
function
Snake
()
{
this
.
snakeBody
=
[];
this
.
color
=
null
;
}
Snake
.
prototype
.
draw
=
function
(
context
)
{
for
(
var
id
in
this
.
snakeBody
)
{
context
.
fillStyle
=
this
.
color
;
context
.
fillRect
(
this
.
snakeBody
[
id
].
x
,
this
.
snakeBody
[
id
].
y
,
Game
.
gridSize
,
Game
.
gridSize
);
}
};
Game
.
initialize
=
function
()
{
this
.
entities
=
[];
canvas
=
document
.
getElementById
(
'playground'
);
if
(
!
canvas
.
getContext
)
{
Console
.
log
(
'Error: 2d canvas not supported by this browser.'
);
return
;
}
this
.
context
=
canvas
.
getContext
(
'2d'
);
window
.
addEventListener
(
'keydown'
,
function
(
e
)
{
var
code
=
e
.
keyCode
;
if
(
code
>
36
&&
code
<
41
)
{
switch
(
code
)
{
case
37
:
if
(
Game
.
direction
!=
'east'
)
Game
.
setDirection
(
'west'
);
break
;
case
38
:
if
(
Game
.
direction
!=
'south'
)
Game
.
setDirection
(
'north'
);
break
;
case
39
:
if
(
Game
.
direction
!=
'west'
)
Game
.
setDirection
(
'east'
);
break
;
case
40
:
if
(
Game
.
direction
!=
'north'
)
Game
.
setDirection
(
'south'
);
break
;
}
}
},
false
);
Game
.
connect
();
};
Game
.
setDirection
=
function
(
direction
)
{
Game
.
direction
=
direction
;
Game
.
socket
.
send
(
direction
);
Console
.
log
(
'Sent: Direction '
+
direction
);
};
Game
.
startGameLoop
=
function
()
{
if
(
window
.
webkitRequestAnimationFrame
)
{
Game
.
nextFrame
=
function
()
{
webkitRequestAnimationFrame
(
Game
.
run
);
};
}
else
if
(
window
.
mozRequestAnimationFrame
)
{
Game
.
nextFrame
=
function
()
{
mozRequestAnimationFrame
(
Game
.
run
);
};
}
else
{
Game
.
interval
=
setInterval
(
Game
.
run
,
1000
/
Game
.
fps
);
}
if
(
Game
.
nextFrame
!=
null
)
{
Game
.
nextFrame
();
}
};
Game
.
stopGameLoop
=
function
()
{
Game
.
nextFrame
=
null
;
if
(
Game
.
interval
!=
null
)
{
clearInterval
(
Game
.
interval
);
}
};
Game
.
draw
=
function
()
{
this
.
context
.
clearRect
(
0
,
0
,
640
,
480
);
for
(
var
id
in
this
.
entities
)
{
this
.
entities
[
id
].
draw
(
this
.
context
);
}
};
Game
.
addSnake
=
function
(
id
,
color
)
{
Game
.
entities
[
id
]
=
new
Snake
();
Game
.
entities
[
id
].
color
=
color
;
};
Game
.
updateSnake
=
function
(
id
,
snakeBody
)
{
if
(
typeof
Game
.
entities
[
id
]
!=
"undefined"
)
{
Game
.
entities
[
id
].
snakeBody
=
snakeBody
;
}
};
Game
.
removeSnake
=
function
(
id
)
{
Game
.
entities
[
id
]
=
null
;
// Force GC.
delete
Game
.
entities
[
id
];
};
Game
.
run
=
(
function
()
{
var
skipTicks
=
1000
/
Game
.
fps
,
nextGameTick
=
(
new
Date
).
getTime
();
return
function
()
{
while
((
new
Date
).
getTime
()
>
nextGameTick
)
{
nextGameTick
+=
skipTicks
;
}
Game
.
draw
();
if
(
Game
.
nextFrame
!=
null
)
{
Game
.
nextFrame
();
}
};
})();
Game
.
connect
=
(
function
()
{
Game
.
socket
=
new
SockJS
(
"/snake"
);
Game
.
socket
.
onopen
=
function
()
{
// Socket open.. start the game loop.
Console
.
log
(
'Info: WebSocket connection opened.'
);
Console
.
log
(
'Info: Press an arrow key to begin.'
);
Game
.
startGameLoop
();
setInterval
(
function
()
{
// Prevent server read timeout.
Game
.
socket
.
send
(
'ping'
);
},
5000
);
};
Game
.
socket
.
onclose
=
function
()
{
Console
.
log
(
'Info: WebSocket closed.'
);
Game
.
stopGameLoop
();
};
Game
.
socket
.
onmessage
=
function
(
message
)
{
// _Potential_ security hole, consider using json lib to parse data in production.
var
packet
=
eval
(
'('
+
message
.
data
+
')'
);
switch
(
packet
.
type
)
{
case
'update'
:
for
(
var
i
=
0
;
i
<
packet
.
data
.
length
;
i
++
)
{
Game
.
updateSnake
(
packet
.
data
[
i
].
id
,
packet
.
data
[
i
].
body
);
}
break
;
case
'join'
:
for
(
var
j
=
0
;
j
<
packet
.
data
.
length
;
j
++
)
{
Game
.
addSnake
(
packet
.
data
[
j
].
id
,
packet
.
data
[
j
].
color
);
}
break
;
case
'leave'
:
Game
.
removeSnake
(
packet
.
id
);
break
;
case
'dead'
:
Console
.
log
(
'Info: Your snake is dead, bad luck!'
);
Game
.
direction
=
'none'
;
break
;
case
'kill'
:
Console
.
log
(
'Info: Head shot!'
);
break
;
}
};
});
var
Console
=
{};
Console
.
log
=
(
function
(
message
)
{
var
console
=
document
.
getElementById
(
'console'
);
var
p
=
document
.
createElement
(
'p'
);
p
.
style
.
wordWrap
=
'break-word'
;
p
.
innerHTML
=
message
;
console
.
appendChild
(
p
);
while
(
console
.
childNodes
.
length
>
25
)
{
console
.
removeChild
(
console
.
firstChild
);
}
console
.
scrollTop
=
console
.
scrollHeight
;
});
Game
.
initialize
();
</script>
</body>
</html>
spring-boot-samples/spring-boot-sample-websocket-jetty/src/test/java/samples/websocket/SampleWebSocketsApplicationTests.java
0 → 100644
View file @
90af8bf5
/*
* 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
;
import
java.util.concurrent.CountDownLatch
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.atomic.AtomicReference
;
import
org.apache.commons.logging.Log
;
import
org.apache.commons.logging.LogFactory
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.boot.CommandLineRunner
;
import
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration
;
import
org.springframework.boot.builder.SpringApplicationBuilder
;
import
org.springframework.boot.test.IntegrationTest
;
import
org.springframework.boot.test.SpringApplicationConfiguration
;
import
org.springframework.context.ConfigurableApplicationContext
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.test.annotation.DirtiesContext
;
import
org.springframework.test.context.junit4.SpringJUnit4ClassRunner
;
import
org.springframework.test.context.web.WebAppConfiguration
;
import
org.springframework.web.socket.client.WebSocketConnectionManager
;
import
org.springframework.web.socket.client.standard.StandardWebSocketClient
;
import
samples.websocket.client.GreetingService
;
import
samples.websocket.client.SimpleClientWebSocketHandler
;
import
samples.websocket.client.SimpleGreetingService
;
import
samples.websocket.config.SampleWebSocketsApplication
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
@RunWith
(
SpringJUnit4ClassRunner
.
class
)
@SpringApplicationConfiguration
(
classes
=
SampleWebSocketsApplication
.
class
)
@WebAppConfiguration
@IntegrationTest
(
"server.port:0"
)
@DirtiesContext
public
class
SampleWebSocketsApplicationTests
{
private
static
Log
logger
=
LogFactory
.
getLog
(
SampleWebSocketsApplicationTests
.
class
);
@Value
(
"${local.server.port}"
)
private
int
port
=
1234
;
@Test
public
void
echoEndpoint
()
throws
Exception
{
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
public
void
reverseEndpoint
()
throws
Exception
{
ConfigurableApplicationContext
context
=
new
SpringApplicationBuilder
(
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
();
AtomicReference
<
String
>
messagePayloadReference
=
context
.
getBean
(
ClientConfiguration
.
class
).
messagePayload
;
context
.
close
();
assertEquals
(
0
,
count
);
assertEquals
(
"Reversed: !dlrow olleH"
,
messagePayloadReference
.
get
());
}
@Configuration
static
class
ClientConfiguration
implements
CommandLineRunner
{
@Value
(
"${websocket.uri}"
)
private
String
webSocketUri
;
private
final
CountDownLatch
latch
=
new
CountDownLatch
(
1
);
private
final
AtomicReference
<
String
>
messagePayload
=
new
AtomicReference
<
String
>();
@Override
public
void
run
(
String
...
args
)
throws
Exception
{
logger
.
info
(
"Waiting for response: latch="
+
this
.
latch
.
getCount
());
if
(
this
.
latch
.
await
(
10
,
TimeUnit
.
SECONDS
))
{
logger
.
info
(
"Got response: "
+
this
.
messagePayload
.
get
());
}
else
{
logger
.
info
(
"Response not received: latch="
+
this
.
latch
.
getCount
());
}
}
@Bean
public
WebSocketConnectionManager
wsConnectionManager
()
{
WebSocketConnectionManager
manager
=
new
WebSocketConnectionManager
(
client
(),
handler
(),
this
.
webSocketUri
);
manager
.
setAutoStartup
(
true
);
return
manager
;
}
@Bean
public
StandardWebSocketClient
client
()
{
return
new
StandardWebSocketClient
();
}
@Bean
public
SimpleClientWebSocketHandler
handler
()
{
return
new
SimpleClientWebSocketHandler
(
greetingService
(),
this
.
latch
,
this
.
messagePayload
);
}
@Bean
public
GreetingService
greetingService
()
{
return
new
SimpleGreetingService
();
}
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/test/java/samples/websocket/echo/CustomContainerWebSocketsApplicationTests.java
0 → 100644
View file @
90af8bf5
/*
* 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
.
echo
;
import
java.util.concurrent.CountDownLatch
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.atomic.AtomicReference
;
import
org.apache.commons.logging.Log
;
import
org.apache.commons.logging.LogFactory
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.boot.CommandLineRunner
;
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.jetty.JettyEmbeddedServletContainerFactory
;
import
org.springframework.boot.test.IntegrationTest
;
import
org.springframework.boot.test.SpringApplicationConfiguration
;
import
org.springframework.context.ConfigurableApplicationContext
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.test.annotation.DirtiesContext
;
import
org.springframework.test.context.junit4.SpringJUnit4ClassRunner
;
import
org.springframework.test.context.web.WebAppConfiguration
;
import
org.springframework.util.SocketUtils
;
import
org.springframework.web.socket.client.WebSocketConnectionManager
;
import
org.springframework.web.socket.client.standard.StandardWebSocketClient
;
import
samples.websocket.client.GreetingService
;
import
samples.websocket.client.SimpleClientWebSocketHandler
;
import
samples.websocket.client.SimpleGreetingService
;
import
samples.websocket.config.SampleWebSocketsApplication
;
import
samples.websocket.echo.CustomContainerWebSocketsApplicationTests.CustomContainerConfiguration
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
@RunWith
(
SpringJUnit4ClassRunner
.
class
)
@SpringApplicationConfiguration
(
classes
=
{
SampleWebSocketsApplication
.
class
,
CustomContainerConfiguration
.
class
})
@WebAppConfiguration
@IntegrationTest
@DirtiesContext
public
class
CustomContainerWebSocketsApplicationTests
{
private
static
Log
logger
=
LogFactory
.
getLog
(
CustomContainerWebSocketsApplicationTests
.
class
);
private
static
int
PORT
=
SocketUtils
.
findAvailableTcpPort
();
@Configuration
protected
static
class
CustomContainerConfiguration
{
@Bean
public
EmbeddedServletContainerFactory
embeddedServletContainerFactory
()
{
return
new
JettyEmbeddedServletContainerFactory
(
"/ws"
,
PORT
);
}
}
@Test
public
void
echoEndpoint
()
throws
Exception
{
ConfigurableApplicationContext
context
=
new
SpringApplicationBuilder
(
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
();
AtomicReference
<
String
>
messagePayloadReference
=
context
.
getBean
(
ClientConfiguration
.
class
).
messagePayload
;
context
.
close
();
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
static
class
ClientConfiguration
implements
CommandLineRunner
{
@Value
(
"${websocket.uri}"
)
private
String
webSocketUri
;
private
final
CountDownLatch
latch
=
new
CountDownLatch
(
1
);
private
final
AtomicReference
<
String
>
messagePayload
=
new
AtomicReference
<
String
>();
@Override
public
void
run
(
String
...
args
)
throws
Exception
{
logger
.
info
(
"Waiting for response: latch="
+
this
.
latch
.
getCount
());
if
(
this
.
latch
.
await
(
10
,
TimeUnit
.
SECONDS
))
{
logger
.
info
(
"Got response: "
+
this
.
messagePayload
.
get
());
}
else
{
logger
.
info
(
"Response not received: latch="
+
this
.
latch
.
getCount
());
}
}
@Bean
public
WebSocketConnectionManager
wsConnectionManager
()
{
WebSocketConnectionManager
manager
=
new
WebSocketConnectionManager
(
client
(),
handler
(),
this
.
webSocketUri
);
manager
.
setAutoStartup
(
true
);
return
manager
;
}
@Bean
public
StandardWebSocketClient
client
()
{
return
new
StandardWebSocketClient
();
}
@Bean
public
SimpleClientWebSocketHandler
handler
()
{
return
new
SimpleClientWebSocketHandler
(
greetingService
(),
this
.
latch
,
this
.
messagePayload
);
}
@Bean
public
GreetingService
greetingService
()
{
return
new
SimpleGreetingService
();
}
}
}
spring-boot-samples/spring-boot-sample-websocket-jetty/src/test/java/samples/websocket/snake/SnakeTimerTests.java
0 → 100644
View file @
90af8bf5
/*
* Copyright 2012-2013 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
.
snake
;
import
java.io.IOException
;
import
org.junit.Test
;
import
static
org
.
hamcrest
.
CoreMatchers
.
is
;
import
static
org
.
junit
.
Assert
.
assertThat
;
import
static
org
.
mockito
.
BDDMockito
.
willThrow
;
import
static
org
.
mockito
.
Matchers
.
anyString
;
import
static
org
.
mockito
.
Mockito
.
mock
;
public
class
SnakeTimerTests
{
@Test
public
void
removeDysfunctionalSnakes
()
throws
Exception
{
Snake
snake
=
mock
(
Snake
.
class
);
willThrow
(
new
IOException
()).
given
(
snake
).
sendMessage
(
anyString
());
SnakeTimer
.
addSnake
(
snake
);
SnakeTimer
.
broadcast
(
""
);
assertThat
(
SnakeTimer
.
getSnakes
().
size
(),
is
(
0
));
}
}
spring-boot-starters/spring-boot-starter-jetty/pom.xml
View file @
90af8bf5
...
@@ -26,5 +26,13 @@
...
@@ -26,5 +26,13 @@
<groupId>
org.eclipse.jetty
</groupId>
<groupId>
org.eclipse.jetty
</groupId>
<artifactId>
jetty-jsp
</artifactId>
<artifactId>
jetty-jsp
</artifactId>
</dependency>
</dependency>
<dependency>
<groupId>
org.eclipse.jetty.websocket
</groupId>
<artifactId>
websocket-server
</artifactId>
</dependency>
<dependency>
<groupId>
org.eclipse.jetty.websocket
</groupId>
<artifactId>
javax-websocket-server-impl
</artifactId>
</dependency>
</dependencies>
</dependencies>
</project>
</project>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment