View Javadoc

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