1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 import java.util.concurrent.atomic.AtomicReference;
28
29 import javax.servlet.AsyncContext;
30 import javax.servlet.AsyncEvent;
31 import javax.servlet.AsyncListener;
32 import javax.servlet.ServletException;
33 import javax.servlet.http.HttpServlet;
34 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpServletResponse;
36
37
38
39
40 @SuppressWarnings("serial")
41 public class ChatServlet extends HttpServlet
42 {
43
44
45 class Member implements AsyncListener
46 {
47 final String _name;
48 final AtomicReference<AsyncContext> _async=new AtomicReference<>();
49 final Queue<String> _queue = new LinkedList<String>();
50
51 Member(String name)
52 {
53 _name=name;
54 }
55
56 @Override
57 public void onTimeout(AsyncEvent event) throws IOException
58 {
59 AsyncContext async = _async.get();
60 if (async!=null && _async.compareAndSet(async,null))
61 {
62 HttpServletResponse response = (HttpServletResponse)async.getResponse();
63 response.setContentType("text/json;charset=utf-8");
64 PrintWriter out=response.getWriter();
65 out.print("{action:\"poll\"}");
66 async.complete();
67 }
68 }
69
70 @Override
71 public void onStartAsync(AsyncEvent event) throws IOException
72 {
73 event.getAsyncContext().addListener(this);
74 }
75
76 @Override
77 public void onError(AsyncEvent event) throws IOException
78 {
79 }
80
81 @Override
82 public void onComplete(AsyncEvent event) throws IOException
83 {
84 }
85 }
86
87 Map<String,Map<String,Member>> _rooms = new HashMap<String,Map<String, Member>>();
88
89
90
91 @Override
92 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
93 {
94
95 String action = request.getParameter("action");
96 String message = request.getParameter("message");
97 String username = request.getParameter("user");
98
99 if (action.equals("join"))
100 join(request,response,username);
101 else if (action.equals("poll"))
102 poll(request,response,username);
103 else if (action.equals("chat"))
104 chat(request,response,username,message);
105 }
106
107 private synchronized void join(HttpServletRequest request,HttpServletResponse response,String username)
108 throws IOException
109 {
110 Member member = new Member(username);
111 Map<String,Member> room=_rooms.get(request.getPathInfo());
112 if (room==null)
113 {
114 room=new HashMap<String,Member>();
115 _rooms.put(request.getPathInfo(),room);
116 }
117 room.put(username,member);
118 response.setContentType("text/json;charset=utf-8");
119 PrintWriter out=response.getWriter();
120 out.print("{action:\"join\"}");
121 }
122
123 private synchronized void poll(HttpServletRequest request,HttpServletResponse response,String username)
124 throws IOException
125 {
126 Map<String,Member> room=_rooms.get(request.getPathInfo());
127 if (room==null)
128 {
129 response.sendError(503);
130 return;
131 }
132 final Member member = room.get(username);
133 if (member==null)
134 {
135 response.sendError(503);
136 return;
137 }
138
139 synchronized(member)
140 {
141 if (member._queue.size()>0)
142 {
143
144 response.setContentType("text/json;charset=utf-8");
145 StringBuilder buf=new StringBuilder();
146
147 buf.append("{\"action\":\"poll\",");
148 buf.append("\"from\":\"");
149 buf.append(member._queue.poll());
150 buf.append("\",");
151
152 String message = member._queue.poll();
153 int quote=message.indexOf('"');
154 while (quote>=0)
155 {
156 message=message.substring(0,quote)+'\\'+message.substring(quote);
157 quote=message.indexOf('"',quote+2);
158 }
159 buf.append("\"chat\":\"");
160 buf.append(message);
161 buf.append("\"}");
162 byte[] bytes = buf.toString().getBytes("utf-8");
163 response.setContentLength(bytes.length);
164 response.getOutputStream().write(bytes);
165 }
166 else
167 {
168 AsyncContext async = request.startAsync();
169 async.setTimeout(10000);
170 async.addListener(member);
171 if (!member._async.compareAndSet(null,async))
172 throw new IllegalStateException();
173 }
174 }
175 }
176
177 private synchronized void chat(HttpServletRequest request,HttpServletResponse response,String username,String message)
178 throws IOException
179 {
180 Map<String,Member> room=_rooms.get(request.getPathInfo());
181 if (room!=null)
182 {
183
184 for (Member m:room.values())
185 {
186 synchronized (m)
187 {
188 m._queue.add(username);
189 m._queue.add(message);
190
191
192 AsyncContext async=m._async.get();
193 if (async!=null & m._async.compareAndSet(async,null))
194 async.dispatch();
195 }
196 }
197 }
198
199 response.setContentType("text/json;charset=utf-8");
200 PrintWriter out=response.getWriter();
201 out.print("{action:\"chat\"}");
202 }
203
204
205
206 @Override
207 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
208 {
209 if (request.getParameter("action")!=null)
210 doPost(request,response);
211 else
212 getServletContext().getNamedDispatcher("default").forward(request,response);
213 }
214
215 }