Sun Java System Web Server 6.1 SP6 Programmer's Guide to Web Applications

Chapter 2 Using Servlets

This chapter describes how to create effective servlets to control web application interactions running on a Sun Java System Web Server, including standard servlets. In addition, this chapter describes the Sun Java System Web Server features used to augment the standards.

This chapter has the following sections:

For information about internationalization issues for servlets, see Appendix A, Internationalization Issues and Appendix B, Migrating Legacy Servlets.

About Servlets

Servlets, like applets, are reusable Java applications. Servlets, however, run on a Web Server rather than in a Web Browser.

Servlets provide a component-based, platform-independent method for building web-based applications, without the performance overheads, process limitations, and platform-specific liabilities of CGI programs.

Servlets supported by the Sun Java System Web Server are based on the Java Servlet 2.3 specification.

The following list describes the fundamental characteristics of servlets.

Servlets:

This section includes the following topics:

Servlet Data Flow

When a user clicks a Submit button, information entered in a display page is sent to a servlet. The servlet processes the incoming data and orchestrates a response by generating content. Once the content is generated, the servlet creates a response page, usually by forwarding the content to a JSP. The response is sent back to the client, which sets up the next user interaction.

The following illustration shows the information flow to and from the servlet.

ProcedureTo show the servlet data flow

  1. The servlet processes the client request.

  2. The servlet generates content.

  3. The servlet creates a response and either:

    1. Sends it back directly to the client

      - or -

    2. Dispatches the task to a JSP

      The servlet remains in memory, available to process another request.

      Figure 2–1 Servlet Data Flow Steps

      Servlet Data Flow Steps

Servlet Types

There are two main servlet types, generic and HTTP:

Creating Servlets

To create a servlet, perform the following tasks:

The rest of this section discusses the following topics:

Creating the Class Declaration

To create a servlet, write a public Java class that includes basic I/O support as well as the package javax.servlet. The class must extend either GenericServlet or HttpServlet. Since Sun Java System Web Server servlets exist in an HTTP environment, the latter class is recommended. If the servlet is part of a package, you must also declare the package name so the class loader can properly locate it.

The following example header shows the HTTP servlet declaration called myServlet:


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

     public class myServlet extends HttpServlet {
        ...servlet methods...
     }

Overriding Methods

Override one or more methods to provide servlet instructions to perform its intended task. A servlet does all the processing on a request-by-request basis and happens in the service methods, either service() for generic servlets or one of the do Operation() methods for HTTP servlets. This method accepts incoming requests, processes them according to the instructions you provide, and directs the output appropriately. You can create other methods in a servlet as well.

Overriding Initialize

Override the class initializer init() to initialize or allocate resources for the servlet instance's life, such as a counter. The init() method runs after the servlet is instantiated but before it accepts any requests. For more information, see the servlet API specification.


Note –

All init() methods must call super.init(ServletConfig) to set their scope. This makes the servlet's configuration object available to other servlet methods. If this call is omitted, a 500 SC_INTERNAL_SERVER_ERROR displays in the browser when the servlet starts up.


The following example of the init() method initializes a counter by creating a public integer variable called thisMany:


public class myServlet extends HttpServlet {
        int thisMany;
public void init (ServletConfig config) throws ServletException
        {
           super.init(config);
           thisMany = 0;
       }
       }

Now other servlet methods can access the variable.

Overriding Destroy

Override the class destructor destroy() to write log messages or to release resources that have been opened in the servlet's life cycle. Resources should be appropriately closed and dereferenced so that they are recycled or garbage collected. The destroy() method runs just before the servlet itself is deallocated from memory. For more information, see the servlet API specification.

Based on the example for Overriding Initialize, the destroy() method could write a log message like the following:


out.println("myServlet was accessed " + thisMany " times.\n");

         

Overriding Service, Get, and Post

When a request is made, the Sun Java System Web Server hands the incoming data to the servlet engine to process the request. The request includes form data, cookies, session information, and URL name-value pairs, all in a type HttpServletRequest object called the request object. Client metadata is encapsulated as a type HttpServletResponse object called the response object. The servlet engine passes both objects as the servlet's service() method parameters.

The default service() method in an HTTP servlet routes the request to another method based on the HTTP transfer method (POST, GET, and so on). For example, HTTP POST requests are routed to the doPost() method, HTTP GET requests are routed to the doGet() method, and so on. This enables the servlet to perform different request data processing depending on the transfer method. Since the routing takes place in service(), there is no need to generally override service() in an HTTP servlet. Instead, override doGet(), doPost(), and so on, depending on the expected request type.

The automatic routing in an HTTP servlet is based simply on a call to request.getMethod(), which provides the HTTP transfer method. In a Sun Java System Web Server, request data is already preprocessed into a name-value list by the time the servlet sees the data, so simply overriding the service() method in an HTTP servlet does not lose any functionality. However, this does make the servlet less portable, since it is now dependent on preprocessed request data.

