001 package examples.webapp.servlet.async;
002
003 import weblogic.servlet.http.RequestResponseKey;
004 import weblogic.servlet.http.AbstractAsyncServlet;
005
006 import javax.servlet.ServletContext;
007 import javax.servlet.http.HttpServletRequest;
008 import java.util.Map;
009 import java.util.Hashtable;
010 import java.util.Iterator;
011 import java.util.List;
012 import java.util.ArrayList;
013 import java.io.IOException;
014
015 /**
016 * @author Copyright (c) 2006 by BEA Systems, Inc. All Rights Reserved.
017 */
018
019 /**
020 * The ChatSession class is responsible for maintaining the user and messages.
021 */
022 public class ChatSession {
023 private static final String CHAT_SESSION = "chat_session";
024 private static final String CHAT_MESSAGE = "chat_message";
025 private static final String CHAT_HANDLE = "chat_handle";
026 public static final String XML_PROLOGUE = "<?xml version=\"1.0\"?>";
027 private static final String RETRY_MSG = XML_PROLOGUE + "<messages></messages>";
028 private static final long CHAT_INACTIVITY_TIMEOUT = 5 * 60 * 1000; // msecs
029
030 private String id_;
031 private Map<String, UserInfo> users_;
032
033 public ChatSession(String chatId) {
034 this.id_ = chatId;
035 this.users_ = new Hashtable<String, UserInfo>();
036 }
037
038 /**
039 * Handle message polling by the clients.
040 *
041 * @param rrk - the RequestResponseKey, containing the servlet request and response
042 * @return true if messages are sending out immediately.
043 * @throws IOException - if an input or output error occurs
044 */
045 public boolean registerForMessages(RequestResponseKey rrk) throws IOException {
046 String user = getUserFromRequest(rrk.getRequest());
047 UserInfo info = users_.get(user);
048
049 log("registerForMessages " + user + " rrk=" + rrk);
050 if (info == null) {
051 info = new UserInfo(user);
052 users_.put(user, info);
053 }
054 return info.registerForMessages(rrk);
055 }
056
057 private static void log(String s) {
058 System.out.println(s);
059 }
060
061 /**
062 * Handle new arriving message.
063 *
064 * @param request
065 * @throws IOException
066 */
067 public void receiveMessage(HttpServletRequest request) throws IOException {
068 String msg = getMessageFromRequest(request);
069 String user = getUserFromRequest(request);
070 log("receiveMessage " + user + " msg=" + msg);
071 StringBuffer xmlMsg = new StringBuffer();
072 xmlMsg.append("<message from=\"").append(user);
073 xmlMsg.append("\">").append(msg).append("</message>");
074 processMessage(xmlMsg.toString());
075 }
076
077 /**
078 * Broadcast the messsage to all users.
079 *
080 * @param msg
081 */
082 private void processMessage(String msg) {
083 for (Iterator<UserInfo> it = users_.values().iterator(); it.hasNext();) {
084 UserInfo info = it.next();
085 if (info.inactive()) {
086 it.remove();
087 } else {
088 info.handleMessage(msg);
089 }
090 }
091 }
092
093 /**
094 * Send a retry message to client upon timeout.
095 *
096 * @param rrk - the RequestResponseKey, containing the servlet request and response
097 * @throws IOException - if an input or output error occurs
098 */
099 public void handleTimeout(RequestResponseKey rrk) throws IOException {
100 String user = getUserFromRequest(rrk.getRequest());
101 UserInfo info = users_.get(user);
102 log("handleTimeout " + user);
103 if (info != null) info.sendRetryMessage(rrk);
104 }
105
106 public Iterator<String> getUsers() {
107 return users_.keySet().iterator();
108 }
109
110 private static String getMessageFromRequest(HttpServletRequest request) {
111 return request.getParameter(CHAT_MESSAGE);
112 }
113
114 private static String getUserFromRequest(HttpServletRequest request) {
115 return (String) request.getSession().getAttribute(CHAT_HANDLE);
116 }
117
118 /**
119 * Return a single instance of ChatSession.
120 *
121 * @param context
122 * @return
123 */
124 public static synchronized ChatSession getOrCreateChatSession(
125 ServletContext context) {
126 ChatSession chat = (ChatSession) context.getAttribute(CHAT_SESSION);
127 if (chat != null) return chat;
128 chat = new ChatSession(Long.toString(System.currentTimeMillis()));
129 context.setAttribute(CHAT_SESSION, chat);
130 return chat;
131 }
132
133 static class UserInfo {
134 long lastAccess;
135 String userName;
136 List msgQueue;
137 RequestResponseKey responseKey;
138
139 public UserInfo(String user) {
140 lastAccess = System.currentTimeMillis();
141 userName = user;
142 msgQueue = new ArrayList();
143 }
144
145 /**
146 * Send the message to the client if a request is pending;
147 * put the message into the user's own queue if no request is pending.
148 *
149 * @param msg
150 * @return return true when message is send to the client directly.
151 */
152 public synchronized boolean handleMessage(String msg) {
153 if (responseKey != null) {
154 try {
155 // send out the msg
156 log("UserInfo.handleMessage notify called");
157 AbstractAsyncServlet.notify(responseKey, msg);
158 responseKey = null;
159 return true;
160 } catch (IOException e) {
161 log("UserInfo.handleMessage notify failed");
162 e.printStackTrace();
163 }
164 }
165 log("UserInfo.handleMessage queuing message " + msg);
166 msgQueue.add(msg);
167 return false;
168 }
169
170 public boolean inactive() {
171 return (System.currentTimeMillis() - lastAccess > CHAT_INACTIVITY_TIMEOUT);
172 }
173
174 /**
175 * Handle the request right now if there are queued messages,otherwise handle it
176 * in the future.
177 *
178 * @param rrk - the RequestResponseKey, containing the servlet request and response
179 * @return true if the request will be handled in future.
180 * @throws IOException - if an input or output error occurs
181 */
182 public synchronized boolean registerForMessages(RequestResponseKey rrk)
183 throws IOException {
184 this.lastAccess = System.currentTimeMillis();
185 if (!msgQueue.isEmpty()) {
186 log("UserInfo.registerForMessages sending queued messages. " + msgQueue.size());
187 StringBuffer xmlMsg = new StringBuffer(XML_PROLOGUE + "<messages>");
188 for (Iterator it = msgQueue.iterator(); it.hasNext();) {
189 xmlMsg.append((String) it.next());
190 it.remove();
191 }
192 xmlMsg.append("</messages>");
193 rrk.getResponse().getWriter().write(xmlMsg.toString());
194 responseKey = null;
195 return false;
196 }
197 responseKey = rrk;
198 log("UserInfo.registerForMessages registered rrk=" + rrk);
199 return true;
200 }
201
202 /**
203 * Send an empty message to ask the client to retry it again.
204 *
205 * @param rrk - the RequestResponseKey, containing the servlet request and response
206 * @throws IOException - if an input or output error occurs
207 */
208 public synchronized void sendRetryMessage(RequestResponseKey rrk)
209 throws IOException {
210 if (responseKey != null) {
211 log("WARN UserInfo.sendRetryMessage sending RETRY on " + responseKey);
212 responseKey = null;
213 }
214 log("UserInfo.sendRetryMessage sending RETRY on " + rrk);
215 rrk.getResponse().getWriter().write(RETRY_MSG);
216 }
217 }
218
219
220 }
|