CATALINA - A PROPOSED ARCHITECTURE FOR TOMCAT

Craig R. McClanahan

Last Revised: January 19, 2000


1. INTRODUCTION

1.1 BACKGROUND

In June 1999, Sun announced that they were contributing the source code of the Tomcat servlet container, and the Josper (later Jasper) JSP engine, to the Jakarta project . At that time, significant work had taken place within the Java Apache project on a new servlet container design with a component based architecture. This architecture offered substantial improvements in the understandability of the basic pieces of a servlet container, as well as flexibility in customizing the underlying functionality for different server environments.

With the announcement of Jakarta, interest declined in a new version of Apache JServ based on this architecture, based on the (quite reasonable) conclusion that Jakarta would become the successor to Apache JServ when it became available. Now that this has occurred, and we have had a chance to examine the source code, it is clear that the core servlet container portions of Tomcat (primarily in package org.apache.tomcat.core) are in need of the same architectural remodel, for the same reasons.

This document proposes just such an architecture for a future version of Tomcat, based on a core set of interfaces for which a variety of implementations may be created. These component implementations can then interoperate cleanly with other new and existing components, as long as the interface contracts are honored.

It is expected that conversion to this architecture will require a major refactoring of the existing Tomcat core package, as well as other packages with internal dependencies on the core. It will also require substantial changes in the deployment configuration files for the engine. Therefore, it seems appropriate to implement these changes at a major revision level update of Tomcat.

1.2 DESIGN GOALS

The new architecture was designed with the following goals in mind:

1.2.1 Diverse Deployment Environments

Tomcat should be easy to configure and deploy in a variety of operational environments, including:

This is accomplished by allowing the deployer to pick and choose among the required Container and Session implementations needed to support the required features.

1.2.2 Plug-In Functionality Support

Tomcat should allow the server administrator to choose whether or not certain functionality (such as persistent support for session data, or load balancing across servers) is required. In addition, it should be possible for server developers to provide customized versions of one or more Tomcat components, and have them interoperate with the remainder of the system seamlessly.

This is accomplished by virtue of the fact that all interactions between Tomcat components are based on standard Java interfaces, which can be implemented in a variety of ways but still interoperate.

1.2.3 Extensible Request Processing

Besides the standard request/response processing described in the servlet API, it is desireable in many environments to support additional request processing functionality within the server. Examples include:

This goal is met through the use of the Interceptor architecture (similar in spirit to the Valve architecture from Apache JServ, as well as the existing Interceptor style support that already exists within Tomcat.

1.3 COMPONENT CATEGORIES

There are three primary families of components in the proposed architecture. The details for each component will be presented in the following section.


2. PRIMARY ARCHITECTURE COMPONENTS

2.1 Communications Adapter Components

The following interfaces define the major components related to communication adapters:

Reduced to its essentials, the functional processing performed by an Adapter is described as follows:

2.2 Servlet Container Components

2.2.1 Container Components

The fundamental servlet container interface is named, naturally enough, Container. The fundamental responsibility of a Container is to execute Requests received from a communications Adapter, or from a parent Container, returning the corresponding Response. This responsibility is represented by the invoke() method of the Container. Each container is optionally associated with an instance of each of the Support Components described below, and also has the following characteristics:

The service() method of a Container is used to implement the required processing logic of this hierarchical level of a Tomcat deployment, independent of any Interceptors that may have been defined. See the following subsection for more information about how request processing with Interceptors is actually implemented.

Two extensions of the Container interface will be used in nearly every deployment of Tomcat:

In an embedded deployment, a single Context instance, representing the entire embedded web application, will usually be the highest level Container component utilized.

When Tomcat is executed in a standalone deployment using an HTTP adapter, the following extensions of the Container interface are provided:

2.2.2 Interceptor Components

As mentioned above, a Container supports the definition of a stack of Interceptor objects that are allowed to participate in request processing both before and after the service() method of the owning Container is called. The functional purpose of Interceptors is best understood by examining several operational scenarios.

Under normal circumstances (each activated Interceptor returns true, and no exceptions are thrown by an Interceptor or the service() method of the associated Container), the following processing logic must be performed by the invoke() method of a Container:

A preService() method of an Interceptor is allowed to return false instead of true. Doing so is an indication that this Interceptor has in fact created the corresponding response, and that no further processing for this request is required. When such a return happens, the following changes to the previously described logic occur:

If an exception is thrown by any call to a preService(), service(), or postService() method, all subsequent method calls described above are skipped. The exception will be rippled out to the highest level Container whose invoke() method was originally called, and from there to the calling Adapter.

The JavaDoc comments for an Interceptor outlines the requirements described above from the viewpoint of an individual Interceptor. Consult this documentation for additional information.

2.2.3 Support Components

A single instance of each of the following support components may optionally be associated with each Container, accessible through the usual JavaBeans property accessor methods. As a general rule, a get method returning such a support component should return the component last specified by a corresponding set method call, if any; otherwise, a corresponding get method call to this Container's parent Container (if any) should be performed. This approach allows a general deployment style where support components are only attached at the highest required hierarchical Container level.

2.3 Session Management Components

Support for the session management functionality of the servlet API specification is provided by components that implement the following interfaces:


3. OTHER ISSUES

3.1 Component Configuration

A significant implementation challenge in this architecture is the elegant initialization and shutdown of the various components. This proposal suggests an approach, also used in the Apache JServ architecture, based on the Lifecycle interface. The use of this approach includes the following elements:

3.2 Package Naming Suggestions

The current Tomcat servlet container architecture has nearly all of the executable code in a single package (org.apache.tomcat.core). This proposal suggests the following package naming conventions:

The suggested naming conventions maximize the ability to localize private implementation details within a package (through the use of package private scoping of method names), while encouraging independence between packages by using only the commonly defined interfaces.

3.3 Example Interceptor Scenarios

The following examples illustrate several types of functionality that might be implemented using the Interceptor feature of the proposed architecture:

3.4 Example Component Implementations

The following implementation examples are included with this proposal, to illustrate the development approach that would normally be used:

In package org.apache.tomcat.core: In package org.apache.tomcat.logger: In package org.apache.tomcat.security:
  • SecurityInterceptor. Implementation of Interceptor that enforces the authentication and access control directives in the deployment descriptor of a web application.
In package org.apache.tomcat.session:
  • StandardManager. Example implementation of Manager that provides the same session management capabilities as Tomcat 3.0.
  • StandardSession. Example implementation of Session that encapsulates an individual HttpSession within Tomcat.