CSCI A348/548
Lecture Notes Fourteen

Spring 2001 (Second semester 2000-2001)


The innards of the web chat application, step by step; code walk.

We aim to present the web based chat application here, line by line.

Thanks to Jaz for making the suggestion that we go through it step by step.

We start with the applet, which is our client program. The applet by itself is not enough for our understanding of the client-side software of the chat application, we also need to look into a helper class, that abstracts HTTP messages, but we'll do that shortly. Meanwhile we start from the applet class; we import a few packages, for a number of necessary classes of objects.

import java.applet.*;
We need to import this package so that we can extend Applet and refer to it by its short name in the process.
import java.awt.*; 
Some of the classes that we will be using are defined in this package, e.g.: So we import the package to be able to refer to these classes, when we need them by their short name, such as Label instead of java.awt.Label and so forth.
import java.io.*; 
The classes that we need here are: These classes manipulate streams.
import java.net.*; 
This package contains the URL class.
import java.util.*; 
For Properties lists.
public class ChatApplet extends Applet implements Runnable {
We name the class (and since we make it public we make sure that the name of the file matches exactly the name of the class, java) and extend Applet and implement the Runnable interface.

This way the run() method provided by this applet will be executed in parallel with anything else that the applet may be experiencing (other methods of the applet).

Next we define the widgets that this applet will be using for its GUI.

  TextArea  text; 
This is a rectangular text area.
  Label     label; 
This is a label, that is, a piece of text.
  TextField input; 
This is a text field.

We have not described yet how we are going to place them on the screen. All we did was to declare variables (names) that could store pointers to such objects one we create some, for our purposes.

