You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2012/12/20 23:17:51 UTC
svn commit: r1424733 - in /tomcat/trunk/webapps/examples/WEB-INF: ./
classes/websocket/snake/
Author: markt
Date: Thu Dec 20 22:17:50 2012
New Revision: 1424733
URL: http://svn.apache.org/viewvc?rev=1424733&view=rev
Log:
Switch the snake WebSocket example to the new API.
Added:
tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.java (with props)
tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer.java
- copied, changed from r1424057, tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java
Removed:
tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java
Modified:
tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Location.java
tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java
tomcat/trunk/webapps/examples/WEB-INF/web.xml
Modified: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Location.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Location.java?rev=1424733&r1=1424732&r2=1424733&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Location.java (original)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Location.java Thu Dec 20 22:17:50 2012
@@ -29,13 +29,13 @@ public class Location {
public Location getAdjacentLocation(Direction direction) {
switch (direction) {
case NORTH:
- return new Location(x, y - SnakeWebSocketServlet.GRID_SIZE);
+ return new Location(x, y - SnakeAnnotation.GRID_SIZE);
case SOUTH:
- return new Location(x, y + SnakeWebSocketServlet.GRID_SIZE);
+ return new Location(x, y + SnakeAnnotation.GRID_SIZE);
case EAST:
- return new Location(x + SnakeWebSocketServlet.GRID_SIZE, y);
+ return new Location(x + SnakeAnnotation.GRID_SIZE, y);
case WEST:
- return new Location(x - SnakeWebSocketServlet.GRID_SIZE, y);
+ return new Location(x - SnakeAnnotation.GRID_SIZE, y);
case NONE:
// fall through
default:
Modified: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java?rev=1424733&r1=1424732&r2=1424733&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java (original)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java Thu Dec 20 22:17:50 2012
@@ -17,19 +17,18 @@
package websocket.snake;
import java.io.IOException;
-import java.nio.CharBuffer;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
-import org.apache.catalina.websocket.WsOutbound;
+import javax.websocket.Session;
public class Snake {
private static final int DEFAULT_LENGTH = 5;
private final int id;
- private final WsOutbound outbound;
+ private final Session session;
private Direction direction;
private int length = DEFAULT_LENGTH;
@@ -37,35 +36,34 @@ public class Snake {
private final Deque<Location> tail = new ArrayDeque<>();
private final String hexColor;
- public Snake(int id, WsOutbound outbound) {
+ public Snake(int id, Session session) {
this.id = id;
- this.outbound = outbound;
- this.hexColor = SnakeWebSocketServlet.getRandomHexColor();
+ this.session = session;
+ this.hexColor = SnakeAnnotation.getRandomHexColor();
resetState();
}
private void resetState() {
this.direction = Direction.NONE;
- this.head = SnakeWebSocketServlet.getRandomLocation();
+ this.head = SnakeAnnotation.getRandomLocation();
this.tail.clear();
this.length = DEFAULT_LENGTH;
}
private synchronized void kill() {
resetState();
- try {
- CharBuffer response = CharBuffer.wrap("{'type': 'dead'}");
- outbound.writeTextMessage(response);
- } catch (IOException ioe) {
- // Ignore
- }
+ sendMessage("{'type': 'dead'}");
}
private synchronized void reward() {
length++;
+ sendMessage("{'type': 'kill'}");
+ }
+
+
+ protected void sendMessage(String msg) {
try {
- CharBuffer response = CharBuffer.wrap("{'type': 'kill'}");
- outbound.writeTextMessage(response);
+ session.getRemote().sendString(msg);
} catch (IOException ioe) {
// Ignore
}
@@ -73,17 +71,17 @@ public class Snake {
public synchronized void update(Collection<Snake> snakes) {
Location nextLocation = head.getAdjacentLocation(direction);
- if (nextLocation.x >= SnakeWebSocketServlet.PLAYFIELD_WIDTH) {
+ if (nextLocation.x >= SnakeAnnotation.PLAYFIELD_WIDTH) {
nextLocation.x = 0;
}
- if (nextLocation.y >= SnakeWebSocketServlet.PLAYFIELD_HEIGHT) {
+ if (nextLocation.y >= SnakeAnnotation.PLAYFIELD_HEIGHT) {
nextLocation.y = 0;
}
if (nextLocation.x < 0) {
- nextLocation.x = SnakeWebSocketServlet.PLAYFIELD_WIDTH;
+ nextLocation.x = SnakeAnnotation.PLAYFIELD_WIDTH;
}
if (nextLocation.y < 0) {
- nextLocation.y = SnakeWebSocketServlet.PLAYFIELD_HEIGHT;
+ nextLocation.y = SnakeAnnotation.PLAYFIELD_HEIGHT;
}
if (direction != Direction.NONE) {
tail.addFirst(head);
Added: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.java?rev=1424733&view=auto
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.java (added)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.java Thu Dec 20 22:17:50 2012
@@ -0,0 +1,113 @@
+/*
+ * 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 websocket.snake;
+
+import java.awt.Color;
+import java.util.Iterator;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.websocket.Session;
+import javax.websocket.WebSocketClose;
+import javax.websocket.WebSocketEndpoint;
+import javax.websocket.WebSocketMessage;
+import javax.websocket.WebSocketOpen;
+
+@WebSocketEndpoint(value = "/websocket/snake")
+public class SnakeAnnotation {
+
+ 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) / 10000f;
+ 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 SnakeAnnotation() {
+ this.id = snakeIds.getAndIncrement();
+ }
+
+
+ @WebSocketOpen
+ public void onOpen(Session session) {
+ 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()));
+ }
+
+
+ @WebSocketMessage
+ public void onTextMessage(String message) {
+ if ("west".equals(message)) {
+ snake.setDirection(Direction.WEST);
+ } else if ("north".equals(message)) {
+ snake.setDirection(Direction.NORTH);
+ } else if ("east".equals(message)) {
+ snake.setDirection(Direction.EAST);
+ } else if ("south".equals(message)) {
+ snake.setDirection(Direction.SOUTH);
+ }
+ }
+
+
+ @WebSocketClose
+ public void onClose() {
+ SnakeTimer.removeSnake(snake);
+ SnakeTimer.broadcast(String.format("{'type': 'leave', 'id': %d}",
+ Integer.valueOf(id)));
+ }
+}
Propchange: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.java
------------------------------------------------------------------------------
svn:eol-style = native
Copied: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer.java (from r1424057, tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java)
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer.java?p2=tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer.java&p1=tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java&r1=1424057&r2=1424733&rev=1424733&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java (original)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer.java Thu Dec 20 22:17:50 2012
@@ -16,77 +16,58 @@
*/
package websocket.snake;
-import java.awt.Color;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
-import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.catalina.websocket.MessageHandler;
-import org.apache.catalina.websocket.StreamHandler;
-import org.apache.catalina.websocket.WebSocketServlet;
-import org.apache.catalina.websocket.WsOutbound;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/**
- * Example web socket servlet for simple multiplayer snake.
+ * Sets up the timer for the multi-player snake game WebSocket example.
*/
-public class SnakeWebSocketServlet extends WebSocketServlet {
-
- private static final long serialVersionUID = 1L;
+public class SnakeTimer {
private static final Log log =
- LogFactory.getLog(SnakeWebSocketServlet.class);
+ LogFactory.getLog(SnakeTimer.class);
- public static final int PLAYFIELD_WIDTH = 640;
- public static final int PLAYFIELD_HEIGHT = 480;
- public static final int GRID_SIZE = 10;
+ private static Timer gameTimer = null;
private static final long TICK_DELAY = 100;
- private static final Random random = new Random();
+ private static final ConcurrentHashMap<Integer, Snake> snakes =
+ new ConcurrentHashMap<>();
- private final Timer gameTimer =
- new Timer(SnakeWebSocketServlet.class.getSimpleName() + " Timer");
+ protected static synchronized void addSnake(Snake snake) {
+ if (snakes.size() == 0) {
+ startTimer();
+ }
+ snakes.put(Integer.valueOf(snake.getId()), snake);
+ }
- private final AtomicInteger connectionIds = new AtomicInteger(0);
- private final ConcurrentHashMap<Integer, Snake> snakes =
- new ConcurrentHashMap<>();
- private final ConcurrentHashMap<Integer, SnakeMessageHandler> connections =
- new ConcurrentHashMap<>();
- @Override
- public void init() throws ServletException {
- super.init();
- gameTimer.scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- try {
- tick();
- } catch (RuntimeException e) {
- log.error("Caught to prevent timer from shutting down", e);
- }
- }
- }, TICK_DELAY, TICK_DELAY);
+ protected static Collection<Snake> getSnakes() {
+ return Collections.unmodifiableCollection(snakes.values());
+ }
+
+
+ protected static synchronized void removeSnake(Snake snake) {
+ snakes.remove(Integer.valueOf(snake.getId()));
+ if (snakes.size() == 0) {
+ stopTimer();
+ }
}
- private void tick() {
+
+ protected static void tick() {
StringBuilder sb = new StringBuilder();
- for (Iterator<Snake> iterator = getSnakes().iterator();
+ for (Iterator<Snake> iterator = SnakeTimer.getSnakes().iterator();
iterator.hasNext();) {
Snake snake = iterator.next();
- snake.update(getSnakes());
+ snake.update(SnakeTimer.getSnakes());
sb.append(snake.getLocationsJson());
if (iterator.hasNext()) {
sb.append(',');
@@ -96,118 +77,31 @@ public class SnakeWebSocketServlet exten
sb.toString()));
}
- private void broadcast(String message) {
- for (SnakeMessageHandler connection : getConnections()) {
- try {
- CharBuffer buffer = CharBuffer.wrap(message);
- connection.getWsOutbound().writeTextMessage(buffer);
- } catch (IOException ignore) {
- // Ignore
- }
+ protected static void broadcast(String message) {
+ for (Snake snake : SnakeTimer.getSnakes()) {
+ snake.sendMessage(message);
}
}
- private Collection<SnakeMessageHandler> getConnections() {
- return Collections.unmodifiableCollection(connections.values());
- }
-
- private Collection<Snake> getSnakes() {
- return Collections.unmodifiableCollection(snakes.values());
- }
- public static String getRandomHexColor() {
- float hue = random.nextFloat();
- // sat between 0.1 and 0.3
- float saturation = (random.nextInt(2000) + 1000) / 10000f;
- 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(SnakeWebSocketServlet.PLAYFIELD_WIDTH));
- int y = roundByGridSize(
- random.nextInt(SnakeWebSocketServlet.PLAYFIELD_HEIGHT));
- return new Location(x, y);
- }
-
- private static int roundByGridSize(int value) {
- value = value + (SnakeWebSocketServlet.GRID_SIZE / 2);
- value = value / SnakeWebSocketServlet.GRID_SIZE;
- value = value * SnakeWebSocketServlet.GRID_SIZE;
- return value;
- }
-
- @Override
- public void destroy() {
- super.destroy();
- if (gameTimer != null) {
- gameTimer.cancel();
- }
- }
-
- @Override
- protected StreamHandler createWebSocketHandler(String subProtocol,
- HttpServletRequest request) {
- return new SnakeMessageHandler(connectionIds.incrementAndGet());
- }
-
- private final class SnakeMessageHandler extends MessageHandler {
-
- private final int id;
- private Snake snake;
-
- private SnakeMessageHandler(int id) {
- this.id = id;
- }
-
- @Override
- protected void onOpen(WsOutbound outbound) {
- this.snake = new Snake(id, outbound);
- snakes.put(Integer.valueOf(id), snake);
- connections.put(Integer.valueOf(id), this);
- StringBuilder sb = new StringBuilder();
- for (Iterator<Snake> iterator = 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(',');
+ public static void startTimer() {
+ gameTimer = new Timer(SnakeTimer.class.getSimpleName() + " Timer");
+ gameTimer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ tick();
+ } catch (RuntimeException e) {
+ log.error("Caught to prevent timer from shutting down", e);
}
}
- broadcast(String.format("{'type': 'join','data':[%s]}",
- sb.toString()));
- }
-
- @Override
- protected void onClose(int status) {
- connections.remove(Integer.valueOf(id));
- snakes.remove(Integer.valueOf(id));
- broadcast(String.format("{'type': 'leave', 'id': %d}",
- Integer.valueOf(id)));
- }
+ }, TICK_DELAY, TICK_DELAY);
+ }
- @Override
- protected void onBinaryMessage(ByteBuffer message) throws IOException {
- throw new UnsupportedOperationException(
- "Binary message not supported.");
- }
- @Override
- protected void onTextMessage(CharBuffer charBuffer) throws IOException {
- String message = charBuffer.toString();
- if ("west".equals(message)) {
- snake.setDirection(Direction.WEST);
- } else if ("north".equals(message)) {
- snake.setDirection(Direction.NORTH);
- } else if ("east".equals(message)) {
- snake.setDirection(Direction.EAST);
- } else if ("south".equals(message)) {
- snake.setDirection(Direction.SOUTH);
- }
+ public static void stopTimer() {
+ if (gameTimer != null) {
+ gameTimer.cancel();
}
}
}
Modified: tomcat/trunk/webapps/examples/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/web.xml?rev=1424733&r1=1424732&r2=1424733&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/web.xml (original)
+++ tomcat/trunk/webapps/examples/WEB-INF/web.xml Thu Dec 20 22:17:50 2012
@@ -354,17 +354,4 @@
<url-pattern>/async/stockticker</url-pattern>
</servlet-mapping>
- <!-- WebSocket Examples -->
- <listener>
- <listener-class>websocket.echo.WsConfigListener</listener-class>
- </listener>
- <servlet>
- <servlet-name>wsSnake</servlet-name>
- <servlet-class>websocket.snake.SnakeWebSocketServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>wsSnake</servlet-name>
- <url-pattern>/websocket/snake</url-pattern>
- </servlet-mapping>
-
</web-app>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org