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