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

- Stand-alone HTTP based application and web server, with full support for multiple applications and multiple virtual hosts.
- Servlet/JSP environment for an existing web server (such as Apache), using either embedded process or separate process implementations.
- Servlet/JSP environment embedded within an application server (such as a J2EE-compatible platform), able to support advanced session persistence and load balancing facilities for distributable applications.
- Servlet/JSP environment providing a web-based administration interface for an existing server application (for example, a firewall or a DBMS system).
- Servlet/JSP environment providing a web-based administration interface for an embedded device application (for example, a router, a WebTV-type set-top box, or a network of X10-based home automation devices).

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:

- Request checking at the entire server, virtual host, or servlet context level. This is very important in an ISP environment hosting multiple applications for multiple customers on the same server.
- Security checking integrated with legacy realm resources (provided by a surrounding application server, or already used by other legacy applications), without requiring this logic to be coded within user applications (servlets and JSP pages).
- Logging facilities (customized application logs, or standard web server style logs when Tomcat runs standalone) at the entire server, virtual host, or servlet context level.

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.

- Communications Adapter Components are the diverse means by which the Tomcat core servlet container is connected to the outside world. The primary interface between the adapter components and the remainder of the system is the Request and Response interfaces, which represent the `HttpServletRequest` being processed, and the `HttpServletResponse` being produced, respectively.
- Servlet Container Components are responsible for accepting a Request from a communications adapter component, invoking the required processing functionality, and returning the corresponding Response. All container components implement the base interface Container, which supports the insertion of arbitrary Interceptor layers that can participate in request processing without requiring re-implementation of the entire underlying Container.
- Session Management Components include Session, which represents an individual `HttpSession`, and Manager, which controls the pool of active sessions visible to a particular container.

# 2. PRIMARY ARCHITECTURE COMPONENTS

## 2.1 Communications Adapter Components

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

- Adapter. Representation of a component which, by some communications protocol or interface API, receives requests from client applications, calls the appropriate processing logic, and returns the generated response. Because the functional requirements of adapters vary widely, few common method signatures are required.
- Request. Representation of an `HttpServletRequest` that is utilized throughout the Tomcat servlet container to represent the incoming request.
- Response. Representation of an `HttpServletResponse` that is utilized throughout the Tomcat servlet container to represent the generated response.

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

- Receive a request from a client application (such as a browser via HTTP, or a web server via some connection protocol).
- Create appropriate Request and Response instances, and populate their properties based on the contents of the received request.
- Identify an appropriate Container to use for processing this request. For a stand-alone Tomcat installation, this will probaby be a singleton Engine implementation. For an adapter attaching Tomcat to a web server such as Apache, this step could take advantage of parsing already performed within the web server to identify the Context, and perhaps even the Wrapper, to be utilized.
- Call the `invoke()` method of the selected container, passing the initialized request and response instances as arguments.
- Return the headers and data contents of the response created by this container to the client application submitting the request.

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

- A set of child Container objects representing the next hierarchically lower level of responsibility for request processing within this Tomcat deployment. For example, a Container representing a virtual host would have child Containers representing the web applications defined for that virtual host.
- A set of Interceptor objects providing plug-in processing layers for this Context. See the next subsection for more information about Interceptors.
- A name that is used to uniquely identify this Container in the set of children of this Container's parent Container.
- A parent Container with which this Container is associated. Parent-child relationships are usually hierarchical, with child Containers being responsible for successively smaller subsets of all possible requests processed by this Tomcat deployment.

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:

- Context. Representation of a single `ServletContext` which will typically be associated with a child Wrapper container for each defined servlet.
- Wrapper. Representation of a single servlet definition (which may support multiple servlet instances if the servlet itself represents `SingleThreadModel`).

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:

- Engine. Representation of the entire Tomcat servlet engine, associated with one or more Host implementations (if this deployment supports multiple virtual hosts) or one or more Context implementations (if this deployment is for a single virtual host). Generally, the implementation of this interface will be deployed as a singleton, with no parent container. Any Interceptor objects associated with an Engine will process all requests passed to the Tomcat engine.
- Host. Representation of a virtual host that is associated with a number of Context children. The parent container will generally be an Engine. This representation is useful when you wish to interpose one or more Interceptor objects to process all requests to web applications within this virtual host.

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

- The `preService()` method of all Interceptors associated with this Container are called, starting with the most recently added Interceptor. It is assumed each `preService()` method returns `true` to indicate that processing should continue. It is also assumed for this scenario that no exception is thrown.
- The `service()` method of the owning Container itself is called. It is assumed for this scenario that no exception is thrown.
- The `postService()` method of all Interceptors associated with this Container are called, starting with the least recently added Interceptor. It is assumed for this scenario that no exception is thrown.

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:

- The `preService()` methods of any Interceptors added to this Container earlier than the current one are not called.
- The `service()` method of the owning Container itself is not called.
- The `postService()` methods of any Interceptors added to this Container earlier than the current one are not called.
- The `postService()` methods of the current Interceptor, and any Interceptor added to this Container after the current Interceptor, are called.

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.

- Loader. Representation of a Java class loader used to load executable servlets and other code utilized within a container. The interface defines support for detection of changes in the underlying repository (essentially a container-specific class path), and the unloading and reloading of all Java classes loaded by this loader.
- Logger. Representation of the `log()` methods defined in the `ServletContext` interface. Implementations can store log output wherever desired, or integrate it with the log output of the server in which Tomcat is embedded.
- Manager. Representation of a manager for the pool of active sessions associated with this Container. See the following section for more information.
- Realm. Representation of a security realm in which a user `Principal` can be authenticated, and from which the set of roles associated with that `Principal` can be identified. Implementations can be created to integrate with existing legacy security realms, integrate with the security realm of the server in which Tomcat is embedded, or for custom, application-specific, security realms.
- Resources. Normally, a container implements methods like `getResource()` and `getResourceAsStream()` by accessing data resources included with the web application. In some environments (such as Tomcat connected to a web server such as Apache), it is desireable to use the web server's facilities to access these resources instead. This interface allows such accesses to be customized.

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

- Manager. Representation of a collection manager for a set of active sessions associated with a container. This interface can be extended to support advanced capabilities such as session persistence across server restarts, swapping of active but idle sessions, and relocation of sessions in a distributed server environment.
- Session. Representation of an `HttpSession` that is utilized throughout the Tomcat servlet container to represent the session and its associated user data objects.

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

- The implementation class of a component may optionally implement the Lifecycle interface, if it wishes to take advantage of the configuration services.
- An object factory that creates a new component will perform the following logic in its creation method:
  - Instantiate a new instance of the component implementation class (usually based on a fully qualified Java class name that is configured as a parameter of the factory component).
  - Determine whether or not the new component implements the Lifecycle interface (usually by use of the `instanceof` operator). If the component does not implement Lifecycle, the remaining steps are skipped.
  - Call the new component's `configure()` method, passing an XML document fragment (`org.w3c.dom.DocumentFragment`) containing the configuration parameters for this component. This approach is based on the assumption that XML-based configuration files will be utilized to configure Tomcat components. [IMHO it is overkill to convert such configuration parameters (which are typically processed only once) into an internal `XmlTree` architecture, as is done in the current Tomcat approach.]
  - Call the new component's `start()` method. Successful return from this method (no exception thrown) indicates that the component has been completely configured, and is available for normal use by other components.
- The component's public methods will be called as required during the lifetime of this component (often the life of the Tomcat deployment itself, but this is not required).
- When the server (usually a parent component) wishes to retire a particular component from active use, it will perform the following logic:
  - Change its own internal state to indicate that this component is no longer available.
  - Determine whether or not the component implements the Lifecycle interface (usually by use of the `instanceof` operator). If the component does not implement Lifecycle, the remaining steps are skipped.
  - Call the component's `stop()` method. This call tells the component to gracefully clean up its resources (for example, a JDBC connection pool would close and release all of its allocated connections to the underlying database).

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

- `org.apache.tomcat` - Contains the interface definitions for all of the core components described in this document.
- `org.apache.tomcat.adapter.xxxxx` - Contains implementations of the each Adapter component, and their associated Request and Response components, in subpackages named for each implementation.
- `org.apache.tomcat.container.xxxxx` - Contains implementations of the various Container interfaces, and the corresponding support component implementations, in subpackages named for each implementation.
- `org.apache.tomcat.session.xxxxx` - Contains implementations of the Manager and Session interfaces, in subpackages named for each implementation.

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:

- Custom Request Validation. In an ISP environment, the server administrator might wish to add an Interceptor that performs site specific validation on all incoming requests (if the Interceptor is attached at the Engine level, for example), or on all requests to a particular customer's virtual host (if the Interceptor is attached at the Host level, for example).
- Enforce Authentication and Access Control Constraints. An Interceptor can be defined to enforce the `<security-constraint>` elements included in a web application's deployment descriptor. Such an Interceptor would include logic in the `preService()` method to examine the incoming Request properties. If an authentication challenge is required, the `preService()` method would complete the corresponding Response with the correct challenge (based on the configuration specified in the corresponding `<login-config>` element), and return `false` to bypass any remaining processing for this Request.
- Integration with Application Server Functionality. An application server environment might provide services, addressable via a URI, while wanting to use standard servlet support as well. An Interceptor could be created that would examine the incoming Request for those requiring internal services, call the internal service API, construct an appropriate Response, and return `false` from the `preService()` method, to skip remaining request processing within Tomcat. Requests that should be processed via normal servlet API functionality are passed on by having the `preService()` method return `true` instead.
- Performance Measurement. The processing time required for each incoming request can be calculated by starting a timer in the `preService()` method, and stopping that timer in the corresponding `postService()` method of the same Interceptor. Measuring performance at this point is useful, because it eliminates the variable overhead of communications adapters. Such measurements may also contribute to decisions made by a load balancing module in an application server that supports distributed processing.
- URI Hit Counter. An Interceptor with an appropriate `postService()` method could be utilized to capture hit count statistics for requested URIs, and store this information in some persistent storage location.
- Web Server Style Access Log. When Tomcat is deployed in a stand alone environment with an HTTP communications adapter, it may be desireable to produce an access log file, in a format similar to that created by standard web servers such as Apache. Such a log can be easily created by an Interceptor with the log file writing logic embedded in the `postService()` method. You can have an access log per web application by attaching this Interceptor to the corresponding Context instance, or a combined log file by attaching it at either the Host or Engine level, with no changes to any of the Container implementation classes.

## 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`:

- ContainerBase. Abstract base class that contains the logic common to all Container implementations. This class can easily be subclassed to provide container-specific logic, because only the `getInfo()` and `service()` methods must be implemented (although additional methods may be overridden if necessary).
- StandardContext. Example implementation of the Context interface.
- StandardEngine. Example implementation of the Engine interface.
- StandardHost. Example implementation of the Host interface.

In package `org.apache.tomcat.logger`:

- FileLogger. Implementation of Logger that writes to disk files in a specified directory, automatically switching to a new file at midnight each night.
- SystemErrLogger. Implementation of Logger that writes to the System.err output stream.
- SystemOutLogger. Implementation of Logger that writes to the System.out output stream.

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.