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
1a9ce42d
Commit
1a9ce42d
authored
Oct 09, 2013
by
Phillip Webb
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Apply source formatting to samples
parent
efe102bd
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
428 additions
and
368 deletions
+428
-368
pom.xml
spring-boot-samples/pom.xml
+20
-0
pom.xml
spring-boot-samples/spring-boot-sample-websocket/pom.xml
+1
-0
GreetingService.java
...mework/boot/samples/websocket/client/GreetingService.java
+1
-0
SimpleClientWebSocketHandler.java
...amples/websocket/client/SimpleClientWebSocketHandler.java
+7
-4
SimpleGreetingService.java
.../boot/samples/websocket/client/SimpleGreetingService.java
+1
-0
DefaultEchoService.java
...ework/boot/samples/websocket/echo/DefaultEchoService.java
+1
-0
EchoService.java
...ingframework/boot/samples/websocket/echo/EchoService.java
+1
-0
EchoWebSocketHandler.java
...ork/boot/samples/websocket/echo/EchoWebSocketHandler.java
+20
-2
Direction.java
...ringframework/boot/samples/websocket/snake/Direction.java
+2
-1
Location.java
...pringframework/boot/samples/websocket/snake/Location.java
+42
-40
Snake.java
...g/springframework/boot/samples/websocket/snake/Snake.java
+111
-111
SnakeTimer.java
...ingframework/boot/samples/websocket/snake/SnakeTimer.java
+74
-80
SnakeUtils.java
...ingframework/boot/samples/websocket/snake/SnakeUtils.java
+29
-31
SnakeWebSocketHandler.java
...k/boot/samples/websocket/snake/SnakeWebSocketHandler.java
+80
-80
SampleWebSocketsApplicationTests.java
...ples/websocket/echo/SampleWebSocketsApplicationTests.java
+12
-9
SnakeTimerTests.java
...amework/boot/samples/websocket/snake/SnakeTimerTests.java
+26
-10
No files found.
spring-boot-samples/pom.xml
View file @
1a9ce42d
...
...
@@ -48,6 +48,26 @@
<scope>
test
</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>
maven-eclipse-plugin
</artifactId>
<configuration>
<useProjectReferences>
false
</useProjectReferences>
<additionalConfig>
<file>
<name>
.settings/org.eclipse.jdt.ui.prefs
</name>
<location>
${main.basedir}/eclipse/org.eclipse.jdt.ui.prefs
</location>
</file>
<file>
<name>
.settings/org.eclipse.jdt.core.prefs
</name>
<location>
${main.basedir}/eclipse/org.eclipse.jdt.core.prefs
</location>
</file>
</additionalConfig>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>
spring-snapshots
</id>
...
...
spring-boot-samples/spring-boot-sample-websocket/pom.xml
View file @
1a9ce42d
...
...
@@ -10,6 +10,7 @@
<version>
0.5.0.BUILD-SNAPSHOT
</version>
</parent>
<properties>
<main.basedir>
${basedir}/../..
</main.basedir>
<java.version>
1.7
</java.version>
<tomcat.version>
8.0.0-RC3
</tomcat.version>
</properties>
...
...
spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/client/GreetingService.java
View file @
1a9ce42d
...
...
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
client
;
public
interface
GreetingService
{
...
...
spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/client/SimpleClientWebSocketHandler.java
View file @
1a9ce42d
...
...
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
client
;
import
java.util.concurrent.CountDownLatch
;
...
...
@@ -33,7 +34,8 @@ public class SimpleClientWebSocketHandler extends TextWebSocketHandlerAdapter {
private
CountDownLatch
latch
;
@Autowired
public
SimpleClientWebSocketHandler
(
GreetingService
greetingService
,
CountDownLatch
latch
)
{
public
SimpleClientWebSocketHandler
(
GreetingService
greetingService
,
CountDownLatch
latch
)
{
this
.
greetingService
=
greetingService
;
this
.
latch
=
latch
;
}
...
...
@@ -45,10 +47,11 @@ public class SimpleClientWebSocketHandler extends TextWebSocketHandlerAdapter {
}
@Override
public
void
handleTextMessage
(
WebSocketSession
session
,
TextMessage
message
)
throws
Exception
{
logger
.
info
(
"Received: "
+
message
+
" ("
+
latch
.
getCount
()
+
")"
);
public
void
handleTextMessage
(
WebSocketSession
session
,
TextMessage
message
)
throws
Exception
{
this
.
logger
.
info
(
"Received: "
+
message
+
" ("
+
this
.
latch
.
getCount
()
+
")"
);
session
.
close
();
latch
.
countDown
();
this
.
latch
.
countDown
();
}
}
spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/client/SimpleGreetingService.java
View file @
1a9ce42d
...
...
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
client
;
public
class
SimpleGreetingService
implements
GreetingService
{
...
...
spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/echo/DefaultEchoService.java
View file @
1a9ce42d
...
...
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
echo
;
public
class
DefaultEchoService
implements
EchoService
{
...
...
spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/echo/EchoService.java
View file @
1a9ce42d
...
...
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
echo
;
public
interface
EchoService
{
...
...
spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/echo/EchoWebSocketHandler.java
View file @
1a9ce42d
/*
* Copyright 2002-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
org
.
springframework
.
boot
.
samples
.
websocket
.
echo
;
import
org.slf4j.Logger
;
...
...
@@ -29,14 +45,16 @@ public class EchoWebSocketHandler extends TextWebSocketHandlerAdapter {
}
@Override
public
void
handleTextMessage
(
WebSocketSession
session
,
TextMessage
message
)
throws
Exception
{
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
{
public
void
handleTransportError
(
WebSocketSession
session
,
Throwable
exception
)
throws
Exception
{
session
.
close
(
CloseStatus
.
SERVER_ERROR
);
}
...
...
spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/snake/Direction.java
View file @
1a9ce42d
...
...
@@ -14,8 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
snake
;
public
enum
Direction
{
NONE
,
NORTH
,
SOUTH
,
EAST
,
WEST
NONE
,
NORTH
,
SOUTH
,
EAST
,
WEST
}
spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/snake/Location.java
View file @
1a9ce42d
...
...
@@ -14,58 +14,60 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
snake
;
import
org.springframework.boot.samples.websocket.snake.Direction
;
package
org
.
springframework
.
boot
.
samples
.
websocket
.
snake
;
public
class
Location
{
public
int
x
;
public
int
y
;
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
(
int
x
,
int
y
)
{
this
.
x
=
x
;
this
.
y
=
y
;
}
public
Location
getAdjacentLocation
(
Direction
direction
)
{
switch
(
direction
)
{
case
NORTH:
return
new
Location
(
x
,
y
-
Location
.
GRID_SIZE
);
case
SOUTH:
return
new
Location
(
x
,
y
+
Location
.
GRID_SIZE
);
case
EAST:
return
new
Location
(
x
+
Location
.
GRID_SIZE
,
y
);
case
WEST:
return
new
Location
(
x
-
Location
.
GRID_SIZE
,
y
);
case
NONE:
// fall through
default
:
return
this
;
}
}
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
;
@Override
public
boolean
equals
(
Object
o
)
{
if
(
this
==
o
)
return
true
;
if
(
o
==
null
||
getClass
()
!=
o
.
getClass
())
return
false
;
Location
location
=
(
Location
)
o
;
Location
location
=
(
Location
)
o
;
if
(
x
!=
location
.
x
)
return
false
;
if
(
y
!=
location
.
y
)
return
false
;
if
(
this
.
x
!=
location
.
x
)
return
false
;
if
(
this
.
y
!=
location
.
y
)
return
false
;
return
true
;
}
return
true
;
}
@Override
public
int
hashCode
()
{
int
result
=
x
;
result
=
31
*
result
+
y
;
return
result
;
}
@Override
public
int
hashCode
()
{
int
result
=
this
.
x
;
result
=
31
*
result
+
this
.
y
;
return
result
;
}
}
spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/snake/Snake.java
View file @
1a9ce42d
...
...
@@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
snake
;
import
java.util.ArrayDeque
;
...
...
@@ -23,117 +24,116 @@ 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
{
length
++;
sendMessage
(
"{'type': 'kill'}"
);
}
protected
void
sendMessage
(
String
msg
)
throws
Exception
{
session
.
sendMessage
(
new
TextMessage
(
msg
));
}
public
synchronized
void
update
(
Collection
<
Snake
>
snakes
)
throws
Exception
{
Location
nextLocation
=
head
.
getAdjacentLocation
(
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
(
direction
!=
Direction
.
NONE
)
{
tail
.
addFirst
(
head
);
if
(
tail
.
size
()
>
length
)
{
tail
.
removeLast
();
}
head
=
nextLocation
;
}
handleCollisions
(
snakes
);
}
private
void
handleCollisions
(
Collection
<
Snake
>
snakes
)
throws
Exception
{
for
(
Snake
snake
:
snakes
)
{
boolean
headCollision
=
id
!=
snake
.
id
&&
snake
.
getHead
().
equals
(
head
);
boolean
tailCollision
=
snake
.
getTail
().
contains
(
head
);
if
(
headCollision
||
tailCollision
)
{
kill
();
if
(
id
!=
snake
.
id
)
{
snake
.
reward
();
}
}
}
}
public
synchronized
Location
getHead
()
{
return
head
;
}
public
synchronized
Collection
<
Location
>
getTail
()
{
return
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
(
head
.
x
),
Integer
.
valueOf
(
head
.
y
)));
for
(
Location
location
:
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
(
id
),
sb
.
toString
());
}
public
int
getId
()
{
return
id
;
}
public
String
getHexColor
()
{
return
hexColor
;
}
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/src/main/java/org/springframework/boot/samples/websocket/snake/SnakeTimer.java
View file @
1a9ce42d
...
...
@@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
snake
;
import
java.util.Collection
;
...
...
@@ -32,84 +33,77 @@ import org.apache.juli.logging.LogFactory;
*/
public
class
SnakeTimer
{
private
static
final
Log
log
=
LogFactory
.
getLog
(
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
e
)
{
log
.
error
(
"Caught to prevent timer from shutting down"
,
e
);
}
}
},
TICK_DELAY
,
TICK_DELAY
);
}
public
static
void
stopTimer
()
{
if
(
gameTimer
!=
null
)
{
gameTimer
.
cancel
();
}
}
private
static
final
Log
log
=
LogFactory
.
getLog
(
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
e
)
{
log
.
error
(
"Caught to prevent timer from shutting down"
,
e
);
}
}
},
TICK_DELAY
,
TICK_DELAY
);
}
public
static
void
stopTimer
()
{
if
(
gameTimer
!=
null
)
{
gameTimer
.
cancel
();
}
}
}
spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/snake/SnakeUtils.java
View file @
1a9ce42d
...
...
@@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
snake
;
import
java.awt.Color
;
...
...
@@ -21,36 +22,33 @@ 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
;
}
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/src/main/java/org/springframework/boot/samples/websocket/snake/SnakeWebSocketHandler.java
View file @
1a9ce42d
...
...
@@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
snake
;
import
java.awt.Color
;
...
...
@@ -28,85 +29,84 @@ import org.springframework.web.socket.adapter.TextWebSocketHandlerAdapter;
public
class
SnakeWebSocketHandler
extends
TextWebSocketHandlerAdapter
{
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
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
(
id
,
session
);
SnakeTimer
.
addSnake
(
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
))
{
snake
.
setDirection
(
Direction
.
WEST
);
}
else
if
(
"north"
.
equals
(
payload
))
{
snake
.
setDirection
(
Direction
.
NORTH
);
}
else
if
(
"east"
.
equals
(
payload
))
{
snake
.
setDirection
(
Direction
.
EAST
);
}
else
if
(
"south"
.
equals
(
payload
))
{
snake
.
setDirection
(
Direction
.
SOUTH
);
}
}
@Override
public
void
afterConnectionClosed
(
WebSocketSession
session
,
CloseStatus
status
)
throws
Exception
{
SnakeTimer
.
removeSnake
(
snake
);
SnakeTimer
.
broadcast
(
String
.
format
(
"{'type': 'leave', 'id': %d}"
,
Integer
.
valueOf
(
id
)));
}
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/src/test/java/org/springframework/boot/samples/websocket/echo/SampleWebSocketsApplicationTests.java
View file @
1a9ce42d
...
...
@@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
samples
.
websocket
.
echo
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
package
org
.
springframework
.
boot
.
samples
.
websocket
.
echo
;
import
java.util.concurrent.Callable
;
import
java.util.concurrent.CountDownLatch
;
...
...
@@ -40,6 +39,8 @@ import org.springframework.context.annotation.Configuration;
import
org.springframework.web.socket.client.WebSocketConnectionManager
;
import
org.springframework.web.socket.client.endpoint.StandardWebSocketClient
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
public
class
SampleWebSocketsApplicationTests
{
private
static
Log
logger
=
LogFactory
.
getLog
(
SampleWebSocketsApplicationTests
.
class
);
...
...
@@ -55,7 +56,7 @@ public class SampleWebSocketsApplicationTests {
new
Callable
<
ConfigurableApplicationContext
>()
{
@Override
public
ConfigurableApplicationContext
call
()
throws
Exception
{
return
(
ConfigurableApplicationContext
)
SpringApplication
return
SpringApplication
.
run
(
SampleWebSocketsApplication
.
class
);
}
});
...
...
@@ -71,7 +72,8 @@ public class SampleWebSocketsApplicationTests {
@Test
public
void
runAndWait
()
throws
Exception
{
ConfigurableApplicationContext
context
=
(
ConfigurableApplicationContext
)
SpringApplication
.
run
(
ClientConfiguration
.
class
,
"--spring.main.web_environment=false"
);
ConfigurableApplicationContext
context
=
SpringApplication
.
run
(
ClientConfiguration
.
class
,
"--spring.main.web_environment=false"
);
long
count
=
context
.
getBean
(
ClientConfiguration
.
class
).
latch
.
getCount
();
context
.
close
();
assertEquals
(
0
,
count
);
...
...
@@ -84,15 +86,16 @@ public class SampleWebSocketsApplicationTests {
@Override
public
void
run
(
String
...
args
)
throws
Exception
{
logger
.
info
(
"Waiting for response: latch="
+
latch
.
getCount
());
latch
.
await
(
10
,
TimeUnit
.
SECONDS
);
logger
.
info
(
"Got response: latch="
+
latch
.
getCount
());
logger
.
info
(
"Waiting for response: latch="
+
this
.
latch
.
getCount
());
this
.
latch
.
await
(
10
,
TimeUnit
.
SECONDS
);
logger
.
info
(
"Got response: latch="
+
this
.
latch
.
getCount
());
}
@Bean
public
WebSocketConnectionManager
wsConnectionManager
()
{
WebSocketConnectionManager
manager
=
new
WebSocketConnectionManager
(
client
(),
handler
(),
WS_URI
);
WebSocketConnectionManager
manager
=
new
WebSocketConnectionManager
(
client
(),
handler
(),
WS_URI
);
manager
.
setAutoStartup
(
true
);
return
manager
;
...
...
@@ -105,7 +108,7 @@ public class SampleWebSocketsApplicationTests {
@Bean
public
SimpleClientWebSocketHandler
handler
()
{
return
new
SimpleClientWebSocketHandler
(
greetingService
(),
latch
);
return
new
SimpleClientWebSocketHandler
(
greetingService
(),
this
.
latch
);
}
@Bean
...
...
spring-boot-samples/spring-boot-sample-websocket/src/test/java/org/springframework/boot/samples/websocket/snake/SnakeTimerTests.java
View file @
1a9ce42d
package
org
.
springframework
.
boot
.
samples
.
websocket
.
snake
;
/*
* Copyright 2002-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.
*/
import
org.junit.Test
;
package
org
.
springframework
.
boot
.
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
.
Matchers
.
anyString
;
...
...
@@ -12,13 +28,13 @@ import static org.mockito.Mockito.mock;
public
class
SnakeTimerTests
{
@Test
public
void
removeDysfunctionalSnakes
()
throws
Exception
{
Snake
snake
=
mock
(
Snake
.
class
);
doThrow
(
new
IOException
()).
when
(
snake
).
sendMessage
(
anyString
());
SnakeTimer
.
addSnake
(
snake
);
@Test
public
void
removeDysfunctionalSnakes
()
throws
Exception
{
Snake
snake
=
mock
(
Snake
.
class
);
doThrow
(
new
IOException
()).
when
(
snake
).
sendMessage
(
anyString
());
SnakeTimer
.
addSnake
(
snake
);
SnakeTimer
.
broadcast
(
""
);
assertThat
(
SnakeTimer
.
getSnakes
().
size
(),
is
(
0
));
}
SnakeTimer
.
broadcast
(
""
);
assertThat
(
SnakeTimer
.
getSnakes
().
size
(),
is
(
0
));
}
}
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