Java Web Server

Replacing CGI Scripts with Servlets


Contents / New Features / Administrator Docs / Developer Docs / Index / Servlet Features

Up until now, the JavaTM story has largely been a client-side story. That's about to change. Now web servers can be extended through Java power just like web browser clients.

In the context of client-server software solutions--including the serving and browsing of Internet web pages--Java applets have enabled client browsers to extend their behavior by downloading compiled code from network servers. Applets have changed the nature and meaning of distributed computing. Applets have opened new worlds by enabling the convenient distribution of compiled code from a central server.

There's no reason that servers can't take advantage of Java in the same way that desktop applications have been able to take advantage of Java. To this end, JavaSoft has introduced server technology that is written in 100% Pure JavaTM and that is capable of being extended dynamically by loading compiled Java code known as a servlet. While applets provide a way of dynamically extending the functionality of client-side browsers, servlets let you dynamically extend the functionality of network servers.

Programmers should think of servlets as server-side components. Servlets are to servers what applets are to browsers. Servlet code can be downloaded into a running server to extend its behavior to provide new, or temporary, services to network clients.

Hello, CGI World

At the most simple level, the JavaTM Web ServerTM can be used to replace standard web servers such as Apache. Since Java Web Servers can handle CGI scripts, you can use existing Perl code, shell scripts, or compiled C programs, and do HTML forms processing the way you always have.

There are two sides to form processing:

First consider a form that looks like Figure 1.


Writing the CGI scripts to process this code could be quite complex. However writing a Java servlet class to handle the task is straightforward.

The following is an example of the HTML used to present the form to user. Note that it uses the POST method in HTTP. This is for two reasons. First, since this data is going to be stored in a database the GET method is inappropriate. Second, the POST method lets you pass much more data to the server than the GET method would. (GET methods are used for queries, and in some other places. Most forms should use POST.)

<html>
  <head>
    <title>JdcSurvey</title>
  </head>

  <body>
    <form action=http://demo:8080/servlet/survey method=POST>
      <input type=hidden name=survey value=Survey01Results>

      <BR><BR>How Many Employees in your Company?<BR>
        <BR>1-100<input type=radio name=employee value=1-100>
        <BR>100-200<input type=radio name=employee value=100-200>
        <BR>200-300<input type=radio name=employee value=200-300>
        <BR>300-400<input type=radio name=employee value=300-400>
        <BR>500-more<input type=radio name=employee value=500-more>

      <BR><BR>General Comments?<BR>
        <BR><input type=text name=comment>

      <BR><BR>What IDEs do you use?<BR>
        <BR>JavaWorkShop<input type=checkbox name=ide value=JavaWorkShop>
        <BR>J++<input type=checkbox name=ide value=J++>
        <BR>Cafe'<input type=checkbox name=ide value=Cafe'>

      <BR><BR><input type=submit><input type=reset>
    </form>
  </body>
</html>

This code file should be placed in the public_html directory immediately below the root directory where you installed your Java Web Server. The form specifies three basic questions to be answered by users. First, the question "How Many Employees in your Company?" offers a set of five radio buttons for potential responses.

      <BR><BR>How Many Employees in your Company?<BR>
        <BR>1-100<input type=radio name=employee value=1-100>
        <BR>100-200<input type=radio name=employee value=100-200>
        <BR>200-300<input type=radio name=employee value=200-300>
        <BR>300-400<input type=radio name=employee value=300-400>
        <BR>500-more<input type=radio name=employee value=500-more>

Second, the question "General Comments?" provides a single text input field for the users to enter a general comment.

      <BR><BR>General Comments?<BR>
        <BR><input type=text name=comment>

Third, the question "What IDEs do you use?" provides a set of three check boxes. Unlike the radio buttons, these are not mutually exclusive choices as a user might use all three products: Java WorkShop, J++, and Cafe.

      <BR><BR>What IDEs do you use?<BR>
        <BR>JavaWorkShop<input type=checkbox name=ide value=JavaWorkShop>
        <BR>J++<input type=checkbox name=ide value=J++>
        <BR>Cafe'<input type=checkbox name=ide value=Cafe'>

Finally a Submit button is provided to submit and process the form when the user has finished answering questions. A Reset button appears next to the Submit button in case the user wants to reset all fields to the original default values.

      <BR><BR><input type=submit><input type=reset>

Once you press the Submit button, the data in the form is sent as a data stream to the servlet specified in the form tag at the top of the HTML file:

    <form action=http://demo:8080/servlet/survey method=POST>

