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,response);
128                 if (continuation.isInitial()) 
129                 {
130                     // No chat in queue, so suspend and wait for timeout or chat
131                     continuation.suspend();
132                     member._continuation=continuation;
133                 }
134                 else
135                 {
136                     // Timeout so send empty response
137                     response.setContentType("text/json;charset=utf-8");
138                     PrintWriter out=response.getWriter();
139                     out.print("{action:\"poll\"}");
140                 }
141             }
142         }
143     }
144 
145     private synchronized void chat(HttpServletRequest request,HttpServletResponse response,String username,String message)
146     throws IOException
147     {
148         Map<String,Member> room=_rooms.get(request.getPathInfo());
149         if (room!=null)
150         {
151             // Post chat to all members
152             for (Member m:room.values())
153             {
154                 synchronized (m)
155                 {
156                     m._queue.add(username); // from
157                     m._queue.add(message);  // chat
158 
159                     // wakeup member if polling
160                     if (m._continuation!=null)
161                     {
162                         m._continuation.resume();
163                         m._continuation=null;
164                     }
165                 }
166             }
167         }
168 
169         response.setContentType("text/json;charset=utf-8");
170         PrintWriter out=response.getWriter();
171         out.print("{action:\"chat\"}");  
172     }
173     
174     // Serve the HTML with embedded CSS and Javascript.
175     // This should be static content and should use real JS libraries.
176     @Override
177     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
178     {
179         if (request.getParameter("action")!=null)
180             doPost(request,response);
181         else
182             getServletContext().getNamedDispatcher("default").forward(request,response);
183     }
184     
185 }