- In an internet driven environment, it is imperative for a company to keep its product or business to the forefront of potential customers' minds. The ...
- A successful enterprise is all about constantly reinventing ways to work more efficiently. In today’s techno age, this translates to testing new too...
- The IT industry plays a pivotal role in providing application development solutions and custom software development to a wide range of industries, i...
- Royal Victorian Eye and Ear Hospital, 2010 Atcomm has been contracted to implement a complex IOP glaucoma management tool which will be distribut...
- We are pleased to announce another implementation of a CMS system for a large Melbourne based fitness center - Star Plate Studio. Atcomm has depl...
Coding an HttpServlet
-
font size
decrease font size
increase font size
Previously, it has been shown that Servlets have a three-part life cycle: initialization, service, and destruction. An HttpServlet object shares this life cycle but makes a few modifications for the HTTP protocol. The HttpServlet object's implementation of the service() method, which is called during each service request, calls one of seven different helper methods. These seven methods correspond directly to the seven HTTP methods and are named as follows: doGet(), doPost(), doPut(), doHead(), doOptions(), doDelete(), and doTrace(). The appropriate helper method is invoked to match the type of method on a given HTTP request. The HttpServlet life cycle can be illustrated as shown in Figure 2-5.

While all seven methods are shown, remember that normally only one of them is called on a given request. More than one might be called if a developer overrides the methods and has them call each other. The initialization and destruction stages of the Servlet life cycle are the same as described before.
Coding an HttpServlet is straightforward. The javax.servlet.http.HttpServlet class takes care of handling the redundant parts of an HTTP request and response, and requires a developer only to override methods that need to be customized. Manipulation of a given request and response is done through two objects, javax.servlet.http.HttpServletRequest and javax.servlet.http.HttpServletResponse. Both of these objects are passed as parameters when invoking the HTTP service methods.
It is time to step through coding and using a basic Servlet. A basic "Hello World" Servlet is appropriate for getting started (see Listing 2-1). Take the following code and save it as HelloWorld.java in the /WEB-INF/classes/com/jspbook directory of the jspbook Web Application.
Listing 2-1. HelloWorld.java
package com.jspbook;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("");
out.println("");
out.println("");
out.println("");
out.println("");
out.println("Hello World!
");
out.println("");
out.println("");
}
}
You can probably see exactly what the preceding code is doing. If not, do not worry about understanding everything just yet since we have not learned how to deploy a Servlet for use which has to come before dissecting the code. For now understand that the preceding is the complete code for an HttpServlet. Once deployed, this example Servlet will generate a simple HTML page that says "Hello World!".
Deploying a Servlet
By itself a Servlet is not a full Java application. Servlets rely on being part of a Web Application that a container manages. Using a Servlet to generate dynamic responses involves both creating the Servlet and deploying the Servlet for use in the Web Application.
Deploying a Servlet is not difficult, but it is not as intuitive as you might think. Unlike a static resource, a Servlet is not simply placed in the root directory of the Web Application. A Servlet class file goes in the /WEB-INF/classes directory of the application with all the other Java classes. For a client to access a Servlet, a unique URL, or set of URLs, needs to be declared in the Web Application Deployment Descriptor. The web.xml deployment description relies on new elements[14]: servlet and servlet-mapping need to be introduced for use in web.xml. The servlet element is used to define a Servlet that should be loaded by a Web Application. The servlet-mapping element is used to map a Servlet to a given URL or set of URLs. Multiple tags using either of these elements can appear to define as many Servlets and Servlet mappings as needed. Both of these elements also have sub-elements used to further describe them. These sub-elements are self-descriptive, and they are introduced by use in an upcoming example.
[14] An element is the proper name for the unique word that comes immediately after the starting less than, "<", of an XML tag.
Open up the /WEB-INF/web.xml file of the jspbook Web Application and edit it to match Listing 2-2.
Listing 2-2. Deploying HelloWorld Servlet
HelloWorld com.jspbook.HelloWorld HelloWorld /HelloWorld welcome.html
Highlighted is the new addition to web.xml. In the highlight, notice that both an instance of the servlet and servlet-mapping element is used. In general this is how every Servlet is deployed. A Servlet is first declared by a servlet element that both names the Servlet and gives the location of the appropriate Java class. After declaration, the Servlet can be referenced by the previously given name and mapped to a URL path. The name and class values are assigned by a given string in the servlet-name and servlet-class tags, respectively. The Servlet's name is arbitrary, but it must be unique from any other Servlet name for that Web Application. In the body of the servlet-mapping tag, the name and URL path for a Servlet are given by a string value in the body of the servlet-name and url-pattern tags, respectively. The name must match a name previously defined by a servlet element. The URL path can be anything as defined by the Servlet specification:
-
An exact pattern to match. The pattern must start with a /, but can contain anything afterwards. This type of pattern is used for a one-to-one mapping of a request to a specific Servlet.
-
An extension match, *.extension. In this case all URLs ending with the given extension are forwarded to the specified Servlet. This is commonly used in Servlet frameworks and can force many requests to go to the same Servlet[15].
[15] It is used, for example, by Tomcat to map all requests to .jsp to a Servlet that knows how to process JavaServer Pages.
-
A path mapping. Path mappings must start with a / and end with a /*. In between anything can appear. Path mappings are usually used for forwarding all requests that fall in a certain directory to a specific Servlet.
-
Default Servlet, /. A default Servlet mapping is used to define a Servlet for forwarding requests when no path information is given. This is analogous to a directory listing[16].
[16] The default Servlet was used when you sent the first request to http://localhost/jspbook.
With the HelloWorld Servlet, an exact pattern match was used that forwards any request for /HelloWorld directly to the Servlet (see Figure 2-6). Translating any of these URL patterns to a full URL involves prefixing the pattern with the URL to the Web Application. For the HelloWorld Servlet in the jspbook Web Application, this would be http://127.0.0.1/jspbook/HelloWorld. Restart Tomcat to update your changes and use this URL to browse to the HelloWorld Servlet[17]. A simple HTML page should be displayed that says "Hello World!". For non-English readers, our apologies; internationalizing this properly would require chapter precedence of 1, 12, then 2.

Understand Servlet Deployment!
Deploying a Servlet is relatively simple but very important. Pay attention in the preceding example because for brevity further examples do not include the verbose deployment description. A single sentence such as "Deploy Servlet x to the URL mapping y". is used to mean the same thing. Only when it is exceptionally important to the example is the full deployment descriptor provided.
Web Application Deployment Descriptor Structure
Each and every Servlet needs to be deployed before it is available for a client to use. The HelloWorld Servlet example introduced the Web Application Deployment Descriptor elements that do this, but before you go on deploying more Servlets, there is some more information to be aware of. The schema for web.xml defines which elements can be used and in what order they must appear. In the previous example this is the reason that both the servlet and servlet-mapping elements appeared before the welcome-file-list element. This is also the reason that the servlet element was required to appear before the servlet-mapping element[18].
[18] Not all Servlet containers enforce the schema, however. Consult your container's documentation for more information.
From the preceding three elements it might seem arrangement is of alphabetical precedence, but this is not the case. The arrangement of elements must match the given listing with the Web Application Deployment Descriptor schema. This rather long title should sound familiarit is the same XML schema that defines what can appear in web.xml. The current complete schema can be found in the Servlet 2.4 specification. The element ordering is defined by the root web-inf element and is, in ascending order, as follows: icon, display-name, description, distributable, context-param, filter, filter-mapping, listener, servlet, servlet-mapping, session-config, mime-mapping, welcome-file-list, error-page, jsp-config, resource-env-ref, message-destination-ref, resource-ref, security-constraint, login-config, security-role, env-entry, ejb-ref, ejb-local-ref., message-destination, and locale-encoding-mapping-list.
Understanding the order is not difficult, but it is a problem quite a few new Servlet developers ask about. It is well worth mentioning it now to avoid causing any confusion later. Keep in mind that this order also applies to multiple elements of the same name. If two Servlets are deployed, both of the servlet elements must be listed before any of the servlet-mapping elements. It does not matter what order a group of the same elements are in, but it does matter that they are properly grouped.
Servlet Configuration
Sometimes it is necessary to provide initial configuration information for Servlets. Configuration information for a Servlet may consist of a string or a set of string values included in the Servlet's web.xml declaration. This functionality allows a Servlet to have initial parameters specified outside of the compiled code and changed without needing to recompile the Servlet. Each servlet has an object associated with it called the ServletConfig[19]. This object is created by the container and implements the javax.servlet.ServletConfig interface. It is the ServletConfig that contains the initialization parameters. A reference to this object can be retrieved by calling the getServletConfig() method. The ServletConfig object provides the following methods for accessing initial parameters:
[19] In fact, in the standard Servlet library a Servlet and a ServletConfig are the same objectthat is, GenericServlet implements both javax.servlet.Servlet and javax.servlet.ServletConfig.
getInitParameter(String name)
The getInitParameter() returns a String object that contains the value of the named initialization parameter or null if the parameter does not exist.
getInitParameterNames()
The getInitParameterNames() method returns the names of the Servlet's initialization parameters as an Enumeration of String objects or an empty Enumeration if the Servlet has no initialization parameters.
Defining initial parameters for a Servlet requires using the init-param, param-name, and param-value elements in web.xml. Each init-param element defines one initial parameter and must contain a parameter name and value specified by children param-name and param-value elements, respectively. A Servlet may have as many initial parameters as needed, and initial parameter information for a specific Servlet should be specified within the servlet element for that particular Servlet.
Using initial parameters, the HelloWorld Servlet can be modified to be more internationally correct. Instead of assuming the Servlet should say "Hello World!", it will be assumed the Servlet should say the equivalent for any given language. To accomplish this, an initial parameter will be used to configure the proper international "Hello" message. While HelloWorld.java will still not be perfectly compliant for all languages, it does demonstrate initial parameters. Modify HelloWorld.java to match the code in Listing 2-3.
Listing 2-3. InternationalizedHelloWorld.java
package com.jspbook;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class InternationalizedHelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("");
out.println("");
String greeting;
greeting =
getServletConfig().getInitParameter("greeting");
out.println("");
out.println("");
out.println("");
out.println("" +greeting+"
");
out.println("");
out.println("");
}
}
Save the preceding code as InternationalizedHelloWorld.java in the /WEB-INF/classes/com/jspbook directory of the jspbook Web Application. Since this is the second code example, a full walk-through is given for deploying the Servlet. In future examples it will be expected that you deploy Servlets on your own to a specified URL.
Open up web.xml in the /WEB-INF folder of the jspbook Web Application and add in a declaration and mapping to /InternationalizedHelloWorld for the InternationalizedHelloWorld Servlet. When finished, web.xml should match Listing 2-4.
Listing 2-4. Updated web.xml
HelloWorld com.jspbook.HelloWorld InternationalizedHelloWorld com.jspbook.InternationalizedHelloWorld InternationalizedHelloWorld /InternationalizedHelloWorld HelloWorld /HelloWorld welcome.html
The InternationalizedHelloWorld Servlet relies on an initial parameter for the classic "Hello World" greeting. Specify this parameter by adding in the following entry to web.xml.
...
InternationalizedHelloWorld
com.jspbook.InternationalizedHelloWorld
greeting
Bonjour!
...
Leave the param-name element's body as greeting, but change the value specified in the body of the param-value tag to be a greeting of your choice. A candidate for a welcome message en francais[20] would be "Bonjour!" After saving any changes, reload the jspbook Web Application and visit the Internationalized-HelloWorld Servlet to see the new message.
Instead of the basic "Hello World!", the Servlet now displays the initial parameter's value. This approach is nowhere near the best of solutions for internationalization issues, but it does work in some cases and is a good example to introduce initial Servlet configuration. In general, the initial parameter mechanism shown previously is used to provide simple configuration information for an entire Web Application. The HelloWorld Servlet example demonstrated initial parameters for one Servlet, but later on in the chapter it will be shown that the same method is used to provide initial parameters for an entire Web Application.
Limitations of Configuration: web.xml Additions
Initial parameters are a good method of providing simple one-string values that Servlets can use to configure themselves. This approach is simple and effective, but is a limited method of configuring a Servlet. For more complex Servlets it is not uncommon to see a completely separate configuration file created to accompany web.xml. When developing Servlets, keep in mind that nothing stops you from doing this. If the parameter name and parameter values mappings are not adequate, do not use them! It is perfectly OK to create a custom configuration file and package it in a WAR with the rest of a Web Application. A great example of doing this is shown by the Jakarta Struts framework appearing in Chapter 11. The Struts framework relies on a control Servlet that is configured via a custom and usually lengthy XML file.
Client/Server Servlet Programming
A Servlet request and response is represented by the javax.servlet.ServletRequest and javax.servlet.ServletResponse objects, or a corresponding subclass of them. For HTTP Servlets the corresponding classes are HttpServletRequest and HttpServletResponse. These two objects were quickly introduced with the HelloWorld Servlet example, but the example was primarily focused on showing how a Servlet is deployed for use. Coding and deploying are the fundamental parts of Servlet development. Deployment was explained first because it is the exact same process for any given Servlet. Once explained it is a fairly safe assumption that you can repeat the process or simply copy and edit what already exists. Servlet code varies greatly depending on what the Servlet are designed to do. Understanding and demonstrating some of the different uses of Servlets are a lot easier if time and space are not devoted to rehashing the mundane act of deployment. Servlet code is where discussion is best focused, and that is exactly what the rest of the chapter does.
Since this is a Servlet-focused book, very little time is going to be spent on discussing the normal techniques and tricks of coding with Java. Any good Java book will discuss these, and they all are valid for use with Servlets. Time is best spent focusing on the Servlet API. Understanding HTTP and the HttpServlet class is a good start, but knowledge of the HttpServletRequest and HttpServletResponse objects are needed before some useful Servlets can be built.
HttpServletRequest and HttpServletResponse
The Servlet API makes manipulating an HTTP request and response pair relatively simple through use of the HttpServletRequest and HttpServletResponse objects. Both of these objects encapsulate a lot of functionality. Do not worry if it seems like this section is skimming through these two objects. Detailing all of the methods and members would be both tedious and confusing without understanding the rest of the Servlet API, but API discussion has to start somewhere and these two objects are arguably the most important. In this section discussion will only focus on a few of the most commonly used methods of each object. Later chapters of the book cover the other methods in full and in the context of which they are best used.
HttpServletResponse
The first and perhaps most important functionality to discuss is how to send information back to a client. As its name implies, the HttpServletResponse object is responsible for this functionality. By itself the HttpServletResponse object only produces an empty HTTP response. Sending back custom content requires using either the getWriter() or getOutputStream() method to obtain an output stream for writing content. These two methods return suitable objects for sending either text or binary content to a client, respectively. Only one of the two methods may be used with a given HttpServletResponse object. Attempting to call both methods causes an exception to be thrown.
With the HelloWorld Servlet example, Listing 2-1, the getWriter() method was used to get an output stream for sending the HTML markup. In the first few lines of HelloWorld.java, a getWriter() call obtained a java.io.PrintWriter object suitable for sending back the text.
PrintWriter out = response.getWriter();
out.println("");
out.println("");
out.println("");
Using an instance of a PrintWriter object consists of providing a String object and calling either the print(), println(), or write() methods. The difference between the methods is that println appends a new line character, '\n', to each line of response text. In both the HelloServlet.java code and the generated HTML page, the println() method was used to make the lines of HTML easy to read.
Using a PrintWriter is not meant to be complex, and it should now be clear how to use the PrintWriter object for sending text. Above and beyond what has previously been shown is sending custom encoded text. So far only one type of text has been sent, the default text encoding of HTTP, ISO-8895-1, but changing the character encoding is possible and is covered in full in Chapter 12.
Compared to using the getWriter() method, the getOutputStream() method is used when more control is needed over what is sent to a client. The returned OutputStream can be used for sending text, but is usually used for sending non-text-related binary information such as images. The reason for this is because the getOutputStream() method returns an instance of a javax.servlet.ServletOutputStream object, not a PrintWriter. The ServletOutputStream object directly inherits from java.io.OutputStream and allows a developer to write raw bytes. The PrintWriter objects lack this functionality because it always assumes you are writing text.
In most practical situations it is rarely needed to send raw bytes rather than text to a client, but this functionality is something a good Servlet developer should be aware of[21]. Often the incorrect mindset is to think Servlets can only send dynamically created text. By sending raw bytes, a Servlet can dynamically provide any form of digital content. The primary restriction on this functionality is being able to create the needed bytes for a desired content. For commonly used formats, including images and audio, it is not uncommon to see a Java API built to simplify the task. Combining this API with the Servlet API, it is then relatively easy to send the custom format. A good example to use would be the Java API for Advanced Imaging (JAI). Using this API many of the popular image formats can be produced from the server-side, even on servers not supporting a GUI.
[21] Note that for better efficiency you may want to use the OutputStream rather than the PrintWriter to send text. The PrintWriter accepts Unicode strings whereas the OutputStream accepts bytes. See Java Performance and Scalability Volume 1 by Dov Bulka for more details.
Full discussion of non-text-producing Servlets is outside the scope of this book. Producing custom images, audio, and other non-text formats via Java is not something specific to Servlets. The only thing a Servlet needs to do is appropriately set a MIME type and send a client some bytes, but that is not a good reason to completely avoid an example. For completeness, Listing 2-5 provides a Servlet that dynamically generates an image and sends the bytes using a ServletOutputStream.
Listing 2-5. DynamicImage.java
package com.jspbook;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.awt.*;
import java.awt.image.*;
import com.sun.image.codec.jpeg.*;
public class DynamicImage extends HttpServlet {
public void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("image/jpeg");
// Create Image
int width = 200;
int height = 30;
BufferedImage image = new BufferedImage(
width, height, BufferedImage.TYPE_INT_RGB);
// Get drawing context
Graphics2D g = (Graphics2D)image.getGraphics();
// Fill background
g.setColor(Color.gray);
g.fillRect(0, 0, width, height);
// Draw a string
g.setColor(Color.white);
g.setFont(new Font("Dialog", Font.PLAIN, 14));
g.drawString("http://www.jspbook.com",10,height/2+4);
// Draw a border
g.setColor(Color.black);
g.drawRect(0,0,width-1,height-1);
// Dispose context
g.dispose();
// Send back image
ServletOutputStream sos = response.getOutputStream();
JPEGImageEncoder encoder =
JPEGCodec.createJPEGEncoder(sos);
encoder.encode(image);
}
}
Save the preceding code as DynamicImage.java in the /WEB-INF/classes/com/jspbook directory of the jspbook Web Application. Compile and deploy the DynamicImage Servlet with a mapping to the /DynamicImage URL extension of the jspbook Web Application. After reloading the Web Application, browse to http://127.0.0.1/jspbook/DynamicImage. A JPEG formatted image is dynamically generated on each request to the Servlet.
Before going out and creating your own image-producing Servlet, a fair warning should be given regarding the preceding code. For simplicity the code uses an object from the com.sun.image.codec.jpeg package that is unofficially included in the J2SDK 1.4. Code from the com.sun package is not guaranteed to be around in future Java releases, nor is it meant for developers to use. A proper solution would be to use an instance of the ImageEncoder class from the Java Advanced Imaging API, but that would have required you download and install the JAI before running the example.
Response Headers
Along with sending content back to a client, the HttpServletResponse object is also used to manipulate the HTTP headers of a response. HTTP response headers are helpful for informing a client of information such as the type of content being sent back, how much content is being sent, and what type of server is sending the content. The HttpServletResponse object includes the following methods for manipulating HTTP response headers:
-
addHeader(java.lang.String name, java.lang.String value): The addHeader() method adds a response header with the given name and value. This method allows response headers to have multiple values.
-
containsHeader(java.lang.String name): The containsHeader() method returns a boolean indicating whether the named response header has already been set.
-
setHeader(java.lang.String name, java.lang.String value): The setHeader() method sets a response header with the given name and value. If the header had already been set, the new value overwrites the previous one. The containsHeader() method can be used to test for the presence of a header before setting its value.
-
setIntHeader(java.lang.String name, int value): The setIntHeader() sets a response header with the given name and integer value. If the header had already been set, the new value overwrites the previous one. The containsHeader() method can be used to test for the presence of a header before setting its value.
-
setDateHeader(java.lang.String name, long date): The setDateHeader() sets a response header with the given name and date value. The date is specified in terms of milliseconds since the epoch. If the header had already been set, the new value overwrites the previous one. The containsHeader() method can be used to test for the presence of a header before setting its value.
-
addIntHeader(java.lang.String name, int value): The addIntHeader() method adds a response header with the given name and integer value. This method allows response headers to have multiple values.
-
addDateHeader(java.lang.String name, long date): The addDateHeader() method adds a response header with the given name and date value. The date is specified in terms of milliseconds since the epoch[22]. This method doesn't override previous response headers and allows response headers to have multiple values.
[22] A common reference in time; January 1, 1970 GMT.
In the introduction to HTTP that appeared earlier in this chapter, a few HTTP response headers were seen, and in the HelloWorld Servlet the Content-Type response header was used. In both these cases, elaboration on the headers' semantics was conveniently skipped. This was done intentionally to simplify the examples, but it is time to clarify what these unexplained HTTP headers mean (see Table 2-2), along with introducing some of the other helpful headers that can be set by an HttpServletResponse object.
|
Header Field |
Header Value |
|---|---|
|
Age |
A positive integer representing the estimated amount of time since the response was generated from the server. |
|
Location |
Some HTTP response codes redirect a client to a new resource. The location of this resource is specified by the Location header as an absolute URI. |
|
Retry-After |
The Retry-After response header field can be used with a 503 (Service Unavailable) response to indicate how long the service is expected to be unavailable to the requesting client. The value of this field can be either a date or an integer number of seconds (in decimals) after the time of the response. |
|
Server |
The Server field is a string representing information about the server that generated this response. |
|
Content-Length |
The Content-Length entity header field indicates the size of the message body, in decimal number of octets (8-bit bytes), sent to the recipient or, in the case of the HEAD method, the size of the entity body that would have been sent had the request been a GET. |
|
Content-Type |
The MIME type that corresponds to the content of the HTTP response. This value is often used by a browser to determine if the content should be rendered internally or launched for rendering by an external application. |
|
Date |
The Date field represents the date and time at which the message was originated. |
|
Pragma |
The Pragma field is used to include implementation-specific directives that may apply to any recipient along the request-response chain. The most commonly used value is "no-cache", indicating a resource shouldn't be cached. |
In most cases the most important header to worry about as a Servlet author is Content-Type. This header should always be set to 'text/html' when a Servlet is sending back HTML. For other formats the appropriate MIME type[23] should be set.
[23] Multipart Internet Mail Extensions defined in RFCs 2045, 2046, 2047, 2048, and 2049
Response Redirection
Any HTTP response code can be sent to a client by using the setStatus() method of an HttpServletResponse object. If everything works OK, Servlet will send back a status code 200, OK. Another helpful status code to understand is 302, "Resource Temporarily Moved". This status code informs a client that the resource they were looking for is not at the requested URL, but is instead at the URL specified by the Location header in the HTTP response. The 302 response code is helpful because just about every Web browser automatically follows the new link without informing a user. This allows a Servlet to take a user's request and forward it any other resource on the Web.
Because of the common implementation of the 302 response code, there is an excellent use for it besides the intended purpose. Most Web sites track where visitors come from to get an idea of what other sites are sending traffic. The technique for accomplishing involves extracting the "referer" (note the slightly inaccurate spelling) header of an HTTP request. While this is simple, there is no equally easy way of tracking where a site sends traffic. The problem arises because any link on a site that leads to an external resource does send a request back to the site it was sent from. To solve the problem, a clever trick can be used that relies on the HTTP 302 response code. Instead of providing direct links to external resources, encode all links to go to the same Servlet on your site but include the real link as a parameter. Link tracking is then provided using the Servlet to log the intended link while sending the client back a 302 status code along with the real link to visit.
As you might imagine, using a Servlet to track links is very commonly done by sites with HTTP-aware developers. The HTTP 302 response code is used so often it has a convenience method, sendRedirect(), in the HttpServletResponse object. The sendRedirect() method takes one parameter, a string representing the new URL, and automatically sets the HTTP 302 status code with appropriate headers. Using the sendRedirect() method and a java.util.Hashtable, it is easy to create a Servlet for tracking link use. Save the code in Listing 2-6 as LinkTracker.java in the /WEB-INF/classes/com/jspbook directory of the jspbook Web Application. Deploy the Servlet to the /LinkTracker URL mapping.
Listing 2-6. LinkTracker.java
package com.jspbook;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class LinkTracker extends HttpServlet {
static private Hashtable links = new Hashtable();
String tstamp;
public LinkTracker() {
tstamp = new Date().toString();
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
String link = request.getParameter("link");
if (link != null && !link.equals("")) {
synchronized (links){
Integer count = (Integer) links.get(link);
if (count == null) {
links.put(link, new Integer(1));
}
else {
links.put(link, new Integer(1+count.intValue()));
}
}
response.sendRedirect(link);
}
else {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
request.getSession();
out.println("");
out.println("");
out.println("");
out.println("");
out.println("");
out.println("Links Tracked Since");
out.println(tstamp+":
");
if (links.size() != 0) {
Enumeration enum = links.keys();
while (enum.hasMoreElements()) {
String key = (String)enum.nextElement();
int count = ((Integer)links.get(key)).intValue();
out.println(key+" : "+count+" visits
");
}
}
else {
out.println("No links have been tracked!
");
}
out.println("");
out.println("");
}
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
doGet(request, response);
}
}
To complement the LinkTracker Servlet, some links are needed that use it. The links can be to any resource as long as they are encoded properly. Encoding the links is not difficult; it requires the real link be passed as the link parameter in a query string. Listing 2-7 is a simple HTML page that includes a few properly encoded links. Save the HTML as links.html in the base directory of the jspbook Web Application.
Listing 2-7. Some Links Encoded for the LinkTracker Servlet
Some good links for Servlets and JSP. Each link is directed through the LinkTracker Servlet. Click on a few and visit the LinkTracker Servlet.
After reloading the Web Application, browse to http://127.0.0.1/jspbook/links.html.
Each link is directed through the LinkTracker Servlet, which in turn directs a browser to visit the correct link. Before each redirection the LinkTracker Servlet logs the use of the link by keying the link URL to an Integer object in a Hashtable. If you browse directly to the LinkTracker Servlet, http://127.0.0.1/jspbook/LinkTracker, it displays information about links visited.
Response Redirection Translation Issues
Response redirection is a good tool to be aware of and works with any implementation of the Servlet API. However, there is a specific bug that tends to arise when using relative response redirection. For instance:
response.sendRedirect("../foo/bar.html");
would work perfectly fine when used in some Servlets but would not in others. The trouble comes from using the relative back, "../", to traverse back a directory. A JSP can correctly use this (assuming the browser translates the URL correctly), but the JSP can use it only if the request URL combined with the redirection ends up at the appropriate resource. For instance, if http://127.0.0.1/foo/bar.html is a valid URL, then http://127.0.0.1/foo/../foo/bar.html should also be valid. However, http://127.0.0.1/foo/foo/../foo/bar.html will not reach the same resource.
This may seem like an irrelevant problem, but we will soon introduce request dispatching that will make it clear why this is an issue. Request dispatching allows for requests to be forwarded on the server-sidemeaning the requested URL does not change, but the server-side resource that handles it can. Relative redirections are not always safe; " . . /" can be bad. The solution is to always use absolute redirections. Either use a complete URL such as:
response.sendRedirect("http://127.0.0.1/foo/bar.html");
Or use an absolute URL from the root, "/", of the Web Application.
response.sendRedirect("/for/bar.html")[24];
[24] Another option is to use the JavaServer Pages
Standard Tag Libraries redirect tag. The JSTL is covered in Chapter 7.
In cases where the Web application can be deployed to a non-root URL, the HttpServletRequest getContextPath() method should be used in conjunction:
response.sendRedirect(request.getContextPath()+"/foo/bar.html");
Further information about the HttpServletRequest object and use of the getContextPath() method is provided later in this chapter.
Auto-Refresh/Wait Pages
Another response header technique that is uncommon but helpful is to send a wait page or a page that will auto-refresh to a new page after a given period of time. This tactic is helpful in any case where a response might take an uncontrollable time to generate, or for cases where you want to ensure a brief pause in a response. The entire mechanism revolves around setting the Refresh response header[25]. The header can be set using the following:
[25] The Refresh header is not part of the HTTP 1.0 or HTTP 1.1 standards. It is an extension supported by Microsoft Internet Explorer, Netscape Navigator 4.x, and Mozilla-based clients.
response.setHeader("Refresh", "time; URL=url" );
Where "time" is replaced with the amount of seconds, the page should wait, and "url" is replaced with the URL that the page should eventually load. For instance, if it was desired to load http://127.0.0.1/foo.html after 10 seconds of waiting, the header would be set as so:
response.setHeader("Refresh", "10; URL=http://127.0.0.1/foo.html");
Auto-refreshing pages are helpful because they allow for a normal "pull" model, waiting for a client's request, to "push" content. A good practical use case would be a simple your-request-is-being-processed-page that after a few seconds refreshes to show the results of the response. The alternative (also the most commonly used approach) is to wait until a request is officially finished before sending back any content. This results in a client's browser waiting for the response, sometimes appearing as if the request might time-out and resulting in the user making a time-consuming request twice[26].
[26] Auto-refresh pages help tremendously reduce this problem, but you should also ensure the Web application accurately maintains state. Chapter 9 thoroughly covers state management.
Another practical use case for wait page would be slowing down a request, perhaps to better ensure pertinent information is seen by the user. For example, a wait page that showed either an advertisement or legal information before redirecting to the appropriately desired page.
It should be clear that there are several situations where the Refresh response header can come in handy. While it is not a standard HTTP 1.1 header, it is something that is considered a de facto standard[27].
[27] Be aware, however, that the Refresh and Redirect solutions shown here do have a downside. They both involved extra roundtrips from the client to the server. Roundtrips are expensive in terms of time and resources used, and a Web application should seek to minimize them.
HttpServletRequest
A client's HTTP request is represented by an HttpServletRequest object. The HttpServletRequest object is primarily used for getting request headers, parameters, and files or data sent by a client. However, the Servlet specification enhances this object to also interact with a Web Application. Some of the most helpful features include session management and forwarding of requests between Servlets.
Headers
HTTP headers set by a client are used to inform a server about what software the client is using and how the client would prefer a server send back requested information. From a Servlet, HTTP request headers can be accessed by calling the following methods:
-
getHeader(java.lang.String name): The getHeader() method returns the value of the specified request header as a string. If the request did not include a header of the specified name, this method returns null. The header name is case insensitive. You can use this method with any request header.
-
getHeaders(java.lang.String name): The getHeaders() method returns all the values of the specified request header as an Enumeration of String objects. Some headers, such as Accept-Language, can be sent by clients as several headers, each with a different value rather than sending the header as a comma-separated list. If the request did not include any headers of the specified name, this method returns an empty Enumeration object. The header name is case insensitive. You can use this method with any request header.
-
getHeaderNames(): The getHeaderNames() method returns an Enumeration of all the names of headers sent by a request. In combination with the getHeader() and getHeaders() methods, getHeaderNames() can be used to retrieve names and values of all the headers sent with a request. Some containers do not allow access of HTTP headers. In this case null is returned.
-
getIntHeader(java.lang.String name): The getIntHeader() method returns the value of the specified request header as an int. If the request does not have a header of the specified name, this method returns 1. If the header cannot be converted to an integer, this method throws a NumberFormatException.
-
getDateHeader(java.lang.String name): The getDateHeader() method returns the value of the specified request header as a long value that represents a Date object. The date is returned as the number of milliseconds since the epoch. The header name is case insensitive. If the request did not have a header of the specified name, this method returns 1. If the header cannot be converted to a date, the method throws an IllegalArgumentException.
HTTP request headers are very helpful for determining all sorts of information. In the later chapters HTTP request headers are used as the primary resource for mining data about a client. This includes figuring out what language a client would prefer, what type of Web browser is being used, and if the client can support compressed content for efficiency. For now it is helpful to understand that these headers exist, and to get a general idea about what type of information the headers contain. Listing 2-8 is a Servlet designed to do just that. Save the following code as ShowHeaders.java in the /WEB-INF/classes/com/jspbook directory of the jspbook Web Application.
Listing 2-8. ShowHeaders.java
package com.jspbook;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ShowHeaders extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("");
out.println("");
out.println("");
out.println("");
out.println("");
out.println("HTTP headers sent by your client:
");
Enumeration enum = request.getHeaderNames();
while (enum.hasMoreElements()) {
String headerName = (String) enum.nextElement();
String headerValue = request.getHeader(headerName);
out.print(""+headerName + ": ");
out.println(headerValue + "
");
}
out.println("");
out.println("");
}
}
Compile the Servlet and deploy it to the /ShowHeaders path of the jspbook Web Application. After reloading the Web application, browse to http://127.0.0.1/jspbook/ShowHeaders to see a listing of all the HTTP headers your browser sends (see Figure 2-11).

The preceding is a good example of the headers normally sent by a Web browser. They are fairly self-descriptive. You can probably imagine how these headers can be used to infer browser and internationalization information. Later on we will do just that[28].
