ChatSession.java
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 = 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 rrkthrows 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 requestthrows 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 rrkthrows IOException {
100     String user = getUserFromRequest(rrk.getRequest());
101     UserInfo info = users_.get(user);
102     log("handleTimeout " + user);
103     if (info != nullinfo.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 (Stringrequest.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 = (ChatSessioncontext.getAttribute(CHAT_SESSION);
127     if (chat != nullreturn 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((Stringit.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 }