/trunk/src/main/java/flowersinthesand/example/HttpChatServlet.java
Java | 154 lines | 116 code | 27 blank | 11 comment | 7 complexity | 5afd4bea525eebf25efa68e287bc51dd 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.UUID; 8import java.util.concurrent.BlockingQueue; 9import java.util.concurrent.ConcurrentHashMap; 10import java.util.concurrent.LinkedBlockingQueue; 11 12import javax.servlet.AsyncContext; 13import javax.servlet.AsyncEvent; 14import javax.servlet.AsyncListener; 15import javax.servlet.ServletConfig; 16import javax.servlet.ServletException; 17import javax.servlet.annotation.WebServlet; 18import javax.servlet.http.HttpServlet; 19import javax.servlet.http.HttpServletRequest; 20import javax.servlet.http.HttpServletResponse; 21 22import com.google.gson.Gson; 23 24@WebServlet(urlPatterns = "/example/httpchat", asyncSupported = true) 25public class HttpChatServlet extends HttpServlet { 26 27 private static final long serialVersionUID = -8823775068689773674L; 28 29 /* Common */ 30 private BlockingQueue<String> messages = new LinkedBlockingQueue<String>(); 31 private Thread notifier = new Thread(new Runnable() { 32 public void run() { 33 boolean done = false; 34 while (!done) { 35 try { 36 String message = messages.take(); 37 for (AsyncContext asyncContext : asyncContexts.values()) { 38 try { 39 // Message 40 PrintWriter writer = asyncContext.getResponse().getWriter(); 41 writer.print(message.length()); 42 writer.print(";"); 43 writer.print(message); 44 writer.print(";"); 45 writer.flush(); 46 } catch (Exception e) { 47 asyncContexts.values().remove(asyncContext); 48 } 49 } 50 } catch (InterruptedException e) { 51 done = true; 52 } 53 } 54 } 55 }); 56 57 @Override 58 public void init(ServletConfig config) throws ServletException { 59 super.init(config); 60 notifier.start(); 61 } 62 63 @Override 64 public void destroy() { 65 messages.clear(); 66 asyncContexts.clear(); 67 notifier.interrupt(); 68 } 69 70 /* HTTP Streaming powered by Servlet 3.0 */ 71 private Map<String, AsyncContext> asyncContexts = new ConcurrentHashMap<String, AsyncContext>(); 72 73 // GET method is used to open stream 74 @Override 75 protected void doGet(HttpServletRequest request, HttpServletResponse response) 76 throws ServletException, IOException { 77 // Wrong access 78 if ("websocket".equalsIgnoreCase(request.getHeader("Upgrade"))) { 79 response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 80 return; 81 } 82 83 response.setCharacterEncoding("utf-8"); 84 85 // Content-Type header 86 response.setContentType("text/plain"); 87 88 // Access-Control-Allow-Origin header 89 response.setHeader("Access-Control-Allow-Origin", "*"); 90 91 PrintWriter writer = response.getWriter(); 92 93 // Id 94 final String id = UUID.randomUUID().toString(); 95 writer.print(id); 96 writer.print(';'); 97 98 // Padding 99 for (int i = 0; i < 1024; i++) { 100 writer.print(' '); 101 } 102 writer.print(';'); 103 writer.flush(); 104 105 final AsyncContext ac = request.startAsync(); 106 ac.setTimeout(5 * 60 * 1000); 107 ac.addListener(new AsyncListener() { 108 public void onComplete(AsyncEvent event) throws IOException { 109 asyncContexts.remove(id); 110 } 111 112 public void onTimeout(AsyncEvent event) throws IOException { 113 asyncContexts.remove(id); 114 } 115 116 public void onError(AsyncEvent event) throws IOException { 117 asyncContexts.remove(id); 118 } 119 120 public void onStartAsync(AsyncEvent event) throws IOException { 121 122 } 123 }); 124 asyncContexts.put(id, ac); 125 } 126 127 // POST method is used to handle data sent by user through the stream 128 @Override 129 protected void doPost(HttpServletRequest request, HttpServletResponse response) 130 throws ServletException, IOException { 131 request.setCharacterEncoding("utf-8"); 132 133 if ("close".equals(request.getParameter("metadata.type"))) { 134 AsyncContext ac = asyncContexts.get(request.getParameter("metadata.id")); 135 if (ac != null) { 136 ac.complete(); 137 } 138 139 return; 140 } 141 142 // Handles data sent from a client 143 Map<String, String> data = new LinkedHashMap<String, String>(); 144 data.put("username", request.getParameter("username")); 145 data.put("message", request.getParameter("message")); 146 147 try { 148 messages.put(new Gson().toJson(data)); 149 } catch (InterruptedException e) { 150 throw new IOException(e); 151 } 152 } 153 154}