Override the service() method (for generic servlets) or the doGet() or doPost() methods (for HTTP servlets) to perform tasks needed to answer the request. Very often, this means collating the needed information (in the request object or in a JDBC result set object), and then passing the newly generated content to a JSP for formatting and delivery back to the client.

Most operations that involve forms use either a GET or a POST operation, so for most servlets you override either doGet() or doPost(). Implement both methods to provide for both input types or simply pass the request object to a central processing method, as shown in the following example:


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

All request-by-request traffic in an HTTP servlet is handled in the appropriate doOperation() method, including session management, user authentication, JSPs, and accessing Sun Java System Web Server features.

If a servlet intends to call the RequestDispatcher method include() or forward(), be aware the request information is no longer sent as HTTP POST, GET, and so on. In other words, if a servlet overrides doPost(), it may not process anything if another servlet calls it, if the calling servlet happens to receive its data through HTTP GET. For this reason, be sure to implement routines for all possible input types, as explained above. RequestDispatcher methods always call service().

For more information, see Calling a Servlet Programmatically.


Note –

Arbitrary binary data, such as uploaded files or images, can be problematic, because the web connector translates incoming data into name-value pairs by default. You can program the web connector to properly handle these kinds of data and package them correctly in the request object.


Accessing Parameters and Storing Data

Incoming data is encapsulated in a request object. For HTTP servlets, the request object type is HttpServletRequest. For generic servlets, the request object type is ServletRequest. The request object contains all request parameters, including your own request values called attributes.

To access all incoming request parameters, use the getParameter() method. For example:


String username = request.getParameter("username");

         

Set and retrieve values in a request object using setAttribute() and getAttribute(), respectively. For example:


request.setAttribute("favoriteDwarf", "Dwalin");

         

Handling Sessions and Security

From a web server's perspective, a web application is a series of unrelated server hits. There is no automatic recognition if a user has visited the site before, even if their last interaction was seconds before. A session provides a context between multiple user interactions by remembering the application state. Clients identify themselves during each interaction by a cookie, or, in the case of a cookie-less browser, by placing the session identifier in the URL.

A session object can store objects, such as tabular data, information about the application's current state, and information about the current user. Objects bound to a session are available to other components that use the same session.

For more information, see Chapter 5, Session Managers.

After a successful login, you should direct a servlet to establish the user's identity in a standard object called a session object. This object holds information about the current session, including the user's login name and any additional information to retain. Application components can then query the session object to obtain user authentication.

For more information about providing a secure user session for your application, see Chapter 6, Securing Web Applications.

Handling Threading Issues

By default, servlets are not thread-safe. The methods in a single servlet instance are usually executed numerous times simultaneously (up to the available memory limit). Each execution occurs in a different thread, though only one servlet copy exists in the servlet engine.

This is efficient system resource usage, but is dangerous because of the way Java manages memory. Because variables belonging to the servlet class are passed by reference, different threads can overwrite the same memory space as a side effect. To make a servlet (or a block within a servlet) thread-safe, do one of the following:


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

   public class myServlet extends HttpServlet {

   public void doGet (HttpServletRequest request,
                     HttpServletResponse response)
            throws ServletException, IOException {
      //pre-processing
       synchronized (this) {
         //code in this block is thread-safe
      }
      //other processing;
      }

    public synchronized int mySafeMethod (HttpServletRequest request)
      {
      //everything that happens in this method is thread-safe
      }
   }

         

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

   public class myServlet extends HttpServlet
       implements SingleThreadModel {
      servlet methods...
   }

         

Note –

If a servlet is specifically written as a single thread, the servlet engine creates a pool of servlet instances to be used for incoming requests. If a request arrives when all instances are busy, it is queued until an instance becomes available. The number of pool instances is configurable in the sun-web.xml file, in the singleThreadedServletPoolSize property of the sun-web-app element.


Delivering Client Results

The final user interaction activity is to provide a response page to the client. The response page can be delivered in two ways, as described in the following topics:

Creating a Servlet Response Page

Generate the output page within a servlet by writing to the output stream. The recommended way to do this depends on the output type.

Always specify the output MIME type using setContentType() before any output commences, as in this example:


response.setContentType("text/html");

            

For textual output, such as plain HTML, create a PrintWriter object and then write to it using println. For example:


PrintWriter output = response.getWriter();
output.println("Hello, World\n");

            

For binary output, write to the output stream directly by creating a ServletOutputStream object and then write to it using print(). For example:


ServletOutputStream output = response.getOutputStream();
output.print(binary_data);

            

Creating a JSP Response Page

Servlets can invoke JSPs in two ways, the include() method and the forward() method:


