PageRenderTime 52ms CodeModel.GetById 13ms app.highlight 34ms RepoModel.GetById 2ms app.codeStats 0ms

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