Richard G Baldwin (512) 223-4758, baldwin@austin.cc.tx.us, http://www2.austin.cc.tx.us/baldwin/

Servlets, They Can Remember

Java Programming, Lecture Notes # 686, Revised 8/22/99.

Preface

Introduction

Sample Program

Interesting Code Fragments

Program Listing


Preface

Students in Prof. Baldwin's Advanced Java Programming classes at ACC will be responsible for knowing and understanding all of the material in this lesson beginning with the spring semester of 1999.

This lesson was originally written on January 7, 1999.

The sample servlet was tested using three machines on the network each running Win95, the JDK 1.2 download package from JavaSoft, and the JSDK 2.0 download package from JavaSoft. The servlet was tested using the servletrunner program that is included in the JSDK. The servlet was also tested using the JavaSoft Java Web Server 1.1.1 program.

Introduction

An earlier lesson provided a brief description of the life cycle of a servlet. This lesson expands on that material, and illustrates certain aspects of the life cycle with a sample servlet.

There are three stages to a servlet's life. The first time a client requests access to a servlet, the server instantiates an object of the servlet type and invokes its init() method. The init() method is invoked only once during the life cycle, and it is invoked on a single thread only. Therefore, it isn't necessary to be concerned about thread safety in your overridden init() method.

During the second stage of a servlet's life, the server invokes various methods, such as doGet() and doPost(), on the servlet object. These methods are invoked on a multithreaded basis, and you do need to be concerned about thread safety in your overridden versions of these methods.

During the final stage of a servlet's life, the server invokes the destroy() method on the servlet object immediately prior to removing it. The intended purpose of the destroy() method is to perform cleanup of some sort. Thread safety can be complex at this stage. If you need to override the destroy() method in such as way as to access shared resources, you should find a good book that explains all of the ramifications of this operation and take those ramifications into account.

Reliable operation of the servlet in this lesson depends on the assumption that the server creates a single instance of the servlet object and uses that instance for an extended period. It would also be possible for the server to create multiple instances of the servlet and to share client requests among those instances. In that case, this servlet would not provide reliable results. You will need to examine the documentation for your server to determine how it operates in this regard.

Sample Program

The name of the sample program is Servlet04. This servlet is designed to illustrate the life cycle of a servlet by exercising the fact that a servlet object can remember historical events based on data maintained in its instance variables.

Once a servlet is loaded, it continues to run until it is unloaded by the server. While it is running, it can remember various aspects of previous activities during that run.

When this servlet is started by the server, it saves a Date object in an instance variable named started. Each time the servlet is accessed by the server, it displays the value stored in that instance variable. The displayed value is always the same until the servlet is unloaded by the server and restarted.

Also, each time the servlet is accessed, it obtains the identification of the client machine and saves that identification along with a new Date object in a Hashtable. Before saving the new Date object in the Hashtable, the servlet reports the value of the Date object already stored in the Hashtable for that client as the time of the previous visit by that client.

Thus, the servlet maintains information about the visits of each client from the time that it is started until the time that it is unloaded.

However, it doesn't remember historical information between successive runs. In order to do that, it would be necessary to save the Hashtable information in a file or a database. This could be done by overriding the destroy() method to save the Hashtable data in a file when the servlet is removed (shut down) by the server, and to retrieve the information from the saved file in the init() method when the servlet is restarted by the server.

Because the Hashtable is accessed in an overridden doGet() method that is multithreaded, access to the Hashtable is synchronized to protect it from being corrupted.

The servlet was tested using three machines on the network each running Win95. The machines were also running the JDK 1.2 download package from JavaSoft, and the JSDK 2.0 download package from JavaSoft.

The servlet was tested using the servletrunner program that is included in the JSDK.

The servlet was also tested using the JavaSoft Java Web Server 1.1.1 program.

The following output shows the result of accessing the servlet three times in succession from one machine on the network. Similar results were produced by accessing the servlet from all three machines. You can see from this output that the servlet not only remembered when it was started, it also remembered the time of the most recent visit by the user of the machine named baldwin.

Hello baldwin 
The time is Thu Jan 07 09:28:24 PST 1999 
I was started on Thu Jan 07 09:28:24 PST 1999 
This is your first visit since then 

