[66] | 1 | /* |
---|
| 2 | * Licensed to the Apache Software Foundation (ASF) under one or more |
---|
| 3 | * contributor license agreements. See the NOTICE file distributed with |
---|
| 4 | * this work for additional information regarding copyright ownership. |
---|
| 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 |
---|
| 6 | * (the "License"); you may not use this file except in compliance with |
---|
| 7 | * the License. You may obtain a copy of the License at |
---|
| 8 | * |
---|
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
---|
| 10 | * |
---|
| 11 | * Unless required by applicable law or agreed to in writing, software |
---|
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
---|
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
---|
| 14 | * See the License for the specific language governing permissions and |
---|
| 15 | * limitations under the License. |
---|
| 16 | */ |
---|
| 17 | |
---|
| 18 | |
---|
| 19 | package chat; |
---|
| 20 | |
---|
| 21 | |
---|
| 22 | import java.io.IOException; |
---|
| 23 | import java.io.InputStream; |
---|
| 24 | import java.io.PrintWriter; |
---|
| 25 | import java.util.ArrayList; |
---|
| 26 | |
---|
| 27 | import org.apache.catalina.CometEvent; |
---|
| 28 | import org.apache.catalina.CometProcessor; |
---|
| 29 | |
---|
| 30 | import javax.servlet.ServletException; |
---|
| 31 | import javax.servlet.http.HttpServlet; |
---|
| 32 | import javax.servlet.http.HttpServletRequest; |
---|
| 33 | import javax.servlet.http.HttpServletResponse; |
---|
| 34 | |
---|
| 35 | |
---|
| 36 | /** |
---|
| 37 | * Helper class to implement Comet functionality. |
---|
| 38 | */ |
---|
| 39 | public class ChatServlet |
---|
| 40 | extends HttpServlet implements CometProcessor { |
---|
| 41 | |
---|
| 42 | protected ArrayList<HttpServletResponse> connections = |
---|
| 43 | new ArrayList<HttpServletResponse>(); |
---|
| 44 | protected MessageSender messageSender = null; |
---|
| 45 | |
---|
| 46 | public void init() throws ServletException { |
---|
| 47 | messageSender = new MessageSender(); |
---|
| 48 | Thread messageSenderThread = |
---|
| 49 | new Thread(messageSender, "MessageSender[" + getServletContext().getContextPath() + "]"); |
---|
| 50 | messageSenderThread.setDaemon(true); |
---|
| 51 | messageSenderThread.start(); |
---|
| 52 | } |
---|
| 53 | |
---|
| 54 | public void destroy() { |
---|
| 55 | connections.clear(); |
---|
| 56 | messageSender.stop(); |
---|
| 57 | messageSender = null; |
---|
| 58 | } |
---|
| 59 | |
---|
| 60 | /** |
---|
| 61 | * Process the given Comet event. |
---|
| 62 | * |
---|
| 63 | * @param event The Comet event that will be processed |
---|
| 64 | * @throws IOException |
---|
| 65 | * @throws ServletException |
---|
| 66 | */ |
---|
| 67 | public void event(CometEvent event) |
---|
| 68 | throws IOException, ServletException { |
---|
| 69 | |
---|
| 70 | // Note: There should really be two servlets in this example, to avoid |
---|
| 71 | // mixing Comet stuff with regular connection processing |
---|
| 72 | HttpServletRequest request = event.getHttpServletRequest(); |
---|
| 73 | HttpServletResponse response = event.getHttpServletResponse(); |
---|
| 74 | |
---|
| 75 | if (event.getEventType() == CometEvent.EventType.BEGIN) { |
---|
| 76 | String action = request.getParameter("action"); |
---|
| 77 | if (action != null) { |
---|
| 78 | if ("login".equals(action)) { |
---|
| 79 | String nickname = request.getParameter("nickname"); |
---|
| 80 | request.getSession(true).setAttribute("nickname", nickname); |
---|
| 81 | response.sendRedirect("post.jsp"); |
---|
| 82 | event.close(); |
---|
| 83 | return; |
---|
| 84 | } else { |
---|
| 85 | String nickname = (String) request.getSession(true).getAttribute("nickname"); |
---|
| 86 | String message = request.getParameter("message"); |
---|
| 87 | messageSender.send(nickname, message); |
---|
| 88 | response.sendRedirect("post.jsp"); |
---|
| 89 | event.close(); |
---|
| 90 | return; |
---|
| 91 | } |
---|
| 92 | } else { |
---|
| 93 | if (request.getSession(true).getAttribute("nickname") == null) { |
---|
| 94 | // Redirect to "login" |
---|
| 95 | log("Redirect to login for session: " + request.getSession(true).getId()); |
---|
| 96 | response.sendRedirect("login.jsp"); |
---|
| 97 | event.close(); |
---|
| 98 | return; |
---|
| 99 | } |
---|
| 100 | } |
---|
| 101 | begin(event, request, response); |
---|
| 102 | } else if (event.getEventType() == CometEvent.EventType.ERROR) { |
---|
| 103 | error(event, request, response); |
---|
| 104 | } else if (event.getEventType() == CometEvent.EventType.END) { |
---|
| 105 | end(event, request, response); |
---|
| 106 | } else if (event.getEventType() == CometEvent.EventType.READ) { |
---|
| 107 | read(event, request, response); |
---|
| 108 | } |
---|
| 109 | } |
---|
| 110 | |
---|
| 111 | protected void begin(CometEvent event, HttpServletRequest request, HttpServletResponse response) |
---|
| 112 | throws IOException, ServletException { |
---|
| 113 | log("Begin for session: " + request.getSession(true).getId()); |
---|
| 114 | |
---|
| 115 | PrintWriter writer = response.getWriter(); |
---|
| 116 | writer.println("<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">"); |
---|
| 117 | writer.println("<html><head><title>JSP Chat</title></head><body bgcolor=\"#FFFFFF\">"); |
---|
| 118 | writer.flush(); |
---|
| 119 | |
---|
| 120 | synchronized(connections) { |
---|
| 121 | connections.add(response); |
---|
| 122 | } |
---|
| 123 | } |
---|
| 124 | |
---|
| 125 | protected void end(CometEvent event, HttpServletRequest request, HttpServletResponse response) |
---|
| 126 | throws IOException, ServletException { |
---|
| 127 | log("End for session: " + request.getSession(true).getId()); |
---|
| 128 | synchronized(connections) { |
---|
| 129 | connections.remove(response); |
---|
| 130 | } |
---|
| 131 | |
---|
| 132 | PrintWriter writer = response.getWriter(); |
---|
| 133 | writer.println("</body></html>"); |
---|
| 134 | |
---|
| 135 | event.close(); |
---|
| 136 | |
---|
| 137 | } |
---|
| 138 | |
---|
| 139 | protected void error(CometEvent event, HttpServletRequest request, HttpServletResponse response) |
---|
| 140 | throws IOException, ServletException { |
---|
| 141 | log("Error for session: " + request.getSession(true).getId()); |
---|
| 142 | synchronized(connections) { |
---|
| 143 | connections.remove(response); |
---|
| 144 | } |
---|
| 145 | event.close(); |
---|
| 146 | } |
---|
| 147 | |
---|
| 148 | protected void read(CometEvent event, HttpServletRequest request, HttpServletResponse response) |
---|
| 149 | throws IOException, ServletException { |
---|
| 150 | InputStream is = request.getInputStream(); |
---|
| 151 | byte[] buf = new byte[512]; |
---|
| 152 | while (is.available() > 0) { |
---|
| 153 | log("Available: " + is.available()); |
---|
| 154 | int n = is.read(buf); |
---|
| 155 | if (n > 0) { |
---|
| 156 | log("Read " + n + " bytes: " + new String(buf, 0, n) |
---|
| 157 | + " for session: " + request.getSession(true).getId()); |
---|
| 158 | } else if (n < 0) { |
---|
| 159 | log("End of file: " + n); |
---|
| 160 | end(event, request, response); |
---|
| 161 | return; |
---|
| 162 | } |
---|
| 163 | } |
---|
| 164 | } |
---|
| 165 | |
---|
| 166 | protected void service(HttpServletRequest request, HttpServletResponse response) |
---|
| 167 | throws IOException, ServletException { |
---|
| 168 | // Compatibility method: equivalent method using the regular connection model |
---|
| 169 | PrintWriter writer = response.getWriter(); |
---|
| 170 | writer.println("<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">"); |
---|
| 171 | writer.println("<html><head><title>JSP Chat</title></head><body bgcolor=\"#FFFFFF\">"); |
---|
| 172 | writer.println("Chat example only supports Comet processing"); |
---|
| 173 | writer.println("</body></html>"); |
---|
| 174 | } |
---|
| 175 | |
---|
| 176 | |
---|
| 177 | /** |
---|
| 178 | * Poller class. |
---|
| 179 | */ |
---|
| 180 | public class MessageSender implements Runnable { |
---|
| 181 | |
---|
| 182 | protected boolean running = true; |
---|
| 183 | protected ArrayList<String> messages = new ArrayList<String>(); |
---|
| 184 | |
---|
| 185 | public MessageSender() { |
---|
| 186 | } |
---|
| 187 | |
---|
| 188 | public void stop() { |
---|
| 189 | running = false; |
---|
| 190 | } |
---|
| 191 | |
---|
| 192 | /** |
---|
| 193 | * Add specified socket and associated pool to the poller. The socket will |
---|
| 194 | * be added to a temporary array, and polled first after a maximum amount |
---|
| 195 | * of time equal to pollTime (in most cases, latency will be much lower, |
---|
| 196 | * however). |
---|
| 197 | * |
---|
| 198 | * @param socket to add to the poller |
---|
| 199 | */ |
---|
| 200 | public void send(String user, String message) { |
---|
| 201 | synchronized (messages) { |
---|
| 202 | messages.add("[" + user + "]: " + message); |
---|
| 203 | messages.notify(); |
---|
| 204 | } |
---|
| 205 | } |
---|
| 206 | |
---|
| 207 | /** |
---|
| 208 | * The background thread that listens for incoming TCP/IP connections and |
---|
| 209 | * hands them off to an appropriate processor. |
---|
| 210 | */ |
---|
| 211 | public void run() { |
---|
| 212 | |
---|
| 213 | // Loop until we receive a shutdown command |
---|
| 214 | while (running) { |
---|
| 215 | // Loop if endpoint is paused |
---|
| 216 | |
---|
| 217 | if (messages.size() == 0) { |
---|
| 218 | try { |
---|
| 219 | synchronized (messages) { |
---|
| 220 | messages.wait(); |
---|
| 221 | } |
---|
| 222 | } catch (InterruptedException e) { |
---|
| 223 | // Ignore |
---|
| 224 | } |
---|
| 225 | } |
---|
| 226 | |
---|
| 227 | synchronized (connections) { |
---|
| 228 | String[] pendingMessages = null; |
---|
| 229 | synchronized (messages) { |
---|
| 230 | pendingMessages = messages.toArray(new String[0]); |
---|
| 231 | messages.clear(); |
---|
| 232 | } |
---|
| 233 | for (int i = 0; i < connections.size(); i++) { |
---|
| 234 | try { |
---|
| 235 | PrintWriter writer = connections.get(i).getWriter(); |
---|
| 236 | for (int j = 0; j < pendingMessages.length; j++) { |
---|
| 237 | // FIXME: Add HTML filtering |
---|
| 238 | writer.println(pendingMessages[j] + "<br/>"); |
---|
| 239 | } |
---|
| 240 | writer.flush(); |
---|
| 241 | } catch (IOException e) { |
---|
| 242 | log("IOExeption sending message", e); |
---|
| 243 | } |
---|
| 244 | } |
---|
| 245 | } |
---|
| 246 | |
---|
| 247 | } |
---|
| 248 | |
---|
| 249 | } |
---|
| 250 | |
---|
| 251 | } |
---|
| 252 | |
---|
| 253 | |
---|
| 254 | |
---|
| 255 | |
---|
| 256 | } |
---|