View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package com.acme;
20  
21  import java.io.IOException;
22  import java.io.PrintWriter;
23  import java.util.HashMap;
24  import java.util.LinkedList;
25  import java.util.Map;
26  import java.util.Queue;
27  
28  import javax.servlet.ServletException;
29  import javax.servlet.http.HttpServlet;
30  import javax.servlet.http.HttpServletRequest;
31  import javax.servlet.http.HttpServletResponse;
32  
33  import org.eclipse.jetty.continuation.Continuation;
34  import org.eclipse.jetty.continuation.ContinuationSupport;
35  
36  
37  // Simple asynchronous Chat room.
38  // This does not handle duplicate usernames or multiple frames/tabs from the same browser
39  // Some code is duplicated for clarity.
40  public class ChatServlet extends HttpServlet
41  {
42      
43      // inner class to hold message queue for each chat room member
44      class Member
45      {
46          String _name;
47          Continuation _continuation;
48          Queue<String> _queue = new LinkedList<String>();
49      }
50  
51      Map<String,Map<String,Member>> _rooms = new HashMap<String,Map<String, Member>>();
52      
53      
54      // Handle Ajax calls from browser
55      @Override
56      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
57      {   
58          // Ajax calls are form encoded
59          String action = request.getParameter("action");
60          String message = request.getParameter("message");
61          String username = request.getParameter("user");
62  
63          if (action.equals("join"))
64              join(request,response,username);
65          else if (action.equals("poll"))
66              poll(request,response,username);
67          else if (action.equals("chat"))
68              chat(request,response,username,message);
69      }
70  
71      private synchronized void join(HttpServletRequest request,HttpServletResponse response,String username)
72      throws IOException
73      {
74          Member member = new Member();
75          member._name=username;
76          Map<String,Member> room=_rooms.get(request.getPathInfo());
77          if (room==null)
78          {
79              room=new HashMap<String,Member>();
80              _rooms.put(request.getPathInfo(),room);
81          }
82          room.put(username,member); 
83          response.setContentType("text/json;charset=utf-8");
84          PrintWriter out=response.getWriter();
85          out.print("{action:\"join\"}");
86      }
87  
88      private synchronized void poll(HttpServletRequest request,HttpServletResponse response,String username)
89      throws IOException
90      {
91          Map<String,Member> room=_rooms.get(request.getPathInfo());
92          if (room==null)
93          {
94              response.sendError(503);
95              return;
96          }
97          Member member = room.get(username);
98          if (member==null)
99          {
100             response.sendError(503);
101             return;
102         }
103 
104         synchronized(member)
105         {
106             if (member._queue.size()>0)
107             {
108                 // Send one chat message
109                 response.setContentType("text/json;charset=utf-8");
110                 StringBuilder buf=new StringBuilder();
111 
112                 buf.append("{\"action\":\"poll\",");
113                 buf.append("\"from\":\"");
114                 buf.append(member._queue.poll());
115                 buf.append("\",");
116 
117                 String message = member._queue.poll();
118                 int quote=message.indexOf('"');
119                 while (quote>=0)
120                 {
121                     message=message.substring(0,quote)+'\\'+message.substring(quote);
122                     quote=message.indexOf('"',quote+2);
123                 }
124                 buf.append("\"chat\":\"");
125                 buf.append(message);
126                 buf.append("\"}");
127                 byte[] bytes = buf.toString().getBytes("utf-8");
128                 response.setContentLength(bytes.length);
129                 response.getOutputStream().write(bytes);
130             }
131             else 
132             {
133                 Continuation continuation = ContinuationSupport.getContinuation(request);
134                 if (continuation.isInitial()) 
135                 {
136                     // No chat in queue, so suspend and wait for timeout or chat
137                     continuation.setTimeout(20000);
138                     continuation.suspend();
139                     member._continuation=continuation;
140                 }
141                 else
142                 {
143                     // Timeout so send empty response
144                     response.setContentType("text/json;charset=utf-8");
145                     PrintWriter out=response.getWriter();
146                     out.print("{action:\"poll\"}");
147                 }
148             }
149         }
150     }
151 
152     private synchronized void chat(HttpServletRequest request,HttpServletResponse response,String username,String message)
153     throws IOException
154     {
155         Map<String,Member> room=_rooms.get(request.getPathInfo());
156         if (room!=null)
157         {
158             // Post chat to all members
159             for (Member m:room.values())
160             {
161                 synchronized (m)
162                 {
163                     m._queue.add(username); // from
164                     m._queue.add(message);  // chat
165 
166                     // wakeup member if polling
167                     if (m._continuation!=null)
168                     {
169                         m._continuation.resume();
170                         m._continuation=null;
171                     }
172                 }
173             }
174         }
175 
176         response.setContentType("text/json;charset=utf-8");
177         PrintWriter out=response.getWriter();
178         out.print("{action:\"chat\"}");  
179     }
180     
181     // Serve the HTML with embedded CSS and Javascript.
182     // This should be static content and should use real JS libraries.
183     @Override
184     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
185     {
186         if (request.getParameter("action")!=null)
187             doPost(request,response);
188         else
189             getServletContext().getNamedDispatcher("default").forward(request,response);
190     }
191     
192 }