The Java EE 5 Tutorial

Chapter 4 Java Servlet Technology

As soon as the web began to be used for delivering services, service providers recognized the need for dynamic content. Applets, one of the earliest attempts toward this goal, focused on using the client platform to deliver dynamic user experiences. At the same time, developers also investigated using the server platform for this purpose. Initially, Common Gateway Interface (CGI) scripts were the main technology used to generate dynamic content. Although widely used, CGI scripting technology has a number of shortcomings, including platform dependence and lack of scalability. To address these limitations, Java Servlet technology was created as a portable way to provide dynamic, user-oriented content.

What Is a Servlet?

A servlet is a Java programming language class that is used to extend the capabilities of servers that host applications accessed by means of a request-response programming model. Although servlets can respond to any type of request, they are commonly used to extend the applications hosted by web servers. For such applications, Java Servlet technology defines HTTP-specific servlet classes.

The javax.servlet and javax.servlet.http packages provide interfaces and classes for writing servlets. All servlets must implement the Servlet interface, which defines life-cycle methods. When implementing a generic service, you can use or extend the GenericServlet class provided with the Java Servlet API. The HttpServlet class provides methods, such as doGet and doPost, for handling HTTP-specific services.

This chapter focuses on writing servlets that generate responses to HTTP requests.

The Example Servlets

This chapter uses the Duke’s Bookstore application to illustrate the tasks involved in programming servlets. The source code for the bookstore application is located in the tut-install/javaeetutorial5/examples/web/bookstore1/ directory, which is created when you unzip the tutorial bundle (see Building the Examples).

Table 4–1 lists the servlets that handle each bookstore function. You can find these servlet classes in tut-install/javaeetutorial5/examples/web/bookstore1/src/java/com/sun/bookstore1/. Each programming task is illustrated by one or more servlets. For example, BookDetailsServlet illustrates how to handle HTTP GET requests, BookDetailsServlet and CatalogServlet show how to construct responses, and CatalogServlet illustrates how to track session information.

Table 4–1 Duke’s Bookstore Example Servlets

Function 

Servlet 

Enter the bookstore 

BookStoreServlet

Create the bookstore banner 

BannerServlet

Browse the bookstore catalog 

CatalogServlet

Put a book in a shopping cart 

CatalogServlet,

BookDetailsServlet

Get detailed information on a specific book 

BookDetailsServlet

Display the shopping cart 

ShowCartServlet

Remove one or more books from the shopping cart 

ShowCartServlet

Buy the books in the shopping cart 

CashierServlet

Send an acknowledgment of the purchase 

ReceiptServlet

The data for the bookstore application is maintained in a database and accessed through the database access class database.BookDBAO. The database package also contains the class Book which represents a book. The shopping cart and shopping cart items are represented by the classes cart.ShoppingCart and cart.ShoppingCartItem, respectively.

    To deploy and run the application using NetBeans IDE, follow these steps:

  1. Perform all the operations described in Accessing Databases from Web Applications.

  2. In NetBeans IDE, select File->Open Project.

  3. In the Open Project dialog, navigate to:


    tut-install/javaeetutorial5/examples/web/
  4. Select the bookstore1 folder.

  5. Select the Open as Main Project check box and the Open Required Projects check box.

  6. Click Open Project.

  7. In the Projects tab, right-click the bookstore1 project, and select Undeploy and Deploy.

  8. To run the application, open the bookstore URL http://localhost:8080/bookstore1/bookstore.

    To deploy and run the application using Ant, follow these steps:

  1. In a terminal window, go to tut-install/javaeetutorial5/examples/web/bookstore1/.

  2. Type ant. This command will spawn any necessary compilations, copy files to the tut-install/javaeetutorial5/examples/web/bookstore1/build/ directory, and create a WAR file and copy it to the tut-install/javaeetutorial5/examples/web/bookstore1/dist/ directory.

  3. Start the Application Server.

  4. Perform all the operations described in Creating a Data Source in the Application Server.

  5. To deploy the example, type ant deploy. The deploy target outputs a URL for running the application. Ignore this URL, and instead use the one shown in the next step.

  6. To run the application, open the bookstore URL http://localhost:8080/bookstore1/bookstore.

To learn how to configure the example, refer to the deployment descriptor (the web.xml file), which includes the following configurations:

Troubleshooting Duke's Bookstore Database Problems

The Duke’s Bookstore database access object returns the following exceptions:

Because you have specified an error page, you will see the message


The application is unavailable. Please try later.

If you don’t specify an error page, the web container generates a default page containing the message


A Servlet Exception Has Occurred

and a stack trace that can help you diagnose the cause of the exception. If you use errorpage.html, you will have to look in the server log to determine the cause of the exception.

Servlet Life Cycle

The life cycle of a servlet is controlled by the container in which the servlet has been deployed. When a request is mapped to a servlet, the container performs the following steps.

  1. If an instance of the servlet does not exist, the web container

    1. Loads the servlet class.

    2. Creates an instance of the servlet class.

    3. Initializes the servlet instance by calling the init method. Initialization is covered in Initializing a Servlet.

  2. Invokes the service method, passing request and response objects. Service methods are discussed in Writing Service Methods.

If the container needs to remove the servlet, it finalizes the servlet by calling the servlet’s destroy method. Finalization is discussed in Finalizing a Servlet.