This syntax is very similar to the way in which you would specify that data in the HTML form to be processed by a CGI script (whether Perl, shell, or compiled C program).

After submitting the form shown in Figure 1, the text in file /tmp/Survey01Results.txt on host demo should look like this:

    
    ide: JavaWorkShop
    survey: Survey01Results
    employee: 300-400
    comment: Great Stuff!!!

Also, after submitting the form, the Java Web Server will display a new web page based on the output written to the response argument passed to the servlet's doPost method. (A query form, using the GET method, would call the doGet method of the servlet.) In this case a "thank you" message is displayed.

So Long, CGI

The big difference you will notice, if you are accustomed to CGI script programming, is how much simpler it is to write a servlet class to process the data than it is to write a comparable CGI script.

Here's the Java code required to process the form, once submitted, from the page described by the JdcSurvey.html file.


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

import javax.servlet.*;
import javax.servlet.http.*;

/**
 * A sample single-threaded servlet that takes input from a form
 * and writes it out to a file.  It is single threaded to serialize
 * access to the file.  After the results are written to the file,
 * the servlet returns a "thank you" to the user.
 *
 * You can run the servlet as provided, and only one thread will run
 * a service method at a time.  There are no thread synchronization
 * issues with this type of servlet, even though the service method
 * writes to a file.  (Writing to a file within a service method
 * requires synchronization in a typical servlet.)
 *
 * You can also run the servlet without using the single thread
 * model by removing the implements statement.  Because the
 * service method does not synchronize access to the file, multiple
 * threads can write to it at the same time.  When multiple threads try
 * to write to the file concurrently, the data from one survey does not
 * follow the data from another survey in an orderly fashion.
 *
 * To see interaction (or lack of interaction) between threads, use
 * at least two browser windows and have them access the servlet as
 * close to simultaneously as possible.  Expect correct results (that
 * is, expect no interference between threads) only when the servlet
 * implements the SingleThreadedModel interface.
 */
public class SurveyServlet extends HttpServlet 
    implements SingleThreadModel
{
    String resultsDir;
    
    public void init(ServletConfig config)
	throws ServletException
    {
	super.init(config);
        resultsDir = getInitParameter("resultsDir");
	if (resultsDir == null) {
	    Enumeration initParams = getInitParameterNames();
	    System.err.println("The init parameters were: ");
	    while (initParams.hasMoreElements()) {
		System.err.println(initParams.nextElement());
	    }
	    System.err.println("Should have seen one parameter name");
	    throw new UnavailableException (this,
		"Not given a directory to write survey results!");
	}
    }

    /**
     * Write survey results to output file in response to the POSTed
     * form.  Write a "thank you" to the client.     
     */
    public void doPost(HttpServletRequest req, HttpServletResponse res)
	throws ServletException, IOException
    {
        // first, set the "content type" header of the response
	res.setContentType("text/html");

	//Get the response's PrintWriter to return text to the client.
        PrintWriter toClient = res.getWriter();

        try {
            //Open the file for writing the survey results.
            String surveyName = req.getParameterValues("survey")[0];
            FileWriter resultsFile = new FileWriter(resultsDir
	        + System.getProperty("file.separator")
	        + surveyName + ".txt", true);
            PrintWriter toFile = new PrintWriter(resultsFile);

	    // Get client's form data & store it in the file
            toFile.println("<BEGIN>");
            Enumeration values = req.getParameterNames();
            while(values.hasMoreElements()) {
                String name = (String)values.nextElement();
		String value = req.getParameterValues(name)[0];
                if(name.compareTo("submit") != 0) {
                    toFile.println(name + ": " + value);
                }
            }
            toFile.println("<END>");

	    //Close the file.
            resultsFile.close();

	    // Respond to client with a thank you
	    toClient.println("<html>");
	    toClient.println("<title>Thank you!</title>");
            toClient.println("Thank you for participating");
	    toClient.println("</html>");

        } catch(IOException e) {
            e.printStackTrace();
            toClient.println(
		"A problem occured while recording your answers.  "
		+ "Please try again.");
        }

        // Close the writer; the response is done.
	toClient.close();
    }
}

This servlet source file, and the compiled servlet class, should also be placed in the servlets directory immediately below the root directory where you installed your Java Web Server. Before clients can make use of the extended server functionality added by the SurveyServlet servlet class, you will also have to install the servlet using the Administration Tool.

Serving up Servlets

A quick code walkthrough should clarify the way in which the form is processed by the doPost method of the SurveyServlet class.

First, two different writers are opened for writing text. One is opened to record the results of the survey and to give feedback to the users. Note that the writer that returns a response to the user is only accessed after the content type for the response is set.

    // first, set the "content type" header of the response
    res.setContentType("text/html");

    //Get the response's PrintWriter to return text to the client.
    PrintWriter toClient = res.getWriter();

    try {
        //Open the file for writing the survey results.
        String surveyName = req.getParameterValues("survey")[0];
        FileWriter resultsFile = new FileWriter(resultsDir
	    + System.getProperty("file.separator")
	    + surveyName + ".txt", true);
        PrintWriter toFile = new PrintWriter(resultsFile);

To allow the survey results to be written to a file a FileWriter object is associated with a PrintWriter object. The resulting PrintWriter is called toFile. Anything written to toFile will be saved in the survey results file. Similarly, anything written to the PrintWriter called toClient will be printed on the HTML page generated by the Java Web Server as a result of processing the form.

When looking at the creation of the FileWriter's file name, recall that the survey name is set in the JdcSurvey.html file:

    <input type=hidden name=survey value=Survey01Results>

and retrieved by the doPost method:

    String surveyName = req.getParameterValues("survey")[0];
and that the resultsDir holds the string that was passed in as the value of the resultsDir property. Its value was stored in the SurveyServlet's resultsDir field in the init method:
    resultsDir = getInitParameter("resultsDir");

The value of the resultsDir property was set in the Administration Tool to resultsDir=/tmp when the SurveyServlet was added to the list of supported servlets. (See the Servlet Tutorial for more information on making the SurveyServlet a supported servlet.)

Thus the name of the file created by:

    FileWriter resultsFile = new FileWriter(resultsDir
	+ System.getProperty("file.separator")
	+ surveyName + ".txt", true);

is /tmp/Survey01Results.txt.

Output that is written to toFile, such as

    // Get client's form data & store it in the correct file
    toFile.println("<BEGIN>");
    ...
    toFile.println(name + ": " + value);
    ...
    toFile.println("<END>");
will be written to the file, /tmp/Survey01Results.txt.

Output that is written to toClient such as

    toClient.println("<html>");
    toClient.println("<title>Thank you!</title>");
    toClient.println("Thank you for participating");
    toClient.println("</html>");

will appear in the client's browser after the form has been processed and control returns from JdcSurvey01.doPost.

The main thing you need to know to understand how a servlet processes form arguments is that input for the form processing is read from the req argument to the doPost method and output is written from the res argument to the doPost method.

    public void doPost(HttpServletRequest req, HttpServletResponse res)
	throws ServletException, IOException
More specifically, the req method contains a list of parameters that can be retrieved with the HttpServletRequest.getParameterNames method.

    Enumeration values = req.getParameterNames();

With the values enumeration object returned by this call, you can now set up a loop to process each of the parameters passed by the HTML form to the servlet doPost routine:

    while(values.hasMoreElements()) {
        ...
    }
The name of the parameter is retrieved on each iteration by Enumeration.nextElement, while the associated values are retrieved by HttpServletRequest.getParameterValues, which accepts the parameter name as an argument. Note that getParameterValues returns an array of values; merely access the first element in the array if there is only one value for the parameter.

    String name = (String)values.nextElement();
    String value = req.getParameterValues(name)[0];
The only value you don't want to print in the results file is the value of the "submit" parameter, which is passed because submit was specified as a button in the html file. The servlet doPost routine weeds out this parameter with a simple string comparison:

    if(name.compareTo("submit") != 0) {
        toFile.println(name + ": " + value);
    }

The only other detail worth mentioning is the exception handling capabilities provided by servlets. Be sure to specify that your doPost and init methods in your HttpServlet subclass can each throw a ServletException:

    public void init (ServletConfig config) throws ServletException
    ...
    public void doPost (HttpServletRequest req, HttpServletResponse res,
    ) throws ServletException, IOException
    ...

Inside the SurveyServlet servlet the doPost method uses a try/catch block to help provide the user and the programmer with adequate feedback when something goes wrong. The user is warned if a problem occurs while attempting to write the survey results to the file. The programmer is given a stack trace, dumped to the standard error device.

    try {
        ...
        } catch(IOException e) {
            e.printStackTrace();
            toClient.println(
		"A problem occured while recording your answers.  "
		+ "Please try again.");
        }

You should now know enough to write simple servlets to extend your Java Web Server to process forms that you would otherwise have to process with complex CGI scripts.


Top
java-server-feedback@java.sun.com
Copyright © 1997 Sun Microsystems, Inc.
All Rights Reserved.