Chapter 5: Container

A container is a module that processes the requests for a servlet and populates the response objects for web clients. A container is represented by the org.apache.catalina.Container interface and there are four types of containers: Engine, Host, Context, Wrapper. This chapter covers Context and Wrapper and leaves Engine and Host to Chapter 13.  This chapter starts with the discussion of the Container interface, followed by the piplining mechanism in a container. It then looks at the Wrapper and Context interfaces by presenting 2 applications.

The Container Interface

A container must implements org.apache.catalina.Container interface. Recall the folling the code from the Bootstrap class in the application in Chapter 4.

HttpConnector connector = new HttpConnector();
SimpleContainter container = new SimpleContainer();
connector.setContainer(container);

The first thing to note about containers in Catalina is that there are four types of containers at different conceptual levels:

  • Engine. Represents the entire Catalina servlet engine.
  • Host. Represents a virtual host with a number of contexts
  • Context. Represents a web application. A context contains one or more wrappers.
  • Wrapper. Represents an individual servlet.

Figure 5.1 shows the class diagram of the Container interface and its sub-interfaces and implementations. Note that all interfaces are part of the org.apache.catalina package and all classes are part of the org.apache.catalina.core package. 

A functional Catalina deployment does not need all the 4 types of containers. For example, the container module in this chapter's first application consists of only a wrapper. 

A container can have zero or more child containers of the lower level. For instance, a context normally has one or more wrappers and a host can have zero or more context. However, a wrapper, being the lowest in the hierachy, cannot contain a child container. There are serveral methods in the Container interface.

public void addChild(Container child);
public void removeChild(Conainter child);
public Container findChind(String name);
public Container[] findChildren();

A container can also contain a number of support components such as Loader, Logger, Manager, Realm, and Resources. We will discuss thest components in later chapter. 

More interestingly, the Container interface has been designed in such a way that at the time of deployment, a Tomcat administrator can determine what a container performs by editing the configuration file(server.xml). This is achieved by introducing a pipline and a set of valves in a container, which we'll disucss in the next section, "Piplining Tasks".

Note: The container interface in Tomcat 4 is slightly different from that in Tomcat 5. For example, in Tomcat 4 this interface has a map method, which no longer exists in the Container interface in Tomcat 5.

Piplining Tasks

This section explain what happens when a container's invoke method is called by the connector.

A pipeline contains tasks that the container will invoke. A valve represents a specific task. There is one basic valve in a container's pipeline, but you can add as many valves as you can. The number of valves is defined to be the number of additional valves, i.e. not including the basic valve. Vavles can be added dynamically by editing Tomcat's configuration file(server.xml).

If you understand servlet filters, it is no hard to imagine how a pipeline and its valves work. A pipeline is like a fiter chain and each valve is filter. Like a filter, a valve can manipulate the request and response objects passed to it. After a valve finishes processing, it calls the next valve in the pipeline. The basic valve is always called the last.

A container can have one pipeline. 

When a container's invoke method is called, the container passes processing to its pipeline and the pipeline invokes the first valve in it, which will then invoke the next valve, and so on, until there is no more valve in the pipeline. You might imagine that you have the following pseudo code inside the pipeline's invoke method:

// invoke each valve added to the pipeline
for(i=0; i<valves.length; i++) {
  valves[i].invoke(...);
}
// then, invoke the basic valve
basicValve.invoke(....);

Howere, the tomcat designer chose a different approach by introducing the org.apache.cataline.ValveContext interface. Here is how it works.

A container does not hard code what it is supposed to do when its invoke method is called by the connector. Instead, the container calls its pipeline's invoke method. The Pipeline interface's invoke method has the following signature, which is exactly the same as the invoke method of the Container interface. 

public void invoke(Request request, Response response) throws IOException, ServletException;

Here is the implementation of the Container interface's invoke method in the org.apache.catalina.core.ContainerBase class.

public void invoke(Request request, Response response) throws IOException, ServletException {
  pipeline.invoke(request, response);  
}

where pipeline is an instance of the Pipeline interface inside the container.

Now, the pipeline has to make sure that all the valves added to it as well as its basic valve must be invoked once. The pipeline does this by creating an instance of the ValveContext interface. The ValveContext is implemented as an inner class of the pipeline so that the ValveContext has access to all members of the pipeline. The most important method of the ValveContext interface is:

public void invokeNext(Request request, Response response) throws IOException, ServletException;

 

The Wrapper Interface

The Context Interface

The Wrapper Application

The Context Application

Summary