Handling Servlet Life-Cycle Events

You can monitor and react to events in a servlet’s life cycle by defining listener objects whose methods get invoked when life-cycle events occur. To use these listener objects you must define and specify the listener class.

Defining the Listener Class

You define a listener class as an implementation of a listener interface. Table 4–2 lists the events that can be monitored and the corresponding interface that must be implemented. When a listener method is invoked, it is passed an event that contains information appropriate to the event. For example, the methods in the HttpSessionListener interface are passed an HttpSessionEvent, which contains an HttpSession.

Table 4–2 Servlet Life-Cycle Events

Object 

Event 

Listener Interface and Event Class 

Web context (see Accessing the Web Context)

Initialization and destruction 

javax.servlet.ServletContextListener and

ServletContextEvent

Attribute added, removed, or replaced 

javax.servlet.ServletContextAttributeListener and

ServletContextAttributeEvent

Session (See Maintaining Client State)

Creation, invalidation, activation, passivation, and timeout 

javax.servlet.http.HttpSessionListener, javax.servlet.http.HttpSessionActivationListener, and

HttpSessionEvent

Attribute added, removed, or replaced 

javax.servlet.http.HttpSessionAttributeListener and

HttpSessionBindingEvent

Request 

A servlet request has started being processed by web components 

javax.servlet.ServletRequestListener and

ServletRequestEvent

Attribute added, removed, or replaced 

javax.servlet.ServletRequestAttributeListener and

ServletRequestAttributeEvent

The tut-install/javaeetutorial5/examples/web/bookstore1/src/java/com/sun/bookstore1/listeners/ContextListener class creates and removes the database access and counter objects used in the Duke’s Bookstore application. The methods retrieve the web context object from ServletContextEvent and then store (and remove) the objects as servlet context attributes.

import database.BookDBAO;
import javax.servlet.*;
import util.Counter;

import javax.ejb.*;
import javax.persistence.*;

public final class ContextListener
    implements ServletContextListener {
    private ServletContext context = null;

    @PersistenceUnit
    EntityManagerFactory emf;

    public void contextInitialized(ServletContextEvent event) {
        context = event.getServletContext();
        try {
            BookDBAO bookDB = new BookDBAO(emf);
            context.setAttribute("bookDB", bookDB);
        } catch (Exception ex) {
            System.out.println(
                "Couldn’t create database: " + ex.getMessage());
        }
        Counter counter = new Counter();
        context.setAttribute("hitCounter", counter);
        counter = new Counter();
        context.setAttribute("orderCounter", counter);
    }

    public void contextDestroyed(ServletContextEvent event) {
        context = event.getServletContext();
        BookDBAO bookDB = context.getAttribute("bookDB");
        bookDB.remove();
        context.removeAttribute("bookDB");
        context.removeAttribute("hitCounter");
        context.removeAttribute("orderCounter");
    }
}

Specifying Event Listener Classes

You specify an event listener class using the listener element of the deployment descriptor. Review The Example Servlets for information on how to specify the ContextListener listener class.

    You can specify an event listener using the deployment descriptor editor of NetBeans IDE by doing the following:

  1. Expand your application’s project node.

  2. Expand the project’s Web Pages and WEB-INF nodes.

  3. Double-click web.xml.

  4. Click General at the top of the web.xml editor.

  5. Expand the Web Application Listeners node.

  6. Click Add.

  7. In the Add Listener dialog, click Browse to locate the listener class.

  8. Click OK.

Handling Servlet Errors

Any number of exceptions can occur when a servlet executes. When an exception occurs, the web container generates a default page containing the message


A Servlet Exception Has Occurred

But you can also specify that the container should return a specific error page for a given exception. Review the deployment descriptor file included with the example to learn how to map the exceptions exception.BookNotFound, exception.BooksNotFound, and exception.OrderException returned by the Duke’s Bookstore application to errorpage.html.

See Mapping Errors to Error Screens for instructions on how to specify error pages using NetBeans IDE.

Sharing Information

Web components, like most objects, usually work with other objects to accomplish their tasks. There are several ways they can do this. They can use private helper objects (for example, JavaBeans components), they can share objects that are attributes of a public scope, they can use a database, and they can invoke other web resources. The Java Servlet technology mechanisms that allow a web component to invoke other web resources are described in Invoking Other Web Resources.

Using Scope Objects

Collaborating web components share information by means of objects that are maintained as attributes of four scope objects. You access these attributes using the [get|set]Attribute methods of the class representing the scope. Table 4–3 lists the scope objects.

Table 4–3 Scope Objects

Scope Object 

Class 

Accessible From 

Web context 

javax.servlet.ServletContext

Web components within a web context. See Accessing the Web Context.

Session 

javax.servlet.http.HttpSession

Web components handling a request that belongs to the session. See Maintaining Client State.

Request 

subtype of javax.servlet.ServletRequest

Web components handling the request. 

Page 

javax.servlet.jsp.JspContext

The JSP page that creates the object. See Using Implicit Objects.

Figure 4–1 shows the scoped attributes maintained by the Duke’s Bookstore application.

Figure 4–1 Duke’s Bookstore Scoped Attributes

Diagram of Duke's Bookstore scoped attributes. Session
attributes are currency and cart, web context attributes are hitCounter, bookDB,
orderCounter.

