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 | } |
---|