Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

Networking our whiteboard with servlets

Find out how to easily replace the RMI and sockets networking layers with servlets

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
You gotta be Java.

Play with the whiteboard!

Note: This applet will run only in the HotJava browser, the Windows 95/NT implementation of Internet Explorer 4.0, or in an AWT 1.1-enabled version of Netscape (see Resources for a link to the patch required to bring Netscape up to speed).

If you are not using any of these browsers, you can use appletviewer to view the applet.

The whiteboard we're using was first developed back inNovember's column. The whiteboard uses 1.1 AWT features, such as lightweight components to display objects as the user draws them.

The whiteboard



In December's column we added to the functionality of the previous month by creating a network of whiteboards that shared a common list of objects. All the whiteboards in the collaborative group could add and move the shared whiteboard objects. We developed both RMI- and sockets-based networking layers to enable communication among the whiteboards.

This month we're going one step further: We're going to use servlets to provide the same function that sockets and RMI provided last month.

Servlets are server extensions written in Java, usually for Web servers. A growing number of Web servers support them, including Apache, Java Web Server (JWS), O'Reilly's WebSite, and Netscape's various offerings.

Servlets are interesting because they don't fork a new process for every request that comes in, which makes them much faster than CGI. Under some Web servers, such as JWS, servlets are even faster than Fast-CGI because there is no process task switch; the servlets run as threads within the server process itself.

Because Web-based servlets respond to HTTP methods such as GET and POST, servlet-based communication is able to get around firewalls, which block sockets and RMI. As an added bonus, it's easy to use SSL to secure communications between SSL-enabled clients (Netscape, for example) and servlets running under an SSL-enabled Web server. SSL (or Secure Sockets Layer) is an encryption-based protocol used to protect data sent over the Internet from eavesdropping.

Servlet structure and life cycle

At a high level, servlets are just like applets, with the exception that they run in the server environment instead of the browser environment. Like applets, they have a definite life cycle, which is controlled by the environment. Unlike applets, however, only one instance of a servlet is actually created for each Web server. Each request to the servlet's URL is passed into the same instance of the servlet.

Servlets are accessed from clients in the same manner as CGI scripts. For example, an HTTP GET request to a URL like http://www.mycom.com/servlet/CurrentTime could return a bit of HTML containing the current time on the server.

The server loads the servlet when the first request is directed to it (unless the server allows pre-loading). The server then calls the servlet's init () method. All other requests arriving before the init () method completes block until it does so.

Once the init () method completes, the servlet is ready to service requests via its service () method. The environment puts each request into its own thread, which then enters the servlet's service () method.

The servlet continues servicing requests until it is unloaded. When the environment unloads the servlet (when the server shuts down, for example), it calls the servlet's destroy () method.

Brief overview of the servlet API

The servlet API is provided in the javax.servlet and javax.servlet.http packages.

javax.servlet is the base servlet API, and contains the interface Servlet and its generic implementation, GenericServlet. This package also contains ServletRequest and ServletResponse interfaces.

The most important method of GenericServlet is service (), which is where request threads enter. Specializations of GenericServlet such as HttpServlet override this method to handle protocol-specific requests.

javax.servlet.http specializes these classes and implements interfaces to handle HTTP requests. We'll look at the HTTP servlet classes in more depth in this application.

A servlet for the whiteboard

The whiteboard, as you will remember from last time, used a distributed data structure called ObservableList to keep track of elements that it displayed. Distributed implementations using sockets and RMI simply subclassed ObservableList and overrode method implementations based on which type of networking was used. In both cases, a server provided a central IDList to keep track of elements shared among the clients.

This time, we're going to do much the same thing, except we'll extend ObservableList to provide a servlet-based implementation called ServletList. The servlet implementation will share methods employed by both networking models we used last month: the client will poll the server periodically to ask for updates as it did in the RMI approach, and it will pass specialized "message objects" to the server using object streams as in the sockets approach.

The client object streams will use the URL and URLConnection classes to send message objects using the HTTP POST protocol, which allows a Web server extension to send binary data with a POST request. The response to the POST will be binary data as well, which an object stream will decode into a response object.

Servlet-based communications architecture



Class ServletList

ServletList extends ObservableList and uses a few of the message wrappers from the previous socket-based implementation to communicate with the server. One new message type, UpdateMsg, is necessary so that the client can alert the server that it needs an update. The server responds with an InitMsg containing the update. Let's take a look.

 
package shoffner.step.jan;