Hello baldwin 
The time is Thu Jan 07 09:29:14 PST 1999 
I was started on Thu Jan 07 09:28:24 PST 1999 
Your last visit was Thu Jan 07 09:28:24 PST 1999 

Hello baldwin 
The time is Thu Jan 07 09:29:44 PST 1999 
I was started on Thu Jan 07 09:28:24 PST 1999 
Your last visit was Thu Jan 07 09:29:14 PST 1999 

The next section discusses some of the interesting code fragments that make up this servlet.

Interesting Code Fragments

The first fragment shows the beginning of the controlling class along with the two instance variables that are used to maintain the historical data.

public class Servlet04 extends HttpServlet{
  Hashtable visitors = new Hashtable();
  Date started;

Next we see the overridden init() method. This method is called only once by the server when it first starts the servlet running. Code in this method saves a Date object that represents the date and time that the servlet was started running.

  public void init(ServletConfig config) 
                                   throws ServletException{
    started = new Date();
  }//end init()

The next fragment shows the typical beginning of the doGet() method. This method is called by the server each time a client sends a GET request to the server with the servlet as the target.

This fragment contains the typical code that you will see in many servlets that are designed to return an HTML page to the client.

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

    res.setContentType("text/html");//Set type of output
    PrintWriter out = res.getWriter();//Get output stream
    out.println("<HTML>");//Construct HTML page
    out.println("<HEAD><TITLE=Servlet04</TITLE></HEAD>");
    out.println("<BODY>");

The next fragment invokes the getRemoteHost() method on the request object to get the identification of the client machine. According to the document, this method "Returns the fully qualified host name of the agent that sent the request ."

    String aVisitor = req.getRemoteHost();

The next fragment deals with the possibility that the getRemoteHost() method may return null for some reason. In this case, a Hello message is sent to the client without identifying the client machine in the message.

    if(aVisitor == null){//just in case
      out.println("Hello");
    }//end if(aVisitor == null)

If the getRemoteHost() method returns the machine identification, the next fragment uses that information to send a personalized greeting message to the client.

    else{//Greet the visitor by machine ID
      out.println("Hello " + aVisitor);

Next, the servlet uses the get() method to check the Hashtable to see if there is a record of this client having visited before. Note that this Hashtable access is synchronized to protect against multiple concurrent accesses by different threads executing the doGet() method.

      Date previousVisit;
      synchronized(visitors){//protect shared Hashtable
        previousVisit = (Date)visitors.get(aVisitor);
      }//end synchronized block

If the Hashtable contains no record of a previous visit by this client, the servlet reports that fact to the client, along with the current date/time and the date/time at which the servlet was started. Recall that this information was saved in the started variable by the overridden init() method.

      if(previousVisit == null){//no previous visit
        out.println("<BR>The time is " + new Date());
        out.println("<BR>I was started on " + started);
        out.println(
                "<BR>This is your first visit since then");
      }//end if(previousVisit == null)

If the Hashtable does contain a record of a previous visit by this client, the servlet reports that fact to the client, along with the date/time of the previous visit, the current date/time, and the date/time at which the servlet was started.

      else{//not the first visit
        out.println("<BR>The time is " + new Date());
        out.println("<BR>I was started on " + started);
        synchronized(visitors){
          out.println("<BR>Your last visit was " + 
                                   visitors.get(aVisitor));
        }//end synchronized block
      }//end else
    }//end else

Finally, doGet() saves the date/time of the current visit by the client in the Hashtable and terminates.

    synchronized(visitors){
      visitors.put(aVisitor, new Date());
    }//end synchronized block
    out.println("</BODY></HTML>");//finish HTML page
  }//end doGet()
}//end class Servlet04

Because a servlet is alive from the time it is started until it is removed by the server, it could also be performing some background task on a continuous basis, such as acquiring and saving stock prices from another server. Then when it is accessed by a client, it could deliver those prices to the client.

Program Listing

A complete listing of the program follows.

