Lecture Notes Twenty-Five: Building A Web Chat Application in Java

Web chat with

Let's start by developing a simple servlet:

burrowww.cs.indiana.edu% cd $myServlets
burrowww.cs.indiana.edu% emacs ChatServlet.java
burrowww.cs.indiana.edu% cat ChatServlet.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*; 

public class ChatServlet extends HttpServlet {
    public void doGet (HttpServletRequest  req, 
                       HttpServletResponse res) 
                throws ServletException,
                       IOException 
    { res.setContentType("text/plain"); 
      PrintWriter out = res.getWriter();
      out.println("Yes, I am here (GET)."); 
    }

    public void doPost (HttpServletRequest  req, 
                        HttpServletResponse res) 
                throws ServletException,
                       IOException 
    {

    }
}
burrowww.cs.indiana.edu% javac ChatServlet.java
burrowww.cs.indiana.edu% 
Test it:
http://burrowww.cs.indiana.edu:32xxx/examples/servlet/ChatServlet
Now let's develop an applet:
burrowww.cs.indiana.edu% cd $TOMCAT_HOME/webapps/ROOT
burrowww.cs.indiana.edu% mkdir chat
burrowww.cs.indiana.edu% cd chat
burrowww.cs.indiana.edu% emacs Larry.html                            
burrowww.cs.indiana.edu% cat Larry.html
<html>
<applet code=ChatApplet
        width=400 height=400>
<param name=user value="Larry"> 
</applet>
</html>
burrowww.cs.indiana.edu% emacs ChatApplet.java
burrowww.cs.indiana.edu% cat ChatApplet.java
import java.applet.*;
import java.awt.*;
import java.net.*; 

public class ChatApplet extends Applet {
    TextArea text; 
    Label label;
    TextField input;
    String user; 
    public void init() {
        URL codebase = getCodeBase(); 

        user = getParameter("user"); 

        if (user == null) user = "anonymous";

        text = new TextArea(); 
        text.setEditable(false); 

        label = new Label("Type here: "); 

        input = new TextField(); 
        input.setEditable(true); 

        setLayout(new BorderLayout());

        add("Center", text); 

        Panel panel = new Panel();
        panel.setLayout(new BorderLayout());
        panel.add("West", label); 
        panel.add("Center", input); 
        add("South",  panel); 

        text.appendText("URL: " + codebase + "\n"); 
    }
    public void start() {
        text.appendText("Your name is: " + user + "\n");
    }
} 
burrowww.cs.indiana.edu% 
Compile and test the applet:
http://burrowww.cs.indiana.edu:32xxx/chat/Larry.html
Create two more files:
burrowww.cs.indiana.edu% pwd
/nfs/paca/home/user1/dgerman/apache/jakarta-tomcat/webapps/ROOT/chat
burrowww.cs.indiana.edu% ls -l Larry.html         
-rw-r--r--   1 dgerman  students      112 Nov  1 19:59 Larry.html
burrowww.cs.indiana.edu% cp Larry.html Michael.html 
burrowww.cs.indiana.edu% cp Larry.html Tony.html
burrowww.cs.indiana.edu% emacs Michael.html
burrowww.cs.indiana.edu% cat Michael.html
<html>
<applet code=ChatApplet
        width=400 height=400>
<param name=user value="Michael"> 
</applet>
</html>
burrowww.cs.indiana.edu% emacs Tony.html
burrowww.cs.indiana.edu% ls -l *.html
-rw-r--r--   1 dgerman  students      112 Nov  1 19:59 Larry.html
-rw-r--r--   1 dgerman  students      114 Nov  1 20:09 Michael.html
-rw-r--r--   1 dgerman  students      111 Nov  1 20:10 Tony.html
burrowww.cs.indiana.edu% cat Tony.html
<html>
<applet code=ChatApplet
        width=400 height=400>
<param name=user value="Tony"> 
</applet>
</html>
burrowww.cs.indiana.edu% ls -l *.html
-rw-r--r--   1 dgerman  students      112 Nov  1 19:59 Larry.html
-rw-r--r--   1 dgerman  students      114 Nov  1 20:09 Michael.html
-rw-r--r--   1 dgerman  students      111 Nov  1 20:10 Tony.html
burrowww.cs.indiana.edu% pwd
/nfs/paca/home/user1/dgerman/apache/jakarta-tomcat/webapps/ROOT/chat
burrowww.cs.indiana.edu%
Test them:
http://burrowww.cs.indiana.edu:32xxx/chat/Michael.html
http://burrowww.cs.indiana.edu:32xxx/chat/Tony.html
Now let's enhance a bit the applet:
import java.applet.*;
import java.awt.*;
import java.net.*; 

