/** *This is a servlet implementing *the server-side functionality of the MagnificentChat Web application. * *Class A_8 * *Version 1.0 * *@author Martin Carlsson * *Internetprogrammering 2 - Course, , Examination Assignment */ import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; /** *Class A_8 is a "Http Chat Servlet" that saves client messages in *sessions. Clients are connected to A_8 by using MagnificentChatApplets. * *@version 1.0 * *@author Martin Carlsson * *@see javax.servlet.http.HttpServlet */ public class A_8 extends HttpServlet { private Hashtable users = new Hashtable(); // Used for saving the client name and session private String userNameTaken = "***nameTaken***"; // Flag sent to a MagnificentChatApplet if a name is taken /** *Gets the client's inbox and returns the latest message, *i.e. the one that is first in line. *@param req client request *@param res used for responding to client requests */ public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { HttpSession session = req.getSession(); res.setContentType("text/plain"); PrintWriter out = res.getWriter(); Inbox inbox =(Inbox) session.getAttribute("inbox"); if(inbox != null) { /* Class WakeUp is used for avoiding "connection reset" when the waiting for a message has been too long. (the problem occurs in the getFirst() method in class Inbox) */ WakeUp wakeUp = new WakeUp(inbox, 60000, true); Thread wakeUpActivity = new Thread(wakeUp); // Tråden för WakeUp-klassen wakeUpActivity.start(); /*The client is put in a waiting mode until a message arrives, or until class WakeUp puts a dummy message in the client inbox, which it does once every minute */ String msg= (String)inbox.getFirst(); if(!msg.equals("wake up")) out.println(msg); wakeUp.setLooping(false); } } // end doGet() /** *Takes care of client messages by putting them in every other connected client inbox, or - if the message is private - only in the client inbox that the message is ment for. *@param req client request *@param res used for responding client requests */ public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { HttpSession session = req.getSession(); res.setContentType("text/plain"); PrintWriter out = res.getWriter(); //Parameters posted by clients String message = req.getParameter("message"); String userToAdd = req.getParameter("addUser"); String userToRemove = req.getParameter("removeUser"); String privateMsg = req.getParameter("privateMsg"); if(message != null) res.setStatus(res.SC_NO_CONTENT); // Nothing is sent back else if(userToAdd != null ) { addUser(userToAdd, out, session, res.encodeUrl(req.getRequestURI())); session.setMaxInactiveInterval(-1); // The session has no timeout } else if(userToRemove != null) { removeUser(userToRemove); res.setStatus(res.SC_NO_CONTENT); // // Nothing is sent back } // Inbox object Inbox myInbox = (Inbox)session.getAttribute("inbox"); String me = (String)session.getAttribute("user"); /*Incoming messages is put last in the line of messages in each client inbox */ Enumeration en = users.elements(); String noMessageSent = null; while(en.hasMoreElements()) { HttpSession s =(HttpSession) en.nextElement(); Inbox inbox = (Inbox) s.getAttribute("inbox"); if(inbox != null) { String user = (String)s.getAttribute("user"); if(privateMsg != null && privateMsg != "") { String[] msgArr = privateMsg.split(","); if(!user.equals(me)) { if(msgArr[0].equals(user)) { inbox.putLast("private message,".concat(me + ","+ msgArr[1])); s.setAttribute("inbox",inbox); noMessageSent = null; break; } else { noMessageSent = "No message sent , " + msgArr[0]; continue; } } } else if(message != null) { inbox.putLast(me+">"+message); } else if(userToAdd != null || userToRemove != null) { Enumeration enu = users.keys(); String u = "users,"; while(enu.hasMoreElements()) u+=(enu.nextElement()+","); inbox.putLast(u);//Puts the comma separated list last in the inbox if(userToRemove != null) inbox.putLast(me + " has left the building!"); else if(userToAdd != null) if(!me.equals(user) && session.isNew()) inbox.putLast(me + " has joined us!"); } } } if(noMessageSent != null && privateMsg != null) myInbox.putLast(noMessageSent); if(userToAdd != null) { myInbox.putLast("***Welcome, " + me + "!***"); } else if(userToRemove != null) session.invalidate(); } //end doPost() /** *Adds a client after the client has chosen a correct username, *or if the client chosen to be anonymous *@param user the name the user wants *@param out out stream to write to *@param session the user session object *@param rewritten address now containing a sessionId */ public void addUser(String user,PrintWriter out, HttpSession session, String rewritten) { if(user.equals("guest")) user+=session.getId().hashCode()/10000; if(users.containsKey(user)) out.println(userNameTaken); else { users.put(user, session); out.println(user+","+rewritten); } session.setAttribute("inbox",new Inbox()); session.setAttribute("user",user); } // end addUser() /** *Removes a user *@param user user to remove */ public void removeUser(String user) { users.remove(user); } /** *Class for creating client inboxes *where messages is retrieved by the "first in first out" rule. */ class Inbox extends Vector { /** *Adds an object/message to the end of the line of messages *@param obj object/message to add */ public synchronized void putLast(Object obj) { add(obj); notify(); } /** *Adds an object/message first in the line of messages. *@param obj object/message to add */ public synchronized void putFirst(Object obj) { add(0,obj); notify(); } /** *Gets a message from the top of the list *@return the first object/message */ public synchronized Object getFirst() { while(isEmpty()) { try { wait(); } catch(InterruptedException e) { } } Object obj = firstElement(); remove(0); return obj; } };// End class Inbox /** *Avoids "Connection reset" problems. */ class WakeUp implements Runnable { private int wakeUpFrequency; private boolean looping = true; private Inbox inbox; /**Constructs a WakeUp instance with a reference to Inbox instance and a number indicating a wakeup frequence. *@param inbox reference to an Inbox instance *@param wakeUpFrequency how frequent should the wakeup call come*/ public WakeUp(Inbox inbox, int wakeUpFrequency) { this.inbox = inbox; this.wakeUpFrequency = wakeUpFrequency; } /**Constructs a WakeUp instance with a reference to Inbox instance, a number indicating a wakeup frequence and a boolean flag. *@param inbox reference to an Inbox instance *@param wakeUpFrequency how frequent should the wakeup call come *@param looping om loopning ska göras*/ public WakeUp(Inbox inbox, int wakeUpFrequency, boolean looping) { this.inbox = inbox; this.wakeUpFrequency = wakeUpFrequency; this.looping = looping; } /** *Adds a dummy message to a user inbox to avoid connection reset at waiting clients */ public void run() { while(true) { try { Thread.sleep(wakeUpFrequency); synchronized(this) { while (!looping) wait(); } } catch (InterruptedException e) { } inbox.putLast("wake up"); } } /** *Sets the looping frequency *@param wakeUpFrequency by what frequency it should loop */ public synchronized void setFrequency(int wakeUpFrequency) { this.wakeUpFrequency = wakeUpFrequency; } /** *Returns the looping frequency *@return loopfrekvensen */ public int getFrequency() { return wakeUpFrequency; } public synchronized void setLooping(boolean b) { looping = b; if (looping) notify(); } /** *Does the thread loop or not? *@return looping true eller false */ public boolean isLooping() { return looping; } };// End class WakeUp /** *Removes all sessions */ public void destroy() { Enumeration en = users.elements(); while(en.hasMoreElements()) { HttpSession s =(HttpSession) en.nextElement(); s.invalidate(); } users = null; } } // End class A_8