/*File Servlet04.java, Copyright 1999, R.G.Baldwin

Rev 1/7/99
The purpose of this program is to illustrate that once
a servlet is loaded, it continues to run until it is
unloaded by the server.  While it is running, it can
remember various aspects of previous activities during
that run.

When the servlet is started by the server, it saves a 
Date object in an instance variable named started.

Each time the servlet is accessed by the server, it 
displays the value stored in that instance variable.  The 
value is always the same until the servlet is unloaded by 
the server and restarted.

Also, each time the servlet is accessed, it obtains the
identification of the client machine and saves that
identification along with a new Date object in a Hashtable.
Before saving the new Date object in the Hashtable, the
servlet reports the value of the Date object already
stored in the Hashtable for that client as the time of
the previous visit by that client.  Thus, the servlet 
maintains information about the visits of each client from
the time that it is started until the time that it is 
unloaded.

However, it doesn't remember that information between
successive runs.  In order to do so, it would be necessary
to save the Hashtable information in a file by overriding
the destroy() method when the servlet is shutdown by the
server, and retrieving the information from the saved file
in the init() method when the servlet is restarted by the
server.

Because the Hashtable is accessed in an overridden doGet()
method that is multithreaded, access to the Hashtable is
synchronized.

The servlet was tested using three machines on the network
each running Win95, the JDK 1.2 download package from 
JavaSoft, and the JSDK 2.0 download package from JavaSoft.

The servlet was tested using the servletrunner program that
is included in the JSDK.

The servlet was also tested using the JavaSoft Java Web 
Server 1.1.1 program.

The following output shows the result of accessing the
servlet three times in succession from one machine on the
network.  Similar results were produced by accessing
the servlet from all three machines on the network.  You
can see from this output that the servlet not only 
remembers when it was started, it also remembers the time 
of the most recent visit by the user of the machine named 
baldwin.


Hello baldwin 
The time is Thu Jan 07 09:28:24 PST 1999 
I was started on Thu Jan 07 09:28:24 PST 1999 
This is your first visit since then 

Hello baldwin 
The time is Thu Jan 07 09:29:14 PST 1999 
I was started on Thu Jan 07 09:28:24 PST 1999 
Your last visit was Thu Jan 07 09:28:24 PST 1999 

Hello baldwin 
The time is Thu Jan 07 09:29:44 PST 1999 
I was started on Thu Jan 07 09:28:24 PST 1999 
Your last visit was Thu Jan 07 09:29:14 PST 1999 
**********************************************************/
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class Servlet04 extends HttpServlet{
  Hashtable visitors = new Hashtable();
  Date started;
  
  //-------------------------------------------------------
  //Overridden init() method.  This method is called only
  // once by the server when it first starts the servlet.
  public void init(ServletConfig config) 
                                   throws ServletException{
    //Save the date and time that the servlet was started
    // by the server.
    started = new Date();
  }//end init()
    
  //-------------------------------------------------------
  //Overridden doGet() method.  This method is called by
  // the server each time a client sends a GET request
  // with the servlet as the target.
  public void doGet(HttpServletRequest req, 
                    HttpServletResponse res)
                      throws ServletException, IOException{

    res.setContentType("text/html");//Set type of output
    PrintWriter out = res.getWriter();//Get output stream
    out.println("<HTML>");//Construct HTML page
    out.println("<HEAD><TITLE=Servlet04</TITLE></HEAD>");
    out.println("<BODY>");
    
    //Get identification of client machine
    String aVisitor = req.getRemoteHost();
    if(aVisitor == null){//just in case
      out.println("Hello");
    }//end if(aVisitor == null)
    else{//Greet the visitor by machine ID
      out.println("Hello " + aVisitor);
      //Get info about previous visit.
      Date previousVisit;
      synchronized(visitors){//protect shared Hashtable
        previousVisit = (Date)visitors.get(aVisitor);
      }//end synchronized block
      if(previousVisit == null){//no previous visit
        out.println("<BR>The time is " + new Date());
        out.println("<BR>I was started on " + started);
        out.println(
                "<BR>This is your first visit since then");
      }//end if(previousVisit == null)
      else{//not the first visit
        out.println("<BR>The time is " + new Date());
        out.println("<BR>I was started on " + started);
        synchronized(visitors){
          out.println("<BR>Your last visit was " + 
                                   visitors.get(aVisitor));
        }//end synchronized block
      }//end else
    }//end else
    
    //Save date and time of this visit
    synchronized(visitors){
      visitors.put(aVisitor, new Date());
    }//end synchronized block
    out.println("</BODY></HTML>");//finish HTML page
  }//end doGet()
}//end class Servlet04

-end-