public class ChatApplet extends Applet {
    TextArea text; 
    Label label;
    TextField input;
    String user; 
    public void init() {
        URL codebase = getCodeBase(); 
        user = getParameter("user"); 
        if (user == null) user = "anonymous";
        text = new TextArea(); 
        text.setEditable(false); 
        label = new Label("Type here: "); 
        input = new TextField(); 
        input.setEditable(true); 
        setLayout(new BorderLayout());
        add("Center", text); 
        Panel panel = new Panel();
        panel.setLayout(new BorderLayout());
        panel.add("West", label); 
        panel.add("Center", input); 
        add("South",  panel); 
        text.appendText("URL: " + codebase + "\n"); 
    }
    public void start() {
        text.appendText("Your name is: " + user + "\n");
    }
    public boolean handleEvent(Event event) {
        switch (event.id) {
            case Event.ACTION_EVENT:
                if (event.target == input) {
                    text.appendText(user + ": " + input.getText() + "\n"); 
                    input.setText(""); 
                    return true; 
                }
        }
        return false; 
    } 
} 
We test again.

At this point the applet takes input from the user and writes it back immediately.

Let's enhance the applet a bit more.

Let's make it contact the server each time the user types something.

import java.applet.*;
import java.awt.*;
import java.net.*; 
import java.io.*; 

public class ChatApplet extends Applet {
    TextArea text; 
    Label label;
    TextField input;
    String user; 
    public void init() {
	URL codebase = getCodeBase(); 
	user = getParameter("user"); 
	if (user == null) user = "anonymous";
	text = new TextArea(); 
        text.setEditable(false); 
	label = new Label("Type here: "); 
	input = new TextField(); 
	input.setEditable(true); 
	setLayout(new BorderLayout());
	add("Center", text); 
	Panel panel = new Panel();
	panel.setLayout(new BorderLayout());
	panel.add("West", label); 
	panel.add("Center", input); 
	add("South",  panel); 
        text.appendText("URL: " + codebase + "\n"); 
    }
    public void start() {
	text.appendText("Your name is: " + user + "\n");
    }
    public boolean handleEvent(Event event) {
        switch (event.id) {
  	    case Event.ACTION_EVENT:
	        if (event.target == input) {
		    text.appendText(user + ": " + input.getText() + "\n"); 
		    input.setText(""); 
		    text.appendText("Server: " + contactServer()); 
                    return true; 
	        }
	}
	return false; 
    } 
    String contactServer() {
	try {
	    URL servlet       = 
              new URL("http://burrowww.cs.indiana.edu:21xxx/examples/servlet/ChatServlet"); 
            URL url           = new URL(servlet.toExternalForm()); 
            URLConnection con = url.openConnection(); 
            con.setUseCaches(false); 
            InputStream in       = con.getInputStream(); 
            DataInputStream data = new DataInputStream(new BufferedInputStream(in)); 
	    return data.readLine(); 
	} catch (Exception e) {
	    return ("Error: " + e.toString()); 
	}
    }
}
Test this again.

We could try to bounce the message off the server but it's better to abstract a bit.

Let's add some potential functionality with a new class:

import java.io.*;
import java.net.*;
import java.util.*;

public class HttpMessage {

  URL    servlet = null; 
  String args    = null; 

  public HttpMessage(URL servlet) 
  {
    this.servlet = servlet; 
  }

  public InputStream sendGetMessage() throws IOException 
  {
    return sendGetMessage(null); 
  } 

  public InputStream sendGetMessage(Properties args) throws IOException 
  { 
    String argString = ""; 

    if (args != null) { argString = "?" + toEncodedString(args); } 

    URL url           = new URL(servlet.toExternalForm() + argString); 
    URLConnection con = url.openConnection();

    con.setUseCaches(false); 
    return con.getInputStream(); 
  } 

  public InputStream sendPostMessage() throws IOException {
    return sendPostMessage(null); 
  } 

  public InputStream sendPostMessage(Properties args) throws IOException 
  {
    String argString = ""; 

    if (args != null) {
      argString = toEncodedString(args); 
    } 

    URLConnection con = servlet.openConnection(); 

    con.setDoInput(true); 
    con.setDoOutput(true); 
    con.setUseCaches(false); 
    con.setRequestProperty(
      "Content-Type", "application/x-www-form-urlencoded"
    ); 

    DataOutputStream out = new DataOutputStream(con.getOutputStream()); 
    out.writeBytes(argString); 
    out.flush(); 
    out.close(); 

    return con.getInputStream(); 
  } 

  private String toEncodedString(Properties args) 
  {
    StringBuffer buf = new StringBuffer();
    Enumeration names = args.propertyNames(); 
    while (names.hasMoreElements()) {
      String name = (String) names.nextElement(); 
      String value = args.getProperty(name); 
      buf.append(URLEncoder.encode(name) + "=" + URLEncoder.encode(value)); 
      if (names.hasMoreElements()) buf.append("&"); 
    } 
    return buf.toString(); 
  } 
}
As you can see this class is abstracting an HTTP message.

It expects a servlet and it has four main methods:

There's also a helper function, used by both these four (sic!) functions.

Modulo the on-line API docs and our current knowledge of HTTP this class definition is clear.

So we place this in the same directory as the applet.

How are we going to use it?

It will be used by the applet to send messages to the server.

Notice that the applet's contactServer is similar to part of sendGetMessage above.

So we need to make a few changes to the applet.

import java.applet.*;
import java.awt.*;
import java.net.*; 
import java.io.*; 

public class ChatApplet extends Applet {
    TextArea text; 
    Label label;
    TextField input;
    String user; 
    public void init() {
	URL codebase = getCodeBase(); 
	user = getParameter("user"); 
	if (user == null) user = "anonymous";
	text = new TextArea(); 
        text.setEditable(false); 
	label = new Label("Type here: "); 
	input = new TextField(); 
	input.setEditable(true); 
	setLayout(new BorderLayout());
	add("Center", text); 
	Panel panel = new Panel();
	panel.setLayout(new BorderLayout());
	panel.add("West", label); 
	panel.add("Center", input); 
	add("South",  panel); 
        text.appendText("URL: " + codebase + "\n"); 
    }
    public void start() {
	text.appendText("Your name is: " + user + "\n");
    }
    public boolean handleEvent(Event event) {
        switch (event.id) {
  	    case Event.ACTION_EVENT:
	        if (event.target == input) {
		    text.appendText(user + ": " + input.getText() + "\n"); 
		    input.setText(""); 
		    text.appendText("Server: " + contactServer()); 
                    return true; 
	        }
	}
	return false; 
    } 
    String contactServer() {
        String nextMessage = null;
	try {
	    URL servlet          = new URL("http://burrowww.cs.indiana.edu:21xxx/examples/servlet/ChatServlet"); 
            HttpMessage msg      = new HttpMessage(servlet); 
            InputStream in       = msg.sendGetMessage();
            DataInputStream data = new DataInputStream(new BufferedInputStream(in)); 
            nextMessage          = data.readLine();
	} catch (Exception e) {
	    return ("Error: " + e.toString()); 
	}
        return nextMessage + "\n"; 
    } 
} 
Now we need to make a significant leap.

Here's what the server becomes:

import java.io.*;
import java.net.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*; 

public class ChatServlet extends HttpServlet {
    MessageSource source = new MessageSource();
    public void doGet (HttpServletRequest  req, 
                       HttpServletResponse res) 
                throws ServletException,
                       IOException 
    { res.setContentType("text/plain"); 
      PrintWriter out = res.getWriter();
      out.println(getNextMessage());
    }

    public void doPost (HttpServletRequest  req, 
                        HttpServletResponse res) 
                throws ServletException,
                       IOException 
    {
        String message = req.getParameter("message");
	if (message != null) broadcastMessage(message);
	res.setStatus(res.SC_NO_CONTENT);;
    }

    public String getNextMessage() 
    {
	return new MessageSink().getNextMessage(source);
    }

    public void broadcastMessage(String message) 
    {
	source.sendMessage(message);
    }
}

class MessageSource extends Observable 
{
    public void sendMessage(String message)
    {
	setChanged();
	notifyObservers(message);
    }
}

class MessageSink implements Observer 
{
    String message = null;

    synchronized public void update(Observable o, Object arg)
    {
	message = (String)arg;
	notify();
    }
         
    synchronized public String getNextMessage(MessageSource source)
    {
	source.addObserver(this);

	while (message == null) {
	    try {
		wait();
	    } catch (Exception ignored) { }
	}

	source.deleteObserver(this);
	String messageCopy = message;
	message = null;
	return messageCopy;
    }
}
This servlet is much more general.

It accepts incoming POST calls that contain messages (producers).

It also accepts incoming GET messages that act as consumers.

If there's at least one producer and no consumers the producer waits until a consumer comes by.

When we get consumers and no producers the consumers wait until at least one producer shows up.

Here's the basic idea:

Let's make the applet send itself a message through the servlet.

First it sends a GET to the servlet to register.

A sink is created for the login.

Then the applet broadcasts something, this goes into the source, and the sink is immediately notified.

The message then returns to the applet.

Here's the applet:

import java.applet.*;
import java.awt.*;
import java.net.*; 
import java.io.*; 
import java.util.*; 

public class ChatApplet extends Applet implements Runnable {
    TextArea text; 
    Label label;
    TextField input;
    Thread thread; 
    String user; 
    public void init() {
	URL codebase = getCodeBase(); 
	user = getParameter("user"); 
	if (user == null) user = "anonymous";
	text = new TextArea(); 
        text.setEditable(false); 
	label = new Label("Type here: "); 
	input = new TextField(); 
	input.setEditable(true); 
	setLayout(new BorderLayout());
	add("Center", text); 
	Panel panel = new Panel();
	panel.setLayout(new BorderLayout());
	panel.add("West", label); 
	panel.add("Center", input); 
	add("South",  panel); 
        text.appendText("URL: " + codebase + "\n"); 
    }
    public void start() {
	thread = new Thread(this); 
	thread.start(); 
    }
    public void run() {
	while (true) {
	    text.appendText(contactServer()); 
	}
    }
    public boolean handleEvent(Event event) {
        switch (event.id) {
  	    case Event.ACTION_EVENT:
	        if (event.target == input) {
		    broadcastMessage(input.getText() + "\n"); 
		    input.setText(""); 
                    return true; 
	        }
	}
	return false; 
    } 
    void broadcastMessage(String message) {
	message = user + ": " + message;
	try {
	    URL url          = 
              new URL("http://burrowww.cs.indiana.edu:21xxx/examples/servlet/ChatServlet");
	    HttpMessage msg  = new HttpMessage(url);
	    Properties props = new Properties();
	    props.put("message", message);
	    msg.sendPostMessage(props); 
	} catch (Exception ignored) { } 
    } 
    String contactServer() {
        String nextMessage = null;
	try {
	    URL servlet          = 
              new URL("http://burrowww.cs.indiana.edu:21xxx/examples/servlet/ChatServlet"); 
            HttpMessage msg      = new HttpMessage(servlet); 
            InputStream in       = msg.sendGetMessage(); 
            DataInputStream data = new DataInputStream(new BufferedInputStream(in)); 
            nextMessage          = data.readLine();    
	} catch (Exception e) {
            try {
		Thread.sleep(5000); 
	    } catch (InterruptedException ignored) { }
	}
        return nextMessage + "\n"; 
    } 
} 
Understanding how the applet works is really crucial.

Because there's nothing else to be done.

We're now finished with this stage, and with all stages.

Let's test it.

Open three windows and connect

Then chat.


Customizing your servlet entry point.

Here's a simple way of avoiding .html point-of-entry files:

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
              
public class Chat extends HttpServlet {

  public void doGet(HttpServletRequest req, 
                    HttpServletResponse resp) 
              throws ServletException, 
                     IOException 
  { 
     resp.setContentType("text/html"); 
     resp.getWriter().println(

         "<html><head><title>WebChat</title></head><body bgcolor=white>"
       + "<form method=POST action=/examples/servlet/Chat>Type your name here: " 
       + "<input type=text name=namefield size=10> <p> then push <input " 
       + " type=submit value=Proceed> (or hit Enter) </form></body></html>"

     ); 

  } 

  public void doPost(HttpServletRequest req, 
                     HttpServletResponse resp) 
              throws ServletException, 
                     IOException 
  {
     resp.setContentType("text/html"); 
     resp.getWriter().println(
           
         "<html><body bgcolor=white><h1>Hello, <font color=blue>" 
       + req.getParameter("namefield") 
       + "</font>!</h1>" 
       + "  <applet code=ChatApplet " 
       + "codebase=\"http://burrowww.cs.indiana.edu:32xxx/chat\"\n" 
       + "               width=400 height=400> \n" 
       + "       <param name=user value=\"" 
       + req.getParameter("namefield") 
       + "\"> \n" 
       + " </applet>" 
       + " </body></html>"

     ); 

  } 
       

}


Last updated: Nov 14, 2001 by Adrian German for A348/A548