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