Servlets in Apache Tomcat and BEA Systems' WebLogic Server

Deploy servlets and Web applications in two popular application servers

In "Develop N-Tier Applications Using J2EE" (JavaWorld, December 1, 2000), I gave an introduction to Java 2 Enterprise Edition (J2EE) and an overview of the technologies it includes. In this article, we delve into a little more detail with one of the more widely used J2EE technologies: servlets. We begin with a brief recap of servlet development fundamentals, then show how to build a Web application to house them. We discuss the use of Web Application Archives (WARs), then illustrate deployment of the servlet and Web application in Apache Tomcat. In addition, we'll look at deploying the same servlet and Web application in one of the most widely-used servlet containers -- BEA's WebLogic Server.

Develop servlets

Servlets were designed to allow for extension of a server providing any service. Currently, however, only HTTP and JSP page servlets are supported. In the future, a developer may be able to extend an FTP server or an SMTP server using servlets.

Generic servlets

A servlet extends a server's functionality by offering a specific service within a well-defined framework. It is a small piece of Java code -- often just a single class -- that provides a specific service. For example, an HTTP servlet may provide a bank customer with details of her recent deposits and withdrawals. Another HTTP servlet could allow a customer to view, and even edit, his mailing address.

To deploy a servlet usually requires configuration of the hosting server application. When the server encounters a particular type of request, it invokes the servlet, passing to it details about the request and a response object for returning the result.

All servlets implement the javax.servlet.Servlet interface either directly -- in the case of generic servlets -- or indirectly, in the case of HTTP or JSP servlets. The javax.servlet.Servlet interface's important methods include:

  • init(): defines any initialization code that should be executed when the servlet loads into memory.
  • service(): the main method called when the servlet receives a service request. It defines the bulk of the processing logic provided by the servlet.
  • destroy(): defines any clean-up code required before removing the servlet from memory.

When the servlet container first loads a servlet it invokes the servlet's init() method to initialize the servlet. Then, as requests are made to execute the servlet, the servlet container repeatedly invokes the servlet's service() method to provide the required service. Finally, when the servlet container no longer needs the servlet, it invokes the servlet's destroy() method and unloads it from memory. Note that during the lifetime of a single servlet instance, the init() and destroy() methods will be invoked only once, whereas the service() method will be invoked many times -- once each time a request is made to execute the servlet.

JSP Page servlets are mostly of interest to implementers of JSP containers and are beyond the scope of this article. Rather, we now go on to look specifically at HTTP servlets.

HTTP servlets

HTTP servlets extend the javax.servlet.http.HttpServlet class. This class extends the javax.servlet.GenericServlet class, which in turn implements javax.servlet.Servlet. The HttpServlet class overrides the service() method in such a way as to handle the different types of HTTP requests: DELETE, GET, OPTIONS, POST, PUT, and TRACE. For each of these request types the HttpServlet class provides a corresponding doXXX() method.

Although you can override the service() method in your servlet class, there is rarely any need to do so. More likely you'll want to override individual doXXX() methods. If you do override the service() method, be aware that the default doXXX() methods will be called only if you call super.service or invoke them directly.

For most applications you will want to override the doPost() and doGet() methods, since one of these usually handles data submitted by a user from an HTML FORM.

To summarize, when writing your HTTP servlets you should:

  1. Import -- at a minimum -- the servlet classes:
    • javax.servlet.ServletException
    • javax.servlet.http.HttpServlet
    • javax.servlet.http.HttpServletRequest
    • javax.servlet.http.HttpServletResponse
  2. Make the class public
  3. Have the class extend HttpServlet
  4. Override the appropriate doXXX() method(s) to implement your request/response logic

We illustrate these with a simple example below.

A sample servlet: RequestDetails

In the example below we illustrate a simple HTTP servlet. The first line simply defines what package the servlet belongs to. The next code block imports the classes used by this servlet. Then comes the servlet class definition. As you can see, the RequestDetails class extends HttpServlet.

The body of RequestDetails defines two methods: doGet() and doPost(). The doGet() method defines the primary functionality of this servlet. The doPost() method simply calls doGet(). The servlet therefore handles both get and post requests in the same way.

The doGet() method constructs an HTML page containing details of the HTTP request sent to the server. Note the method's first two lines. The first line sets the content type for the response. In general, you will be constructing an HTML page, in which case the content type should be set to text/html. The second line in the doGet() method obtains a reference to a PrintWriter output stream. All output to be returned to the client is then written to that output stream:

package org.stevengould.javaworld;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * This class provides a simple example of a servlet, and
 * illustrates some of the information available from an
 * HTTP request.
 */
public class RequestDetails extends HttpServlet
  {
  /**
   * Handler for all GET requests. We simply dump out the
   * request header information, followed by the body of
   * the request.
   * @param request the HTTP request submitted to the
   *        server for processing. It is this object that
   *        contains the details of the requested URL, and
   *        it is the details of this object that we
   *        output as a response.
   * @param response the response object to be used to
   *        send a result back to the client.
   * @exception IOException thrown if a communications
   *        error occurs.
   * @exception ServletException if the GET request could
   *        could not be handled
   */
  public void doGet( HttpServletRequest request,
                     HttpServletResponse response )
    throws IOException, ServletException
    {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("<html>");
    out.println("<head>");
    out.println("<title>Request Details Example</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<h3>HTTP Request Header</h3>");
    out.println("<table border='1'>");
    out.println(" <tr bgcolor=#e0e0e0>");
    out.println("  <td><strong>Name</strong></td>");
    out.println("  <td><strong>Value</strong></td>");
    out.println(" </tr>");
    Enumeration e = request.getHeaderNames();
    while (e.hasMoreElements())
      {
      String name = (String)e.nextElement();
      String value = request.getHeader(name);
      out.println(" <tr>");
      out.println("  <td bgcolor=#e0e0e0>"+name+"</td>");
      out.println("  <td>"+value+"</td>");
      out.println(" </tr>");
      }
    out.println("</table>");
    out.println("<h3>HTTP Request Information</h3>");
    out.println("<table border='1'>");
    out.println(" <tr bgcolor=#e0e0e0>");
    out.println("  <td><strong>Name</strong></td>");
    out.println("  <td><strong>Value</strong></td>");
    out.println(" </tr>");
    out.println(" <tr>");
    out.println("  <td bgcolor=#e0e0e0>Method:</td>");
    out.println("  <td>"+request.getMethod()+"</td>");
    out.println(" </tr>");
    out.println(" <tr>");
    out.println("  <td bgcolor=#e0e0e0>Request URI:</td>");
    out.println("  <td>"+request.getRequestURI()+"</td>");
    out.println(" </tr>");
    out.println(" <tr>");
    out.println("  <td bgcolor=#e0e0e0>Protocol:</td>");
    out.println("  <td>"+request.getProtocol()+"</td>");
    out.println(" </tr>");
    out.println(" <tr>");
    out.println("  <td bgcolor=#e0e0e0>PathInfo:</td>");
    out.println("  <td>"+request.getPathInfo()+"</td>");
    out.println(" </tr>");
    out.println(" <tr>");
    out.println("  <td bgcolor=#e0e0e0>Remote Address:</td>");
    out.println("  <td>"+request.getRemoteAddr()+"</td>");
    out.println(" </tr>");
    out.println("</table>");
    out.println("<hr>");
    Date date = new Date();
    out.println("<p align=center>Page generated on "+date);
    out.println("</body>");
    out.println("</html>");
    out.close();
    }
  /**
   * For POST requests, we will simply perform the same
   * operations as for GET requests. The best way to do this
   * is to simply invoke the doGet() method with the appropriate
   * parameters.
   * @param request the HTTP request submitted to the server
   *        for processing. It is this object that contains
   *        the details of the requested URL, and it is the
   *        details of this object that we output as a
   *        response.
   * @param response the response object to be used to send a
   *        result back to the client.
   */
  public void doPost( HttpServletRequest request,
                      HttpServletResponse response )
    throws IOException, ServletException
    {
    doGet(request, response);
    }
  }

Compile the servlet

Since servlets use Java extension classes (classes that are not part of the core JDK) you must be sure to correctly set up your CLASSPATH before attempting to compile any servlet. The Java compiler needs to be able to find the javax.servlet.* packages and classes. Other than that, compilation proceeds just as for any other Java program:

  javac RequestDetails.java

Create a Web application

Now that you have created the servlet, you need to think about deployment. The Java Servlet 2.2 specification introduced at least two significant new features: a Web application and a Web application archive (WAR). According to the Servlet 2.2 specifications:

A Web application is a collection of servlets, HTML pages, classes, and other resources that can be bundled and run on multiple containers from multiple vendors.

WARs are simply Java archives of a Web application with a different extension to differentiate them from commonly used JARs.

Before the Servlet 2.2 specifications, servlet deployment differed significantly between servlet containers -- also previously called servlet engines. The 2.2 specifications standardized deployment across containers, thus taking Java code portability one step further. We shall see the power of this later in this article, when we illustrate the creation of a single Web application that is then deployed on both Apache Tomcat and WebLogic Server without any modification or recompilation.

Web application directory structure

The Servlet 2.2 specifications define the directory structure of the files in a Web application. The top directory -- or root directory -- should be given the name of your Web application and will define that document root for your Web application. All files beneath this root can be served to the client except for files under the special directories META-INF and WEB-INF in the root directory. All private files -- such as servlet class files -- should be stored under the WEB-INF directory.

The directory structure of your Web application should look something like that shown in Figure 1.

Figure 1. Web application directory structure

To create a Web application, begin by creating this directory structure. Take your compiled servlet classes and place them in the WEB-INF/classes directory. If you have defined your servlet to belong in a package, you must follow the standard Java rules and create the appropriate subdirectories so the JVM will be able to find your classes. For example, if your servlet is defined in a package com.mycompany.myproject, you should create the following directory structure:

  .../WEB-INF
      |-- classes
          |-- com
              |-- mycompany
                  |-- myproject

Place your Java classes in the myproject subdirectory.

A useful alternative to copying the class files into the appropriate directory is to configure your build environment (a Makefile or IDE) to save the compiled class files directly in the required directories. Doing so thereby eliminates that step during development.

Modify the deployment descriptor

You should now have all of your files in place to create your first Web application. At that point, one other task needs to be done: update the deployment descriptor to register your servlets with the servlet container. To easily create a deployment descriptor, simply edit an existing one. A skeletal web.xml file is given below:

<?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>
  <!-- Your servlet definitions go here  -->
</web-app>

You insert your servlet deployment descriptors between the <web-app> and </web-app> tags in this file. Servlet deployment descriptors must include the following tags (in this order):

  <servlet>
     <servlet-name>name</servlet-name>
     <servlet-class>package.name.MyClass</servlet-class>
  </servlet>

Various optional tags, which define the servlet's other runtime properties, are allowed before the closing </servlet>. Those tags define properties such as initialization parameters, whether the servlet should be loaded at startup, security roles, and display properties (including small and large icons, a display name, and a description). Refer to the specifications for more details on those.

So far, the deployment descriptors describe the servlet to the servlet container. Next, we must describe when the servlet container should invoke the servlet -- referred to as mapping. In other words, we must describe how to map a URL to a servlet. In the web.xml file URL, mapping follows this form:

  <servlet-mapping>
     <servlet-name>name</servlet-name>
     <url-pattern>pattern</url-pattern>
  </servlet-mapping>

OK, enough theory. Let's look at an example of a real Web application deployment descriptor. Below you'll find a minimal web.xml file describing our sample RequestDetails servlet:

<?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>RequestDetails</servlet-name>
  <servlet-class>org.stevengould.javaworld.RequestDetails</servlet-class>
 </servlet>
 <servlet-mapping>
  <servlet-name>RequestDetails</servlet-name>
  <url-pattern>SampleServlet</url-pattern>
 </servlet-mapping>
</web-app>

As you can see from the servlet mapping tags, we have chosen to map our RequestDetails servlet to the URL /SampleServlet.

That's it. We have created our first Web application containing a single servlet. We should now be able to deploy that Web application to any Servlet 2.2-compliant servlet container.

More than likely, that will be how you work with and deploy your Web applications in a development mode. In a production environment, however, keeping related files bundled together is more convenient. In the next section, we look at creating Web application archive files (WARs) that do just that.

Create WARs

As mentioned earlier, a WAR file is simply a Java archive with the extension changed to reflect its different purpose. We have already seen the directory structure required by a Web application. To create a WAR file, we use that same directory structure.

To create a WAR for your Web application, go to the root directory containing your Web application and type the following command:

  jar cv0f myWebApp.war .

Note the required period at the end of the above line; it tells the jar program to archive the current directory.

The aforementioned jar command will create a WAR file called myWebApp.war. Next, we shall look at how to deploy that WAR file in both Tomcat 3.2 and WebLogic Server 6.0.

Deploy Web applications in Tomcat 3.2

Tomcat 3.2 serves as the reference implementation for the Java Servlet 2.2 specifications. For the purpose of this article I assume you will be using Tomcat 3.2.1 or later. At the time this article was written, 3.2.1 was the latest production release of Tomcat, though version 4.0 was in beta.

To deploy your Web application in Tomcat, copy your root Web application directory -- the one containing web.xml and its subdirectories -- into the webapps/ROOT/ subdirectory of your Tomcat installation. You may want to save a copy of the default Web application before overwriting it.

Under Unix, for example, if you installed Tomcat into the directory /opt/jakarta-tomcat-3.2.1, you would copy your servlet classes into the directory beneath:

 /opt/jakarta-tomcat-3.2.1/webapps/ROOT/

If you run Tomcat under Windows and installed Tomcat into the C:\Program Files\Jakarta-Tomcat-3.2.1 directory, you would copy your servlet classes into the directory beneath:

 C:\Program Files\Jakarta-Tomcat-3.2.1\webapps\ROOT

The webapps/ROOT/WEB-INF/classes subdirectory is the default directory in which Tomcat will look for your Java classes. If you have defined your servlet to belong in a package, you must follow the standard Java rules and create the appropriate subdirectories so that the JVM will be able to find your classes, as we did before. For example, if you defined your servlet in a package com.mycompany.myproject, then you should have the directory structure shown in Figure 2.

Figure 2. Package layout in Tomcat

Your Java servlet classes will then be in the myproject subdirectory.

That is all that is involved. There is no further configuration. Keeping with the RequestDetails example, try copying the Web application files into Tomcat's default Web application.

Test the servlet

To test your servlet, start the Tomcat server, open a Web browser, and open a URL of the following form:

  http://{address}:{port}/{servletName}

where:

  • address is the name or IP address of the machine running Tomcat. You can use localhost if the browser is running on the same machine as Tomcat.
  • port is the port on which Tomcat is listening. By default, that is port 8080.
  • servletName is the name of the servlet you want to invoke. That should match the value contained in the <url-pattern></url-pattern> tags in the web.xml deployment descriptor file.

For example, if Tomcat is running on the same machine as the browser and listening on the default port (8080), you can test your RequestDetails sample servlet (which is mapped to the URL SampleServlet) by opening the following URL:

  http://localhost:8080/SampleServlet

Notice how little work was involved deploying the Web application. Copy some files and test. The ease of use is made possible by the Java Servlet 2.2 specifications and the use of the deployment descriptors.

Now that we have seen how to deploy servlets in Tomcat, we shall go on to look at servlet deployment in WebLogic Server.

Deploy Web applications in WebLogic Server 6.0

While WebLogic Server 5.1 was the first release of WebLogic Server to provide support for the Java Servlet 2.2 specification and Web applications, WebLogic Server 6.0 has some significant usability improvements that simplify the deployment of Web applications (in both their expanded form and when bundled as a WAR file).

Deploy WARs using the Console

With your instance of WebLogic Server running, start up the WebLogic Server Console. Assuming a default installation, the Console can be brought up on the localhost machine by opening the following URL in a Web browser:

  http://localhost:7001/console

You will be prompted for the system user name and password before being allowed into the Console.

To deploy your WAR once you have access to the Console:

  1. Click the Web Applications node in the left-hand panel of the Console
  2. In the right pane, click "Install a new Web Application..."
  3. Either type in the full path and filename of your WAR, or use the Browse... button to locate it.
  4. Click the Upload button.

That's it. If everything went smoothly, you should see your Web application listed under Web Applications in the left-hand panel of the Console. You may need to refresh the view for that to appear.

As an alternative to using the WebLogic Server Console, it is possible to copy the complete Web application directory structure as we did when deploying with Tomcat.

Deploy Web applications manually

Normally when one thinks of doing any task manually, or by hand, the automatic reaction is to expect the task to be a little more involved or cumbersome than the automated equivalent. In the case of deploying Web applications under WebLogic Server 6.0, the manual approach is as easy, if not easier, than using the Console.

Simply copy your WAR file or your entire Web application directory structure into the config/mydomain/applications subdirectory of your WebLogic Server distribution (where mydomain is the name of your WebLogic Server domain). As soon as the files have been copied, WebLogic Server deploys the Web application.

Test the servlet

To test your servlet, open up a Web browser and open a URL of the following form:

  http://{address}:{port}/{servletName}

where:

  • address is the name or IP address of the machine running WebLogic Server. You can use localhost if the browser is running on the same machine as WebLogic Server.
  • port is the port on which WebLogic Server is listening. By default this is port 7001.
  • servletName is the name of the servlet you want to invoke. This should match the value contained in the <url-pattern></url-pattern> tags in the web.xml deployment descriptor file.

For example, if WebLogic Server is running on localhost and is listening on the default port (7001), you can test our RequestDetails sample servlet (mapped to the URL SampleServlet), by opening the following URL:

  http://localhost:7001/SampleServlet

You should see the results of running the servlet displayed in the browser window.

Again, the deployment of your Web application or WAR file required that you simply copy some files and test the servlet -- no configuration necessary.

Reconfigure Web applications

Once you have deployed your Web applications into WebLogic Server, you can use the Console to configure and reconfigure the application. As changes are made to any of the settings, the configuration details will automatically be written to WebLogic Server's config.xml file. Next time WebLogic Server restarts, that file will be used to configure your application.

Conclusion

In this article we reviewed some of the fundamentals of servlet development. We looked at servlets in general and HTTP servlets in detail. We then examined how to create a Web application in which to house the servlets. I also introduced you to Web Application Archives, or WARs, as a more convenient way to deploy a Web application or collection of Web components.

We then illustrated deployment of the Web application and of the WAR in two widely used servlet containers: Tomcat 3.2.1 from the Apache organization and WebLogic Server 6.0 from BEA Systems. Anyone who has worked with pre-2.2 versions of the Java servlet specification will agree that the introduction of Web applications significantly improves servlet deployment. Previously, it was necessary to edit configuration files -- with different files and formats for each different servlet engine -- then restart the server. With the introduction of the Java Servlet 2.2 specification, it's simply a matter of copying files and testing.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies