Introduction to Java Servlets

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 a 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:


   <context-param>
      <param-name>DBINFO</param-name>
      <param-value>oracle,127.0.0.1,1521,test,2Rbpg+ek=,TESTDB</param-value>
      <description>Database connection info</description>
   </context-param>

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.



Comments

David Keener By dkeener on Monday, March 07, 2011 at 09:40 AM EST

Finally got this article back on-line. It got missed somehow when I ported all of the content into the new Rails-based edition of KeenerTech a few years back.


Leave a Comment

Comments are moderated and will not appear on the site until reviewed.

(not displayed)