RequestDispatcher dispatcher =
       getServletContext().getRequestDispatcher("JSP_URI");
 dispatcher.include(request, response);
   ... //processing continues

            

Note –

You cannot use the forward() method if you have already defined a PrintWriter or ServletOutputStream object.


This example shows a JSP using forward():


RequestDispatcher dispatcher =
       getServletContext().getRequestDispatcher("JSP_URI");
 dispatcher.forward(request, response);

            

Note –

Identify which JSP to call by specifying a Universal Resource Identifier (URI). The path is a String describing a path within the ServletContext scope. There is also a getRequestDispatcher() method in the request object that takes a String argument indicating a complete path. For more information about this method, see the Java Servlet 2.3 specification, section 8.


For more information about JSPs, see Chapter 4, Using JavaServer Pages.

Invoking Servlets

You can invoke a servlet by directly addressing it from a Web page with a URL or by calling it programmatically from an already running servlet, as described in the following sections:

Calling a Servlet with a URL

You can call servlets by using URLs embedded as links in HTML or JSP pages. The format of these URLs is as follows:

http://server:port/context_root/servlet/servlet_name?name=value

The following table describes each URL section. The left column lists the URL elements, and the right column lists descriptions of each URL element.

Table 2–1 URL Fields for Servlets within a Web Application

URL Element  

Description  

server:port

The IP address (or host name) and optional port number. 

To access the default web application for a virtual server, specify only this URL section. You do not need to specify the context_root or servlet_name unless you also wish to specify name-value parameters.

context_root

The context path without the leading “/” at which the web application is installed. 

servlet

Only needed if no servlet-mapping is defined in the web.xml file.

servlet_name

The servlet-name (or servlet-mapping if defined) as configured in the web.xml file.

?name=value...

Optional servlet name-value parameters. 

In this example, leMort is the host name, MortPages is the context root, and calcMortgage is the servlet name:

http://www.leMort.com/MortPages/servlet/calcMortgage?rate=8.0&per=360&bal=180000

Calling a Servlet Programmatically

First, identify which servlet to call by specifying a URI. This is normally a path relative to the current application. For example, if your servlet is part of an application with a context root called OfficeFrontEnd, the URL to a servlet called ShowSupplies from a browser is as follows:

http://server:port/OfficeApp/OfficeFrontEnd/servlet/ShowSupplies?name=value

You can call this servlet programmatically from another servlet in one of two ways, as described below.


   RequestDispatcher dispatcher =
       getServletContext().getRequestDispatcher("/ShowSupplies");
    dispatcher.include(request, response);

         

   RequestDispatcher dispatcher =
       getServletContext().getRequestDispatcher("/ShowSupplies");
    dispatcher.forward(request, response);

         

Servlet Output

By default, the System.out and System.err output of servlets is sent to the server log, and during startup server log messages are echoed to the System.err output. Also by default, on Windows, there is no console created for the System.err output.

You can change these defaults using the Administration interface. For more information, see “Setting Error Logging Options” in Chapter 10 of the Sun Java System Web Server 6.1 SP6 Administrator’s Guide.

Caching Servlet Results

The Sun Java System Web Server can cache the results of invoking a servlet, a JSP, or any URL pattern to make subsequent invocations of the same servlet, JSP, or URL pattern faster. The Sun Java System Web Server caches the request results for a specific amount of time. In this way, if another data call occurs, the Sun Java System Web Server can return the cached data instead of performing the operation again. For example, if your servlet returns a stock quote that updates every 5 minutes, you set the cache to expire after 300 seconds.

Whether to cache results and how to cache them depends on the data involved. For example, it makes no sense to cache the results of a quiz submission, because the input to the servlet is different each time. However, you could cache a high-level report showing demographic data taken from quiz results that is updated once an hour.

You can define how a Sun Java System Web Server web application handles response caching by editing specific fields in the sun-web.xml file. In this way, you can create programmatically standard servlets that still take advantage of this valuable Sun Java System Web Server feature.

For more information about JSP caching, see JSP Cache Tags.

The rest of this section covers the following topics:

Caching Features

Sun Java System Web Server 6.1 has the following web application response caching capabilities:

Default Cache Configuration

If you enable caching but do not provide any special configuration for a servlet or JSP, the default cache configuration is as follows:

Caching Example

Here is an example cache element in the sun-web.xml file:

<cache max-capacity="8192" timeout="60">
   <cache-helper name="myHelper" class-name="MyCacheHelper"/>
   <cache-mapping>
      <servlet-name>myservlet</servlet name>
      <timeout name="timefield">120</timeout>
      <http-method>GET</http-method>
      <http-method>POST</http-method>
   </cache-mapping>
   <cache-mapping>
      <url-pattern> /catalog/* </url-pattern>
      <!-- cache the best selling category; cache the responses to
        -- this resource only when the given parameters exist. cache
        -- only when the catalog parameter has 'lilies' or 'roses'
        -- but no other catalog varieties:
        -- /orchard/catalog?best&category='lilies'
        -- /orchard/catalog?best&category='roses'
        -- but not the result of
        -- /orchard/catalog?best&category='wild'
      -->
      <constraint-field name='best' scope='request.parameter'/>
      <constraint-field name='category' scope='request.parameter'>
        <value> roses </value>
        <value> lilies </value>
      </constraint-field>
      <!-- Specify that a particular field is of given range but the
        -- field doesn't need to be present in all the requests -->
      <constraint-field name='SKUnum' scope='request.parameter'>
        <value match-expr='in-range'> 1000 - 2000 </value>
      </constraint-field>
      <!-- cache when the category matches with any value other than
        -- a specific value -->
      <constraint-field name="category" scope="request.parameter>
        <value match-expr="equals" cache-on-match-failure="true">bogus</value>
      </constraint-field>
   </cache-mapping>
   <cache-mapping>
      <servlet-name> InfoServlet </servlet name>
      <cache-helper-ref>myHelper</cache-helper-ref>
   </cache-mapping>
</cache>

For more information about the sun-web.xml caching settings, see Caching Elements.

CacheHelper Interface

Here is the CacheHelper interface:

package com.sun.appserv.web.cache;
import java.util.Map
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
/** CacheHelper interface is an user-extensible interface to customize:
 * a) the key generation b) whether to cache the response.
 */
public interface CacheHelper {
// name of request attributes
   public static final String ATTR_CACHE_MAPPED_SERVLET_NAME =
                           "com.sun.appserv.web.cachedServletName";
   public static final String ATTR_CACHE_MAPPED_URL_PATTERN =
                           "com.sun.appserv.web.cachedURLPattern";
public static final int TIMEOUT_VALUE_NOT_SET = -2;
/** initialize the helper
    * @param context the web application context this helper belongs to
    * @exception Exception if a startup error occurs
    */
   public void init(ServletContext context, Map props) throws Exception;
/** getCacheKey: generate the key to be used to cache this request
    * @param request incoming <code>HttpServletRequest</code> object
    * @returns the generated key for this requested cacheable resource.
    */
   public String getCacheKey(HttpServletRequest request);
/** isCacheable: is the response to given request cacheable?
    * @param request incoming <code>HttpServletRequest</code> object
    * @returns <code>true</code> if the response could be cached. or
    * <code>false</code> if the results of this request must not be cached.
    */
   public boolean isCacheable(HttpServletRequest request);
/** isRefreshNeeded: is the response to given request to be refreshed?
    * @param request incoming <code>HttpServletRequest</code> object
    * @returns <code>true</code> if the response needs to be refreshed.
    * or return <code>false</code> if the results of this request
    * don't need to be refreshed.
    */
   public boolean isRefreshNeeded(HttpServletRequest request);
/** get timeout for the cached response.
    * @param request incoming <code>HttpServletRequest</code> object
    * @returns the timeout in seconds for the cached response; a return
    * value of -1 means the response never expires and a value of -2 indicates
    * helper cannot determine the timeout (container assigns default timeout)
   */
public int getTimeout(HttpServletRequest request);
/**
    * Stop the helper from active use
    * @exception Exception if an error occurs
    */
   public void destroy() throws Exception;
}

CacheKeyGenerator Interface

The built-in default CacheHelper implementation allows web applications to customize the key generation. An application component (in a servlet or JSP) can set up a custom CacheKeyGenerator implementation as an attribute in the ServletContext.

The name of the context attribute is configurable as the value of the cacheKeyGeneratorAttrName property in the default-helper element of the sun-web.xml deployment descriptor. For more information, see default-helper.

Here is the CacheKeyGenerator interface:

package com.sun.appserv.web.cache;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
/** CacheKeyGenerator: a helper interface to generate the key that
 * is used to cache this request.
 *
 * Name of the ServletContext attribute implementing the
 * CacheKeyGenerator is configurable via a property of the
 * default-helper in sun-web.xml:
 * <default-helper>
 * <property
 * name="cacheKeyGeneratorAttrName"
 * value="com.acme.web.MyCacheKeyGenerator" />
 * </default-helper>
 *
 * Caching engine looks up the specified attribute in the servlet
 * context; the result of the lookup must be an implementation of the
 * CacheKeyGenerator interface.
 */
public interface CacheKeyGenerator {
/** getCacheKey: generate the key to be used to cache the
    * response.
    * @param context the web application context
    * @param request incoming <code>HttpServletRequest</code>
    * @returns key string used to access the cache entry.
    * if the return value is null, a default key is used.
    */
   public String getCacheKey(ServletContext context,
                           HttpServletRequest request);
}

Maximizing Servlet Performance

Consider the following guidelines for improving servlet performance: