// ========================================================================
// Copyright (c) 2008-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package com.acme;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationSupport;
// Simple asynchronous Chat room.
// This does not handle duplicate usernames or multiple frames/tabs from the same browser
// Some code is duplicated for clarity.
public class ChatServlet extends HttpServlet
{
// inner class to hold message queue for each chat room member
class Member
{
String _name;
Continuation _continuation;
Queue<String> _queue = new LinkedList<String>();
}
Map<String,Map<String,Member>> _rooms = new HashMap<String,Map<String, Member>>();
// Handle Ajax calls from browser
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
// Ajax calls are form encoded
String action = request.getParameter("action");
String message = request.getParameter("message");
String username = request.getParameter("user");
if (action.equals("join"))
join(request,response,username);
else if (action.equals("poll"))
poll(request,response,username);
else if (action.equals("chat"))
chat(request,response,username,message);
}
private synchronized void join(HttpServletRequest request,HttpServletResponse response,String username)
throws IOException
{
Member member = new Member();
member._name=username;
Map<String,Member> room=_rooms.get(request.getPathInfo());
if (room==null)
{
room=new HashMap<String,Member>();
_rooms.put(request.getPathInfo(),room);
}
room.put(username,member);
response.setContentType("text/json;charset=utf-8");
PrintWriter out=response.getWriter();
out.print("{action:\"join\"}");
}
private synchronized void poll(HttpServletRequest request,HttpServletResponse response,String username)
throws IOException
{
Map<String,Member> room=_rooms.get(request.getPathInfo());
if (room==null)
{
response.sendError(503);
return;
}
Member member = room.get(username);
if (member==null)
{
response.sendError(503);
return;
}
synchronized(member)
{
if (member._queue.size()>0)
{
// Send one chat message
response.setContentType("text/json;charset=utf-8");
StringBuilder buf=new StringBuilder();
buf.append("{\"action\":\"poll\",");
buf.append("\"from\":\"");
buf.append(member._queue.poll());
buf.append("\",");
String message = member._queue.poll();
int quote=message.indexOf('"');
while (quote>=0)
{
message=message.substring(0,quote)+'\\'+message.substring(quote);
quote=message.indexOf('"',quote+2);
}
buf.append("\"chat\":\"");
buf.append(message);
buf.append("\"}");
byte[] bytes = buf.toString().getBytes("utf-8");
response.setContentLength(bytes.length);
response.getOutputStream().write(bytes);
}
else
{
Continuation continuation = ContinuationSupport.getContinuation(request);
if (continuation.isInitial())
{
// No chat in queue, so suspend and wait for timeout or chat
continuation.setTimeout(20000);
continuation.suspend();
member._continuation=continuation;
}
else
{
// Timeout so send empty response
response.setContentType("text/json;charset=utf-8");
PrintWriter out=response.getWriter();
out.print("{action:\"poll\"}");
}
}
}
}
private synchronized void chat(HttpServletRequest request,HttpServletResponse response,String username,String message)
throws IOException
{
Map<String,Member> room=_rooms.get(request.getPathInfo());
if (room!=null)
{
// Post chat to all members
for (Member m:room.values())
{
synchronized (m)
{
m._queue.add(username); // from
m._queue.add(message); // chat
// wakeup member if polling
if (m._continuation!=null)
{
m._continuation.resume();
m._continuation=null;
}
}
}
}
response.setContentType("text/json;charset=utf-8");
PrintWriter out=response.getWriter();
out.print("{action:\"chat\"}");
}
// Serve the HTML with embedded CSS and Javascript.
// This should be static content and should use real JS libraries.
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
if (request.getParameter("action")!=null)
doPost(request,response);
else
getServletContext().getNamedDispatcher("default").forward(request,response);
}
}