Starting Up Tomcat 6: Part 2

Exclusive offer: get 50% off this eBook here
Tomcat 6 Developer's Guide

Tomcat 6 Developer's Guide — Save 50%

Build better web applications by learning how a servlet container actually works.

$26.99    $13.50
by Damodar Chetty | December 2009 | Java Open Source

Read Part One of Starting Up Tomcat 6 here.

Bootstrapping the embedded container

As we saw earlier, Bootstrap is simply a convenience class that is used to run the Embedded class, or rather to run Catalina, which subclasses Embedded. The Catalina class is intended to add the ability to process a server.xml file to its parent class. It even exposes a main() method, so you can invoke it directly with appropriate command-line arguments.

Bootstrap uses its newly constructed serverLoader to load the Catalina class, which is then instantiated.

It delegates the loading process to this Catalina instance's load() method. This method updates the catalina.base and catalina.home system properties to absolute references, verifies that the working directory is set appropriately, and initializes the naming system, which is Tomcat's implementation of the JNDI API. For now, all we need to note is that it indicates that JNDI is enabled by setting the catalina.useNaming system property to true, and prefixing the Context.URL_PKG_PREFIXES system property with the package org.apache.naming using a colon delimiter.

The Context.URL_PKG_PREFIXES property indicates a list of fully qualified package prefixes for URL context factories. Setting org.apache.naming as the first entry makes it the first URL context factory implementation that will be located.

For the java:comp/env Environment Naming Context (ENC), the actual class name for the URL context factory implementation is generated as org.apache.naming.java.javaURLContextFactory. If the Context.INITIAL_CONTEXT_FACTORY is currently not set for this environment, then this is set as the default INITIAL_CONTEXT_FACTORY to be used.

Bootstrapping the Tomcat component hierarchy

The configuration for a Tomcat instance is found in the confserver.xml file. This file is now processed, converting each element found into a Java object. The net result at the end of this processing is a Java object tree that mirrors this configuration file.

This conversion process is facilitated by the use of the Apache Commons Digester project (http://commons.apache.org/digester/), an open source Commons project that allows you to harness the power of a SAX parser while at the same time avoiding the complexity that comes with event driven parsing.

Commons Digester

The Digester project was originally devised as a way of unmarshalling the struts-config.xml configuration file for Struts, but was moved out to a Commons project due to its general purpose usefulness.

The basic principle behind the Digester is very simple. It takes an XML document and a RuleSet document as inputs, and generates a graph of Java objects that represents the structure that is defined in the XML instance document.

 Tomcat 6 Developer's Guide

There are three key concepts that come into play when using the Digester—a pattern, a rule, and an object stack.

The pattern

As the digester parses the input XML instance document, it keeps track of the elements it visits. Each element is identified by its parent's name followed by a forward slash ('/') and then by its name.

For instance, in the example document below, the root element is represented by the pattern rolodex. Two <contact> elements are represented by the pattern rolodex/contact, the <company> elements are represented by the pattern rolodex/contact/company, and so on.

<rolodex type=paperSales>
<contact id="1">
<firstname>Damodar</firstname>
<lastname>Chetty</lastname>
<company>Software Engineering Solutions, Inc.</company>
</contact>
<contact id="2">
<firstname>John</firstname>
<lastname>Smith</lastname>
<company>Ingenuitix, Inc.</company>
</contact>
</rolodex>

The rule

A rule specifies the action(s) that the Digester should take when a particular pattern is encountered.

The common rules you will encounter are:

  • Creational actions (create an instance of a given class to represent this XML element)
  • Property setting actions (call setters on the Java object representing this XML element, passing in the value of either a child element or an attribute)
  • Method invocation actions (call the specified method on the Java object representing this element, passing in the specified parameters)
  • Object linking actions (set an object reference by calling a setter on one object while passing in the other as an argument)

The object stack

As objects are created, using the creational actions discussed above, Digester pushes them to the top of its internal stack. All actions typically affect the object at the top of the stack.

A creational action will automatically pop the element on the top of the stack when the end tag for the pattern is detected.

Using the Digester

The typical sequence of actions is to create an object using a creational action, set its properties using a property setting action, and once the object is fully formed, to pop it off the top of the stack by linking it to its parent, which is usually just below it on the stack. Once the child has been popped off, the parent is once again at the top of the stack. This repeats as additional children objects are created, initialized, linked, and popped. Once all the children are processed and the parent object is fully initialized, the parent itself is popped off the stack, and we are done.

You instantiate an org.apache.commons.digester.Digester by invoking the createDigester() method of org.apache.commons.digester.xmlrules.DigesterLoader and passing it the URL for the file containing the patterns and rules.

Patterns and rules can also be specified programmatically by calling methods directly on the digester instance. However, defining them in a separate XML RuleSet instance document is much more modular, as it extracts rule configuration out of program code, making the code more readable and maintainable.

Then, you invoke the parse() method of a Digester instance and pass it the actual XML instance document. The digester uses its configured rules to convert elements in the instance document into Java objects.

The server.xml Digester

The Catalina instance creates a Digester to process the server.xml file. Every element in this file is converted into an instance of the appropriate class, its properties are set based on configuration information in this file, and connections between the objects are set up, until what you are left with is a functioning framework of classes.

This ability to configure the structure of cooperating classes using a declarative approach makes it easy to customize a Tomcat installation with very little effort.

The createStartDigester() method in Catalina does the work of instantiating a new Digester and registering patterns and rules with it. The Catalina instance is then pushed to the top of the Digester stack, making it the root ancestor for all the elements parsed from the server.xml document.

The rules can be described as follows:

Pattern

Rule

Server

Creational action: Instantiates an org.apache.catalina.core.StandardServer

Set properties action: Copies attribute values over to the topmost object of the stack using mutator methods that are named similarly to the attribute

Object linking action: Invokes setServer()to set this newly minted Server instance on the Catalina instance found on the stack.

Server/

GlobalNamingResources

Creational action: Instantiate an org.apache.catalina.deploy.NamingResources

Set properties action: Copies attribute values from this element over to the topmost object on the stack

Object linking action: Sets this newly instantiated object on the StandardServer instance at the top of the stack, by invoking its setGlobalNamingResources().

Server/Listener

Creational action: Instantiate the class specified by the fully qualified class name provided as an attribute.

Set properties action: Copy attributes from this element.

Object linking action: Sets this instance on the StandardServer instance at the top of the stack, by invoking its addLifecycleListener() method with this new instance.

Server/Service

Creational action: Instantiates an org.apache.catalina.core.StandardService.

Set properties action: Copy attributes from this element

Object linking action: Invokes addService()on the StandardServer instance at the top of the stack passing in this newly minted instance

Server/Service/Listener

Creational action: Instantiate the class specified by the fully qualified class name provided as the className attribute

Set properties action: Copy attributes from this element

Object linking action: Invokes addLifecycleListener() on the StandardService instance at the top of the stack, passing in this listener instance

Server/Service/Executor

 

Creational action: Instantiate the class org.apache.catalina.core.StandardThreadExecutor

Set properties action: Copy attributes for this element

Object linking action: Invokes addExecutor() with this instance, on the StandardService instance at the top of the stack

Server/Service/Connector

 

Creational action: Instantiate the class org.apache.catalina.startup.ConnectorCreateRule

Set properties action: Copy all attributes for this element except for the executor property

Object linking action: Invokes addConnector(), passing in this instance, on the StandardService instance at the top of the stack

Server/Service/Connector/Listener

 

Creational action: Instantiate the class specified by the fully qualified class name provided as the className attribute

Set properties action: Copy attributes from this element

Object linking action: Invokes addLifecycleListener(), passing in this instance, on the Connector instance at the top of the stack

Server/Service/Engine

 

Set the Engine instance's parent class loader to the serverLoader

Tomcat 6 Developer's Guide Build better web applications by learning how a servlet container actually works.
Published: December 2009
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

In addition to setting rules individually on the digester, you can also define sets of rules, known appropriately as RuleSets. The rule sets we add to our digester include a NamingRuleSet, EngineRuleSet, HostRuleSet, and a ContextRuleSet.

The NamingRuleSet is constructed for the patterns that begin with Server/GlobalNamingResources/ and which match naming resource elements, such as Resource, ResourceEnvRef, and Environment.

For each pattern matched, we create the appropriate instance of org.apache.catalina.deploy.Context<patternName>, for example, ContextResource, ContextResourceEnvRef, and ContextEnvironment. We then set the instance's properties from the XML element, and finally we set this instance on the GlobalNamingResources instance using the appropriate add<patternName>() method, such as addResource(), addResourceEnvRef(), and addEnvironment().

The EngineRuleSet is constructed for patterns that begin with Server/Service/Engine. It instantiates an org.apache.catalina.core.StandardEngine to represent an Engine element, sets its properties, and invokes addLifecycleListener() on the Engine instance to add an org.apache.catalina.startup.EngineConfig instance as a listener. It also invokes setContainer() on the Service with this Engine instance to indicate the container that will handle all requests that come in on all connectors that are associated with this Service. It also adds other lifecycle listeners that are specified using the <Listener> element for this Engine and adds any valves specified using the <Valve> element within this Engine.

The HostRuleSet does the same for virtual hosts. It instantiates an org.apache.catalina.core.StandardHost to represent each host, and sets its properties from the element's attributes. By default, it sets an instance of org.apache.catalina.startup.HostConfig as a lifecycle listener, invokes addChild() on its Engine to register itself as a child, invokes addAlias() on the host instance with the <Alias> element's body as an argument, and adds any registered listeners and valves for this host.

The ContextRuleSet works in a similar fashion to process the Context elements for a given Host. It instantiates a new org.apache.catalina.core.StandardContext, and sets its properties from the element. It also sets up an org.apache.catalina.startup.ContextConfig as a lifecycle listener, add invokes the addChild() method of its Host to add this context instance as a child. A WatchedResource child element is added to the Context instance using its addWatchedResource() method with the element's body as an argument. Similarly, any Valve child elements are instantiated using the class name specified in the element, the properties are set from the element, and then the Valve instance is added to the context using the addValve() method. Rules are also defined for child elements that are naming resources, lifecycle listeners, loaders, session managers, stores, and context parameters.

Parsing the server.xml file

Once the digester is completely set up with all its rules, we are ready to begin.

The conf/server.xml file under catalina.base is located, the current Catalina instance is set as the top object on the digester's stack, and the digester's parse() method is invoked to parse the server.xml file using its configured rules.

The digester parses the input stream, constructing objects, setting properties, and linking objects as it works through the server.xml file.

Normally, the parse() method returns the first object in the stack, all configured with the appropriate relationships. Interestingly, we don't bother with that fully configured object. This is because we have added the Catalina instance as the first object in the stack, and the process of constructing a fully configured Tomcat instance culminates in invoking setServer() on the Catalina instance with that configured Server instance.

Initializing the Server

Next, the newly constructed Server instance is initialized by invoking its initialize() method. This causes initialize() to be invoked on each service. Initialization events are fired before, during, and after the server is initialized to allow server lifecycle listeners to react. Initialization of a service causes its associated connectors to be initialized as well.

The start() method on the Bootstrap instance is invoked as the last step of the bootstrapping process. This method turns around and invokes start() on the Catalina instance. This method has two main purposes. First, it starts up its server instance, by invoking start() on its topmost StandardServer instance, and second, it sets up a shutdown hook.

The Server instance's start() method fires startup notification lifecycle events to notify all listeners that the server is starting up, and then invokes start() on each of its registered services. The services in turn invoke start() on their components, and so on, until the entire server has been started.

In an ideal world, a server is always shutdown by the receipt of a shutdown command on the port that it monitors. However, the real world is not quite so orderly, and the JVM may be shutdown for other reasons, such as the computer having been shutdown or the process having been killed.

In such cases, you can ask the JVM to notify your program of the shutdown request. You do this by registering a shutdown hook with the Java runtime system.

A shutdown hook is a standard Thread that encapsulates cleanup actions that should be taken before the Java runtime exits. All shutdown hooks are called by the runtime when the JVM is shutting down.

Therefore, the last task that we perform is to install a shutdown hook, as implemented by CatalinaShutdownHook. This hook is registered with the Java Runtime by invoking its addShutdownHook() method:

Runtime.getRuntime().addShutdownHook()

CatalinaShutdownHook is an inner class of Catalina and so has access to all the data members of Catalina. Its run() method is very simple. It just ensures that stop() is called on this instance of Catalina. This method invokes stop() on the StandardServer instance, which in turn performs a cascaded invocation of stop() on all its child components. Each child does the same for its children, until the entire server has been cleanly stopped.

With this, the server is now up and running, and is ready to serve requests.

Adding a new web application to Tomcat in Eclipse

In this section, we'll create a brand new web application, which will be made available at the context path, /devguide. This context can be accessed at the URL http://localhost:8080/devguide. We will add a single servlet to this web application called HelloWorldServlet.java, which will simply print a Hello World message when requested.

Our main objectives with this example are to see how we might deploy a new web application context into our Tomcat installation and to have all code (both Tomcat itself as well as our web application) run within the same Eclipse project so that we can debug it by seamlessly stepping through both the Tomcat source as well as your application source.

Let us begin by adding a new directory called devguide under the webapps folder within our project. This folder will hold our new context's resources.

Add the following deployment descriptor as webapps/devguide/WEB-INF/web.xml:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<description>Tomcat Developer's Guide</description>
<display-name>Tomcat Developer's Guide</display-name>
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>com.swengsol.HelloWorldServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
</web-app>

This is a fairly minimal deployment descriptor and indicates that we have a single servlet, com.swengsol.HelloWorldServlet that we will map to the path /HelloWorld.

Our static content consists of the single file, webapps/devguide/index.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Hello World</TITLE>
<META http-equiv = Content-Type content = "text/html">
</HEAD>
<BODY>
<H3>Tomcat Developer's Guide</H3>
<ul>
<li><a href = "HelloWorld">Hello World Example Servlet</a></li>
</ul>
</BODY>
</HTML>

This file provides a launching point for our HelloWorld servlet.

Create a new servlet class, com.swengsol.HelloWorldServlet.java, and place it in webapps/devguide/WEB-INF/classes:

package com.swengsol;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloWorldServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
String title = "Hello World - Tomcat Developer's Guide";
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor = "white">");
out.println("<h1>Hello World from the Developer's Guide!</h1>");
out.println("</body>");
out.println("</html>");
}
}

This servlet class extends HttpServlet and overrides the doGet() method to print out a welcoming message.

Now, add the webapps/devguide/WEB-INF/classes as a source folder to the build path. You can do this by right clicking on the folder, picking Build Path, and then Use as Source Folder from the context menu.

Next, let us add a new context fragment, webapps/devguide/META-INF/context.xml, for this web application.

<?xml version = "1.0" encoding = "UTF-8"?>
<Context antiResourceLocking = "false" reloadable = "true" privileged = "false" />

With this step, the application is complete.

The final step is to make sure that Ant is aware of this new web application so that it is deployed to the output folders along with the other example applications.

To do this, we edit the project's build.xml file (found in the project's root folder) to enhance the deploy target to make it aware of our new web application.

The first section that we will edit is the copy task, which copies the webappsdevguide folder tree out to the outputbuild folder.

<!-- Copy other regular webapps -->
<copy todir = "${tomcat.build}/webapps">
<fileset dir = "webapps">
<include name = "ROOT/**"/>
<include name = "examples/**"/>
<include name = "manager/**"/>
<include name = "host-manager/**"/>
<include name = "devguide/**"/>
</fileset>
</copy>

Second, we add a new javac task to the same deploy target, to compile the servlets that are part of this new web application.

<!-- Build classes for devguide webapp -->
<javac srcdir = "webapps/devguide/WEB-INF/classes"
destdir = "${tomcat.build}/webapps/devguide/WEB-INF/classes"
debug = "${compile.debug}" deprecation = "${compile.deprecation}"
source = "${compile.source}" target = "${compile.target}"
optimize = "${compile.optimize}" classpath = "${tomcat.classes}"
excludes = "**/CVS/**,**/.svn/**">
</javac>

These edits to build.xml register our new web application as an additional web application that can be potentially distributed along with Tomcat.

All the pieces are in place now, and all that remains is to run an Ant build. Once the build completes, run the usual Debug configuration that we have set up for this project. Experiment by setting breakpoints in your servlet and by editing the servlet class. Any edits that you make will need to be followed by an Ant build for the changes to be picked up.

As a convenience, you can set up a new Ant builder for your project to automatically start an Ant build whenever a file in the project is changed.

Summary

In this two-part article, we looked at how a Tomcat instance can be started using either the standard script-based mechanism or the alternative Run/Debug configuration. We looked at the various class loader hierarchies that are set up during the initialization process. We also looked at how the Apache Commons Digester library provides a convenient way of converting an XML file into a Java object graph. We ended this article with an example of a web application that was deployed into our Tomcat instance.

[ 1 | 2 ]

If you have read this article you may be interested to view :

Tomcat 6 Developer's Guide Build better web applications by learning how a servlet container actually works.
Published: December 2009
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

About the Author :


Damodar Chetty

Damodar Chetty is a lifelong programmer with almost two decades in the computer software industry. He cut his teeth on assembler and BASIC programming, and has journeyed through FORTRAN, COBOL, Visual Basic, C++, and Java. Along the way he has stubbed his toes often enough to develop a keen sense of where dragons lie. He is currently an independent consultant at Software Engineering Solutions, Inc. doing what he loves most – building high quality software.

Damodar has a degree in Electronics & Telecommunications engineering from the University of Bombay, and higher degrees in Management Sciences from the University of Goa, and in Computer Engineering from the University of Minnesota.

He currently lives in Woodbury, Minnesota with his wife, Devi, and his children, Ashwin and Anita.

Books From Packt

Apache Geronimo 2.1: Quick Reference
Apache Geronimo 2.1: Quick Reference

JBoss AS 5 Development
JBoss AS 5 Development

GlassFish Administration
GlassFish Administration

MooTools 1.2 Beginner's Guide
MooTools 1.2 Beginner's Guide

JSF 1.2 Components
JSF 1.2 Components

jBPM Developer Guide
jBPM Developer Guide

Spring Persistence with Hibernate
Spring Persistence with Hibernate

Apache Roller 4.0 – Beginner's Guide
Apache Roller 4.0 Beginner's Guide

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software