  Thread    thread; 
This class is defined in the java.lang package that is imported by default.
  String    user;  
Same thing for String, except that the functionality is much different.
  public void init()  
Ever applet has an init() method. Here we override it with the following definition:
  { 
    URL codebase = getCodeBase();  
Get a URL object, that represents the place on the web where this applet came from.

getCodeBase() is an instance method defined in class Applet.

    user = getParameter("user");  
getParameter() is another one.

This line retrieves the text that is associated with the applet's user parameter from the HTML that serves it up. (See Larry.html, Michael.html, Tony.html and so forth).

    if (user == null) user = "anonymous";  
If there is no such parameter we'll call the user "anonymous".
    text = new TextArea();  
Note that a TextArea extends (and inherits from) TextComponent.

Now we create a few GUI objects: a textarea, that we then set as not editable,

    text.setEditable(false);  
a label, that prompts the user to participate in the discussion
    label = new Label("Type here: "); 
and a text field whose goal is to collect the messages from clients:
    input = new TextField();
This being used as input channel for the humans using the client software, it must be editable, unlike the text area (which is a read-only area).
    input.setEditable(true); 
(Please find the setEditable method in the TextField's web description page).

So the text field is readable and writeable at the same time.

Now we set a graphics layout.

Every applet is a Panel, and every Panel is a Container.

We set the layout for this applet to be a border layout.

    setLayout(new BorderLayout()); 
Then we create a separate Panel that we call panel,
    Panel panel = new Panel(); 
and set a border layout for it too, if we ever put things in it.
    panel.setLayout(new BorderLayout()); 
We place the text area in the center of the applet:
    add("Center", text);
and the new panel at the bottom (in the south)
    add("South",  panel); 
The panel contains two widgets: a label on its left
    panel.add("West",   label); 
and the text field on its right
    panel.add("Center", input); 
We're done with the GUI now. The rest of the interface is event-handling.

The init() method ends with the applet showing where it's coming from.

    tex.appendText("URL: " + codebase + "\n"); 
  } 
Every applet has a start() method.

  public void start() 
  { 
Ours creates a new Thread whose run() method is defined in this class.
    thread = new Thread(this); 
And then starts the thread.
    thread.start(); 
Once we look at the run() we'll be able to realize what we're starting here. This actually is the process of contacting the server to continuously update the text area.
  } 
This was the applet's method start().

thread.start() calls the thread's run() method, which continuously calls contactServer(), described below.

  public void run() { 
Forever (while true, that is),
    while (true)      
update the text area by appending the text obtained from the server (if any).
      text.appendText(contactServer()); 
That means that we block if nothing is coming (and see below).
  } 
This is the "get next message" method.

It returns a String, as sent to the server.

  String contactServer() 
It has no arguments, it doesn't need any.
  {
A local variable will serve as the place where the message will be assembled.
    String nextMessage = null; 
It's null when the method starts.
    while (nextMessage == null) {
And for as long as there is no message
      try {
get a URL to the servlet (offered by the same web server)
        URL         servlet  = new URL( getCodeBase(), 
                                        "/examples/servlet/ChatServlet");  
And based on that url build an HttpMessage kind of object.
        HttpMessage     msg  = new HttpMessage(servlet);  
Once we have it, we can get an InputStream to read from the server
        InputStream     in   = msg.sendGetMessage(); 
bu invoking sendGetMessage() on the HttpMessage object.
        DataInputStream data = new DataInputStream(
                                 new BufferedInputStream(
                                   in)); 
So we start reading lines of text from it (after we turn it into a buffered reader).
        nextMessage          = data.readLine(); 
Notice that the process blocks until readLine() returns something
      } catch (Exception e) { 
If an exception is thrown (something goes wrong),
        try { Thread.sleep(5000); }  
we sleep 5 seconds and
        catch (InterruptedException ignored) { }  
start again (we don't do anything when the thread wakes up).
      } 
This is the end of the outer catch.
    } 
The end of the while (nextMessage == null) ...
    return nextMessage + "\n"; 
If the loop gets broken and a message can be collected it will be returned with a newline at the end.
  }  
And that's the end of getNextMessage().

Every applet can have a stop() method.

  public void stop() { 
This one stops the current thread.
    thread.stop(); 
    thread = null; 
  } 
But we didn't include this in our applet code.

Here's how the message gets sent to the server.

  void broadcastMessage(String message) { 
If we have a String that we call message,
    message = user + ": " + message; 
then we prefix it with the user's name
    try { 
and attempt the transmission (with POST) over the network.
      URL url          = new URL(  getCodeBase(), 
                                   "/examples/servlet/ChatServlet"); 
This is where we want to send it: (I used a shortcut for) the http:// address of the server where the applet came from, followed by the location of the ChatServlet servlet. Once we have this connection we store it in a url object. At this point it would be quite useful to check the documentation on the URL objects in the java.net package.
      HttpMessage msg  = new HttpMessage(url); 
Then the method creates a HttpMessage object to communicate with that url. Notice that objects of that kind get constructed around URLs, which the constructor receives as a parameter. This object does all the "dirty" work involved in making the connection and sending the data.
      Properties props = new Properties(); 
We create a string of pairs, names and values separated by = and joined by &'s. In this case we only create one such pair where the name is message and the value is the actual message.
      props.put("message", message); 
That's what we do when we create this entry in the Properties list.
      msg.sendPostMessage(props); 
Now we invoke the sendPostMessage() method on msg and it will send its argument (props) to the URL around which it has been built.
    } catch (Exception ignored) { } 
Let's ignore the errors in this stage.
  } 
And we're done describing how the applet handles broadcasting. It's all in the HttpMessage object that has all the required functionality.
  public boolean handleEvent(Event event) { 
This is the part of the applet that notices user input.
    switch (event.id) { 
Check the type of the event the user has produced.
      case Event.ACTION_EVENT: 
If it's an action event (as opposed to, for example, mouse event).
        if (event.target == input) { 
If it comes from the input text field.
          broadcastMessage(input.getText() + "\n"); 
Get the text from the field and broadcast it.
          input.setText(""); 
Then clear the text.
          return true; 
And let the event propagate through the hierarchy.
	} 
That's AWT 1.0 but you can use it with no problem.
    } 
Most browsers would support it.
    return false; 
For all other events stop the processing of the event here.
  } 
Using AWT 1.1 or even Swing (and JFC) requires newer browsers.
}
This, therefore, was the applet.

Notice how it talks to the server named ChatServer by using an object of type HttpMessage that is exemplified below. (We also notice that it makes use of the 1.0 (deprecated) version of the AWT but that can be fixed easily if you want to use AWT 1.1 you know how to do it).

We'll take a look now to the HttpMessage class. One way in which it could work would be to establish a raw socket connection to the server and proceed to speak HTTP. This approach would certainly work but it isn't at all necessary. The higher-level

classes already provide this functionality in a convenient abstraction.

Let's do a quick walk-through of HttpMessage. HttpMessage is designed to communicate with just one URL, the URL given in its constructor.

It can send multiple GET and/or POST requests to that URL, but it always communicates with just one URL, which is passed as an argument to its constructor.

We start by importing a few packages.

import java.io.*;
This is for the reading and writing we will need to do.
import java.net.*;
This is for the networking of it.
import java.util.*;
This is for Properties.
public class HttpMessage {
This is where the class starts. It does not extend anything.
  URL    servlet = null; 
It defines an instance variable of type URL where it will keep the URL of the servlet that it is supposed to communicate with. Read about objects of this type here:
java.net.URL
A URL is essentially a string that describes how to locate a resource on the Internet. The URL class represents URLs and provides methods to construct and obtain components of the URL (its protocol, host name, port number, etc.) In addition it provides methods that, after a URL has been created, uses the URL to retrieve the resource identified by the URL. It also supports lower-level methods, such as opening a connection or imput stream to the server that is managing the resource identified by the URL.
  String args    = null; 
We also have another instance variable for the arguments that we will be passing to the servlet with which we communicate through the URL.
  public HttpMessage(URL servlet) 
This is the constructor, it receives a URL as a parameter.
  {
And stores it, nothing unusual here.
    this.servlet = servlet; 
It's like we store the name of the object.
  }
Next two methods overload the same method name by defining a function with one argument and one with no arguments that uses the one with one argument where the passed argument is null.
  public InputStream sendGetMessage() throws IOException 
This one here is the function with no arguments.
  {
It simply calls its namesake with a parameter of null.
    return sendGetMessage(null); 
It's only a convention.
  } 
This other one is expecting a Properties argument.

You can read about Properties objects here:

java.util.Properties
The Properties class is used to represent a properties list. Each item on the list is called a property and consists of a property name and a property value. Each porperty name and property value is a Unicode string. If the property list is to be loaded or stored from IO streams, then syntactic rules apply that the property names and values must follow.
  public InputStream sendGetMessage(Properties args) throws IOException 
Notice that this method returnes an InputStream.
  { 
This will correspond to a connection to the URL contacted with an url-encoded string.
    String argString = ""; 
We'll store that string here.
    if (args != null) { argString = "?" + toEncodedString(args); } 
If we have data for the servlet pass it on the URL followed by the ? sign.

We've seen this in homework 2, and later, and in the notes of Tuesday.

Notice also how the arguments are encoded by our toEncodedString() function detailed further below.

    URL url           = new URL(servlet.toExternalForm() + argString); 
So after we create a URL-encoded query string from the passed-in arguments, we append this query string to the saved URL, creating a new URL object. At this point, it could elect to use this new URL (named url) to communicate with the servlet. A call to
url.openStream()
would return an InputStream that contains the response. But, unfortunately for our purposes, by default all connections made using a URL object are cached. We don't want this - we want the most recent answer that the servlet can provide. So we need to turn caching off. The URL class doesn't directly support this low-level control, so we need to get the URL object's URLConnection object's InputStream, which contains the servlet's response. You can read about URLConnection kind of objects here:
java.net.URLConnection
The URLConnection class represents an active connection to the resource identified by the URL.
    URLConnection con = url.openConnection();
We can get such an object by invoking an appropriate instance method on url.
    con.setUseCaches(false); 
Once we have the url connnection we disable caching.
    return con.getInputStream(); 
And we obtain and return the input stream associated with that connection.
  } 
The code HttpMessage uses to send a POST request is similar.

  public InputStream sendPostMessage() throws IOException {
First, not sending anything is done by calling the namesake method with an argument of null.
    return sendPostMessage(null); 
So we only need to worry about this second method.
  } 
The major difference with how it sends a GET request is that it directly writes the URL-encoded parameter information in the body of the request. This follows the protocol for how POST requests submit their information. The other difference is that it manually sets the request's content type to
"application/x-www-form-urlencoded"
This should be set automatically by Java, but setting it manually works around a bug in some (older) versions of Netscape's browser. (Netscape is good though).
  public InputStream sendPostMessage(Properties args) throws IOException 
So that's the signature of the method.
  {
And we start the body here.
    String argString = ""; 
This variable holds the data we want to send.
    if (args != null) {
If it's not an invocation started from the method with no arguments.
      argString = toEncodedString(args); 
We encode the string we want to send.
    } 
Then we open a connection.
    URLConnection con = servlet.openConnection(); 
And once we have it we get ready to use it.
    con.setDoInput(true); 
We say we want to read from it.
    con.setDoOutput(true); 
And we want to write to it.
    con.setUseCaches(false); 
And no caching.
    con.setRequestProperty(
      "Content-Type", "application/x-www-form-urlencoded"
    ); 
Now we declare what we're going to send.
    DataOutputStream out = new DataOutputStream(con.getOutputStream()); 
Use the right I/O object.
    out.writeBytes(argString); 
Write the characters one by one.
    out.flush(); 
Make sure they're all sent.
    out.close(); 
Close the pipe.
    return con.getInputStream(); 
Return it for the response to be read (if needed).
  } 
That's how we POST.
  private String toEncodedString(Properties args) 
Encoding is done by like ReadParse of our first CGI module is expecting it.
  {
We start with a string buffer.
    StringBuffer buf = new StringBuffer();
And get ready to look through the names of the properties in a sequence.
    Enumeration names = args.propertyNames(); 
For each one, in turn.
    while (names.hasMoreElements()) {
Get it in this variable.
      String name = (String) names.nextElement(); 
Get the value also.
      String value = args.getProperty(name); 
Put them together with an = sign between them.
      buf.append(URLEncoder.encode(name) + "=" + URLEncoder.encode(value)); 
And if there's more make sure these strings are separated by ampersands (&'s) as expected.
      if (names.hasMoreElements()) buf.append("&"); 
And when we're done we're done.
    } 
So we return the buffer as a String.
    return buf.toString(); 
And that's the end of the client side, really.
  } 
We should mention however that HttpMessage is a general-purpose class for HTTP communication. It doesn't have to be used by applets, and it doesn't have to connect to servlets. It's usable by any Java client that needs to connect to an HTTP resource.
}
Now that we have these two files we can compile the applet, and two classes are going to be created, one for the applet the other one for the helper class (that abstracts HTTP messages).

Now we write the servlet.

The servlet is really quite simple.

import java.io.*;
import java.net.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*; 
We're always importing something.
public class ChatServer extends HttpServlet { 