Controlling Concurrent Access to Shared Resources

In a multithreaded server, it is possible for shared resources to be accessed concurrently. In addition to scope object attributes, shared resources include in-memory data (such as instance or class variables) and external objects such as files, database connections, and network connections.

Concurrent access can arise in several situations:

When resources can be accessed concurrently, they can be used in an inconsistent fashion. To prevent this, you must control the access using the synchronization techniques described in the Threads lesson in The Java Tutorial, Fourth Edition, by Sharon Zakhour et al. (Addison-Wesley, 2006).

The preceding section showed five scoped attributes shared by more than one servlet: bookDB, cart, currency, hitCounter, and orderCounter. The bookDB attribute is discussed in the next section. The cart, currency, and counters can be set and read by multiple multithreaded servlets. To prevent these objects from being used inconsistently, access is controlled by synchronized methods. For example, here is the Counter class, located at tut-install/javaeetutorial5/examples/web/bookstore1/src/java/com/sun/bookstore1/util/:

public class Counter {
    private int counter;
    public Counter() {
        counter = 0;
    }
    public synchronized int getCounter() {
        return counter;
    }
    public synchronized int setCounter(int c) {
        counter = c;
        return counter;
    }
    public synchronized int incCounter() {
        return(++counter);
    }
}

Accessing Databases

Data that is shared between web components and is persistent between invocations of a web application is usually maintained by a database. Web components use the Java Persistence API to access relational databases. The data for Duke’s Bookstore is maintained in a database and is accessed through the database access class tut-install/javaeetutorial5/examples/web/bookstore1/src/java/com/sun/bookstore1/database/BookDBAO. For example, ReceiptServlet invokes the BookDBAO.buyBooks method to update the book inventory when a user makes a purchase. The buyBooks method invokes buyBook for each book contained in the shopping cart, as shown in the following code.

public void buyBooks(ShoppingCart cart) throws OrderException{

    Collection items = cart.getItems();
    Iterator i = items.iterator();
    
    try {
        while (i.hasNext()) {
            ShoppingCartItem sci = (ShoppingCartItem)i.next();
            Book bd = (Book)sci.getItem();
            String id = bd.getBookId();
            int quantity = sci.getQuantity();
            buyBook(id, quantity);
        }
    } catch (Exception ex) {
        throw new OrderException("Commit failed: " +
            ex.getMessage());
     }
}

public void buyBook(String bookId, int quantity)
     throws OrderException {

    try {
        Book requestedBook = em.find(Book.class, bookId);
        
        if (requestedBook != null) {
            int inventory = requestedBook.getInventory();
            if ((inventory - quantity) >= 0) {
                int newInventory = inventory - quantity;
                requestedBook.setInventory(newInventory);
            } else{
                throw new OrderException("Not enough of "
                     + bookId + " in stock to complete order.");
            }
        }
    } catch (Exception ex) {
        throw new OrderException("Couldn’t purchase book: "
             + bookId + ex.getMessage());
    }
}

To ensure that the order is processed in its entirety, the call to buyBooks is wrapped in a single transaction. In the following code, the calls to the begin and commit methods of UserTransaction mark the boundaries of the transaction. The call to the rollback method of UserTransaction undoes the effects of all statements in the transaction so as to protect the integrity of the data.

try {
    utx.begin();
    bookDB.buyBooks(cart);
    utx.commit();
} catch (Exception ex) {
    try {
        utx.rollback();
    } catch(Exception e) {
        System.out.println("Rollback failed: "+e.getMessage());
    }
    System.err.println(ex.getMessage());
    orderCompleted = false;}
}

Initializing a Servlet

After the web container loads and instantiates the servlet class and before it delivers requests from clients, the web container initializes the servlet. To customize this process to allow the servlet to read persistent configuration data, initialize resources, and perform any other one-time activities, you override the init method of the Servlet interface. A servlet that cannot complete its initialization process should throw UnavailableException.

All the servlets that access the bookstore database (BookStoreServlet, CatalogServlet, BookDetailsServlet, and ShowCartServlet) initialize a variable in their init method that points to the database access object created by the web context listener:

public class CatalogServlet extends HttpServlet {
    private BookDBAO bookDB;
    public void init() throws ServletException {
        bookDB = (BookDBAO)getServletContext().
            getAttribute("bookDB");
        if (bookDB == null) throw new
            UnavailableException("Couldn’t get database.");
    }
}

Writing Service Methods

The service provided by a servlet is implemented in the service method of a GenericServlet, in the doMethod methods (where Method can take the value Get, Delete, Options, Post, Put, or Trace) of an HttpServlet object, or in any other protocol-specific methods defined by a class that implements the Servlet interface. In the rest of this chapter, the term service method is used for any method in a servlet class that provides a service to a client.

The general pattern for a service method is to extract information from the request, access external resources, and then populate the response based on that information.

For HTTP servlets, the correct procedure for populating the response is to first retrieve an output stream from the response, then fill in the response headers, and finally write any body content to the output stream. Response headers must always be set before the response has been committed. Any attempt to set or add headers after the response has been committed will be ignored by the web container. The next two sections describe how to get information from requests and generate responses.

Getting Information from Requests

