http://www.developer.com/

Back to article

The Servlet Container Model


October 24, 2002


This is Chapter 4: Servlet Container Model from the book Sun Certification Training Guide (310-080): Java 2 Enterprise Edition (J2EE) Web Component Developer (ISBN:0-7897-2821-4) written by Alain Trottier, published by Que.


Chapter 4: Servlet Container Model

Chapter 4: Servlet Container Model

Objectives

This chapter covers the following objectives listed by Sun in "Section 1—The Servlet Model" and "Section 3—The Servlet Container Model."

1.1 For each of the HTTP methods, GET, POST, and PUT, identify the corresponding method in the HttpServlet class.

The HTTP methods GET, POST, and PUT are how browsers and Web servers communicate the purpose of communication. A GET simply wants to retrieve a page without providing much information. A POST, however, can package lots of form or file information with its request. A PUT is for uploading a file. The HttpServlet class has a corresponding method for each HTTP method, including doGet(), doPost(), and doPut().

1.2 For each of the HTTP methods, GET, POST, and HEAD, identify triggers that might cause a browser to use the method, and identify benefits or functionality of the method.

This objective asks you to understand the events associated with each type of request. For example, clicking a hyperlink will send a GET request to a Web server, but clicking a Submit button (when the action is set to "post") will send a POST request.

1.3 For each of the following operations, identify the interface and method name that should be used to

  • Retrieve HTML form parameters from the request
  • Retrieve a servlet initialization parameter
  • Retrieve HTTP request header information
  • Set an HTTP response header; set the content type of the response
  • Acquire a text stream for the response
  • Acquire a binary stream for the response
  • Redirect an HTTP request to another URL

    This objective is huge. It encompasses the heart of a servlet process, especially the request and response objects. The request parameters for the servlet are the strings sent by the client to the Servlet Container. The container parses the request and puts the information in an HttpServletRequest object which is passed to the servlet. Going the other way, the container wraps the response parameters in an HttpServletResponse object which is passed back to the container. The associated chapter section later in this chapter ("Overriding HttpServlet GET, POST, and PUT methods") goes into much detail on the methods involved.

1.4 Identify the interface and method to access values and resources and to set object attributes within the following three Web scopes:

  • Request
  • Session
  • Context

    This objective addresses the idea of scope. When something has Context scope, it is application-wide and all users can share data. Session scope means one user can share data across page views, but other users can't. Request scope restricts data to only that page.

1.5 Given a life-cycle method, identify correct statements about its purpose or about how and when it is invoked. These methods are

  • init
  • service
  • destroy

    The container manages the servlet life-cycle. This part of the chapter explains, with examples, how the container initializes a servlet with a call to the init() method. Then it calls the service() method upon every request. Finally, when the servlet is about to be removed from memory, the container calls its destroy() method. This gives the servlet one last chance to clean up resources.

1.6 Use a RequestDispatcher to include or forward to a Web resource.

The RequestDispatcher object is the servlet forwarding mechanism. You will see in the section "Servlet Life-cycle" how you can transfer processing of the request from one servlet to another (which the browser will be unaware of). This is how a servlet can pass the request to some other Web component within the same Web container.

3.1 Identify the uses for and the interfaces (or classes) and methods to achieve the following features:

  • Servlet context initialization parameters
  • Servlet context listener
  • Servlet context attribute listener
  • Session attribute listeners

    These elements let you get and monitor servlet attributes. Not only can you get them and change them too, but you can actually put in place behavior to occur when an attribute changes. The listeners are event-driven triggers. When an attribute changes, special targeted methods are called. In them, you can define special actions, such as adding a note to the log every time the user count changes (perhaps a context attribute called counter).

3.3 Distinguish the behavior of the following in a distributable:

  • Servlet context initialization parameters
  • Servlet context listener
  • Servlet context attribute listener
  • Session attribute listeners

    As explained in the previous objective, these elements let you get and monitor Servlet attributes. There is a difference here in that Sun wants you to understand how this works in a distributable Web application.

Outline

Introduction

Overriding HttpServlet GET, POST, and PUT Methods

GET

POST

PUT

Triggering HttpServlet GET, POST, and PUT Methods

GET

POST

HEAD

Interfacing with HTML Requests

Form Parameters

Retrieving a Servlet Initialization Parameter

Retrieving HTTP Request Header Information

Acquiring a Binary Stream for the Response

Redirecting an HTTP Request to Another URL

Web Application Scope

Request

Session

Context

Servlet Life-cycle

Using a RequestDispatcher

Web Application Context

Context Within a Distributable Web Application

The key to this section of the exam is understanding how servlets implement the Servlet interface, which defines life-cycle methods. The Servlet Container (such as Apache Tomcat) is itself an application that monitors a port on a given IP address. Servlets generate responses to HTTP requests. To do so, the container loads your servlet (if it isn't in memory already) and calls the methods defined in the interface. This is the foundation of servlet and JSP architecture.

There are many methods to know. It is easier if you learn the methods in groups according to theme. For example, write a servlet that has HttpServlet methods which handle all three GET, POST, and PUT types of request.

Each JavaServer Page is transformed into a servlet that is compiled and then loaded. Therefore much of what you learn here applies to the JSP section of the exam too.

Introduction

JSP and servlets have greatly enhanced the way in which you can create and manage Web pages. The difficulty level of coding JSP is between that of coding HTML and pure Java. Servlets are pure Java. The idea behind having both is providing a way for non-programmers to contribute functionality through JSP. You can "program" a JSP page almost as easily as you can write an HTML page. For simple tasks like displaying the current date, you write a normal HTML page and add only a small amount of Java as a scriptlet. For big tasks like processing a shopping cart, you use JSP as the mediator between the Web form and a component(s) (bean or servlet) that has all the horsepower. Most of the code in a Web application will go into servlets. The JSP portion is a soft front end to the application that, typically, marketing can use comfortably.

There is a lot that happens when a servlet is invoked. This chapter covers much material that explains each step of the process. At this point, it will help to provide an overview of what happens in a typical JSP/servlet request. The sequence of events starts with a browser sending a request to a Web server. The server hands the request to a Servlet Container. The container loads the servlet (if it isn't already loaded), instantiates a request and response objects, and then hands these objects to the servlet by calling first its init() method, then its service() method, and lastly the destroy() method. The service() method will typically call one of the doXXX() methods such as doGet().

All these steps are covered in detail later in this chapter. Presently, just review the overall process presented in Figure 4.1.

Let's study an example of a servlet. The following is a fully functioning, albeit trivial, servlet example. Listing 4.1 represents all that is required to have a complete servlet.

Figure 4.1
Servlet handling of an HTTP Request.

Listing 4.1 The Source Code of a Minimum Servlet

/* SimpleServletExample.java, v 1.0
 *
 */

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

/**
 * A simple servlet.
 * SCWCD Exam Objective 1.1 = doGet(), doPost(), doPut()
 *
 * @author Reader@Que
 */

public class SimpleServletExample extends HttpServlet 
{
  // doGet() - SCWCD Exam Objective 1.1
  public void doGet(HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException
  {
    // set the MIME type
    response.setContentType("text/html");

    // use this to print to browser
    PrintWriter out = response.getWriter();

    out.println("<html>");
    out.println("<head>");
    out.println("<title> A simple servlet. </title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<h1>Simple Servlet</h1>");
    out.println("This is a trivial " +
          "example of a servlet.");
    out.println("</body>");
    out.println("</html>");
  }
}

Listing 4.1 showed you an example of a servlet. The code is ordinary, but notice one small thing about printing to the browser. This example uses PrintWriter instead of using ServletOutputStream. The former is used for text, while the latter is used for bytes. See Figure 4.2 for a picture of the output. Listing 4.2 is the HTML the servlet generates and sends to the browser.

Listing 4.2 The Source Code Returned to the Browser by Listing 4.1

<html>
<head>
<title> A simple servlet. </title>
</head>
<body>
<h1>Simple Servlet</h1>
This is a trivial example of a servlet.
</body>
</html>

The HTML in Listing 4.2 is rendered by a browser so that it looks like Figure 4.2.

Figure 4.2
You can create dynamic content using a servlet.


How Does a Servlet Work?

You write a servlet and compile it, and then place it in the appropriate directory. When the Servlet Container starts, it will preload your servlet in memory if specified in the web.xml configuration file. If your servlet is not already loaded (not listed in the web.xml configuration file), its instance will be created as soon as a request for it is received by the Servlet Container. The first time it is loaded, the container calls your servlet's init() method, if there is one. Notice that it gets called only once, so place one-off functionality in this method (such as database connection, file object). Now that your servlet is ready, it waits for requests. The container will call your service() method each time a request is received for your servlet. The HttpServlet class (which your servlet must extend) already has this method, so you don't have to write one, but you can override it. The service() method then passes the request on to the appropriate method (usually GET for simple requests and POST to submit data, say a Web page form) such as the doGet() method if it is a GET request, or the doPost() method if it is a POST request. The doXXX() methods are the ones you need to override and where you will spend most of your effort. The servlet processes the request (code you write in doGet()), returning a response to the container. The container sends the text of the response back to the browser.

The preceding JSP and servlet examples are part of a Web application. A Web application is a collection of servlets, JSP pages, HTML documents, and other Web resources (such as image files, compressed archives, and other data). This collection may be packaged into an archive or exist as separate files in an open directory structure. Since you have many servlet classes, JSP pages, HTML pages, and other supporting libraries and files for a given Web application, there are many dependencies. These are not trivial to manage. It is vital that all parts go in their correct locations in the Web application archive or in an open directory structure. Once you get the dependencies resolved, it is a good idea to package the collection into a Web application archive, a single file with the .war extension that contains all of the components of a Web application. You can do this using standard JAR tools.

Now, we need to define what is meant regarding deploying a Web application. Normally, Web applications run on only one VM at any one time. When we talk about deploying a Web application, we mean that the collection of files that comprise a Web application is placed into a Web server's runtime (at least one part goes into JVM, which can then link to or grab other parts). What happens if you want to deploy your Web application in a Web farm? In this case, your Web application will run on several VMs simultaneously.

A distributable Web application is written so that it can be deployed in a Web container, distributed across multiple Java virtual machines running on the same host or different hosts. The two keys to making this possible are how you thread the servlets and what you tell the deployment descriptor. With the right combination of these, your Web application will run on several VMs simultaneously. The servlet declaration, which is part of the deployment descriptor, controls how the Servlet Container provides instances of the servlet. Normally, the Servlet Container uses only one instance per servlet declaration. However, for a servlet implementing the SingleThreadModel interface, the Servlet Container may instantiate multiple instances to handle a heavy request load and serialize requests to a particular instance.

In the case where a servlet is marked in the deployment descriptor as distributable and the application implements the SingleThreadModel interface, the container may instantiate multiple instances of that servlet in each VM of the container or across many machines (clustering servlets usually through serialization). The container has a complicated task in managing requests, sessions, and contexts across JVMs. How each vendor accomplishes this is beyond the scope of this book. You do need to know that to convert your Web application into a distributable one, you must implement the SingleThreadModel interface and mark the servlet as distributable in the deployment descriptor (see Chapter 10, "Web Applications," for more about web.xml).


Overriding HttpServlet GET, POST, and PUT Methods

1.1 For each of the HTTP methods, GET, POST, and PUT, identify the corresponding method in the HttpServlet class.

GET

POST

PUT

This exam objective addresses the most-used feature of servlets, namely, responding to HTTP requests. The exam will have several questions on this topic. These three types of requests dominate browser-server traffic.

The following code is a minimal template showing you how to use the GET, POST, and PUT methods. GET appends data to the URL before the URL is submitted to the server, whereas POST sends the data to the server separately from the URL. GET submissions can only be 1KB in length in most cases, whereas POST submissions can be arbitrarily large. The results of a GET submission can be reliably bookmarked, whereas the results of a POST submission can't. There are several differences between them which are explained later in this section.

These methods are called by the service method (not required in your servlet as it is in HttpServlet, as this method is inherited from HttpServlet). Together they can look like Listing 4.3.

Listing 4.4 Servlet That Handles GET, POST, and PUT Requests

/* doGetdoPostdoPutServlet.java, v 1.0
* SCWCD Exam Objective 1.1 = doGet(), doPost(), doPut()
 *
 */

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

/**
 * A servlet that reports back to the browser
 * the type of request received.
 *
 * @author Reader@Que
 */

public class doGetdoPostdoPutServlet extends HttpServlet 
{
  // doGet() - SCWCD Exam Objective 1.1
  public void doGet(HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException
  {
    reportType("doGet", response);
  }
  
  // doPost() - SCWCD Exam Objective 1.1
  public void doPost(HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException
  {
    reportType("doPost", response);
  }
  
  // doPut() - SCWCD Exam Objective 1.1
  public void doPut(HttpServletRequest request,
           HttpServletResponse response) 
    throws IOException, ServletException
  {
    reportType("doPut", response);
  }
  
  public void reportType(String requestType, 
              HttpServletResponse response)
    throws IOException, ServletException
  {
    // set the MIME type
    response.setContentType("text/html");

    // use this to print to browser
    PrintWriter out = response.getWriter();

    out.println("<html>");
    out.println("<head>");
    out.println("<title>doGetdoPostdoPutServlet" +
           "</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<h1>Your Request</h1>");
    out.println("Your request type: " + requestType);
    out.println("</body>");
    out.println("</html>");
  }
} 

Listing 4.3 sends a basic HTML message back to the browser that looks like Figure 4.3.

Figure 4.3
Filtering request types using a servlet.

It is the service method that calls the doXXX() methods. While you normally wouldn't override the service method, Listing 4.4 presents a skeleton example of what you could do with it. You might want to preprocess the request before sending it on to the appropriate doXXX() method.

Listing 4.4 Service Method Example

protected void service(HttpServletRequest req, 
            HttpServletResponse resp)
  throws ServletException, IOException
{
   String method = req.getMethod();

   if (method.equals(METHOD_GET)) 
   {
     doGet(req, resp);
   } else if (method.equals(METHOD_POST)) 
   {
     doPost(req, resp);
   } else if (method.equals(METHOD_PUT)) 
   {
     doPut(req, resp);
   } else 
   {
    // Servlet doesn't currently support 
    // other types of request.
    String errMsg = "Method Not Supported");
    resp.sendError(
      HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
   }
}

GET

The GET type request is normally used for simple HTML page requests. It has this syntax:

  public void doGet(HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException
    { //your code here}

When you write a servlet, this is the one method that you will start with. If this method is all you had in your servlet, it would handle the majority of your Web server's needs regarding this servlet. Notice, that the init() and service() methods involved in a request are already provided by the HttpServlet class, so they don't need to be overridden, although you can do so.

The GET is the most common type of browser request. According to the Hypertext Transfer Protocol standard, the GET method means "retrieve whatever information (in the form of an entity) is identified by the Request-URI." For a full discussion on naming and addressing (URL vs. URI) please see http://www.w3.org/Addressing/. If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the entity in the response and not the source text of the process, unless that text happens to be the output of the process." In our example, the test message is the entity.


GET Method

The GET is the most common type of browser request. The GET request is defined by the Internet Society's RFC 2616: Hypertext Transfer Protocol—HTTP/1.1. See section 9.3 of RFC 2616 at ftp://ftp.isi.edu/in-notes/rfc2616.txt.


This method is where your servlet does most of its labor. You could process a simple HTML response or hit a database with a query.

Table 4.1 provides a list of differences between GET and POST requests.

Table 4.1 GET vs. POST Request

GET

POST

Query string or form data is simply appended to the URL as name-value pairs.

Form name-value pairs are sent in the body of the request, not in the URL itself.

Query length is limited (~1KB).

Query length is unlimited.

Users can see data in address bar.

Data hidden from users.

http://mycompany.com/support?
Name=John+Smith&Product=go+ kart&Complaint=the+engine+ is+sputtering+oil
.

http://mycompany.com/support
<values of Name, Product, and Complaint are in request body>.

doGet().

doPost().

For getting (retrieving) data only.

For causing a change on the server (store data in DB).

ASCII.

ASCII + Binary.

Easy to bookmark.

Hard to bookmark.

Used more.

Used less.


The following short listings exemplify the GET and POST requests

<html>
 <body>
 <form method="GET" action="/myservlet/process">
  <input type="text" name="product" size="25"
  maxlength="35" value="type product name here">
  <input type="submit" name="submitButton" value="Submit">
 </form>
 </body>
</html>
<html>
 <body>
 <form method="post" action="/myservlet/process">
  <input type="text" name="product" size="25"
  maxlength="35" value="type product name here">
  <input type="submit" name="submitButton" value="Submit">
 </form>
 </body>
</html>

POST

The POST type request is most often used by HTML forms. It has this syntax:

  public void doPost(HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException
    { //your code here}

The POST method is more sophisticated than a GET request. Normally, a Web form has fields whose names and values are sent to the server in key-value pairs. The POST is designed for posting long messages (for example, to a bulletin board, newsgroup, mailing list); providing a block of data, such as the result of submitting a form; and submitting long data fields to a database (such as a SQL insert of lengthy string). Sometimes the action performed by the POST method doesn't return a standard HTML page. Suppose you updated a database table. The database doesn't send back an HTML confirmation on its own. You would have to wrap the database results in HTML manually. You also have the option of sending back an empty response with a header status of either 200 (OK) or 204 (No Content). A No Content status tells the browser that it shouldn't expect any HTML. You might want to do this if it is software to software interaction and no eyeballs are waiting to see a Web page.


POST Method

The POST is the most sophisticated of the methods covered by this part of the exam. The POST request is defined by the Internet Society's RFC 2616: Hypertext Transfer Protocol—HTTP/1.1. See section 9.5 of RFC 2616 at ftp://ftp.isi.edu/in-notes/rfc2616.txt.


Normally, this method is used to process a form submitted by a browser. You will very likely be looking for form field names and values. For example, the following snippet is how you would grab the value of the field formCountField that the user supplied a value for:

//read the query string
int customerRequest = 0;
String count = request.getParameter("formCountField");
try 
{
  customerRequest = Integer.parseInt(count);
} catch (Exception e) 
{ // NumberFormat or NullPointerException
  processError(e);
}  

PUT

The PUT type request is a means of uploading files to the server. While uploading is its original intent, I have not seen it used much. Instead, POST is generally used to upload files. The PUT handler has this syntax:

  public void doPut(HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException
    { //your code here}

The doPut() method is called by the server (via the service method) to handle a PUT request. Uploading files from a browser has always been difficult. The idea behind the PUT operation is to make uploading straightforward. It is supposed to allow a client to place a file on the server, just like sending a file by FTP. The javadoc for this method warns that when overriding this method, you should leave intact any content headers sent with the request (including Content-Length, Content-Type, Content-Transfer-Encoding, Content-Encoding, Content-Base, Content-Language, Content-Location, Content-MD5, and Content-Range). This method is rarely used, but it is powerful if you need it.

Listing 4.5 is a simplified HTML page that creates a file upload page that will direct the file contents to a servlet.

Listing 4.5 HTML Form Example

<html>
<body>
<form enctype="multipart/form-data" method="PUT"
action="localhost:8080/examples/servlet/UploadServlet">
<input type="file" size="20" name="FileToUpload"
value="Select File">
<input type="submit" name="UploadFile" value="Upload">
<input type="reset" value="Reset">
</form>
</body>
</html>

Listing 4.6 is a servlet that can accept an uploaded file.

Listing 4.6 Servlet That Handles a File Upload from the Client

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

public class UploadServlet extends HttpServlet 
{
    static final String dir="C:/temp";
    
    public void doPut(HttpServletRequest req, 
    HttpServletResponse res)
    throws ServletException, IOException
    {
     PrintWriter outHTML = res.getWriter();
     outHTML.println("done");
        
     try 
     {
        int i;
        InputStream input;
        input = req.getInputStream();
        BufferedInputStream in = 
           new BufferedInputStream(input);
        BufferedReader reader = 
           new BufferedReader(
             new InputStreamReader(in));
        File outputFile = 
           new File("c:/temp/out.txt");
        FileWriter out = 
           new FileWriter(outputFile);
    
        while ((i = reader.read()) != -1) 
        {
          out.write(i);
        }
    
        out.close();
        in.close();
    
      }
      catch (IOException e) {}
    }
}

You need to account for the header and footer lines that the stream attaches to the actual file contents. The stream looks like this:

------------BB1rHqKAOHkiUoiFS3VI6v
Content-Disposition: form-data; name="FileToUpload"; 
                 filename="Candle.txt"
Content-Type: application/octet-stream; name="Candle. txt"

// ...
//actual file content here
// ...

------------BB1rHqKAOHkiUoiFS3VI6v
Content-Disposition: form-data; name="UploadFile"

Upload
------------BB1rHqKAOHkiUoiFS3VI6v

Therefore, you will need to search (indexOf) for the start of the actual file content by looking for the Content-Type and subsequent name parameters like so:

int headerEnd = line.indexOf("Content-Type: ");
headerEnd = line.indexOf("name=\"", headerEnd); 
headerEnd = line.indexOf("\"", headerEnd + 7); //last quote

Likewise, you need to search the end of the file for the telltale Content-Disposition and preceding "------------" marker like so:

int footerStart = 
       line.lastIndexOf ("Content- Disposition: ");
footerStart = line.lastIndexOf ("---------", footerStart); 

Lastly, you will grab the text between the two like so:

fileContent = line.substring(headerEnd, footerStart);

You can refer to RFC 1867 to learn more about uploading files through an HTML form (http://www.servlets.com/rfcs/rfc1867.html). This is all tedious, so you might just grab an open source (http://www.servlets.com/cos/index.html) or commercial Bean that uploads files such as uploadBean (http://www.javazoom.net/jzservlets/uploadbean/uploadbean.html) or jspSmartUpload (http://www.jspsmart.com/).

Listing 4.6 worked when placed in the doPost() method (and the form method of Listing 4.5 is set to post), but did not work in the doPut() method using IE or Opera against Tomcat (version 4). I verified that the doPut() method is called as expected in the servlet. However, even after much tweaking, this file upload code failed when placed in the doPut method as shown previously. If you only change doPut to doPost it works?! Although I need to research this problem with Tomcat, you do need to understand that PUT is used to upload files to a Web server and that this is usually done by non-browser, client-side Web content development tools.

Triggering HttpServlet GET, POST, and PUT Methods

1.2 For each of the HTTP methods, GET, POST, and HEAD, identify triggers that might cause a browser to use the method, and identify benefits or functionality of the method.

GET

POST

HEAD

This exam objective focuses on what triggers the events or methods in your servlets. For example, what action can a client take that results in the doGet() method being called in your servlet?

GET

As noted previously, the GET type request is normally used for simple HTML page requests. The types of events that generate this type of request are clicking on a hyperlink, changing the address directly by typing in the address textbox on a browser or application that has HTML functionality, and submitting an HTML form where the method header is set to get as in method=get. Also, a GET request is triggered when selecting a favorite from the Favorites list and using JavaScript to change location.href. Usually the browser is configured to send a GET request even if no method is set explicitly by the HTML.

The benefits of the GET method are

  • It retrieves information such as a simple HTML page or the results of a database query.

  • It supports query strings (name-value pairs appended to URL). Servers usually limit query strings to about 1000 characters.

  • It allows bookmarks.

POST

This occurs when a browser or application submits an HTML form with the method attribute set to post as in method=post.

The benefits of the POST method are

  • It sends information to the server such as form fields, large text bodies, and key-value pairs.

  • It hides form data because it isn't passed as a query string, but in the message body.

  • It sends unlimited length data as part of its HTTP request body.

  • It disallows bookmarks.

HEAD

A browser or application will sometimes send a request to a server just to check the status or get information (for example, "can you handle file upload?") from the server.

The HEAD method returns the same header lines that a GET method would return; however, no body or content is returned. This is often accomplished by calling doGet(), setting the headers but not setting any output, and then returning the response (without any body) to the requester.

The primary benefit of this method is message size. The HEAD method receives and returns very small messages. Therefore it is fast and lightweight on both ends.

Interfacing with HTML Requests

In this section we deal with interfacing with HTML requests: how to process them and how to return a response to one. Since the HTTP client is sending the request, how do you know what it wants? While the container handles things like parsing the request and placing the information into a Request object, sometimes you have manually code processing routines. This section tells you how to write these routines that perform actions such as retrieve HTML form parameters, request headers, servlet initialization parameters, and redirects.

1.3 For each of the following operations, identify the interface and method name that should be used to

  • Retrieve HTML form parameters from the request
  • Retrieve a servlet initialization parameter
  • Retrieve HTTP request header information
  • Set an HTTP response header; set the content type of the response
  • Acquire a text stream for the response
  • Acquire a binary stream for the response
  • Redirect an HTTP request to another URL

This is a broad-stroke objective. It is asking you to be familiar with the most important servlet interfaces and their methods. Thankfully, this objective reduces the task from remembering almost 1,000 methods to just a few of them, which happen to be the most interesting ones.

Form Parameters

The interface that defines the form parameter methods is ServletRequest. This interface is implemented by the Web container to get the parameters from a request. Parameters are sent in the query string or posted form data. The four methods associated with getting parameters are

  • getParameter(String). You use this method if you know the particular parameter name. It returns the value of a request parameter as a string, or null if the parameter does not exist. Use this method when you are sure the parameter has only one value; otherwise use getParameterValues(). Be careful: If you use this method with a multivalued parameter, you won't get an error. You will get the first value in the array returned by getParameterValues().

  • getParameterMap(). You use this method to create a map of the form parameters supplied with this request.

  • getParameterNames(). This one returns an Enumeration of string objects containing the names of the parameters contained in this request, or an empty Enumeration if the request has no parameters.

  • getParameterValues(String). This method returns an array of values as strings, or null if the parameter does not exist. If the parameter has a single value, the array has a length of 1. One of the common uses of getParameterValues() is for processing <select> lists that have their "multiple" attribute set.

Listing 4.7, the following code snippet, demonstrates how you would grab the parameters from a request.

Listing 4.7 Servlet That Walks the Request Parameter List

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

public class ShowRequestParameters extends HttpServlet 
{
  public void doPost(HttpServletRequest request, 
           HttpServletResponse response)
  throws IOException, ServletException
  {
    Enumeration parameterNames = 
           request.getParameterNames();

    // acquire text stream for response
    PrintWriter out = res.getWriter ();

    while (parameterNames.hasMoreElements()) {
      String name = 
        (String)parameterNames.nextElement();
      String value = request.getParameter(name);
      out.println(name + " = " + value + "<br/>");
    }
  }
}

Retrieving a Servlet Initialization Parameter

A Web application includes many parts; it rarely is just one class or file. It can be a combination of JSP pages, servlets, tag libraries, Java beans, and other class files. The Java Virtual Machine creates a memory box for all of these called a ServletContext object which maintains information (context) about your Web application. You access the ServletContext for information about the application state. As the API states, the ServletContext allows you access many types of information. You can get application-level initialization parameters. You can also set and get application attributes, as well as the major and minor version of the Servlet API that this Servlet Container supports. One very interesting capability is to get hold of RequestDispatcher object to forward requests to other application components within the server, or to include responses from certain components within the servlet and to log a message to application log file. The ServletContext object is how you can set, get, and change application (not session) level attributes and talk to the Servlet Container.

Context means application scope. The getInitParameter and getInitParameterNames methods retrieve context-wide, application-wide, or "Web application" parameters. The getInitParameter method returns a string containing the value of the parameter (you provide the name), or null if the parameter does not exist.

Some parameters have no information, so this method will return a string containing at least the Servlet Container name and version number. The getInitParameterNames method retrieves the names of the servlet's initialization parameters as an Enumeration of string objects. If there aren't any, it returns an empty Enumeration. Be careful; don't confuse this with session-wide attributes.

Listing 4.8 shows an example of displaying servlet initialization parameters.

Listing 4.8 Servlet That Walks the Context Initialization Parameter List

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.*;
import javax.servlet.http.*;

public class InitializationParameters extends HttpServlet 
{
  /**
   * Print servlet configuration init. parameters.
   *
   * @param request The servlet request we are processing
   * @param response The servlet response we are creating
   *
   * @exception IOException if an input/output error
   * @exception ServletException for a 
   * servlet-specified error
   */
  public void doGet(HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException 
  {

    response.setContentType("text/html");
    PrintWriter writer = response.getWriter();

    // servlet configuration initialization parameters
    writer.println("<h1>ServletConfig " +
            "Initialization Parameters</h1>");
    writer.println("<ul>");
    Enumeration params = 
        getServletConfig().getInitParameterNames();
    while (params.hasMoreElements()) 
    {
      String param = (String) params.nextElement();
      String value = 
        getServletConfig().getInitParameter(param);
      writer.println("<li><b>" + param + 
              "</b> = " + value);
    }
    writer.println("</ul>");
    writer.println("<hr>");
   }
}

Retrieving HTTP Request Header Information

The request header is where all the details of the request are bundled. This is where the browser specifies the file wanted, date, image file support, and more. Listing 4.9 shows a popular way to display the header parameters by walking through an Enumeration of them.

Listing 4.9 Servlet That Displays the HTTP Header Information

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

/**
 * Displaying request headers
 *
 * @author Reader@Que
 */

public class DisplayRequestHeaders extends HttpServlet {

  public void doGet(HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException
  {
    response.setContentType("text/html");

    PrintWriter out = response.getWriter();
    out.println("<html>");
    out.println("<head>");

    String title = "Requestheader Example";
    out.println("<title>" + title + "</title>");
    out.println("</head>");
    out.println("<body>");

    out.println("<h3>" + title + "</h3>");
    out.println("<table>");
    Enumeration e = request.getHeaderNames();
    while (e.hasMoreElements()) 
    {
      String headerName = (String)e.nextElement();
      String headerValue = 
              request.getHeader(headerName);
      out.println("<tr><td bgcolor=\"#CCCCCC\">" + 
             headerName);
      out.println("</td><td>" + headerValue + 
            "</td></tr>");
    }
    out.println("</table>");
    out.println("</body>");
    out.println("</html>");
  }
}

The output of this listing looks like Figure 4.4.

Figure 4.4
You can retrieve request header information using a servlet.

Acquiring a Binary Stream for the Response

Suppose you want to open a binary file in a browser from a servlet. It isn't text so you have to write the file to the servlet's output stream. Let's practice with a PDF document. First, you get the servlet's output stream with:

ServletOutputStream out = res.getOutputStream();

Next, you set the file type in the response object using one of the standard MIME (Multipurpose Internet Mail Extension) protocols. Several listings of content type names are available on the Internet including one at ftp://ftp.isi.edu/in-notes/iana/assignments/media-types. Then you use an HTTP response header named content-disposition. This header allows the servlet to specify information about the file's presentation. Using that header, you can indicate that the content should be opened separately (not actually in the browser) and that it should not be displayed automatically, but rather upon some further action by the user. You can also suggest the filename to be used if the content is to be saved to a file. That filename would be the name of the file that appears in the Save As dialog box. If you don't specify the filename, you are likely to get the name of your servlet in that box. To find out more about the content-disposition header, check out Resources or go to http://www.alternic.org/rfcs/rfc2100/rfc2183.txt.

Sending a binary stream to the client is not easy. Listing 4.10 will help you do it right.

Listing 4.10 Servlet That Sends a File to the Client

public class BinaryResponse extends HttpServlet {

  /**Set global variables*/
  public void init(ServletConfig config) 
      throws ServletException 
  {
   super.init(config);
  }

  /**Process HTTP Post request with doPost*/
  public void doPost(HttpServletRequest request, 
           HttpServletResponse response) 
    throws ServletException, IOException 
  {
  
   String fileName = "index.html"; //set file name  
   String contentType = getContentType(fileName);
   //contentType = getType(); //get the content type
   
   // get the file
   File file = new File(fileName);
   long length = file.length();
   if(length > Integer.MAX_VALUE)
   {
     //handle too large file error
     //perhaps log and return error message to client 
   }
   byte[] bytes = new byte[(long)length];
   BufferedInputStream in = 
    new BufferedInputStream(new FileInputStream(file));
   // then place it into a byte array
   if(length != in.read(bytes))
   {
     //handle too large file error
     //perhaps log and return error message to client 
   }

   //get the servlet's output stream
   BufferedOutputStream out = 
   new BufferedOutputStream(response.getOutputStream());
   //set the content type of the response
   response.setContentType( contentType );
   //send the file to the client
   out.write( bytes );
  }
 }

  /**Clean up resources*/
  public void destroy() 
  {
   //If you need to clean up resources.
   //Otherwise don't override.
  }
  String getContentType(String fileName)
  {
   String extension[] = 
   {              // File Extensions
     "txt",            //0 - plain text 
     "htm",            //1 - hypertext 
     "jpg",            //2 - JPEG image 
     "gif",            //3 - gif image 
     "pdf",            //4 - adobe pdf
     "doc",            //5 - Microsoft Word 
   },                // you can add more
   mimeType[] = 
   {             // mime types
     "text/plain",         //0 - plain text 
     "text/html",         //1 - hypertext 
     "image/jpg",         //2 - image 
     "image/gif",         //3 - image 
     "application/pdf",      //4 - Adobe pdf 
     "application/msword",     //5 - Microsoft Word 
   },                // you can add more
   contentType = "text/html";    // default type
   
   // dot + file extension
   int dotPosition = fileName.lastIndexOf('.');
   // get file extension
   String fileExtension = 
       fileName.substring(dotPosition + 1);
   // match mime type to extension
   for(int index = 0; index < MT.length; index++)
   {
     if(fileExtension.equalsIgnoreCase(
                  extension[index])) 
     {
      contentType = mimeType[index]; 
      break;
     }
   } 
    
   return contentType;
  }
}

Redirecting an HTTP Request to Another URL

It often happens that pages move around and a URL becomes invalid. Throwing back a 404 error isn't nice. The response object has the sendRedirect method, which sends a temporary redirect response to the client sending with it a new location URL. You can use relative or absolute URLs, because the Servlet Container translates a relative URL to an absolute URL before sending the response to the client.

The two potential problems with this method are sending a bad URL to the client and using this method after the response has already been committed. The bad URL will look bad, but not produce an error. The latter, though, will throw an IllegalStateException. Furthermore, after using this method, the response is committed and can't be written to, or you'll get an error. One nice feature is that this method writes a short response body including a hyperlink to the new location. This way, if the browser doesn't support redirects, it will still get the new link. Use the following syntax for this method:

// Suppose this portion of the server is down.
// Redirect the user to an explanation page.
redirectPath = "./error/notAvailable.html";
response.sendRedirect(redirectPath); 

Web Application Scope

This section discusses scope. There are three scopes to worry about for the exam: namely, Request, Session, and Context. Suppose you had to keep track of all your customer visits to your support Web site. Where would you place the counter? You would place it in Context scope. To better understand what I mean, please study the following objective.

1.4 Identify the interface and method to access values and resources and to set object attributes within the following three Web scopes:

  • Request
  • Session
  • Context

This objective requires you to understand how to set and get name-value attributes at three different levels. The breadth of scope increases from Request to Session to Context, the widest scope.

Table 4.2 provides a definition of the three object scopes of concern under this objective, namely, Request, Session, and Application.

Table 4.2 Request, Session, and Application Scope

Name

Accessibility

Lifetime

Request

Current, included, or forwarded pages

Until the response is returned to the user.

Session

All requests from same browser within session timeout

Until session timeout or session ID invalidated (such as user quits browser).

application

All request to same Web application

Life of container or explicitly killed (such as container administration action).


The idea here is if you set an attribute (that is, request.setAttribute()), when can you access it? The answer depends on which object was used to the attribute. So, if you set an attribute with the request object, then the scope of that specific attribute is only Request. You can't access it once the request is complete. You can't see this attribute in another request even if it is in the same session. Conversely, any attribute set with the ServletContext object can be seen in all sessions and all requests.

Listing 4.11 is a program that demonstrates how you could use access attributes from the three primary scopes of Request, Session, and Application. You can also use setAttribute() for each of these scopes.

Listing 4.11 Attributes from Request, Session, and Application Scopes

import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import javax.servlet.*;
import javax.servlet.http.*;

public class AttributeScope extends HttpServlet {

  public void printHeader(PrintWriter out, String header) {
    out.println("  <tr>");
    out.println("  <td bgcolor=\"#999999\" " +
          "colspan=\"2\">");
    out.println("   <b>"+header+"</b>");
    out.println("  </td>");
    out.println("  </tr>");
  }

  public void printValue(PrintWriter out, String key, 
      String val) 
  {
    if (val!=null) 
    {
      if (val.length()>255) 
      val=val.substring(0,128)+" <i>(... more)</i>";
    }
    out.println("<tr>");
    out.println("<td bgcolor=\"#cccccc\">"+key+"</td>");
    out.println("<td bgcolor=\"#ffffff\">"+val+"</td>");
    out.println("</tr>");
  }

  public void printVoid(PrintWriter out) 
  {
    out.println("  <tr><td bgcolor=\"#ffffff\" " +
          "colspan=\"2\">&nbsp;</td></tr>");
  }

  public void doPost(HttpServletRequest request, 
      HttpServletResponse response)
   throws ServletException, IOException 
  {
    doGet(request,response);
  }

  public void doGet(HttpServletRequest request, 
      HttpServletResponse response)
   throws ServletException, IOException 
  {

    Enumeration enum = getInitParameterNames();
    ServletContext context = getServletContext();
    PrintWriter out = response.getWriter();
    response.setContentType("text/html");

    out.println("<html>");
    out.println(" <head>");
    out.println(" <title>Attribute Scope Example" +
            "</title>");
    out.println(" </head>");
    out.println(" <body>");
    out.println(" <p align=center>");
    out.println("  <h2>Attribute Scope Example</h2>");

    String url=request.getScheme()+"://"+
          request.getServerName()+":"+
          request.getServerPort()+
          request.getRequestURI();

    out.println("  <table border=\"0\" cellspacing=" +
            \"2\" cellpadding=\"2\">");
    out.println("  <tr>");
    out.println("   <td>");

    out.println("   <form action=\""+url+
              "\" method=\"GET\">");
    out.println("    <input type=\"hidden\" " +
              "name=\"hiddenName\" " +
              "value=\"hiddenValue\">");
    out.println("    <input type=\"submit\" " +
              "name=\"submitName\" " +
              "value=\"Submit using GET\">");
    out.println("   </form>");

    out.println("   </td>");
    out.println("  </tr>");
    out.println("  </table>");

    out.println(" </p>");
    out.println(" <br>");

    out.println(" <table width=\"100%\" border=\"1\""+
         " cellspacing=\"0\" cellpadding=\"1\">");

    printHeader(out,"Context attributes:");
    enum = context.getAttributeNames();
    while (enum.hasMoreElements()) {
      String key = (String)enum.nextElement();
      Object val = context.getAttribute(key);
      printValue(out,key,val.toString());
    }
    printVoid(out);

    printHeader(out,"Request attributes:");
    enum = request.getAttributeNames();
    while (enum.hasMoreElements()) {
      String key = (String)enum.nextElement();
      Object val = request.getAttribute(key);
      printValue(out,key,val.toString());
    }
    printVoid(out);

    printHeader(out,"Parameter names in request:");
    enum = request.getParameterNames();
    while (enum.hasMoreElements()) {
      String key = (String)enum.nextElement();
      String[] val = request.getParameterValues(key);
      for(int i = 0; i < val.length; i++)
        printValue(out,key,val[i]);
    }
    printVoid(out);


    printHeader(out,"Session information:");
    SimpleDateFormat format = 
     new SimpleDateFormat("yyyy/MM/dd hh:mm:ss.SSS z");
    HttpSession session = request.getSession();
    if (session!=null) {
    printValue(out,"Requested Session Id:", 
             request.getRequestedSessionId());
      printValue(out,"Current Session Id:",
                     session.getId());
      printValue(out,"Current Time:", 
                format.format(new Date()));
      printValue(out,"Session Created Time:", 
    format.format(new Date(session.getCreationTime())));
      printValue(out,"Session Last Accessed Time:", 
  format.format(new Date(session.getLastAccessedTime())));
      printValue(out,"Session Max Inactive Interval"+
            " Seconds:", 
    Integer.toString(session.getMaxInactiveInterval()));
      printVoid(out);

      printHeader(out,"Session values:");
      enum = session.getAttributeNames();
      while (enum.hasMoreElements()) {
        String key = (String) enum.nextElement();
        Object val = session.getAttribute(key);
        printValue(out,key,val.toString());
      }
    }
    printVoid(out);

    out.println("</pre>");
    out.println("  </td>");
    out.println("  </tr>");

    out.println(" </table>");
    out.println(" </body>");
    out.println("</html>");
    out.flush();
  }
}

The output of this listing looks like Figure 4.5.

Figure 4.5
You can get attributes with Request, Session, or Application scope.

The previous listing demonstrates how to retrieve attributes from the three primary scopes. Let us now focus on the Request object.

Request

When a user hits a URL with a servlet at the other end, the Servlet Container creates an HttpServletRequest object. It passes this object as an argument to the servlet's service methods (doPut(), doGet(), and doPost()). There is a lot of information in this object, including the login of the user making this request (getRemoteUser()) and the name of the HTTP method with which this request was made (getMethod()). However, this exam objective is restricted to header information. Therefore, you need to know the following HttpServletRequest methods.

The following list of methods summarizes the Request (Interfaces: ServletRequest and HttpServletRequest) methods you need to be familiar with. While, strictly speaking, all are fair game, I marked with an asterisk those that are more likely to be on the exam:

  • *getAttribute(String name). Returns the value of the named attribute as an Object, or null if no attribute of the given name exists.

  • *getAttributeNames(). Returns an Enumeration containing the names of the attributes available to this request.

  • getAuthType(). Returns the name of the authentication scheme used to protect the servlet.

  • getCharacterEncoding(). Returns the name of the character encoding used in the body of this request.

  • getContentLength(). Returns the length, in bytes, of the request body if made available by the input stream, or -1 if the length is not known.

  • getContentType(). Returns the MIME type of the body of the request, or null if the type is not known.

  • getContextPath(). Returns the portion of the request URI that indicates the context of the request.

  • getCookies(). Returns an array containing all of the Cookie objects the client sent with this request.

  • getDateHeader(java.lang.String name). Returns the value of the specified request header as a long value that represents a Date object.

  • *getHeader(java.lang.String name). Returns the value of the specified request header as a String.

  • *getHeaderNames(). Returns an enumeration of all the header names this request contains.

  • getHeaders(java.lang.String name). Returns all the values of the specified request header as an Enumeration of String objects.

  • getInputStream(). Retrieves the body of the request as binary data using a ServletInputStream.

  • getIntHeader(java.lang.String name). Returns the value of the specified request header as an int.

  • getLocale(). Returns the preferred Locale that the client will accept content in, based on the Accept-Language header.

  • getLocales(). Returns an Enumeration of Locale objects indicating, in decreasing order starting with the preferred locale, the locales that are acceptable to the client based on the Accept-Language header.

  • *getMethod(). Returns the name of the HTTP method with which this request was made; for example, GET, POST, or PUT.

  • *getParameter(java.lang.String name). Returns the value of a request parameter as a string, or null if the parameter does not exist.

  • getParameterMap(). Returns a java.util.Map of the parameters of this request.

  • *getParameterNames(). Returns an Enumeration of string objects containing the names of the parameters contained in this request.

  • *getParameterValues(java.lang.String name). Returns an array of string objects containing all of the values the given request parameter has, or null if the parameter does not exist.

  • getPathInfo(). Returns any extra path information associated with the URL the client sent when it made this request.

  • getPathTranslated(). Returns any extra path information after the servlet name but before the query string, and translates it to a real path.

  • getProtocol(). Returns the name and version of the protocol the request uses in the form protocol/majorVersion.minorVersion, for example, HTTP/1.1.

  • *getQueryString(). Returns the query string that is contained in the request URL after the path.

  • getReader(). Retrieves the body of the request as character data using a BufferedReader.

  • getRemoteAddr(). Returns the Internet Protocol (IP) address of the client that sent the request.

  • getRemoteHost(). Returns the fully qualified name of the client that sent the request.

  • getRemoteUser(). Returns the login of the user making this request, if the user has been authenticated, or null if the user has not been authenticated.

  • *getRequestDispatcher(java.lang.String path). Returns a RequestDispatcher object that acts as a wrapper for the resource located at the given path.

  • getRequestURI(). Returns the part of this request's URL from the protocol name up to the query string in the first line of the HTTP request.

  • getRequestURL(). Reconstructs the URL the client used to make the request.

  • getRequestedSessionId(). Returns the session ID specified by the client.

  • getScheme(). Returns the name of the scheme used to make this request; for example, http, https, or ftp.

  • getServerName(). Returns the host name of the server that received the request.

  • getServerPort(). Returns the port number on which this request was received.

  • getServletPath(). Returns the part of this request's URL that calls the servlet.

  • *getSession(). Returns the current session (HttpSession) associated with this request, or if the request does not have a session, creates one.

  • *getSession(boolean create). Returns the current HttpSession associated with this request, or, if there is no current session and create is true, returns a new session.

  • getUserPrincipal(). Returns a java.security.Principal object containing the name of the current authenticated user.

  • isRequestedSessionIdFromCookie(). Checks whether the requested session ID came in as a cookie.

  • isRequestedSessionIdFromURL(). Checks whether the requested session ID came in as part of the request URL.

  • isRequestedSessionIdValid(). Checks whether the requested session ID is still valid.

  • isSecure(). Returns a boolean indicating whether this request was made using a secure channel, such as HTTPS.

  • isUserInRole(java.lang.String role). Returns a boolean indicating whether the authenticated user is included in the specified logical "role".

  • *removeAttribute(java.lang.String name). Removes an attribute from this request.

  • *setAttribute(java.lang.String name, java.lang.Object o). Stores an attribute in this request.

  • setCharacterEncoding(java.lang.String env). Overrides the name of the character encoding used in the body of this request.

Several of the HttpServletRequest methods are not mentioned specifically in the objectives, but you should be familiar with them. Listing 4.11 is a program that demonstrates how you would retrieve attributes from the Request object, the main concern in the current exam objective. However, I thought you might also like to see what else you can do with this object. Listing 4.12 uses the Request object's methods to retrieve HTTP request header information (similar to Listing 4.10). Of course, the information you get out of the Request object has only Request scope.

Listing 4.12 Servlet That Retrieves Request Header Information

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

/**
 * set request attributes
 *
 * @author Reader@Que
 */

public class RequestDislay extends HttpServlet 
{
  public void doGet(HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException
  {

  // Set MIME type response header
  response.setContentType("text/html");

  // Acquire a text stream for the response
  PrintWriter out = response.getWriter();

  //prefer to buffer html then print all at once
  StringBuffer html = new StringBuffer();

  // HTML header
  html.append("<html>");
  html.append("<head>");
  html.append("<title>");
  html.append("Servlet Header Example");
  html.append("</title>");
  html.append("<head>");
  html.append("<body>");

  // begin the HTML body
  html.append("<h1>Request info</h1>");

  // build list of header name-value pairs
  String headerName;
  String headerValue;
  Enumeration headerNames = request.getHeaderNames();
  while (headerNames.hasMoreElements()) 
  {
   //enumeration returns object so cast as String
   headerName = (String) headerNames.nextElement();
   headerValue = request.getHeader(headerName);
   html.append(" " + headerName + " : " + headerValue +
         "<br/>");
  }

  //These methods are not named by objectives, but are
  // good to know. 
  //A simple march down the API:
  html.append("Auth Type: " + request.getAuthType());
  html.append("Character Encoding: " + 
              request.getCharacterEncoding());
  html.append("Content Length: " + 
                request.getContentLength());
  html.append("Content Type: "+ request.getContentType());
  html.append("Method: " + request.getMethod());
  html.append("Path Info: " + request.getPathInfo());
  html.append("Path Translated: " + 
                request.getPathTranslated());
  html.append("Protocol: " + request.getProtocol());
  html.append("Query String: " + request.getQueryString());
  html.append("Remote Address: "+ request.getRemoteAddr());
  html.append("Remote Host: " + request.getRemoteHost());
  html.append("Request URI: " + request.getRequestURI());
  html.append("Remote User: " + request.getRemoteUser());
  html.append("Scheme: " + request.getScheme());
  html.append("Server Name: " + request.getServerName());
  html.append("Servlet Path: " + request.getServletPath());
  html.append("Server Port: " + request.getServerPort());

  // build list of parameter name-value pairs
  html.append("Parameters:");
  Enumeration parameterNames =request.getParameterNames();
  while (parameterNames.hasMoreElements()) 
  {
   String parameterName = 
           (String) parameterNames.nextElement();
   String[] parameterValues = 
         request.getParameterValues(parameterName);
   html.append("  " + parameterName + ":");
   for (int i = 0; i < parameterValues.length; i++) 
   {
     html.append("   " + parameterValues[i]);
   }
  }

  // build list of cookie name-value pairs
  html.append("Cookies:");
  Cookie[] cookies = request.getCookies();
  for (int i = 0; i < cookies.length; i++) 
  {
   String cookieName = cookies[i].getName();
   String cookieValue = cookies[i].getValue();
   html.append(" " + cookieName + " : " + cookieValue);
  }
  html.append("</body>");
  html.append("</html>");

  // optional use descriptive elements to clarify code
  final String BEGIN_TAG = "<";
  final String CLOSE_TAG = "/";
  final String END_TAG = ">";

  // Print the HTML footer
  html.append(BEGIN_TAG + CLOSE_TAG + "body" + END_TAG);
  html.append(BEGIN_TAG + CLOSE_TAG + "html" + END_TAG);

  // Sometimes it is better (performance improvement) 
  // to send html to stream all at once.
  out.print( html );
  out.close();
 }
}

Session

A Session is made up of multiple hits from the same browser across some period of time. The session scope includes all hits from a single machine (multiple browser windows if they share cookies). Servlets maintain state with sessions. Listing 4.13 is a modification of a sample servlet that ships with Tomcat. It demonstrates how you can use session attributes.

Listing 4.13 Servlet That Demonstrates Session Attributes

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

public class SessionExample extends HttpServlet 
{

  public void doGet(HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException
  {
    response.setContentType("text/html");

    PrintWriter out = response.getWriter();
    out.println("<html>");
    out.println("<body bgcolor=\"white\">");
    out.println("<head>");

    String title = "Session Servlet";
    out.println("<title>" + title + "</title>");
    out.println("</head>");
    out.println("<body>");

     out.println("<h3>" + title + "</h3>");

    HttpSession session = request.getSession();
    out.println("session id" + " " + session.getId());
    out.println("<br/>");
    out.println("session created" + " ");
    out.println(new Date(session.getCreationTime()) + 
          "<br/>");
    out.println("session lastaccessed" + " ");
    out.println(
         new Date(session.getLastAccessedTime()));

    //get these from the HTML form or query string
    String dataName = request.getParameter("dataname");
    String dataValue =request.getParameter("datavalue");
    if (dataName != null && dataValue != null) 
    {
      session.setAttribute(dataName, dataValue);
    }

    out.println("<p/>");
    out.println("session data" + "<br/>");
    Enumeration names = session.getAttributeNames();
    while (names.hasMoreElements()) 
    {
      String name = (String) names.nextElement(); 
      String value = 
          session.getAttribute(name).toString();
      out.println(name + " = " + value + "<br/>");
    }

    out.println("<p/>");
    out.print("<form action=\"");
    out.print(response.encodeURL("SessionExample"));
    out.print("\" ");
    out.println("method=POST>");
    out.println("Name of Session Attribute:");
    out.println("<input type=text size=20 " +
          "name=dataname>");
    out.println("<br/>");
    out.println("Value of Session Attribute:");
    out.println("<input type=text size=20 " +
          "name=datavalue>");
    out.println("<br/>");
    out.println("<input type=submit>");
    out.println("</form>");

    out.println("<p/>GET based form:<br/>");
    out.print("<form action=\"");
    out.print(response.encodeURL("SessionExample"));
    out.print("\" ");
    out.println("method=GET>");
    out.println("Name of Session Attribute:");
    out.println("<input type=text size=20 " +
          "name=dataname>");
    out.println("<br/>");
    out.println("Value of Session Attribute:"); 
    out.println("<input type=text size=20 " +
          "name=datavalue>");
    out.println("<br/>");
    out.println("<input type=submit>");
    out.println("</form>");

    out.print("<p/><a href=\"");
    String url = "SessionExample?dataname=scwcd&" +
           "datavalue=pass!";
    out.print(response.encodeURL(url));
    out.println("\" >URL encoded </a>");

    out.println("</body>");
    out.println("</html>");
    
    out.println("</body>");
    out.println("</html>");
  }

  public void doPost(HttpServletRequest request,
           HttpServletResponse response)
    throws IOException, ServletException
  {
    doGet(request, response);
  }

}
//returns a page that looks like:
//Session Servlet
//
//session id 9805A5C4C084F5B47788242406C22455 
//session created Tue Apr 16 22:11:06 PDT 2002
//session lastaccessed Tue Apr 16 22:13:27 PDT 2002
//
//session data
//publisher = Que
//author = trottier
//scwcd = pass!

To summarize, sessions are what you can use to track a single user over a short time. You get the session object (HttpSession) from the request object. To track multiple users over time you must jump to context, covered next.

Context

A Web application includes many parts; it rarely is just one class or one JSP. To help manage an application, you will sometimes need to set and get information that all of the servlets share together, which we say is context-wide. An example would be using a login servlet to create an application-level attribute such as application name like so:

public void init(ServletConfig config) 
   throws ServletException 
{
    super.init(config);

    // set application scope parameter 
    // to "Certification by Que"
    ServletContext context = config.getServletContext();
    context.setAttribute(
       "applicationName", "Certification by Que");
}

Later, in another servlet you may use the application name as demonstrated in Listing 4.14.

Listing 4.14 Servlet doGet Method Demonstration

public void doGet(HttpServletRequest request, 
         HttpServletResponse response)
    throws ServletException, IOException 
{
    response.setContentType("text/html");
    
    PrintWriter out = response.getWriter();
    out.print("<html>");
    out.print("<head>");
    out.print("<title>");
    
    // get value of applicationName from ServletContext
    out.print(getServletContext().getAttribute(
              "applicationName").toString());
    
    out.print("</title>");
    out.print("<head>");
    out.print("<body>");
    
    //complete body content here...
    
    out.print("</body></html>");
    out.close();
}

Besides setting and retrieving your custom attributes, you can get additional information from the Servlet Container, such as its major and minor version, the path to a given servlet, and more. The following summarizes the additional methods you might use:

  • getAttributeNames(). Returns an Enumeration object containing the attribute names available within this servlet context.

  • getContext(String uripath). Returns a ServletContext object that corresponds to a specified URL on the server.

  • getInitParameter(String name). Returns a string containing the value of the named context-wide initialization parameter, or null if the parameter does not exist.

  • getInitParameterNames(). Returns the names of the context's initialization parameters as an Enumeration of string objects, or an empty Enumeration if the context has no initialization parameters.

  • getMajorVersion(). Returns the major version as an int of the Java Servlet API that this Servlet Container supports.

  • getMimeType(java.lang.String file). Returns the MIME type as a string of the specified file, or null if the MIME type is not known.

  • getMinorVersion(). Returns the minor version as an int of the Servlet API that this Servlet Container supports.

  • getNamedDispatcher(String name). Returns a RequestDispatcher object that acts as a wrapper for the named servlet.

  • getRealPath(String path). Returns a string containing the real path for a given virtual path.

  • getRequestDispatcher(String path). Returns a RequestDispatcher object that acts as a wrapper for the resource located at the given path.

  • getResource(String path). Returns a URL to the resource that is mapped to a specified path.

  • getResourceAsStream(String path). Returns the resource located at the named path as an InputStream object.

getServerInfo(). Returns the name and version as a String of the Servlet Container on which the servlet is running.

Servlet Life-cycle

1.5 Given a life-cycle method, init, service, or destroy, identify correct statements about its purpose or about how and when it is invoked.

The servlet life-cycle is not obvious. The container calls three methods—namely, init(), service() and destroy()—in that order. Ordinarily, that is how the container talks to your servlet. With some containers, you can modify this behavior, but the exam will assume this order.


When is init() called?

A common question on the exam tests your understanding of when init() is called. Knowledge of a servlet's life-cycle is crucial to answering these types of questions. Remember, init() may be called when the server starts (tell web.xml to load servlet upon startup), when first requested, and sometimes the container management console will allow you to call it as part of the server administration. The exam expects you to know that init() will only be called once per servlet instance, that it is not used to send information back to the browser (HttpServletResponse is not a parameter), and that it throws a ServletException to the container that called the servlet if anything goes wrong.


The init method is called first, the first time the servlet is invoked. This happens one time. However, the service method is called every time a servlet is requested. Lastly, the destroy method is called one time, upon the removal of the servlet from memory due either to explicit removal or lack of use (for example, the session expires). You can configure the container to load certain servlets upon startup (<load-on-startup/> in web.xml), but most of them will be loaded upon first request. Either way, the init method is called first. Place in this method things you will use across requests, like database connections, and class member values such as finalized constants.

The destroy() method, like init(), is called only once. It is called when the servlet is taken out of service and all pending requests to a given servlet (that one with the mentioned destroy() method) are completed or have timed-out. This method is called by the container to give you a chance to release resources such as database connections and threads. You can always call super.destroy() (GenericServlet.destroy()) to add a note to the log about what is going on. You might want to do this even if only to place a timestamp in there.


destroy() is not called if the container crashes!

You should log activity from somewhere other than the destroy() method if a given piece of information is essential, but might not be logged if the logging functionality is placed in the destroy() method. This is because the destroy() method is not called if the Servlet Container quits abruptly (crashes).


Listings 4.15 and 4.16 are sample Web applications (HTML page and servlet combination) that demonstrate how to use the init(), service(), and destroy() methods, and when they are called. You could combine them and just have one servlet, but there are two pieces here to illustrate the relationship between static and dynamic parts of an application. The first part, Listing 4.15, is the HTML page.

Listing 4.15 HTML Page That Works with Servlet in Listing 4.16 Illustrating the Relationship

<html>
<head>
<title>LifeCycle Demonstration Using SQL Server</title>
</head>
<body bgcolor="#FFFFFF"> 

<p align=center>
<h1>LifeCycle Demonstration Using DB</h1>
<form name="formSearch" method="post" action=
"localhost:8080/examples/servlet/SearchLastNameServlet">
<table border="0" cellspacing="0" cellpadding="6">
  <tr>
   <td><h2>Search</h2></td>
   <td></td>
  </tr>
  <tr>
   <td><b>Last Name</b></td> 
   <td><input type="text" name="LastName" 
         value="Fuller">
   </td> 
  </tr> 
  <tr> 
   <td></td>
   <td align="center"><input type="submit" name="Submit"
     value="Submit">
   </td> 
  </tr> 
</table>
</form>
</p>
</body>
</html>

Servlet Reloading!

Servlets are loaded in one of three ways. The first way is when the Web server starts. You can set this in the configuration file. Reload can happen automatically after the container detects that its class file (under servlet dir, for example, WEB-INF/classes) has changes. The third way, with some containers, is through an administrator interface.


The HTML page contains a form with one field for a last name. When submitted, the container takes the lastname field and hands it to the servlet in the request object. This object is where you normally extract requester information. The servlet grabs the lastname, if any, and builds a SQL WHERE clause with it. Then the servlet establishes a connection with the database server (I'm using MS SQL Server) and executes the statement. Then it walks through the resultset getting the data from each field of every row. Finally, it builds the HTML page and sends it off to the client browser. While the database portion is not on the exam, it is an excellent example of how you can take advantage of the methods that are called by the container.


Servlet Synchronizing!

Servlets are run each in their own thread. When the synchronized keyword is used with a servlet's service() method, requests to that servlet are handled one at a time in a serialized manner. This means that multiple requests won't interfere with each other when accessing variables and references within one servlet. It also means the processing capabilities of the Servlet Container are reduced because the more efficient multithreaded mode has been disallowed for a given servlet that has been declared with the synchronized keyword.


Listing 4.16 is the servlet that queries the database based on the form data. Notice that you can forgo the above HTML file by appending the FirstName parameter to the URL like so: http://localhost:8080/examples/servlet/SearchLastNameServlet?LastName=Fuller. Also, you need to set up a data source with system data source names (DSNs), whether to a data source that is local to your computer or remote on the network.

Listing 4.16 Servlet That Queries a Database Based on Form Input from Listing 4.15

/* Don't use "java.io.*"
  Be explicit to see which classes are expected
*/
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.ServletConfig;

public class SearchLastNameServlet extends HttpServlet
{
  //These will be used across requests,
  //so declare it at class level, 
  //not service or doGet level.
  //While it is common to use con,stmt,rs
  //I find these cryptic so I prefer to spell
  //them out completely for clarity.
  private Connection _connection = null;
  //Can differentiate between attributes of a class 
  //and local variables within a method 
  //with preceding underscore.
  private String _driverName = 
              "sun.jdbc.odbc.JdbcOdbcDriver";
  //connects to Northwind DB in SQL Server on my machine
  private String _connectionString = 
                  "jdbc:odbc:employeeDB";
  //optional, declare these in doPost() or service()
  //to avoid conflict between requests.
  private Statement statement = null;
  private ResultSet resultset = null;
  //not here, keep query local  
  //private String query = null;

  //common types
  final int COMMA = 1;
  final int TABLE_COLUMN = 2;
  final int TABLE_HEADER = 3;
  final boolean DELIMITED = true;

  //This is called only once during lifecycle!
  public void init(ServletConfig _config) 
   throws ServletException
  {
   super.init(_config);

    //warning! userid and password is exposed:
   String username = "sa";
   String password = "sa";

   try
   {
     Class.forName(_driverName);
     //warning! userid and password is exposed:
     _connection = DriverManager.getConnection
      (_connectionString, username, password);
   } catch(Exception ex)
   {
     throw new ServletException(ex.getMessage());
   }
  }

  public void service(HttpServletRequest _request, 
       HttpServletResponse _response) 
   throws ServletException, IOException
  {
   _response.setContentType("text/html");

   String table = " Employees ";
   // query string where clause constraint
   String where = "";
   if (_request.getParameter("LastName").length() > 0) 
   {
     String lastName = _
             request.getParameter("LastName");
     where = " where LastName like \'";
     where += lastName;
     where += "%\'";
 
   } else
   {
     where = "";
   }
   
   StringBuffer htmlResult = new StringBuffer();
   try
   {
     String sqlQuery = "SELECT * from "+ table + where; 
     statement = _connection.createStatement();
     resultset = statement.executeQuery(sqlQuery);
  
     while(resultset.next())
     {   
      //Not necessary to place in array, but...
      String[] field = new String[8];
      //warning! these should be in same order as 
      //DB table field order
      //otherwise you can get errors, a Sun todo.
      field[0] = ""+resultset.getInt("EmployeeID");
      field[1] = resultset.getString("LastName");
      field[2] = resultset.getString("FirstName");
      field[3] = resultset.getString("Title");
      field[4] = ""+resultset.getDate("BirthDate");
      field[5] = resultset.getString("City");
      field[6] = resultset.getString("Region");
      field[7] = resultset.getString("Country");
      htmlResult.append( getTableBody(field) );
     }
   } catch(Exception ex)
   {
     throw new ServletException(ex.getMessage());
   }

   StringBuffer html = new StringBuffer();
   html.append( htmlHeader() );

   //build results
   html.append( getTableHeader() );
   html.append( htmlResult.toString() );
   html.append( getTableFooter() );

   html.append( htmlFooter() );

   ServletOutputStream out = response.getOutputStream();
   out.println( html.toString() );
  }

  public void destroy() 
  {
   try
   {
    // Give connection to garbage collector
     connection.close();
     connection = null;
   } catch(Exception ex)
   {
     throw new ServletException(ex.getMessage());
   }
  }  
  //
  // convenience methods providing abstraction
  //
  
  /*
  * Prints the table header.
  */
  public String getTableHeader()
  {
     StringBuffer header = new StringBuffer();

   header.append("<table border=\"2\">\n");
   header.append("<tr>\n");
   header.append("<th align=\"left\">EmployeeID</th>\n");
   header.append("<th align=\"left\">LastName</th>\n");
   header.append("<th align=\"left\">FirstName</th>\n");
   header.append("<th align=\"left\">Title</th>\n");
   header.append("<th align=\"left\">BirthDate</th>\n");
   header.append("<th align=\"left\">City</th>\n");
   header.append("<th align=\"left\">Region</th>\n");
   header.append("<th align=\"left\">Country</th>\n");
   header.append("</tr>\n");
    return header.toString();    
  }

  /*
  * Prints the table body.
  */
  public String getTableBody(String[] field)
  {
   StringBuffer body = new StringBuffer();
   body.append("<tr>\n");

   for(int index=0; index<field.length; index++)
   {
     body.append("  <td align=\"left\">"); 
     body.append(field[index]); 
     body.append("</td>\n");
   }
   body.append("</tr>\n");
   return body.toString();    
  }

  //you would bother to have a whole method for this
  //because someone might ask you to add extra
  //stuff to the bottom of every table so it is smart
  //to separate it like this.
  public String getTableFooter()
  {
   StringBuffer footer = new StringBuffer();

   footer.append("</table>\n");
   
   return footer.toString();    
  }

  /*
  * Prints the html file header.
  */
  public String htmlHeader()
  {
   StringBuffer html = new StringBuffer();

   html.append("<html>");
   html.append("<head>");
   html.append("<title>LifeCycle Servlet Response" +
         "</title>");
   html.append("</head>");
   html.append("<body bgcolor=\"#FFFFFF\"> ");
   html.append("<p align=center><h1>LifeCycle Servlet "+
         " Response</h1></p>"); 
    
   return html.toString();    
  }

  /*
  * Prints the html file footer. 
  * This will change often due to 
  * marketing and lawyers.
  */
  public String htmlFooter()
  {
   StringBuffer html = new StringBuffer();

   html.append("<a href=\"http://localhost:8080/" +
     "examples/servlets/LifeCycle.html\">" +
     "</b>BACK</b></a>");
   html.append("</p>");
   html.append("</body>");
   html.append("</html>");
    
   return html.toString();    
  }
}

Once you set up a proper System DSN, or use a fully qualified connection string, the servlet will query the database. Listing 4.15 shows how you can create an HTML form to call this servlet. The output of the servlet query looks similar to Figure 4.6.

Figure 4.6
The result of a query by a servlet.

Listing 4.16 is just an example of when you might invoke the destroy() method. This code example could be improved in two ways. First, it is not thread-safe (statement and resultset variables could be local, not instance variables). That way separate instances wouldn't walk over each other's results. Second, this example doesn't make use of the Data Access Object pattern. You could do better by having separate objects for the Presentation and Database ("separation of concerns") portions of the program. I've lumped it all together here just to demonstrate the section point of how the destroy() method is used.

Using a RequestDispatcher

1.6 Use a RequestDispatcher to include or forward to a Web resource.

The RequestDispatcher is a powerful tool. You can perform programmatic server-side includes or route the whole request to another servlet or JSP with a forward. There are three ways to get the RequestDispatcher. The first two are through the Context, with ServletContext.getRequestDispatcher(java.lang.String) or with ServletContext.getNamedDispatcher(java.lang.String). Either returns a RequestDispatcher object that acts as a wrapper for the named servlet (in web.xml, the Web application deployment descriptor). The final way is with ServletRequest.getRequestDispatcher(java.lang.String). Notice that you can use a relative pathname here. You must use absolutes with ServletContext.getRequestDispatcher(java.lang.String). Be careful with your paths. If the path begins with a "/", it is interpreted as relative to the current context root. You will get a null if the Servlet Container cannot return a RequestDispatcher.

A RequestDispatcher object can be used to forward a request to the resource or to include output from the resource in a response. The specification allows the resource to be dynamic or static. If it is dynamic, such as a servlet, the container will invoke that servlet, and then take the output and include it. If it is static, such as a text file, then the container will include the text as is. Listing 4.17 demonstrates how one servlet can transfer the request to another servlet.

Listing 4.17 Using a RequestDispatcher to Forward a Request to Another Servlet

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

public class ServletToServlet extends HttpServlet 
{
  public void doGet (HttpServletRequest request,
           HttpServletResponse response) 
      throws ServletException, IOException 
  {
    try {
      getServletConfig()
         .getServletContext()
         .getRequestDispatcher("/HelloWorldExample")
         .forward(request, response);
    } catch (Exception ex) {
      ex.printStackTrace ();
    }
  }
}

You can also include content from a static page or another servlet. You would use a snippet like so:

RequestDispatcher dispatcher =
   getServletContext().getRequestDispatcher(path);
if (dispatcher == null) 
{
  out.println(path + " not available");
  return;
} else
{
  dispatcher.include(request, response);
}

There are a few characteristics about the forward and include methods. The ServletRequest's path elements and parameters remain unchanged from the caller's. The included servlet cannot set headers. This is a good candidate for a trick question. The servlet cannot change the response status code either (if you try, it will be ignored). The best way to send along information from the calling servlet to the called servlet is using a query string or, even better, using the setAttribute() method to set request object attributes where they are easy to access.

There is a matter of timing to consider. You can call an include anytime, but the forward has to be called before the response is committed. Otherwise you'll throw an IllegalStateException exception.


Request Dispatcher Paths

ServletContext.getRequestDispatcher()—This method uses absolute paths.

ServletRequest.getRequestDispatcher(String path)—The path may be relative, but cannot extend outside current servlet context.

ServletRequest.getNamedDispatcher(String name)—This name is the name of the servlet for which a dispatcher is requested, and is in the web.xml file (see Chapter 10, "Web Applications," for more about web.xml).


Regarding the forward method, one reason you may want to use it is so you can dedicate a servlet as the controller. In this way, the controller can filter, preprocess requests, and manage the transaction. The gotcha here is once a servlet forwards a request, it loses control. The controller has no capability to regain access directly. You can create an architecture where requests are returned (forwarded back by a slave servlet), but the native functionality isn't helpful for this. There is another problem. When you run Listing 4.17, you'll notice something missing—the URL in the address bar doesn't change. The client loses path information when it receives a forwarded request. That means all relative URLs in the HTML become invalid. Your browser will consider the links broken. Sometimes this doesn't matter, but when it does, use sendRedirect() instead.

Web Application Context

3.1 Identify the uses for and the interfaces (or classes) and methods to achieve the following features:

  • Servlet context initialization parameters.
  • Servlet context listener.
  • Servlet context attribute listener.
  • Session attribute listeners.

Please see the section "Interfacing with HTML Requests," earlier in this chapter, where the related objective 1.3 "Retrieve a servlet initialization parameter" is discussed. Listing 4.8 is especially helpful here because it demonstrates how to enumerate the context initialization parameter list.

Regarding listeners, you can monitor and react to servlet events by defining listener objects. These objects have methods that the container invokes when life-cycle events occur. To make this happen, you define a listener class by implementing a listener interface. The container will invoke the listener method and pass it information (methods in the HttpSessionListener interface are passed an HttpSessionEvent) about that event.

Listing 4.18 demonstrates how you could use the initialization and destruction events.

Listing 4.18 Listening for a Context Initialization and Destruction

import java.util.Date;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public final class ContextListener
  implements ServletContextListener 
{
  public void contextInitialized(
                ServletContextEvent event) 
  {
   ServletContext context = event.getServletContext();
   context.setAttribute("StartDate", Date);
  }
   
  public void contextDestroyed(ServletContextEvent event) 
  {
   ServletContext context = event.getServletContext();
   Date startDate = context.getAttribute("StartDate");
   customLog(startDate);
   context.removeAttribute("StartDate");
  }
}

The attribute StartDate is set when the container initializes the application. Then when the application quits, the same attribute is logged and then deleted. For an excellent article that provides an overview of application life-cycle events, please see Servlet App Event Listeners by Stephanie Fesler (04/12/2001, http://www.onjava.com/pub/a/onjava/2001/04/12/listeners.html). The four interfaces that you can expect to see on the exam are these:

  • When a servlet is initialized or destroyed:

    • javax.servlet.ServletContextListener.

    • contextDestroyed(ServletContextEvent sce) Notification that the servlet context is about to be shut down.

    • contextInitialized(ServletContextEvent sce) Notification that the Web application is ready to process requests.

  • When a context attribute is added, removed, or replaced:

    • javax.servlet.ServletContextAttributeListener.

    • attributeAdded(ServletContextAttributeEvent scab) Notification that a new attribute was added to the servlet context.

    • attributeRemoved(ServletContextAttributeEvent scab) Notification that an existing attribute has been removed from the servlet context.

    • attributeReplaced(ServletContextAttributeEvent scab) Notification that an attribute on the servlet context has been replaced.

  • When a session is initialized or destroyed:

    • javax.servlet.http.HttpSessionListener.

    • sessionCreated(HttpSessionEvent se) Notification that a session was created.

    • sessionDestroyed(HttpSessionEvent se) Notification that a session became invalid or timed out.

  • When a session attribute is added, removed, or replaced:

    • HttpSessionAttributeListener.

    • attributeAdded(HttpSessionBindingEvent se) Notification that an attribute has been added to a session.

    • attributeRemoved(HttpSessionBindingEvent se) Notification that an attribute has been removed from a session.

    • attributeReplaced(HttpSessionBindingEvent se) Notification that an attribute has been replaced in a session.

Context Within a Distributable Web Application

3.3 Distinguish the behavior of the following in a distributable:

  • Servlet context init. parameters.
  • Servlet context listener.
  • Servlet context attribute listener.
  • Session attribute listeners.

The behavior of these listeners in a distributable is exactly the same as those discussed in the previous section, with one notable exception: Event notification of addition, removal, or replacement will affect the listener for only that context. No other context, such as other JVMs on the same or other machine, will know about the listener events.

Chapter Summary

The HTTP methods GET, POST, and PUT are how browsers and Web servers trade data with each other. The GET retrieves a page without providing much information, while a POST can package huge amounts of information with its request. A PUT is for uploading a file. There are events associated with each type of request, such as clicking a hyperlink sending a GET request, but clicking a form button sends a POST request.

The most important objects in the servlet process are the request and response objects. The request parameters for the servlet are the strings sent by the client to the Servlet Container. The container parses the request and puts the information in a HttpServletRequest object which is passed to the servlet. Going the other way, the container wraps the response parameters with the HttpServletResponse object which is passed back to the container.

Containers have the idea of scope. When something has Context scope it is application-wide and all users can share data. Session scope means one user can share data across page views, but other users can't. Request scope restricts data to only that page. The container also manages the servlet life-cycle by initializing a servlet with a call to the init() method, a call to the service() method upon every request, and by calling a servlet's destroy() method just prior to removing it from memory. The container also allows you to monitor context and session events with listeners that are event-driven triggers. When an attribute changes, special targeted methods are called. In them, you can define special actions such as "add a note to the log every time the user count changes."

Lastly, the servlet specifies a RequestDispatcher object which performs servlet forwarding. Notice that this is different from redirection, where the servlet would return a new URL to the browser that triggers the browser to try to get that page. The RequestDispatcher doesn't redirect; rather it "dispatches" or performs forwarding.

KEY TERMS

  • Redirection

  • Servlet Life-Cycle

  • Servlet Forwarding and Includes

  • Servlet attribute

  • Context parameters

  • Application session

  • listeners

Apply Your Knowledge

Review Questions

  1. What methods of the Servlet interface are invoked at different points in the servlet life cycle?

  2. What HTTP methods are supported by HttpServlet?

  3. What objects are passed to the servlet's service() method?

  4. What is a distributable application?

  5. Why is it a bad idea to synchronize a servlet's service() method?

  6. What is the relationship between an application's ServletConfig object and ServletContext object?

  7. What mechanisms are used by a Servlet Container to maintain session information?

  8. What are the four events that are defined in the Servlet API?

  9. How are request dispatchers used?

Exam Questions

  1. Which of the following methods are defined in the Servlet interface?

    1. init()

    2. service()

    3. finalize()

    4. destroy()

  2. Which of the following objects are passed to a servlet's service() method?

    1. ServletRequest

    2. HttpServletRequest

    3. ServletResponse

    4. HttpServletResponse

  3. By default, how many instances of a servlet are created by a Servlet Container?

    1. One

    2. One per request

    3. One per session

    4. None of the above

  4. Which of the following exceptions are defined by the Servlet API?

    1. ServletException

    2. InitializationException

    3. UnavailableException

    4. ServletContextException

  5. Which of the following are used by Servlet Containers to maintain session information?

    1. cookies

    2. hidden form fields

    3. HTTPS protocol information

    4. URL rewriting

  6. Which of the following event listeners are defined by the Servlet API?

    1. HttpSessionBindingListener

    2. HttpSessionEventListener

    3. HttpSessionParameterListener

    4. HttpSessionAttributeListener

  7. Which of the following methods are defined by the RequestDispatcher interface?

    1. dispatch()

    2. include()

    3. redirect()

    4. forward()

  8. Which of the following is the name of the cookie used by Servlet Containers to maintain session information?

    1. SESSIONID

    2. SERVLETID

    3. JSESSIONID

    4. CONTAINERID

Answers to Review Questions

  1. The init() method is invoked during the initialization phase. The service() method is invoked during the request processing (service) phase. In other words, init() is invoked the first time the servlet runs, but service() is invoked once for every request the servlet receives. The destroy() method is invoked when the servlet is to be taken out of service. Refer to the section, "Servlet Life-cycle."

  2. The GET, POST, HEAD, PUT, DELETE, TRACE, and OPTIONS methods are supported by HttpServlet. Refer to the section, "Interfacing with HTML Requests."

  3. ServletRequest and ServletResponse objects are passed to the servlet's service method. Refer to the section, "Interfacing with HTML Requests."

  4. A distributable application is an application that is distributed over multiple JVMs. Refer to the In the Field, "How Does a Servlet Work?"

  5. When the synchronized keyword is used with a servlet's service() method, requests to that servlet are handled one at a time in a serialized manner. This means that the processing capabilities of the Servlet Container are minimized. Refer to the section, "Servlet Life-cycle."

  6. An application's ServletConfig object contains its ServletContext object and provides access to this object via its getServletContext() method. Refer to the section, "Web Application Context."

  7. Cookies, URL rewriting, and HTTPS protocol information are used to maintain session information. Refer to the section, "Session."

  8. The four events that are defined by the Servlet API are HttpSessionEvent, HttpSessionBindingEvent, ServletContextEvent, and ServletContextAttributeEvent. Refer to the section, "Servlet Life-cycle."

  9. Request dispatchers are used to forward requests to other servlets or to include the results of other servlets. Refer to the section, "Using a RequestDispatcher."

Answers to Exam Questions

  1. C. The finalize() method is not defined by the Servlet interface. Refer to the section, "Servlet Life-cycle."

  2. A, C. ServletRequest and ServletResponse methods are passed to the service() method. Refer to the section, "Servlet Life-cycle."

  3. A. By default, only one instance of a servlet is created by a Servlet Container. Refer to the section, "Servlet Life-cycle."

  4. A, C. The Servlet API defines ServletException and UnavailableException. Refer to the section, "Servlet Life-cycle."

  5. A, C, D. Hidden form fields are not used by Servlet Containers to maintain session information. Refer to the section, "Form Parameters."

  6. A. Only HttpSessionBindingListener is defined by the Servlet API. Refer to the section, "Servlet Life-cycle."

  7. B, D. The RequestDispatcher interface defines the include() and forward() methods. Refer to the section, "Using a RequestDispatcher."

  8. C. The JSESSIONID cookie is used by Servlet Containers to maintain session information. Refer to the section, "Session."

Suggested Readings and Resources

  1. Sun's excellent J2EE Tutorial—java.sun.com/j2ee/tutorial/1_3-fcs/doc/ J2eeTutorialTOC.html.

  2. The Java Language Specification—(java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html).

  3. Exam objectives for the Sun Certified Web Component Developer for J2EE Platform—http://suned.sun.com/US/certification/java/exam_objectives.html.

  4. The Java Servlet 2.3 Specification—http://jcp.org/aboutJava/communityprocess/first/ jsr053/index.html.

  5. Sun's official Servlet page—http://java.sun.com/products/servlet/.

  6. Java Software FAQ Index—http://java.sun.com/docs/faqindex.html.

  7. Tomcat—an implementation of the Java Servlet 2.2 and JavaServer Pages 1.1 Specifications—http://jakarta.apache.org/tomcat/index.html.

  8. Java Training by the MageLang Institute—http://www.magelang.com/.

  9. Servlets.com, Web site companion to Java Servlet Programming by O'Reilly—http://www.servlets.com/.

  10. Glossary of Java Technology-Related Terms—http://java.sun.com/docs/glossary.html.

Source of this material

This is Chapter 4: Servlet Container Model from the book Sun Certification Training Guide (310-080): Java 2 Enterprise Edition (J2EE) Web Component Developer (ISBN:0-7897-2821-4) written by Alain Trottier, published by Que.

To access the full Table of Contents for the book

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date