import java.io.*; import java.util.*; import java.net.*;
import org.merlin.step.dec.*; import org.merlin.step.dec.socket.*;
public class ServletList extends ObservableList implements Runnable { public static final int UPDATE_DELAY = 10 * 1000;
URL server; IDList list; Thread processor;
public ServletList (String host, String loc) { try { server = new URL ("http://" + host + "/" + loc); } catch (MalformedURLException ex) { ex.printStackTrace (); } list = new IDList (); update (); processor = new Thread (this); processor.start (); }


We import classes from the dec and dec.socket packages because we are going to use some of the data structures and socket message classes from last month. The constructor sets up a URL to the host and a location supplied by the class that instantiates the ServletList.

  public Enumeration elements () {
    return list.elements ();
  }

public void addElement (Object element) { Object id = queryServer (new AddElementMsg (element)); list.addElementWithID (id, element); }
public void replaceElementAtEnd (Object oldE, Object newE) { Object oldID = list.getIDOfElement (oldE); Object id = queryServer (new ReplaceElementMsg (oldID, newE)); if (id != null) list.replaceElementWithID (oldID, id, newE); else System.out.println ("Unknown id:" + oldID); }


These three methods (elements (), addElement (), and replaceElementAtEnd ()) are part of the public interface to the ServletList. They override methods found in ObservableList. The central feature of these methods is the call to queryServer (), which returns an Object result from the server to the calling method. Notice that we're wrapping the requests in objects to denote their type to the server.

  public void run () {
    while (true) {
      try {
        Thread.sleep (UPDATE_DELAY);
        update ();
      }
      catch (Exception ignored) {
        ignored.printStackTrace ();
      }
    }
  }

public void stop () { if (processor != null) { processor.stop (); processor = null; } }
void update () { InitMsg i = (InitMsg) queryServer (new UpdateMsg (list.getUpdateCount ())); IDList initList = i.getList (); if (initList != null) { list = initList; fireUpdate (); } }


Here we see the update thread that periodically polls the server. If the server has a different updateCount from that maintained by list, the server sends back its own IDList. We replace our local copy with IDList and call fireUpdate (). Obviously, we could use a more sophisticated update mechanism here, but this approach is easy and adequate for our needs.

The run () method must not be allowed to exit while the client is active, or the client will stop polling for updates. Therefore, run () sits in a while loop and catches exceptions that may be generated if the server goes down while the client is attempting an update.

Now we get to the interesting part of the client implementation:

  Object queryServer (Object arg) {
    URLConnection con;
    ObjectOutputStream req = null;
    ObjectInputStream res = null;
    Object result = null;
    try {
      con = server.openConnection ();
      con.setDoOutput (true);
      req = new ObjectOutputStream (new BufferedOutputStream
                                    (con.getOutputStream ()));
      req.writeObject (arg);
      req.flush ();
      req.close ();

res = new ObjectInputStream (new BufferedInputStream (con.getInputStream ())); result = res.readObject (); } catch (IOException ignored) { ignored.printStackTrace (); } catch (ClassNotFoundException ex) { ex.printStackTrace (); } finally { try { res.close (); } catch (IOException ex) {} } return result; } }


The queryServer () method sends an object, arg, to the servlet using the HTTP POST method to access the servlet's URL. Before the object can be sent, it must be serialized using ObjectOutputStream. The server's response will be read in from an ObjectInputStream and returned by the method.

Interestingly, the queryServer () method does not require a servlet at the other end. Any Web server extension/CGI that can understand POST, and can accept and return a serialized Java object will work fine. Alternatively, we could override the queryServer () method to make it access other types of message-oriented servers besides those dealing in serialized Java objects.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources
  • Download the article and the complete source as a gzipped tar file
    http://www.javaworld.com/javaworld/jw-01-1998/step/jw-01-step.tar.gz
  • Download the article and the complete source as a zip file
    http://www.javaworld.com/javaworld/jw-01-1998/step/jw-01-step.zip
  • The JavaSoft Servlets page contains information about servlets and includes a link to the Servlet Development Kit (JSDK) http://jserv.javasoft.com/products/java-server/servlets/
  • Apache's Java development effort has its own site, with information about
  • current Java modules, links of interest, and downloads http://java.apache.org/
  • Download the Apache Week patch for mod_servlet.c http://www.apacheweek.com/issues/97-06-13#status
  • Apache's Java group provides an Apache/servlet mailing list http://java.apache.org/
  • JavaSoft's Java Web Server site is the reference site for JWS, offering
  • downloads -- plus information on how to subscribe to the JWS mailing list http://jserv.javasoft.com/index.html
  • The W3C maintains the HTTP protocol reference site http://www.w3.org/Protocols/
  • W3C provides an in-depth CGI spec reference http://www.w3.org/CGI/
  • Step-by-step instructions on how to apply the Netscape 4.03 patch for Windows and some flavors of Unix http://developer.netscape.com/software/jdk/download.html#NT_INSTALL
  • Previous Step by Step articles