  MessageSource source = new MessageSource(); 
This class is an Observable and is used to collect broadcasted messages.
  public void doGet(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException 
  { 
    res.setContentType("text/plain"); 
    PrintWriter out = res.getWriter(); 
    out.println(getNextMessage()); 
This method (that processes a GET request) is calling another message below that is simply creating a new message sink, and sets it waiting for the message source. Once the source has the data the sink returns the message and that is returned by the method invoked above and then sent over through out back to the client that issued the GET request in the first place.
  }
  public void doPost(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException 
  { 
    String message = req.getParameter("message");
    if (message != null) broadcastMessage(message); 
When something gets POST-ed it's a String, a message that's broadcasted by one client. This is placed in the MessageSource object, for all the waiting sinks to pick it up for their clients.

Setting the response status code to SC_NO_CONTENT indicates that there is no content in the response.

    res.setStatus(res.SC_NO_CONTENT);     
  }

  public String getNextMessage() 
  {
This only creates a new sink and passes the buck to it.
    return new MessageSink().getNextMessage(source); 
  }

  public void broadcastMessage(String message) 
  {
This delegates the work to the MessageSource.

There's only one source but as many sinks as clients.

    source.sendMessage(message); 
  }
} 

class MessageSource 
  extends Observable 
Read about Observables here:
java.util.Observable
An Observable is an object that holds some data. An Observer is an object that monitors changes to the data in an observable object. You can associate a set of observers with an observable object.
{
  public void sendMessage(String message) 
  {
    setChanged();
When a change is made to this observable object, the set of observers are notified by the change.
    notifyObservers(message);

  } 
}

class MessageSink 
  implements Observer 
The observable object must be a subclass of the Observable class. Each observer needs to implement the Observable interface. You can read about it here:
java.util.Observer
In our program there are many observers but only one and the same observable.
{
  String message = null; 

  synchronized public void update(Observable o, Object arg) 
  {
    message = (String)arg; 
    notify(); 
  }  
This is the first thing a sink has to do after creation. It's synchronized with the update method through the monitor (lock) that each Object in Java has. Once it starts it adds itself as an observer to the source and then waits to be notified.

Notification comes from the synchronized method update(), above, and it is called by the observed object (the source) when it has the data.

  synchronized public String getNextMessage(MessageSource source) 
  {
    source.addObserver(this); 

    while (message == null) {
      try {
        wait(); 
      } catch (Exception ignored) { } 
    } 
Once the notification comes we delete ourselves as an observer and return the message to the client whose request created us in the first place. That client will display the returned string and issue a new request, that will immediately create a new, fresh sink.
    source.deleteObserver(this); 
    String messageCopy = message;
    message = null;
    return messageCopy; 
  } 
}
This file contains the source code for three classes. If we compile it we obtain three class files. Once we obtain the bytecode files we move them to the $myServlets directories and get ready to run the application. For more info on source to sinks communication see last example of Tuesday notes.

But how do we distribute the clients?

We need to send the applets to the browsers.

So we create three files which differ in only one place.

An alternative would be to use a dispatcher servlet or CGI script.


Last updated on Feb 22, 2001, by Adrian German for A348/A548