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

http://jquery-stream.googlecode.com/ · Java · 151 lines · 113 code · 27 blank · 11 comment · 6 complexity · 4344347e54e00e0b323cb29f47a05bdf MD5 · raw file

  1. package flowersinthesand.example;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import java.util.LinkedHashMap;
  5. import java.util.Map;
  6. import java.util.UUID;
  7. import java.util.concurrent.BlockingQueue;
  8. import java.util.concurrent.ConcurrentHashMap;
  9. import java.util.concurrent.LinkedBlockingQueue;
  10. import javax.servlet.AsyncContext;
  11. import javax.servlet.AsyncEvent;
  12. import javax.servlet.AsyncListener;
  13. import javax.servlet.ServletConfig;
  14. import javax.servlet.ServletException;
  15. import javax.servlet.annotation.WebServlet;
  16. import javax.servlet.http.HttpServlet;
  17. import javax.servlet.http.HttpServletRequest;
  18. import javax.servlet.http.HttpServletResponse;
  19. import com.google.gson.Gson;
  20. @WebServlet(urlPatterns = "/chat", asyncSupported = true)
  21. public class ChatServlet extends HttpServlet {
  22. private static final long serialVersionUID = -277914015930424042L;
  23. private Map<String, AsyncContext> asyncContexts = new ConcurrentHashMap<String, AsyncContext>();
  24. private BlockingQueue<String> messages = new LinkedBlockingQueue<String>();
  25. private Thread notifier = new Thread(new Runnable() {
  26. public void run() {
  27. while (true) {
  28. try {
  29. // Waits until a message arrives
  30. String message = messages.take();
  31. // Sends the message to all the AsyncContext's response
  32. for (AsyncContext asyncContext : asyncContexts.values()) {
  33. try {
  34. sendMessage(asyncContext.getResponse().getWriter(), message);
  35. } catch (Exception e) {
  36. asyncContexts.values().remove(asyncContext);
  37. }
  38. }
  39. } catch (InterruptedException e) {
  40. break;
  41. }
  42. }
  43. }
  44. });
  45. private void sendMessage(PrintWriter writer, String message) throws IOException {
  46. // default message format is message-size ; message-data ;
  47. writer.print(message.length());
  48. writer.print(";");
  49. writer.print(message);
  50. writer.print(";");
  51. writer.flush();
  52. }
  53. @Override
  54. public void init(ServletConfig config) throws ServletException {
  55. super.init(config);
  56. notifier.start();
  57. }
  58. // GET method is used to establish a stream connection
  59. @Override
  60. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  61. throws ServletException, IOException {
  62. // Content-Type header
  63. response.setContentType("text/plain");
  64. response.setCharacterEncoding("utf-8");
  65. // Access-Control-Allow-Origin header
  66. response.setHeader("Access-Control-Allow-Origin", "*");
  67. PrintWriter writer = response.getWriter();
  68. // Id
  69. final String id = UUID.randomUUID().toString();
  70. writer.print(id);
  71. writer.print(';');
  72. // Padding
  73. for (int i = 0; i < 1024; i++) {
  74. writer.print(' ');
  75. }
  76. writer.print(';');
  77. writer.flush();
  78. final AsyncContext ac = request.startAsync();
  79. ac.addListener(new AsyncListener() {
  80. public void onComplete(AsyncEvent event) throws IOException {
  81. asyncContexts.remove(id);
  82. }
  83. public void onTimeout(AsyncEvent event) throws IOException {
  84. asyncContexts.remove(id);
  85. }
  86. public void onError(AsyncEvent event) throws IOException {
  87. asyncContexts.remove(id);
  88. }
  89. public void onStartAsync(AsyncEvent event) throws IOException {
  90. }
  91. });
  92. asyncContexts.put(id, ac);
  93. }
  94. // POST method is used to communicate with the server
  95. @Override
  96. protected void doPost(HttpServletRequest request, HttpServletResponse response)
  97. throws ServletException, IOException {
  98. request.setCharacterEncoding("utf-8");
  99. AsyncContext ac = asyncContexts.get(request.getParameter("metadata.id"));
  100. if (ac == null) {
  101. return;
  102. }
  103. // close-request
  104. if ("close".equals(request.getParameter("metadata.type"))) {
  105. ac.complete();
  106. return;
  107. }
  108. // send-request
  109. Map<String, String> data = new LinkedHashMap<String, String>();
  110. data.put("username", request.getParameter("username"));
  111. data.put("message", request.getParameter("message"));
  112. try {
  113. messages.put(new Gson().toJson(data));
  114. } catch (InterruptedException e) {
  115. throw new IOException(e);
  116. }
  117. }
  118. @Override
  119. public void destroy() {
  120. messages.clear();
  121. asyncContexts.clear();
  122. notifier.interrupt();
  123. }
  124. }