1 package com.acme;
2
3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import java.util.HashMap;
6 import java.util.LinkedList;
7 import java.util.Map;
8 import java.util.Queue;
9
10 import javax.servlet.ServletException;
11 import javax.servlet.ServletRequest;
12 import javax.servlet.http.HttpServlet;
13 import javax.servlet.http.HttpServletRequest;
14 import javax.servlet.http.HttpServletResponse;
15 import javax.servlet.http.HttpSession;
16
17
18
19
20 public class ChatServlet extends HttpServlet
21 {
22
23 class Member
24 {
25 String _name;
26 ServletRequest _pollRequest;
27 Queue<String> _queue = new LinkedList<String>();
28 }
29
30 Map<String,Map<String,Member>> _rooms = new HashMap<String,Map<String, Member>>();
31
32
33
34 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
35 {
36
37 String action = request.getParameter("action");
38 String message = request.getParameter("message");
39 String username = request.getParameter("user");
40
41 if (action.equals("join"))
42 join(request,response,username);
43 else if (action.equals("poll"))
44 poll(request,response,username);
45 else if (action.equals("chat"))
46 chat(request,response,username,message);
47 }
48
49 private synchronized void join(HttpServletRequest request,HttpServletResponse response,String username)
50 throws IOException
51 {
52 Member member = new Member();
53 member._name=username;
54 Map<String,Member> room=_rooms.get(request.getPathInfo());
55 if (room==null)
56 {
57 room=new HashMap<String,Member>();
58 _rooms.put(request.getPathInfo(),room);
59 }
60 room.put(username,member);
61 response.setContentType("text/json;charset=utf-8");
62 PrintWriter out=response.getWriter();
63 out.print("{action:'join'}");
64 }
65
66 private synchronized void poll(HttpServletRequest request,HttpServletResponse response,String username)
67 throws IOException
68 {
69 Map<String,Member> room=_rooms.get(request.getPathInfo());
70 Member member = room.get(username);
71
72 if (member._queue.size()>0)
73 {
74
75 response.setContentType("text/json;charset=utf-8");
76 PrintWriter out=response.getWriter();
77 out.print("{action:'poll',");
78 out.print("from:'"+member._queue.poll()+"',");
79 out.print("chat:'"+member._queue.poll()+"'}");
80 }
81
82 else if (request.isTimeout())
83 {
84
85 response.setContentType("text/json;charset=utf-8");
86 PrintWriter out=response.getWriter();
87 out.print("{action:'poll'}");
88 }
89 else
90 {
91
92 request.suspend();
93 member._pollRequest=request;
94 }
95 }
96
97 private synchronized void chat(HttpServletRequest request,HttpServletResponse response,String username,String message)
98 throws IOException
99 {
100 Map<String,Member> room=_rooms.get(request.getPathInfo());
101
102 for (Member m:room.values())
103 {
104 m._queue.add(username);
105 m._queue.add(message);
106
107
108 if (m._pollRequest!=null)
109 {
110 m._pollRequest.resume();
111 m._pollRequest=null;
112 }
113 }
114
115 response.setContentType("text/json;charset=utf-8");
116 PrintWriter out=response.getWriter();
117 out.print("{action:'chat'}");
118 }
119
120
121
122
123 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
124 {
125 if (!request.getRequestURI().endsWith("/"))
126 {
127 response.sendRedirect(request.getRequestURI()+"/");
128 return;
129 }
130
131 response.setContentType("text/html");
132 PrintWriter out=response.getWriter();
133 out.println("<html><head>");
134 out.println(" <title>async chat</title>");
135 out.println(" <script type='text/javascript'>");
136 out.println(" function $() { return document.getElementById(arguments[0]); }");
137 out.println(" function $F() { return document.getElementById(arguments[0]).value; }");
138 out.println(" function getKeyCode(ev) { if (window.event) return window.event.keyCode; return ev.keyCode; } ");
139 out.println(" function xhr(method,uri,body,handler) {");
140 out.println(" var req=(window.XMLHttpRequest)?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');");
141 out.println(" req.onreadystatechange=function() { if (req.readyState==4 && handler) { eval('var o='+req.responseText);handler(o);} }");
142 out.println(" req.open(method,uri,true);");
143 out.println(" req.setRequestHeader('Content-Type','application/x-www-form-urlencoded');");
144 out.println(" req.send(body);");
145 out.println(" };");
146 out.println(" function send(action,user,message,handler){");
147 out.println(" if (message) message=message.replace('%','%25').replace('&','%26').replace('=','%3D');");
148 out.println(" if (user) user=user.replace('%','%25').replace('&','%26').replace('=','%3D');");
149 out.println(" xhr('POST','chat','action='+action+'&user='+user+'&message='+message,handler);");
150 out.println(" };");
151 out.println(" ");
152 out.println(" var room = {");
153 out.println(" join: function(name) {");
154 out.println(" this._username=name;");
155 out.println(" $('join').className='hidden';");
156 out.println(" $('joined').className='';");
157 out.println(" $('phrase').focus();");
158 out.println(" send('join', room._username,null);");
159 out.println(" send('chat', room._username,'has joined!');");
160 out.println(" send('poll', room._username,null, room._poll);");
161 out.println(" },");
162 out.println(" chat: function(text) {");
163 out.println(" if (text != null && text.length>0 )");
164 out.println(" send('chat',room._username,text);");
165 out.println(" },");
166 out.println(" _poll: function(m) {");
167 out.println(" //console.debug(m);");
168 out.println(" if (m.chat){");
169 out.println(" var chat=document.getElementById('chat');");
170 out.println(" var spanFrom = document.createElement('span');");
171 out.println(" spanFrom.className='from';");
172 out.println(" spanFrom.innerHTML=m.from+': ';");
173 out.println(" var spanText = document.createElement('span');");
174 out.println(" spanText.className='text';");
175 out.println(" spanText.innerHTML=m.chat;");
176 out.println(" var lineBreak = document.createElement('br');");
177 out.println(" chat.appendChild(spanFrom);");
178 out.println(" chat.appendChild(spanText);");
179 out.println(" chat.appendChild(lineBreak);");
180 out.println(" chat.scrollTop = chat.scrollHeight - chat.clientHeight; ");
181 out.println(" }");
182 out.println(" if (m.action=='poll')");
183 out.println(" send('poll', room._username,null, room._poll);");
184 out.println(" },");
185 out.println(" _end:''");
186 out.println(" };");
187 out.println(" </script>");
188 out.println(" <style type='text/css'>");
189 out.println(" div { border: 0px solid black; }");
190 out.println(" div#chat { clear: both; width: 40em; height: 20ex; overflow: auto; background-color: #f0f0f0; padding: 4px; border: 1px solid black; }");
191 out.println(" div#input { clear: both; width: 40em; padding: 4px; background-color: #e0e0e0; border: 1px solid black; border-top: 0px }");
192 out.println(" input#phrase { width:30em; background-color: #e0f0f0; }");
193 out.println(" input#username { width:14em; background-color: #e0f0f0; }");
194 out.println(" div.hidden { display: none; }");
195 out.println(" span.from { font-weight: bold; }");
196 out.println(" span.alert { font-style: italic; }");
197 out.println(" </style>");
198 out.println("</head><body>");
199 out.println("<div id='chat'></div>");
200 out.println("<div id='input'>");
201 out.println(" <div id='join' >");
202 out.println(" Username: <input id='username' type='text'/><input id='joinB' class='button' type='submit' name='join' value='Join'/>");
203 out.println(" </div>");
204 out.println(" <div id='joined' class='hidden'>");
205 out.println(" Chat: <input id='phrase' type='text'></input>");
206 out.println(" <input id='sendB' class='button' type='submit' name='join' value='Send'/>");
207 out.println(" </div>");
208 out.println("</div>");
209 out.println("<script type='text/javascript'>");
210 out.println("$('username').setAttribute('autocomplete','OFF');");
211 out.println("$('username').onkeyup = function(ev) { var keyc=getKeyCode(ev); if (keyc==13 || keyc==10) { room.join($F('username')); return false; } return true; } ; ");
212 out.println("$('joinB').onclick = function(event) { room.join($F('username')); return false; };");
213 out.println("$('phrase').setAttribute('autocomplete','OFF');");
214 out.println("$('phrase').onkeyup = function(ev) { var keyc=getKeyCode(ev); if (keyc==13 || keyc==10) { room.chat($F('phrase')); $('phrase').value=''; return false; } return true; };");
215 out.println("$('sendB').onclick = function(event) { room.chat($F('phrase')); $('phrase').value=''; return false; };");
216 out.println("</script>");
217 out.println("</body></html>");
218 }
219 }