Cover | Table of Contents | Colophon
javax.servlet
and javax.servlet.http packages constitute this
Servlet API. The latest version of these classes is available for
download from http://java.sun.com/products/servlet/download.html. All
web servers that support servlets must
use these classes internally (although they could use an alternate
implementation), so generally this JAR file can also be found
somewhere within the distribution of your servlet-enabled web server.GET /intro.html HTTP/1.0
User-Agent: Mozilla/4.0 (compatible; MSIE 4.0; Windows 95) Accept: image/gif, image/jpeg, text/*, */*
User-Agent header provides information about
the client software, while the Accept header
specifies the
media (MIME) types that the client prefers to
accept. (We'll talk more about request headers in the context
of servlets in Chapter 4.) After the headers, the
client sends a blank line, to indicate the end of the header section.
The client can also send additional data, if appropriate for the
method being used, as it is with the POST method that we'll
discuss shortly. If the request doesn't send any data, it ends
with an empty line.javax.servlet
and javax.servlet.http.
The javax.servlet package contains classes and
interfaces to support generic, protocol-independent servlets. These
classes are extended by the classes in the
javax.servlet.http package to add HTTP-specific
functionality. The top-level package name is javax
instead of the familiar java, to indicate that the
Servlet API is an Optional Package (formerly
called a Standard Extension).javax.servlet.Servlet
interface. Most servlets implement
this interface by extending one of two special classes:
javax.servlet.GenericServlet
or
javax.servlet.http.HttpServlet
. A protocol-independent servlet should
subclass GenericServlet, while an HTTP servlet
should subclass HttpServlet, which is itself a
subclass of GenericServlet with added
HTTP-specific functionality.main( )
method. Instead,
certain methods of a servlet are invoked by the server in the process
of handling requests. Each time the server dispatches a request to a
servlet, it invokes the servlet's service( )
method.service(
)
method to handle requests as
appropriate for the servlet. The service( ) method
accepts two parameters: a request object and a response object. The
request object tells the servlet about the request, while the
response object is used to return a response. Figure 2-1 shows how a generic servlet handles requests.
service( ) method. Instead, it overrides
doGet(
)
to handle GET requests and
doPost( ) to handle POST requests. An HTTP servlet
can override either or both of these methods, depending on the type
of requests it needs to handle. The import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<HTML>");
out.println("<HEAD><TITLE>Hello World</TITLE></HEAD>");
out.println("<BODY>");
out.println("<BIG>Hello World</BIG>");
out.println("</BODY></HTML>");
}
}HttpServlet class and
overrides the doGet(
)
method
inherited from it. Each time the web server receives a
GET request for this
servlet, the server invokes this doGet( ) method,
passing it an HttpServletRequest object and an
HttpServletResponse object.HttpServletRequest
represents the client's
request. This object gives a servlet access to information about the
client, the parameters for this request, the HTTP headers passed
along with the request, and so forth. Chapter 4
explains the full capabilities of the request object. For this
example, we can completely ignore it. After all, this servlet is
going to say "Hello World" no matter what the request!HttpServletResponse
represents the servlet's
response. A servlet can use this
object to return data to the client. This data can be of any content
type, though the type should be specified as part of the response. A
servlet can also use this object to set
HTTP response
headers. Chapter 5 and Chapter 6, explain everything a servlet can do as part
of its response.index.html feedback.jsp images/banner.gif images/jumping.gif WEB-INF/web.xml WEB-INF/lib/bhawk4j.jar WEB-INF/classes/MyServlet.class WEB-INF/classes/com/mycorp/frontend/CorpServlet.class WEB-INF/classes/com/mycorp/frontend/SupportClass.class
ClassLoader objects are designed to load a class
just once. To get around this limitation and load servlets again and
again, servers use custom class loaders that load servlets from
special directories such as WEB-INF/classes.ClassCastException
to be thrown when the servlets shared
information (because a class loaded by one class loader is not the
same as the class loaded by a second class loader, even if the
underlying class data is identical). Beginning in
Servlet API 2.2, it's mandated that
these
ClassCastException
problems must not occur for
servlets inside the same context. So most server implementations now
load each web application context within a single class loader and
use a new class loader to reload the entire context when any servlet
in the context changes. Since all servlets and support classes in the
context always have the same class loader, there will be no
unexpected init( ) and destroy( ) methods.
The server calls a servlet's init( ) method
after the server constructs the servlet instance and before the
servlet handles any requests. The server calls the destroy(
) method after the servlet has been taken out of service
and all pending requests to the servlet have completed or timed
out.
init( ) method may be called at any of these
times:service( ) method is invokedinit( ) is guaranteed to be called
and completed before the servlet handles its first request.init( ) method is typically used to perform
servlet
initialization—creating or loading
objects that are used by the servlet in the handling of its requests.
During the init( ) method a servlet may want to
read its initialization (init)
parameters. These parameters are
given to the servlet itself and are not associated with any single
request. They can specify initial values, like where a counter should
begin counting, or default values, perhaps a template to use when not
specified by the request. Init parameters for a servlet are set in
the web.xml
deployment descriptor, although some
servers have graphical interfaces for modifying this file. See Example 3-3.<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<servlet>
<servlet-name>
counter
</servlet-name>
<servlet-class>
InitCounter
</servlet-class>
<init-param>
<param-name>
initial
</param-name>
<param-value>
1000
</param-value>
<description>
The initial value for the counter <!-- optional -->
</description>
</init-param>
</servlet>
</web-app>javax.servlet.SingleThreadModel interface. This is
an empty, "tag" interface that defines no methods or
variables and serves only to flag the servlet as wanting the
alternate lifecycle.SingleThreadModel servlet
must guarantee, according to the Servlet API documentation,
"that no two threads will execute concurrently in the
servlet's
service
method." To accomplish this, each thread uses a free servlet
instance from the pool, as shown in Figure 3-3.
Thus, any servlet implementing SingleThreadModel
can be considered thread safe and isn't required to synchronize
access to its instance variables. Some servers allow the number of
instances per pool to be configured, others don't. Some servers
use pools with just one instance, causing behavior identical to a
synchronized service( ) method.
SingleThreadModel lifecycle is pointless for a
counter or other servlet application that requires central state
maintenance. The lifecycle can be of some use, however, in avoiding
synchronization while still performing
efficient request handling.SingleThreadModel and having one
"connection" instance variable per servlet, a servlet can
easily handle concurrent requests because each instance has its own
connection. The skeleton code is shown in Example 3-6.init( ) performs
continuous work while request-handling threads display the current
status with doGet( ). It's a similar
technique to that used in animation applets, where one thread changes
the picture and another paints the display.import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class PrimeSearcher extends HttpServlet implements Runnable {
long lastprime = 0; // last prime found
Date lastprimeModified = new Date(); // when it was found
Thread searcher; // background search thread
public void init() throws ServletException {
searcher = new Thread(this);
searcher.setPriority(Thread.MIN_PRIORITY); // be a good citizen
searcher.start();
}
public void run() {
// QTTTBBBMMMTTTOOO
long candidate = 1000000000000001L; // one quadrillion and one
// Begin loop searching for primes
while (true) { // search forever
if (isPrime(candidate)) {
lastprime = candidate; // new prime
lastprimeModified = new Date(); // new "prime time"
}
candidate += 2; // evens aren't prime
// Between candidates take a 0.2 second break.
// Another way to be a good citizen with system resources.
try {
searcher.sleep(200);
}
catch (InterruptedException ignored) { }
}
}
private static boolean isPrime(long candidate) {
// Try dividing the number by all odd numbers between 3 and its sqrt
long sqrt = (long) Math.sqrt(candidate);
for (long i = 3; i <= sqrt; i += 2) {
if (candidate % i == 0) return false; // found a factor
}
// Wasn't evenly divisible, so it's prime
return true;
}
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/plain");
PrintWriter out = res.getWriter();
if (lastprime == 0) {
out.println("Still searching for first prime...");
}
else {
out.println("The last prime discovered was " + lastprime);
out.println(" at " + lastprimeModified);
}
}
public void destroy() {
searcher.stop();
}
}PrimeSearcher start searching for primes as
quickly as possible, we can configure the servlet's web
application to load the servlet at server start. This is accomplished
by adding the <load-on-startup> tag to the
<servlet> entry of the
deployment descriptor, as shown in Example 3-8.<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<servlet>
<servlet-name>
ps
</servlet-name>
<servlet-class>
PrimeSearcher
</servlet-class>
<load-on-startup/>
</servlet>
</web-app>PrimeSearcher under the registered name
ps and init( ) the servlet
during the server's startup sequence. The servlet can then be
accessed at the URL /servlet/ps. Note that the
servlet instance handling the URL
/servlet/PrimeSearcher is not loaded at startup.<load-on-startup> tag shown in Example 3-8 is empty. The tag can also contain a positive
integer indicating the order in which the servlet should be loaded
relative to other servlets in the context. Servlets with lower
numbers are loaded before those with higher numbers. Servlets with
negative values or noninteger values may be loaded at any time in the
startup sequence, with the exact order depending on the server. For
example, the web.xml shown in Example 3-9 guarantees first is loaded
before second, while anytime
could be loaded anytime during the server startup.<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<servlet>
<servlet-name>
first
</servlet-name>
<servlet-class>
First
</servlet-class>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet>
<servlet-name>
second
</servlet-name>
<servlet-class>
Second
</servlet-class>
<load-on-startup>20</load-on-startup>
</servlet>
<servlet>
<servlet-name>
anytime
</servlet-name>
<servlet-class>
Anytime
</servlet-class>
<load-on-startup/>
</servlet>
</web-app>doGet( ) method. And that's almost true. The
full truth is that not every request really needs to invoke
doGet(
)
. For example, a web browser that
repeatedly accesses PrimeSearcher should need to
call doGet( ) only after the searcher thread has
found a new prime. Until that time, any call to doGet(
) just generates the same page the user has already seen, a
page probably stored in the browser's cache. What's
really needed is a way for a servlet to report when its output has
changed. That's where the getLastModified(
)
method comes in.Last-Modified
header. An example
Last-Modified header value might be:Tue, 06-May-98 15:41:02 GMT
If-Modified-Since header. Its structure is
identical to the Last-Modified header:Tue, 06-May-98 15:41:02 GMT
Last-Modified time of the page when it was last
downloaded by the browser. The server can read this header and
determine if the file has changed since the given time. If the file
has changed, the server must send the newer content. If the file
hasn't changed, the server can reply with a simple, short
response that tells the browser the page has not changed, and it is
sufficient to redisplay the cached version of the document. For those
familiar with the details of HTTP, this response is the
304 Not Modified status code.getLastModified(
)
method
can be used, with a little trickery, to
help manage a server-side cache of the servlet's output.
Servlets implementing this trick can have their output caught and
cached on the server side, then automatically resent to clients as
appropriate according to the servlet's
getLastModified( ) method. This can greatly speed
servlet page generation, especially for servlets whose output takes a
significant time to produce but changes only rarely, such as servlets
that display database results.com.oreilly.servlet.CacheHttpServlet
instead of HttpServlet
getLastModified(HttpServletRequest)
method as usualCacheHttpServlet
. It's a guestbook
servlet that displays user-submitted comments. The servlet stores the
user comments in memory as a Vector of
GuestbookEntry objects. We'll see a version
of this servlet running off a database in Chapter 9. For now, to simulate reading from a slow
database, the display loop has a half-second delay per entry. As the
entry list gets longer, the rendering of the page gets slower.
However, because the servlet extends
CacheHttpServlet, the rendering only has to occur
during the first GET request after a new comment is added. All later
GET requests send the cached response. Sample output is shown in
Figure 3-4.import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.oreilly.servlet.CacheHttpServlet;
public class Guestbook extends CacheHttpServlet {
private Vector entries = new Vector(); // User entry list
private long lastModified = 0; // Time last entry was added
// Display the current entries, then ask for a new entry
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
printHeader(out);
printForm(out);
printMessages(out);
printFooter(out);
}
// Add a new entry, then dispatch back to doGet()
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
handleForm(req, res);
doGet(req, res);
}
private void printHeader(PrintWriter out) throws ServletException {
out.println("<HTML><HEAD><TITLE>Guestbook</TITLE></HEAD>");
out.println("<BODY>");
}
private void printForm(PrintWriter out) throws ServletException {
out.println("<FORM METHOD=POST>"); // posts to itself
out.println("<B>Please submit your feedback:</B><BR>");
out.println("Your name: <INPUT TYPE=TEXT NAME=name><BR>");
out.println("Your email: <INPUT TYPE=TEXT NAME=email><BR>");
out.println("Comment: <INPUT TYPE=TEXT SIZE=50 NAME=comment><BR>");
out.println("<INPUT TYPE=SUBMIT VALUE=\"Send Feedback\"><BR>");
out.println("</FORM>");
out.println("<HR>");
}
private void printMessages(PrintWriter out) throws ServletException {
String name, email, comment;
Enumeration e = entries.elements();
while (e.hasMoreElements()) {
GuestbookEntry entry = (GuestbookEntry) e.nextElement();
name = entry.name;
if (name == null) name = "Unknown user";
email = entry.email;
if (name == null) email = "Unknown email";
comment = entry.comment;
if (comment == null) comment = "No comment";
out.println("<DL>");
out.println("<DT><B>" + name + "</B> (" + email + ") says");
out.println("<DD><PRE>" + comment + "</PRE>");
out.println("</DL>");
// Sleep for half a second to simulate a slow data source
try { Thread.sleep(500); } catch (InterruptedException ignored) { }
}
}
private void printFooter(PrintWriter out) throws ServletException {
out.println("</BODY>");
}
private void handleForm(HttpServletRequest req,
HttpServletResponse res) {
GuestbookEntry entry = new GuestbookEntry();
entry.name = req.getParameter("name");
entry.email = req.getParameter("email");
entry.comment = req.getParameter("comment");
entries.addElement(entry);
// Make note we have a new last modified time
lastModified = System.currentTimeMillis();
}
public long getLastModified(HttpServletRequest req) {
return lastModified;
}
}
class GuestbookEntry {
public String name;
public String email;
public String comment;
}$port = $ENV{'SERVER_PORT'};$port is an untyped variable. A CGI program
written in C calls:char *port = getenv("SERVER_PORT");port is a pointer to a character string. The
chance for accidental errors is high. The environment variable name
could be misspelled (it happens often enough) or the datatype might
not match what the environment variable returns.int port = req.getServerPort()
init( ) to set initial or
default values for a servlet or to customize the servlet's
behavior in some way. Init parameters are more fully explained in
Chapter 3.getInitParameter(
)
method for access to its init parameters:public String ServletConfig.getInitParameter(String name)
null if it does not exist. The return value is
always a single String. It is up to the servlet to
interpret the value.GenericServlet
class implements the
ServletConfig interface and thus provides direct
access to the getInitParameter( ) method. This
means the method can be called like this:public void init() throws ServletException {
String greeting = getInitParameter("greeting");
}establishConnection(
)
method to abstract
away the details of JDBC, as shown in Example 4-1.java.sql.Connection con = null;
public void init() throws ServletException {
String host = getInitParameter("host");
int port = Integer.parseInt(getInitParameter("port"));
String db = getInitParameter("db");
String user = getInitParameter("user");
String password = getInitParameter("password");
String proxy = getInitParameter("proxy");
con = establishConnection(host, port, db, user, password, proxy);
}ServletContext
object in which
it executes. Before API 2.2, the ServletContext
was generally thought of as a reference to the server itself. Since
API 2.2 the rules have changed and there now must be a different
ServletContext for each web application on the
server. The ServletContext has become a reference
to the web application, not a reference to the server. For simple
server queries, there's not much difference.ServletRequest
object passed to the servlet and
three that are called from the ServletContext
object in which the servlet is executing.getServerName(
)
and getServerPort(
), respectively:public String ServletRequest.getServerName() public int ServletRequest.getServerPort()
ServletRequest
because the values can change for different requests if the server
has more than one name (a technique called virtual
hosting). The returned name might be something like
www.servlets.com while the returned port might be
something like 8080.getServerInfo(
)
and getAttribute(
) methods of ServletContext provide
information about the server software and its attributes:public String ServletContext.getServerInfo() public Object ServletContext.getAttribute(String name)
getServerInfo( ) returns the name and version of
the server software, separated by a slash. The string returned might
be something like getRemoteAddr(
)
and getRemoteHost(
) to retrieve the IP address and hostname of the client
machine, respectively:public String ServletRequest.getRemoteAddr() public String ServletRequest.getRemoteHost()
String objects. The
information comes from the socket that connects the server to the
client, so the remote address and hostname may be that of a
proxy server.
An example remote address might be 192.26.80.118
while an example remote host might be
dist.engr.sgi.com.java.net.InetAddress object using
InetAddress.getByName(
)
:InetAddress remoteInetAddress = InetAddress.getByName(req.getRemoteAddr());