A request contains data passed between a client and the servlet. All requests implement the ServletRequest interface. This interface defines methods for accessing the following information:

For example, in CatalogServlet the identifier of the book that a customer wishes to purchase is included as a parameter to the request. The following code fragment illustrates how to use the getParameter method to extract the identifier:

String bookId = request.getParameter("Add");
if (bookId != null) {
    Book book = bookDB.getBook(bookId);

You can also retrieve an input stream from the request and manually parse the data. To read character data, use the BufferedReader object returned by the request’s getReader method. To read binary data, use the ServletInputStream returned by getInputStream.

HTTP servlets are passed an HTTP request object, HttpServletRequest, which contains the request URL, HTTP headers, query string, and so on.

An HTTP request URL contains the following parts:


http://[host]:[port][request-path]?[query-string]

The request path is further composed of the following elements:

If the context path is /catalog and for the aliases listed in Table 4–4, Table 4–5 gives some examples of how the URL will be parsed.

Table 4–4 Aliases

Pattern 

Servlet 

/lawn/*

LawnServlet

/*.jsp

JSPServlet

Table 4–5 Request Path Elements

Request Path 

Servlet Path 

Path Info 

/catalog/lawn/index.html

/lawn

/index.html

/catalog/help/feedback.jsp

/help/feedback.jsp

null

Query strings are composed of a set of parameters and values. Individual parameters are retrieved from a request by using the getParameter method. There are two ways to generate query strings:

Constructing Responses

A response contains data passed between a server and the client. All responses implement the ServletResponse interface. This interface defines methods that allow you to:

HTTP response objects, HttpServletResponse, have fields representing HTTP headers such as the following:

In Duke’s Bookstore, BookDetailsServlet generates an HTML page that displays information about a book that the servlet retrieves from a database. The servlet first sets response headers: the content type of the response and the buffer size. The servlet buffers the page content because the database access can generate an exception that would cause forwarding to an error page. By buffering the response, the servlet prevents the client from seeing a concatenation of part of a Duke’s Bookstore page with the error page should an error occur. The doGet method then retrieves a PrintWriter from the response.

To fill in the response, the servlet first dispatches the request to BannerServlet, which generates a common banner for all the servlets in the application. This process is discussed in Including Other Resources in the Response. Then the servlet retrieves the book identifier from a request parameter and uses the identifier to retrieve information about the book from the bookstore database. Finally, the servlet generates HTML markup that describes the book information and then commits the response to the client by calling the close method on the PrintWriter.

public class BookDetailsServlet extends HttpServlet {
     ...
     public void doGet (HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
        ...
        // set headers before accessing the Writer
        response.setContentType("text/html");
        response.setBufferSize(8192);
        PrintWriter out = response.getWriter();

        // then write the response
        out.println("<html>" +
            "<head><title>+
            messages.getString("TitleBookDescription")
            +</title></head>");

        // Get the dispatcher; it gets the banner to the user
        RequestDispatcher dispatcher =
            getServletContext().
            getRequestDispatcher("/banner");
        if (dispatcher != null)
            dispatcher.include(request, response);

        // Get the identifier of the book to display
        String bookId = request.getParameter("bookId");
        if (bookId != null) {
            // and the information about the book
            try {
                Book bd =
                    bookDB.getBook(bookId);
                ...
                // Print the information obtained
                out.println("<h2>" + bd.getTitle() + "</h2>" +
                ...
            } catch (BookNotFoundException ex) {
                response.resetBuffer();
                throw new ServletException(ex);
            }
        }
        out.println("</body></html>");
        out.close();
    }
}

BookDetailsServlet generates a page that looks like Figure 4–2.

Figure 4–2 Book Details

Screen capture of book details. Shows "Web Servers for
Fun and Profit" author, review, and price, with links "Add to Cart" and "Continue
Shopping."

Filtering Requests and Responses

A filter is an object that can transform the header and content (or both) of a request or response. Filters differ from web components in that filters usually do not themselves create a response. Instead, a filter provides functionality that can be “attached” to any kind of web resource. Consequently, a filter should not have any dependencies on a web resource for which it is acting as a filter; this way it can be composed with more than one type of web resource.

The main tasks that a filter can perform are as follows:

Applications of filters include authentication, logging, image conversion, data compression, encryption, tokenizing streams, XML transformations, and so on.

You can configure a web resource to be filtered by a chain of zero, one, or more filters in a specific order. This chain is specified when the web application containing the component is deployed and is instantiated when a web container loads the component.

In summary, the tasks involved in using filters are

Programming Filters

The filtering API is defined by the Filter, FilterChain, and FilterConfig interfaces in the javax.servlet package. You define a filter by implementing the Filter interface.

The most important method in this interface is doFilter, which is passed request, response, and filter chain objects. This method can perform the following actions:

In addition to doFilter, you must implement the init and destroy methods. The init method is called by the container when the filter is instantiated. If you wish to pass initialization parameters to the filter, you retrieve them from the FilterConfig object passed to init.

The Duke’s Bookstore application uses the filters HitCounterFilter and OrderFilter, located at tut-install/javaeetutorial5/examples/web/bookstore1/src/java/com/sun/bookstore1/filters/, to increment and log the value of counters when the entry and receipt servlets are accessed.

In the doFilter method, both filters retrieve the servlet context from the filter configuration object so that they can access the counters stored as context attributes. After the filters have completed application-specific processing, they invoke doFilter on the filter chain object passed into the original doFilter method. The elided code is discussed in the next section.

public final class HitCounterFilter implements Filter {
    private FilterConfig filterConfig = null;

    public void init(FilterConfig filterConfig)
         throws ServletException {
        this.filterConfig = filterConfig;
    }
    public void destroy() {
        this.filterConfig = null;
    }
    public void doFilter(ServletRequest request,
        ServletResponse response, FilterChain chain)
         throws IOException, ServletException {
        if (filterConfig == null)
            return;
        StringWriter sw = new StringWriter();
        PrintWriter writer = new PrintWriter(sw);
        Counter counter = (Counter)filterConfig.
            getServletContext().
            getAttribute("hitCounter");
        writer.println();
        writer.println("===============");
        writer.println("The number of hits is: " +
            counter.incCounter());
        writer.println("===============");
        // Log the resulting string
        writer.flush();
        System.out.println(sw.getBuffer().toString());
        ...
        chain.doFilter(request, wrapper);
        ...
    }
}

Programming Customized Requests and Responses

There are many ways for a filter to modify a request or response. For example, a filter can add an attribute to the request or can insert data in the response. In the Duke’s Bookstore example, HitCounterFilter inserts the value of the counter into the response.

A filter that modifies a response must usually capture the response before it is returned to the client. To do this, you pass a stand-in stream to the servlet that generates the response. The stand-in stream prevents the servlet from closing the original response stream when it completes and allows the filter to modify the servlet’s response.

To pass this stand-in stream to the servlet, the filter creates a response wrapper that overrides the getWriter or getOutputStream method to return this stand-in stream. The wrapper is passed to the doFilter method of the filter chain. Wrapper methods default to calling through to the wrapped request or response object. This approach follows the well-known Wrapper or Decorator pattern described in Design Patterns, Elements of Reusable Object-Oriented Software, by Erich Gamma et al. (Addison-Wesley, 1995). The following sections describe how the hit counter filter described earlier and other types of filters use wrappers.

To override request methods, you wrap the request in an object that extends ServletRequestWrapper or HttpServletRequestWrapper. To override response methods, you wrap the response in an object that extends ServletResponseWrapper or HttpServletResponseWrapper.

HitCounterFilter wraps the response in a tut-install/javaeetutorial5/examples/web/bookstore1/src/java/com/sun/bookstore1/filters/CharResponseWrapper. The wrapped response is passed to the next object in the filter chain, which is BookStoreServlet. Then BookStoreServlet writes its response into the stream created by CharResponseWrapper. When chain.doFilter returns, HitCounterFilter retrieves the servlet’s response from PrintWriter and writes it to a buffer. The filter inserts the value of the counter into the buffer, resets the content length header of the response, and then writes the contents of the buffer to the response stream.

PrintWriter out = response.getWriter();
CharResponseWrapper wrapper = new CharResponseWrapper(
    (HttpServletResponse)response);
chain.doFilter(request, wrapper);
CharArrayWriter caw = new CharArrayWriter();
caw.write(wrapper.toString().substring(0,
    wrapper.toString().indexOf("</body>")-1));
caw.write("<p>\n<center>" +
     messages.getString("Visitor") + "<font color=’red’>" +
     counter.getCounter() + "</font></center>");
caw.write("\n</body></html>");
response.setContentLength(caw.toString().getBytes().length);
out.write(caw.toString());
out.close();

public class CharResponseWrapper extends
    HttpServletResponseWrapper {
    private CharArrayWriter output;
    public String toString() {
        return output.toString();
    }
    public CharResponseWrapper(HttpServletResponse response){
        super(response);
        output = new CharArrayWriter();
    }
    public PrintWriter getWriter(){
        return new PrintWriter(output);
    }
}

Figure 4–3 shows the entry page for Duke’s Bookstore with the hit counter.

Figure 4–3 Duke’s Bookstore with Hit Counter

Screen capture of Duke's Bookstore with "Web Components
for Web Developers" recommendation, "Start Shopping" link and "You are visitor
number 2."

Specifying Filter Mappings

A web container uses filter mappings to decide how to apply filters to web resources. A filter mapping matches a filter to a web component by name, or to web resources by URL pattern. The filters are invoked in the order in which filter mappings appear in the filter mapping list of a WAR. You specify a filter mapping list for a WAR in its deployment descriptor, either with NetBeans IDE or by coding the list by hand with XML.

    To declare the filter and map it to a web resource using NetBeans IDE, do the following:

  1. Expand the application’s project node in the Project pane.

  2. Expand the Web Pages and WEB-INF nodes under the project node.

  3. Double-click web.xml.

  4. Click Filters at the top of the editor pane.

  5. Expand the Servlet Filters node in the editor pane.

  6. Click Add Filter Element to map the filter to a web resource by name or by URL pattern.

  7. In the Add Servlet Filter dialog, enter the name of the filter in the Filter Name field.

  8. Click Browse to locate the servlet class to which the filter applies. You can include wildcard characters so that you can apply the filter to more than one servlet.

  9. Click OK.

    To constrain how the filter is applied to requests, do the following:

  1. Expand the Filter Mappings node in the Filters tab of the editor pane.

  2. Select the filter from the list of filters.

  3. Click Add.

  4. In the Add Filter Mapping dialog, select one of the following dispatcher types:

    • REQUEST: Only when the request comes directly from the client

    • FORWARD: Only when the request has been forwarded to a component (see Transferring Control to Another Web Component)

    • INCLUDE: Only when the request is being processed by a component that has been included (see Including Other Resources in the Response)

    • ERROR: Only when the request is being processed with the error page mechanism (see Handling Servlet Errors)

      You can direct the filter to be applied to any combination of the preceding situations by selecting multiple dispatcher types. If no types are specified, the default option is REQUEST.

    You can declare, map, and constrain the filter by editing the XML in the web application deployment descriptor directly by following these steps:

  1. While in the web.xml editor pane in NetBeans IDE, click XML at the top of the editor pane.

  2. Declare the filter by adding a filter element right after the display-name element. The filter element creates a name for the filter and declares the filter’s implementation class and initialization parameters.

  3. Map the filter to a web resource by name or by URL pattern using the filter-mapping element:

    1. Include a filter-name element that specifies the name of the filter as defined by the filter element.

    2. Include a servlet-name element that specifies to which servlet the filter applies. The servlet-name element can include wildcard characters so that you can apply the filter to more than one servlet.

  4. Constrain how the filter will be applied to requests by specifying one of the enumerated dispatcher options (described in step 4 of the preceding set of steps) with the dispatcher element and adding the dispatcher element to the filter-mapping element.

    You can direct the filter to be applied to any combination of the preceding situations by including multiple dispatcher elements. If no elements are specified, the default option is REQUEST.

If you want to log every request to a web application, you map the hit counter filter to the URL pattern /*. Table 4–6 summarizes the filter definition and mapping list for the Duke’s Bookstore application. The filters are matched by servlet name, and each filter chain contains only one filter.

Table 4–6 Duke’s Bookstore Filter Definition and Mapping List

Filter 

Class 

Servlet 

HitCounterFilter

filters.HitCounterFilter

BookStoreServlet

OrderFilter

filters.OrderFilter

ReceiptServlet

You can map a filter to one or more web resources and you can map more than one filter to a web resource. This is illustrated in Figure 4–4, where filter F1 is mapped to servlets S1, S2, and S3, filter F2 is mapped to servlet S2, and filter F3 is mapped to servlets S1 and S2.

Figure 4–4 Filter-to-Servlet Mapping

Diagram of filter-to-servlet mapping with filters F1-F3
and servlets S1-S3. F1 filters S1-S3, then F2 filters S2, then F3 filters
S1 and S2.

Recall that a filter chain is one of the objects passed to the doFilter method of a filter. This chain is formed indirectly by means of filter mappings. The order of the filters in the chain is the same as the order in which filter mappings appear in the web application deployment descriptor.

When a filter is mapped to servlet S1, the web container invokes the doFilter method of F1. The doFilter method of each filter in S1’s filter chain is invoked by the preceding filter in the chain by means of the chain.doFilter method. Because S1’s filter chain contains filters F1 and F3, F1’s call to chain.doFilter invokes the doFilter method of filter F3. When F3’s doFilter method completes, control returns to F1’s doFilter method.

Invoking Other Web Resources

Web components can invoke other web resources in two ways: indirectly and directly. A web component indirectly invokes another web resource when it embeds a URL that points to another web component in content returned to a client. In the Duke’s Bookstore application, most web components contain embedded URLs that point to other web components. For example, ShowCartServlet indirectly invokes the CatalogServlet through the following embedded URL:

/bookstore1/catalog

A web component can also directly invoke another resource while it is executing. There are two possibilities: The web component can include the content of another resource, or it can forward a request to another resource.

To invoke a resource available on the server that is running a web component, you must first obtain a RequestDispatcher object using the getRequestDispatcher("URL") method.

You can get a RequestDispatcher object from either a request or the web context; however, the two methods have slightly different behavior. The method takes the path to the requested resource as an argument. A request can take a relative path (that is, one that does not begin with a /), but the web context requires an absolute path. If the resource is not available or if the server has not implemented a RequestDispatcher object for that type of resource, getRequestDispatcher will return null. Your servlet should be prepared to deal with this condition.

Including Other Resources in the Response

It is often useful to include another web resource (for example, banner content or copyright information) in the response returned from a web component. To include another resource, invoke the include method of a RequestDispatcher object:

include(request, response);

If the resource is static, the include method enables programmatic server-side includes. If the resource is a web component, the effect of the method is to send the request to the included web component, execute the web component, and then include the result of the execution in the response from the containing servlet. An included web component has access to the request object, but it is limited in what it can do with the response object:

The banner for the Duke’s Bookstore application is generated by BannerServlet. Note that both doGet and doPost are implemented because BannerServlet can be dispatched from either method in a calling servlet.

public class BannerServlet extends HttpServlet {
     public void doGet (HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException {
            output(request, response);
    }
    public void doPost (HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException {
            output(request, response);
}

private void output(HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        out.println("<body bgcolor=\"#ffffff\">" +
        "<center>" + "<hr> <br> &nbsp;" + "<h1>" +
        "<font size=\"+3\" color=\"#CC0066\">Duke’s </font>" +
        <img src=\"" + request.getContextPath() +
        "/duke.books.gif\">" +
         "<font size=\"+3\" color=\"black\">Bookstore</font>" +
        "</h1>" + "</center>" + "<br> &nbsp; <hr> <br> ");
    }
}

Each servlet in the Duke’s Bookstore application includes the result from BannerServlet using the following code:

RequestDispatcher dispatcher =
    getServletContext().getRequestDispatcher("/banner");
if (dispatcher != null)
    dispatcher.include(request, response);
}

Transferring Control to Another Web Component

In some applications, you might want to have one web component do preliminary processing of a request and have another component generate the response. For example, you might want to partially process a request and then transfer to another component depending on the nature of the request.

To transfer control to another web component, you invoke the forward method of a RequestDispatcher. When a request is forwarded, the request URL is set to the path of the forwarded page. The original URI and its constituent parts are saved as request attributes javax.servlet.forward.[request-uri|context-path|servlet-path|path-info|query-string]. The tut-install/javaeetutorial5/examples/web/bookstore2/src/java/com/sun/bookstore2/dispatcher/Dispatcher servlet, used by a version of the Duke’s Bookstore application described in The Example JSP Pages, saves the path information from the original URL, retrieves a RequestDispatcher from the request, and then forwards to the JSP page, tut-install/javaeetutorial5/examples/web/bookstore3/web/template/template.jsp.

public class Dispatcher extends HttpServlet {
    public void doGet(HttpServletRequest request,
         HttpServletResponse response) {
        RequestDispatcher dispatcher = request.
            getRequestDispatcher("/template.jsp");
        if (dispatcher != null)
            dispatcher.forward(request, response);
    }
    public void doPost(HttpServletRequest request,
     ...
}

The forward method should be used to give another resource responsibility for replying to the user. If you have already accessed a ServletOutputStream or PrintWriter object within the servlet, you cannot use this method; doing so throws an IllegalStateException.

Accessing the Web Context

The context in which web components execute is an object that implements the ServletContext interface. You retrieve the web context using the getServletContext method. The web context provides methods for accessing:

The web context is used by the Duke’s Bookstore filters HitCounterFilter and OrderFilter, which are discussed in Filtering Requests and Responses. Each filter stores a counter as a context attribute. Recall from Controlling Concurrent Access to Shared Resources that the counter’s access methods are synchronized to prevent incompatible operations by servlets that are running concurrently. A filter retrieves the counter object using the context’s getAttribute method. The incremented value of the counter is recorded in the log.

public final class HitCounterFilter implements Filter {
    private FilterConfig filterConfig = null;
    public void doFilter(ServletRequest request,
        ServletResponse response, FilterChain chain)
         throws IOException, ServletException {
        ...
        StringWriter sw = new StringWriter();
        PrintWriter writer = new PrintWriter(sw);
        ServletContext context = filterConfig.
            getServletContext();
        Counter counter = (Counter)context.
            getAttribute("hitCounter");
        ...
        writer.println("The number of hits is: " +
            counter.incCounter());
        ...
        System.out.println(sw.getBuffer().toString());
        ...
    }
}

Maintaining Client State

Many applications require that a series of requests from a client be associated with one another. For example, the Duke’s Bookstore application saves the state of a user’s shopping cart across requests. Web-based applications are responsible for maintaining such state, called a session, because HTTP is stateless. To support applications that need to maintain state, Java Servlet technology provides an API for managing sessions and allows several mechanisms for implementing sessions.

Accessing a Session

Sessions are represented by an HttpSession object. You access a session by calling the getSession method of a request object. This method returns the current session associated with this request, or, if the request does not have a session, it creates one.

Associating Objects with a Session

You can associate object-valued attributes with a session by name. Such attributes are accessible by any web component that belongs to the same web context and is handling a request that is part of the same session.

The Duke’s Bookstore application stores a customer’s shopping cart as a session attribute. This allows the shopping cart to be saved between requests and also allows cooperating servlets to access the cart. CatalogServlet adds items to the cart; ShowCartServlet displays, deletes items from, and clears the cart; and CashierServlet retrieves the total cost of the books in the cart.

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

        // Get the user’s session and shopping cart
        HttpSession session = request.getSession();
        ShoppingCart cart =
            (ShoppingCart)session.
                getAttribute("cart");
         ...
        // Determine the total price of the user’s books
        double total = cart.getTotal();

Notifying Objects That Are Associated with a Session

Recall that your application can notify web context and session listener objects of servlet life-cycle events (Handling Servlet Life-Cycle Events). You can also notify objects of certain events related to their association with a session such as the following:

Session Management

Because there is no way for an HTTP client to signal that it no longer needs a session, each session has an associated timeout so that its resources can be reclaimed. The timeout period can be accessed by using a session’s [get|set]MaxInactiveInterval methods.

    You can also set the timeout period in the deployment descriptor using NetBeans IDE:

  1. Open the web.xml file in the web.xml editor.

  2. Click General at the top of the editor.

  3. Enter an integer value in the Session Timeout field. The integer value represents the number of minutes of inactivity that must pass before the session times out.

To ensure that an active session is not timed out, you should periodically access the session by using service methods because this resets the session’s time-to-live counter.

When a particular client interaction is finished, you use the session’s invalidate method to invalidate a session on the server side and remove any session data. The bookstore application’s ReceiptServlet is the last servlet to access a client’s session, so it has the responsibility to invalidate the session:

public class ReceiptServlet extends HttpServlet {
     public void doPost(HttpServletRequest request,
                    HttpServletResponse response)
                     throws ServletException, IOException {
        // Get the user’s session and shopping cart
        HttpSession session = request.getSession();
        // Payment received -- invalidate the session
        session.invalidate();
        ...

Session Tracking

A web container can use several methods to associate a session with a user, all of which involve passing an identifier between the client and the server. The identifier can be maintained on the client as a cookie, or the web component can include the identifier in every URL that is returned to the client.

If your application uses session objects, you must ensure that session tracking is enabled by having the application rewrite URLs whenever the client turns off cookies. You do this by calling the response’s encodeURL(URL) method on all URLs returned by a servlet. This method includes the session ID in the URL only if cookies are disabled; otherwise, it returns the URL unchanged.

The doGet method of ShowCartServlet encodes the three URLs at the bottom of the shopping cart display page as follows:

out.println("<p> &nbsp; <p><strong><a href=\"" +
    response.encodeURL(request.getContextPath() +
        "/bookcatalog") +
        "\">" + messages.getString("ContinueShopping") +
        "</a> &nbsp; &nbsp; &nbsp;" +
        "<a href=\"" +
    response.encodeURL(request.getContextPath() +
        "/bookcashier") +
        "\">" + messages.getString("Checkout") +
        "</a> &nbsp; &nbsp; &nbsp;" +
        "<a href=\"" +
     response.encodeURL(request.getContextPath() +
        "/bookshowcart?Clear=clear") +
        "\">" + messages.getString("ClearCart") +
        "</a></strong>");

If cookies are turned off, the session is encoded in the Check Out URL as follows:


http://localhost:8080/bookstore1/cashier;jsessionid=c0o7fszeb1

If cookies are turned on, the URL is simply


http://localhost:8080/bookstore1/cashier

Finalizing a Servlet

When a servlet container determines that a servlet should be removed from service (for example, when a container wants to reclaim memory resources or when it is being shut down), the container calls the destroy method of the Servlet interface. In this method, you release any resources the servlet is using and save any persistent state. The following destroy method releases the database object created in the init method described in Initializing a Servlet:

public void destroy() {
    bookDB = null;
}

All of a servlet’s service methods should be complete when a servlet is removed. The server tries to ensure this by calling the destroy method only after all service requests have returned or after a server-specific grace period, whichever comes first. If your servlet has operations that take a long time to run (that is, operations that may run longer than the server’s grace period), the operations could still be running when destroy is called. You must make sure that any threads still handling client requests complete; the remainder of this section describes how to do the following:

Tracking Service Requests

To track service requests, include in your servlet class a field that counts the number of service methods that are running. The field should have synchronized access methods to increment, decrement, and return its value.

public class ShutdownExample extends HttpServlet {
    private int serviceCounter = 0;
    ...
    // Access methods for serviceCounter
    protected synchronized void enteringServiceMethod() {
        serviceCounter++;
    }
    protected synchronized void leavingServiceMethod() {
        serviceCounter--;
    }
    protected synchronized int numServices() {
        return serviceCounter;
    }
}

The service method should increment the service counter each time the method is entered and should decrement the counter each time the method returns. This is one of the few times that your HttpServlet subclass should override the service method. The new method should call super.service to preserve the functionality of the original service method:

protected void service(HttpServletRequest req,
                    HttpServletResponse resp)
                    throws ServletException,IOException {
    enteringServiceMethod();
    try {
        super.service(req, resp);
    } finally {
        leavingServiceMethod();
    }
}

Notifying Methods to Shut Down

To ensure a clean shutdown, your destroy method should not release any shared resources until all the service requests have completed. One part of doing this is to check the service counter. Another part is to notify the long-running methods that it is time to shut down. For this notification, another field is required. The field should have the usual access methods:

public class ShutdownExample extends HttpServlet {
    private boolean shuttingDown;
    ...
    //Access methods for shuttingDown
    protected synchronized void setShuttingDown(boolean flag) {
        shuttingDown = flag;
    }
    protected synchronized boolean isShuttingDown() {
        return shuttingDown;
    }
}

Here is an example of the destroy method using these fields to provide a clean shutdown:

public void destroy() {
    /* Check to see whether there are still service methods /*
    /* running, and if there are, tell them to stop. */
    if (numServices() > 0) {
        setShuttingDown(true);
    }

    /* Wait for the service methods to stop. */
    while(numServices() > 0) {
        try {
            Thread.sleep(interval);
        } catch (InterruptedException e) {
        }
    }
}

Creating Polite Long-Running Methods

The final step in providing a clean shutdown is to make any long-running methods behave politely. Methods that might run for a long time should check the value of the field that notifies them of shutdowns and should interrupt their work, if necessary.

public void doPost(...) {
    ...
    for(i = 0; ((i < lotsOfStuffToDo) &&
         !isShuttingDown()); i++) {
        try {
            partOfLongRunningOperation(i);
        } catch (InterruptedException e) {
            ...
        }
    }
}

Further Information about Java Servlet Technology

For more information on Java Servlet technology, see: