|
A servlet is an application that uses the Java Servlet API (application
programming interface) to deliver dynamically generated content via a web server. More
technically, the Java Servlet API defines the interactions between a web container and a
Java application, where a web container is the logical component of a web server that
communicates with Java entities such as servlets.
This article will demonstrate how to code servlets using the Java
Servlet API. The basic servlet code is applicable across a variety of web containers,
including the widely available (and free) Tomcat, WebLogic and others.
Java Servlet API
The API is contained in the javax.servlet package
hierarchy. The API was originally created in 1997 with version 1.0, but there was no
formal specification until version 2.1 arrived in 1998. As of May 2006, the current
version of the specification is 2.5.
A servlet is basically a Java class that extends the
javax.servlet.HttpServlet class. Within this class, there
are really only five methods that a servlet developer needs to know about:
public void init(ServletConfig config)
This method is called once when the servlet is first loaded by the container, before
the servlet can even process its first request.
public void service(ServletRequest request, ServletResponse
response)
This method is called whenever a servlet request needs to be processed. If the
request represents an HTTP GET request (such as a normal request for a web page),
then the doGet method will be called. If the request represents an HTTP
POST request (such as the submission of a form), then the doPost method
will be called. Multiple threads (one per request) can execute this method in
parallel, so it must be thread-safe.
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
This method handles HTTP GET requests. It may be called by multiple threads, so
it must be coded in a thread-safe manner.
public void doPost(HttpServletRequest request,
HttpServletResponse response)
This method handles HTTP POST requests, which generally represent submissions from
a form. It may be called by multiple threads, so it must be coded in a
thread-safe manner.
public void destroy()
This method is called once just before the servlet is unloaded by the container
and taken out of service.
To create a servlet, developers extend the javax.servlet.HttpServlet
class, overriding the above methods as needed to implement their desired functionality. In
practice, developers should never override the service method; they should instead
provide implementations for the doGet or doPost methods. Initialization tasks,
if any, can be performed in the init method. Likewise, cleanup tasks, if any, can be
performed in the destroy method.
It sounds more complicated than it really is. My guess is that 98% of
servlets only implement one method, either the doGet or doPost methods.
A Simple Servlet
OK, it's time to create a really simple servlet. This servlet is going
to respond to a request by simply returning an HTML web page with the text "Hello, World!"
as the page content. The servlet is shown in Listing 1 below:
Listing 1: A Simple Servlet
package com.KeenerTech.Servlets;
import java.io.*;
import java.text.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloServlet extends HttpServlet
{
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello World!</h1>");
// We'll be inserting more code here
out.println("</body>");
out.println("</html>");
}
}
|
The HelloServlet class extends the HttpServlet class, providing an
implementation for the doGet method. This method generates the HTML that will
be sent back to the requester.
What it's really doing is providing content for the
response object that will be sent back. To eliminate any possible confusion, the
method calls the setContentType method of the HttpServletResponse parameter
to ensure that the requester knows that what is being provided is HTML content (as
opposed to XML, a PDF file, an image or other types of content).
Next, the method calls the getWriter method to get access
to a PrintWriter object for the response. This allows content to be dynamically
inserted into the response object.
Information Available to Servlets
The doGet and doPost methods both accept the same
parameters: HttpServletRequest and HttpServletResponse, generally referred to as
simply the request and response objects. The servlet also has access to the
System object, which provides information about the web server
environment. The request object provides the developer with
the capability to access a wide range of information, including cookies, URL
parameters, data items posted from a form, HTTP headers and local (to the server)
environment variables. Likewise, the response object provides developers with
the capability to tailor the output that will be sent back to the requester.
Listing 2 shows how to access information available in the
request object. Add this code to the doGet method of the simple servlet from
the previous section. First, the code shows the output of many of the methods
provided by the request object. For example, the getMethod method shows
whether the request is a GET or POST request. Not all of these values will be
provided by every web server.
Next, the code checks for any parameters that may have been
passed to the servlet. Parameters can either be passed as part of the URL or
posted from a form. An example of passing a URL parameter to a servlet is
shown below, with the test parameter:
http://127.0.0.1/servlet/hello?test=53
There are some things to remember about accessing
parameters from the request object. As mentioned before, parameters can
appear as part of a URL or be posted from a form. It's also possible for a
form to have multiple fields with the same name, i.e. - it's possible
for a parameter to have multiple values. Accordingly, you can use
getParameterNames to get a list of the available parameters. Then
you can use getParameterValues to get a string array containing
all of the values for that parameter.
Alternately, if you know that a
parameter will only have one value, you can use the getParameter
method (note that the method name is singular) to retrieve just one
value for a parameter (if you're wrong, and there are multiple values,
only the first one will be returned).
The getHeaderNames method can be used to retrieve
HTTP headers associated with the request. This information isn't commonly
needed for a servlet, but it's still available. Cookies are also accessible
from the request object.
Listing 2: Accessing Information in a Servlet
out.println("<pre>");
out.println("getAuthType: " + request.getAuthType());
out.println("getCharacterEncoding: " + request.getCharacterEncoding());
out.println("getContentLength: " + request.getContentLength());
out.println("getContentType: " + request.getContentType());
out.println("getMethod: " + request.getMethod());
out.println("getPathInfo: " + request.getPathInfo());
out.println("getPathTranslated: " + request.getPathTranslated());
out.println("getProtocol: " + request.getProtocol());
out.println("getQueryString: " + request.getQueryString());
out.println("getRemoteAddr: " + request.getRemoteAddr());
out.println("getRemoteHost: " + request.getRemoteHost());
out.println("getRemoteUser: " + request.getRemoteUser());
out.println("getRequestURI: " + request.getRequestURI());
out.println("getScheme: " + request.getScheme());
out.println("getServerName: " + request.getServerName());
out.println("getServerPort: " + request.getServerPort());
out.println("getServletPath: " + request.getServletPath());
out.println();
out.println("Parameters:");
Enumeration paramNames = request.getParameterNames();
while (paramNames.hasMoreElements())
{
String name = (String) paramNames.nextElement();
String[] values = request.getParameterValues(name);
out.println(" " + name + ":");
for (int i = 0; i < values.length; i++)
{
out.println(" " + values[i]);
}
}
out.println();
out.println("Request Headers:");
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements())
{
String name = (String) headerNames.nextElement();
String value = request.getHeader(name);
out.println(" " + name + " : " + value);
}
out.println();
out.println("Cookies:");
Cookie[] cookies = request.getCookies();
for (int i = 0; i < cookies.length; i++)
{
String name = cookies[i].getName();
String value = cookies[i].getValue();
out.println(" " + name + " : " + value);
}
out.println();
out.println("Environment Variables:");
Properties objProperties = System.getProperties();
Enumeration varNames = objProperties.propertyNames();
while (varNames.hasMoreElements())
{
String name = (String) varNames.nextElement();
String value = objProperties.getProperty(name);
out.println(" " + name + " : " + value);
}
out.println("</pre>");
|
The last section of code retrieves the names and values of
environment variables that are defined in the environment in which the web server
is running. Environment variables are not available from the request object,
but they are available from the System object.
The output of a sample run of the servlet using Tomcat is
shown below, and clearly illustrates the type of information that is accessible
from within a servlet. In fact, I've found this servlet, and the
information that it provides, useful for debugging web server configuration
issues, as it answers the question: "Now what information is my application
actually getting?"
Output 1: Sample Output from the Servlet
getAuthType: null
getCharacterEncoding: null
getContentLength: -1
getContentType: null
getMethod: GET
getPathInfo: null
getPathTranslated: null
getProtocol: HTTP/1.1
getQueryString: null
getRemoteAddr: 10.181.102.113
getRemoteHost: 10.181.102.113
getRemoteUser: null
getRequestURI: /RM/hello
getScheme: http
getServerName: 127.0.0.1
getServerPort: 80
getServletPath: /hello
Parameters:
test : 53
Request Headers:
accept : */*
accept-language : en-us
accept-encoding : gzip, deflate
user-agent : Mozilla/4.0 (compatible; MSIE 6.0;
Windows NT 5.1; SV1; .NET CLR 2.0.50727)
host : 127.0.0.1:80
connection : Keep-Alive
cookie : JSESSIONID=59C9F616C6D53; ....
Cookies:
JSESSIONID : 3579D0413DEC693EB9659C9F616C6D53
RSP_COOKIE : type=24&name=RGFMQ%3D%3D&stype=0
rsi_segs : J05532_10365 ....
aolweatherlocation : 19903
trzip : 19903
lk_city : washington
Environment Variables:
java.version : 1.4.0.05
sun.cpu.isalist : pa2.0 pa1.2 pa1.1 pa1.0
java.endorsed.dirs : /home/rm12/tomcat/bin: ...
java.specification.vendor : Sun Microsystems Inc.
java.awt.graphicsenv : sun.awt.X11GraphicsEnvironment
sun.io.unicode.encoding : UnicodeBig
java.runtime.name : Java(TM) 2 Runtime Environment,
Standard Edition
java.specification.version : 1.4
user.home : /home/dkeener
java.vm.info : mixed mode
catalina.home : /home/dkeener/tomcat
user.dir : /home/dkeener
java.io.tmpdir : /home/dkeener/tomcat/temp
java.ext.dirs : /opt/java1.4/jre/lib/ext
java.class.version : 48.0
sun.cpu.endian : big
sun.arch.data.model : 32
sun.java2d.fontpath : ...
java.awt.headless : true
java.class.path : /opt/java1.4/lib/tools.jar: ...
os.name : HP-UX
java.vendor.url.bug : http://www.hp.com/go/Java
sun.boot.class.path :
/home/dkeener/tomcat/bin/tomcat-jni.jar: ...
user.timezone : America/New_York
java.vm.name : Java HotSpot(TM) Server VM
java.util.prefs.PreferencesFactory :
java.util.prefs.FileSystemPreferencesFactory
java.vm.specification.name : Java Virtual Machine Spec
catalina.base : /home/dkeener/tomcat
sun.os.patch.level : unknown
java.vm.vendor : Hewlett-Packard Company
user.language : en
java.library.path : /opt/java1.4/jre/lib/PA_RISC2.0 ...
path.separator : :
java.awt.printerjob : sun.print.PSPrinterJob
java.vm.specification.version : 1.0
file.separator :
java.vm.specification.vendor : Sun Microsystems Inc.
java.runtime.version : 1.4.0.05-030522-20:30
catalina.useNaming : true
java.vendor : Hewlett-Packard Co.
file.encoding.pkg : sun.io
java.naming.factory.url.pkgs : org.apache.naming
java.naming.factory.initial :
org.apache.naming.java.javaURLContextFactory
java.vendor.url : http://www.hp.com/go/Java
os.version : B.11.00
os.arch : PA_RISC2.0
java.specification.name : Java Platform API Specification
java.home : /opt/java1.4/jre
line.separator :
sun.boot.library.path : /opt/java1.4/jre/lib/PA_RISC2.0
file.encoding : 8859_1
user.name : dkeener
java.vm.version : 1.4 1.4.0.05-21:51-PA_RISC2.0
|
Thread-Safe Code
At any given time, multiple threads could be executing the doGet
or doPost methods. When coding a servlet, it's necessary to code a servlet in a
thread-safe manner. Great. But what does that mean?
Well, it's actually pretty simple. Basically, this means no global
variables, i.e. - no class-level variables that are shared across instances of your
class because multiple threads might access the shared variable at the same time.
You'll have to code with an eye towards making sure that your methods are
essentially self-contained.
If your methods absolutely must access shared elements, Java does
support locking features to serialize access to objects. However, that's generally
not required, and also somewhat out-of-scope for this article. I've never had to
worry about locking in the servlets that I've done.
Configuration Settings
Well, you've seen how create a simple servlet that sends HTML
back to the requester. But you probably want to do more than that. It would be
nice to be able to make some configuration settings available to the servlet.
As a prime example, you might want to have database connection information specified
in way that is accessible throughout an entire web application, including from
within servlets.
When it comes to configuration settings, you're entering an area
that may potentially differ between web containers. This section describes how to
set up configuration settings using Tomcat; the details are likely to be similar
for other web containers.
Servlets have access to a ServletContext object that, in
turn, provides access to application-specific settings. For Tomcat, configuration
settings are defined in a file called "web.xml". The ServletContext class
provides the getInitParameter method, which allows configuration settings
to be retrieved. Within a servlet, the following code can be used to access a
user-defined "DBINFO" parameter defined in the "web.xml" file.
String dbinfo = getServletContext().getInitParameter("DBINFO");
if (dbinfo != null)
{
// Do something with the value
}
Within the "web.xml" file for the application, configuration
settings can be defined using the <context-param>
construct, as shown below:
In this example, the context parameter is named "DBINFO". This
parameter has a value that includes a comma-delimited
set of data items related to establishing a database connection. Specifically, the list
includes the type of database, the host where the database can be found, the name
of the user account for the database, the encrypted account password and the
name of the database.
Again, this is only an example. Clearly, using this technique, you can
define whatever configuration settings your web application, and your servlets, will
need.
Conclusion
The Java Servlet API provides a powerful and effective way to
dynamically generate and deliver content to users. The API ensures that detailed
information about each request is available to developers and provides extensive
capabilities for tailoring the response that is sent back.
The API also provides a useful platform for other Java technologies,
notably Java Server Pages (JSP). With JSP pages, users integrate Java functionality into
HTML templates, which are then automatically compiled into Java servlets.
|