PageRenderTime 44ms CodeModel.GetById 9ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 1ms

/sub-projects/jquery-stream-jetty/trunk/src/main/java/flowersinthesand/example/ChatServlet.java

http://jquery-stream.googlecode.com/
Java | 205 lines | 154 code | 38 blank | 13 comment | 7 complexity | 3e75b58a8f78f725dc166d19e5d5ab13 MD5 | raw file
  1package flowersinthesand.example;
  2
  3import java.io.IOException;
  4import java.io.PrintWriter;
  5import java.util.LinkedHashMap;
  6import java.util.Map;
  7import java.util.Queue;
  8import java.util.UUID;
  9import java.util.concurrent.BlockingQueue;
 10import java.util.concurrent.ConcurrentHashMap;
 11import java.util.concurrent.ConcurrentLinkedQueue;
 12import java.util.concurrent.LinkedBlockingQueue;
 13
 14import javax.servlet.AsyncContext;
 15import javax.servlet.AsyncEvent;
 16import javax.servlet.AsyncListener;
 17import javax.servlet.ServletConfig;
 18import javax.servlet.ServletException;
 19import javax.servlet.annotation.WebServlet;
 20import javax.servlet.http.HttpServletRequest;
 21import javax.servlet.http.HttpServletResponse;
 22
 23import org.eclipse.jetty.util.UrlEncoded;
 24import org.eclipse.jetty.websocket.WebSocket;
 25import org.eclipse.jetty.websocket.WebSocketServlet;
 26
 27import com.google.gson.Gson;
 28
 29@WebServlet(urlPatterns = "/chat", asyncSupported = true)
 30public class ChatServlet extends WebSocketServlet {
 31
 32	private static final long serialVersionUID = 4805728426990609124L;
 33
 34	private Map<String, AsyncContext> asyncContexts = new ConcurrentHashMap<String, AsyncContext>();
 35	private Queue<ChatWebSocket> webSockets = new ConcurrentLinkedQueue<ChatWebSocket>();
 36	private BlockingQueue<String> messages = new LinkedBlockingQueue<String>();
 37	private Thread notifier = new Thread(new Runnable() {
 38		public void run() {
 39			while (true) {
 40				try {
 41					// Waits until a message arrives
 42					String message = messages.take();
 43
 44					// Sends the message to all the AsyncContext's response
 45					for (AsyncContext asyncContext : asyncContexts.values()) {
 46						try {
 47							sendMessage(asyncContext.getResponse().getWriter(), message);
 48						} catch (Exception e) {
 49							asyncContexts.values().remove(asyncContext);
 50						}
 51					}
 52
 53					// Sends the message to all the WebSocket's connection
 54					for (ChatWebSocket webSocket : webSockets) {
 55						try {
 56							webSocket.connection.sendMessage(message);
 57						} catch (Exception e) {
 58							webSockets.remove(webSocket);
 59						}
 60					}
 61				} catch (InterruptedException e) {
 62					break;
 63				}
 64			}
 65		}
 66	});
 67
 68	private void sendMessage(PrintWriter writer, String message) throws IOException {
 69		// default message format is message-size ; message-data ;
 70		writer.print(message.length());
 71		writer.print(";");
 72		writer.print(message);
 73		writer.print(";");
 74		writer.flush();
 75	}
 76
 77	@Override
 78	public void init(ServletConfig config) throws ServletException {
 79		super.init(config);
 80		notifier.start();
 81	}
 82
 83	// GET method is used to establish a stream connection
 84	@Override
 85	protected void doGet(HttpServletRequest request, HttpServletResponse response)
 86			throws ServletException, IOException {
 87
 88		// Content-Type header
 89		response.setContentType("text/plain");
 90		response.setCharacterEncoding("utf-8");
 91
 92		// Access-Control-Allow-Origin header
 93		response.setHeader("Access-Control-Allow-Origin", "*");
 94
 95		PrintWriter writer = response.getWriter();
 96
 97		// Id
 98		final String id = UUID.randomUUID().toString();
 99		writer.print(id);
100		writer.print(';');
101
102		// Padding
103		for (int i = 0; i < 1024; i++) {
104			writer.print(' ');
105		}
106		writer.print(';');
107		writer.flush();
108
109		final AsyncContext ac = request.startAsync();
110		ac.addListener(new AsyncListener() {
111			public void onComplete(AsyncEvent event) throws IOException {
112				asyncContexts.remove(id);
113			}
114
115			public void onTimeout(AsyncEvent event) throws IOException {
116				asyncContexts.remove(id);
117			}
118
119			public void onError(AsyncEvent event) throws IOException {
120				asyncContexts.remove(id);
121			}
122
123			public void onStartAsync(AsyncEvent event) throws IOException {
124
125			}
126		});
127		asyncContexts.put(id, ac);
128	}
129
130	// POST method is used to communicate with the server
131	@Override
132	protected void doPost(HttpServletRequest request, HttpServletResponse response)
133			throws ServletException, IOException {
134		request.setCharacterEncoding("utf-8");
135
136		AsyncContext ac = asyncContexts.get(request.getParameter("metadata.id"));
137		if (ac == null) {
138			return;
139		}
140
141		// close-request
142		if ("close".equals(request.getParameter("metadata.type"))) {
143			ac.complete();
144			return;
145		}
146
147		// send-request
148		Map<String, String> data = new LinkedHashMap<String, String>();
149		data.put("username", request.getParameter("username"));
150		data.put("message", request.getParameter("message"));
151
152		try {
153			messages.put(new Gson().toJson(data));
154		} catch (InterruptedException e) {
155			throw new IOException(e);
156		}
157	}
158
159	@Override
160	public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
161		return new ChatWebSocket();
162	}
163
164	class ChatWebSocket implements WebSocket.OnTextMessage {
165
166		Connection connection;
167
168		@Override
169		public void onOpen(Connection connection) {
170			this.connection = connection;
171			webSockets.add(this);
172		}
173
174		@Override
175		public void onClose(int closeCode, String message) {
176			webSockets.remove(this);
177		}
178
179		@Override
180		public void onMessage(String queryString) {
181			// Parses query string
182			UrlEncoded parameters = new UrlEncoded(queryString);
183
184			Map<String, String> data = new LinkedHashMap<String, String>();
185			data.put("username", parameters.getString("username"));
186			data.put("message", parameters.getString("message"));
187
188			try {
189				messages.put(new Gson().toJson(data));
190			} catch (InterruptedException e) {
191				throw new RuntimeException(e);
192			}
193		}
194
195	}
196
197	@Override
198	public void destroy() {
199		messages.clear();
200		webSockets.clear();
201		asyncContexts.clear();
202		notifier.interrupt();
203	}
204
205}