/wiki/EchoExample.wiki

http://jquery-stream.googlecode.com/ · Unknown · 350 lines · 274 code · 76 blank · 0 comment · 0 complexity · e0a93be9a72602f344cfd9e771a33831 MD5 · raw file

  1. #summary Echo Example
  2. *Please, see ChatExample for practical use. This wiki will not be updated.*
  3. = Echo Example =
  4. <wiki:toc max_depth="3" />
  5. == Web Page ==
  6. Web Page is an echo client.
  7. Whenever user enters a message, the page makes !JavaScript object containing it and send it to the echo server using the jQuery Stream, and when a message returns from the server, the page displays it.
  8. According to the server support, the stream type may have to be set to {{{http}}} and its URL may have to be modified.
  9. Since the web page is plain HTML page and the jQuery Stream is plain JavaScript library, they don't need any server-side support, so choose a server implementation in [#WebSocket_/HTTP_Echo_Server WebSocket/HTTP Echo Server] according to your preference.
  10. {{{/echo.html}}}
  11. {{{
  12. <!DOCTYPE html>
  13. <html>
  14. <head>
  15. <title>Echo</title>
  16. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  17. <script type="text/javascript" src="./js/jquery-1.4.1.js"></script>
  18. <script type="text/javascript" src="./js/jquery.stream.js"></script>
  19. <script type="text/javascript">
  20. $.stream.setup({enableXDR: true});
  21. $(function() {
  22. $.stream("./echo", {
  23. open: function() {
  24. $("#textfield").removeAttr("disabled").focus();
  25. },
  26. message: function(event) {
  27. $("<p />").text(event.data).prependTo("#content");
  28. },
  29. error: function() {
  30. $("#textfield").attr("disabled", "disabled");
  31. },
  32. close: function() {
  33. $("#textfield").attr("disabled", "disabled");
  34. }
  35. });
  36. $("#textfield").keyup(function(event) {
  37. if (event.which === 13 && $.trim(this.value)) {
  38. $.stream().send({message: this.value});
  39. this.value = "";
  40. }
  41. });
  42. });
  43. </script>
  44. <style>
  45. body {padding: 0; margin: 0; font-family: 'Trebuchet MS','Malgun Gothic'; font-size: 62.5%; color: #333333}
  46. #editor {margin: 15px 25px;}
  47. #textfield {width: 100%; height: 28px; line-height: 28px; font-family: 'Trebuchet MS','Malgun Gothic';
  48. border: medium none; border-color: #E5E5E5 #DBDBDB #D2D2D2; border-style: solid; border-width: 1px;}
  49. #content {height: 100%; overflow-y: auto; padding: 0 25px;}
  50. #content p {margin: 0; padding: 0; font-size: 1.3em; color: #444444; line-height: 1.7em; word-wrap: break-word;}
  51. </style>
  52. </head>
  53. <body>
  54. <div id="editor">
  55. <input id="textfield" type="text" disabled="disabled" />
  56. </div>
  57. <div id="content"></div>
  58. </body>
  59. </html>
  60. }}}
  61. == !WebSocket/HTTP Echo Server ==
  62. !WebSocket/HTTP Echo Server is just a typical echo server which sends back the client's message through ws or http protocol.
  63. See ServerSideProcessing for details about what methods do what.
  64. If you have implemented the server logic using technology or platform not listed, please annotate it and create a issue to append that code to this wiki.
  65. === Java - Servlet 3.0 ===
  66. The Servlet 3.0 specification includes support for asynchronous processing of request, but there is no support for !WebSocket.
  67. The following example runs with any servlet container implementing Servlet 3.0 such as Tomcat 7 and Jetty 8 only via HTTP protocol.
  68. {{{flowersinthesand.example.EchoServlet}}}
  69. {{{
  70. package flowersinthesand.example;
  71. import java.io.IOException;
  72. import java.io.PrintWriter;
  73. import java.util.Map;
  74. import java.util.UUID;
  75. import java.util.concurrent.ConcurrentHashMap;
  76. import javax.servlet.AsyncContext;
  77. import javax.servlet.AsyncEvent;
  78. import javax.servlet.AsyncListener;
  79. import javax.servlet.ServletException;
  80. import javax.servlet.annotation.WebServlet;
  81. import javax.servlet.http.HttpServlet;
  82. import javax.servlet.http.HttpServletRequest;
  83. import javax.servlet.http.HttpServletResponse;
  84. // Registers a servlet by newly introduced annotation, @WebServlet
  85. @WebServlet(urlPatterns = "/chat", asyncSupported = true)
  86. public class EchoServlet extends HttpServlet {
  87. private static final long serialVersionUID = -8823775068689773674L;
  88. private Map<String, AsyncContext> asyncContexts = new ConcurrentHashMap<String, AsyncContext>();
  89. // GET method is used to open stream
  90. @Override
  91. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  92. throws ServletException, IOException {
  93. // Rejects WebSocket opening handshake
  94. if ("websocket".equalsIgnoreCase(request.getHeader("Upgrade"))) {
  95. response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
  96. return;
  97. }
  98. response.setCharacterEncoding("utf-8");
  99. // Content-Type header
  100. response.setContentType("text/plain");
  101. // Access-Control-Allow-Origin header
  102. response.setHeader("Access-Control-Allow-Origin", "*");
  103. PrintWriter writer = response.getWriter();
  104. // Id
  105. final String id = UUID.randomUUID().toString();
  106. writer.print(id);
  107. writer.print(';');
  108. // Padding
  109. for (int i = 0; i < 1024; i++) {
  110. writer.print(' ');
  111. }
  112. writer.print(';');
  113. writer.flush();
  114. // Starts asynchronous mode
  115. // AsyncContext, AsyncListener and AsyncEvent are used for asynchronous operation
  116. final AsyncContext ac = request.startAsync();
  117. ac.setTimeout(5 * 60 * 1000);
  118. ac.addListener(new AsyncListener() {
  119. public void onComplete(AsyncEvent event) throws IOException {
  120. asyncContexts.remove(id);
  121. }
  122. public void onTimeout(AsyncEvent event) throws IOException {
  123. asyncContexts.remove(id);
  124. }
  125. public void onError(AsyncEvent event) throws IOException {
  126. asyncContexts.remove(id);
  127. }
  128. public void onStartAsync(AsyncEvent event) throws IOException {
  129. }
  130. });
  131. // Manages AsyncContext instances by the id
  132. asyncContexts.put(id, ac);
  133. }
  134. // POST method is used to handle data sent by user via the stream
  135. @Override
  136. protected void doPost(HttpServletRequest request, HttpServletResponse response)
  137. throws ServletException, IOException {
  138. request.setCharacterEncoding("utf-8");
  139. // Finds AsyncContext instance by stream id
  140. AsyncContext ac = asyncContexts.get(request.getParameter("metadata.id"));
  141. if (ac == null) {
  142. return;
  143. }
  144. // Close request means that browser closed this stream
  145. if ("close".equals(request.getParameter("metadata.type"))) {
  146. ac.complete();
  147. return;
  148. }
  149. String message = request.getParameter("message");
  150. PrintWriter writer = ac.getResponse().getWriter();
  151. // Sends message
  152. writer.print(message.length() + ";" + message + ";");
  153. writer.flush();
  154. }
  155. }
  156. }}}
  157. === Java - Servlet 3.0 and Jetty 8 ===
  158. Jetty is a servlet container, implements Servlet 3.0 and also provides !WebSocket based on servlet.
  159. Currently, Jetty 8.0.0 M3 does not seem to support new annotations.
  160. {{{web.xml}}}
  161. {{{
  162. <web-app
  163. xmlns="http://java.sun.com/xml/ns/javaee"
  164. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  165. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  166. version="3.0">
  167. <servlet>
  168. <servlet-name>Echo</servlet-name>
  169. <servlet-class>flowersinthesand.example.EchoServlet</servlet-class>
  170. <async-supported>true</async-supported>
  171. </servlet>
  172. <servlet-mapping>
  173. <servlet-name>Echo</servlet-name>
  174. <url-pattern>/echo</url-pattern>
  175. </servlet-mapping>
  176. </web-app>
  177. }}}
  178. {{{flowersinthesand.example.EchoServlet}}}
  179. {{{
  180. package flowersinthesand.example;
  181. import java.io.IOException;
  182. import java.io.PrintWriter;
  183. import java.util.Map;
  184. import java.util.UUID;
  185. import java.util.concurrent.ConcurrentHashMap;
  186. import javax.servlet.AsyncContext;
  187. import javax.servlet.AsyncEvent;
  188. import javax.servlet.AsyncListener;
  189. import javax.servlet.ServletException;
  190. import javax.servlet.http.HttpServletRequest;
  191. import javax.servlet.http.HttpServletResponse;
  192. import org.eclipse.jetty.util.UrlEncoded;
  193. import org.eclipse.jetty.websocket.WebSocket;
  194. import org.eclipse.jetty.websocket.WebSocketServlet;
  195. // WebSocketServlet extending HttpServlet is base class
  196. public class EchoServlet extends WebSocketServlet {
  197. private static final long serialVersionUID = -8823775068689773674L;
  198. private Map<String, AsyncContext> asyncContexts = new ConcurrentHashMap<String, AsyncContext>();
  199. @Override
  200. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  201. throws ServletException, IOException {
  202. response.setCharacterEncoding("utf-8");
  203. response.setContentType("text/plain");
  204. response.setHeader("Access-Control-Allow-Origin", "*");
  205. PrintWriter writer = response.getWriter();
  206. final String id = UUID.randomUUID().toString();
  207. writer.print(id);
  208. writer.print(';');
  209. for (int i = 0; i < 1024; i++) {
  210. writer.print(' ');
  211. }
  212. writer.print(';');
  213. writer.flush();
  214. final AsyncContext ac = request.startAsync();
  215. ac.setTimeout(5 * 60 * 1000);
  216. ac.addListener(new AsyncListener() {
  217. public void onComplete(AsyncEvent event) throws IOException {
  218. asyncContexts.remove(id);
  219. }
  220. public void onTimeout(AsyncEvent event) throws IOException {
  221. asyncContexts.remove(id);
  222. }
  223. public void onError(AsyncEvent event) throws IOException {
  224. asyncContexts.remove(id);
  225. }
  226. public void onStartAsync(AsyncEvent event) throws IOException {
  227. }
  228. });
  229. asyncContexts.put(id, ac);
  230. }
  231. @Override
  232. protected void doPost(HttpServletRequest request, HttpServletResponse response)
  233. throws ServletException, IOException {
  234. request.setCharacterEncoding("utf-8");
  235. AsyncContext ac = asyncContexts.get(request.getParameter("metadata.id"));
  236. if (ac == null) {
  237. return;
  238. }
  239. if ("close".equals(request.getParameter("metadata.type"))) {
  240. ac.complete();
  241. return;
  242. }
  243. String message = request.getParameter("message");
  244. PrintWriter writer = ac.getResponse().getWriter();
  245. writer.print(message.length() + ";" + message + ";");
  246. writer.flush();
  247. }
  248. // Handles WebSocket connection
  249. @Override
  250. public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
  251. // WebSocket for receiving text messages
  252. return new WebSocket.OnTextMessage() {
  253. Connection connection;
  254. @Override
  255. public void onOpen(Connection connection) {
  256. this.connection = connection;
  257. }
  258. @Override
  259. public void onClose(int closeCode, String message) {
  260. }
  261. @Override
  262. public void onMessage(String data) {
  263. // Decodes query string
  264. UrlEncoded parameters = new UrlEncoded(data);
  265. try {
  266. connection.sendMessage(parameters.getString("message"));
  267. } catch (IOException e) {
  268. throw new RuntimeException(e);
  269. }
  270. }
  271. };
  272. }
  273. }
  274. }}}