Tomcat(8) Container

A container is a module that processes the request for a servlet and populates the response object for web client. A container is represented by org.apache.catalina.Container interface and there are four types of containers: Engine, Host, Context, and Wrapper. This section covers Context and Wrapper. We start with discussion of the Container interface, followed by the pipelining mechanism in a container. it then looks at the Wrapper and Context interfaces. Two applications conclude this section by presenting a simple wrapper and a simple context respectively.

The Container Interface

A container must implements the org.apache.catalina.Container. As you have seen in previous chapter, you pass an instance of Container to setContainer method of Connector so that the connector can call the invoke method of Container.

The first things to note that containers in Catalina is there are for types of containers at difference conceptual level:

  • 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.

Each conceptual level above is represents by and interface in org.apache.catalina package. These interfaces are Engine, Host, Context, and Wrapper. All the four extend the Container interface. Standard implementations of the four containers are StandardEngine, StandardHost, StandardContext, StandardWrapper, respectively, all of which are part of the org.apache.catalina.core package. All the implementation classes derive from the abstract class ContainerBase.

ContainerInterface_thumb2

A functional deployment does not need all the four types of containers. A container can have zero or more child containers of the lower level. However, a wrapper, being the lowest in the 'hierarchy',  can not contain a child container. A container can also contains a number of other components such as Loader, Logger, Manager, Realm and Resources.

More interestingly, the Container interface has been designed in such a way what 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 pipeline and a set of valves in a container.

Pipelining Tasks

This section explains what happens when a container’s invoke method is called by the connector. This section then discusses in the sub-section the four related interfaces in the org.apache.catalina package: Pipeline, Valve, ValveContext, and Contained.

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

Valve -1 Valve -2 Valve –n

If you understand servlet filters, it is not hard to imagine how a pipeline and its valves work. A pipeline is like a filter chain and each valve is a filter. Like a filter, a valve can manipulate the request and response object 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 pipeline invokes the first valve in it, which will invoke the next valve, and so on. until there is no more valve in the pipeline. You might imagine that you could have the following pseudo code inside the pipeline’s invoke method.

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

However, the Tomcat designer chose a difference approach by introducing the org.apache.catalina.ValveContext interface. Here is how it works.

A container does not hard code what it is supported 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 Container interface’s invoke method.

// invoke method of Pipeline interface
public void invoke(Request request, Response response) throws IOException, ServletException;

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

// invoke method of ContainerBase
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 its as well as its basic valve must be invoked once. The pipeline does this by creating an instance of ValveContext interface. The ValveContext is implemented as an inner class of pipeline so that the ValveContext has access to all members of the pipeline. The most important method of ValveContext interface is invokeNext:

// invokeNext method of ValveContext
public void invokeNext(Request request, Response response) throws IOException, ServletException

After creating an instance of ValveContext, the pipeline calls the invokeNext method of the ValveContext. The ValveContext will first invoke the first valve in the pipeline and the first valve will invoke the next valve before the first valve does it task. The ValveContext passes itself to each valve so that the valve can call the invokeNext method of the ValveContext. Here is the signature of the invoke method of the Valve interface.

// invoke method of Valve interface
/**
* Perform request processing as required by this Valve.
*
* An individual Valve MAY perform the following actions, in the specified order:
* <li>Examine and/or modify the properties of the specified Request and  Response.
* <li>Examine the properties of the specified Request, completely generate the corresponding Response, and return control to the caller.
* <li>Examine the properties of the specified Request and Response, wrap either or both of these objects to supplement their functionality,  and pass them on.
* <li>If the corresponding Response was not generated (and control was not returned, call the next Valve in the pipeline (if there is one) by executing <code>context.invokeNext()</code>.
* <li>Examine, but not modify, the properties of the resulting Response (which was created by a subsequently invoked Valve or Container).
*
* A Valve MUST NOT do any of the following things:
* <li>Change request properties that have already been used to direct the flow of processing control for this request (for instance, trying to change the virtual host to which a Request should be sent from a pipeline attached to a Host or Context in the standard implementation).
* <li>Create a completed Response <strong>AND</strong> pass this Request and Response on to the next Valve in the pipeline.
* <li>Consume bytes from the input stream associated with the Request, unless it is completely generating the response, or wrapping the request before passing it on.
* <li>Modify the HTTP headers included with the Response after the invokeNext() method has returned.
* <li>Perform any actions on the output stream associated with the specified Response after the invokeNext()  method has  returned.
*
* @param request The servlet request to be processed
* @param response The servlet response to be created
* @param context The valve context used to invoke the next valve in the current processing pipeline
*
* @exception IOException if an input/output error occurs, or is thrown  by a subsequently invoked Valve, Filter, or Servlet
* @exception ServletException if a servlet error occurs, or is thrown by a subsequently invoked Valve, Filter, or Servlet
*/
public void invoke(Request request, Response response, ValveContext context) throws IOException, ServletException;

The org.apache.catalina.core.StandardPipeline class is the implementation of Pipeline in all containers. In Tomcat 4, this class has an inner class called StandarPipelineValveContext that implements the ValveContext interface.

// invoke method of StandardPipeline class
/**
  * Cause the specified request and response to be processed by the Valves associated with this pipeline, until one of these valves causes the response to be created and returned.  The implementation must ensure
  * that multiple simultaneous requests (on different threads) can be processed through the same Pipeline without interfering with each other's control flow.
  *
  * @param request The servlet request we are processing
  * @param response The servlet response we are creating
  *
  * @exception IOException if an input/output error occurs
  * @exception ServletException if a servlet exception is thrown
  */
public void invoke(Request request, Response response)  throws IOException, ServletException { 
     // Invoke the first Valve in this pipeline for this request
    (new StandardPipelineValveContext()).invokeNext(request, response); 
}

// invokeNext method of StandardPipelineValveContext class inside StandardPipeline class
   /**
     * Cause the <code>invoke()</code> method of the next Valve that is  part of the Pipeline currently being processed (if any) to be executed, passing on the specified request and response objects plus this <code>ValveContext</code> instance.  Exceptions thrown by a subsequently executed Valve (or a Filter or Servlet at the  application level) will be passed on to our caller.
     *
     * If there are no more Valves to be executed, an appropriate ServletException will be thrown by this ValveContext.
     *
     * @param request The request currently being processed
     * @param response The response currently being created
     *
     * @exception IOException if thrown by a subsequent Valve, Filter, or
     *  Servlet
     * @exception ServletException if thrown by a subsequent Valve, Filter,
     *  or Servlet
     * @exception ServletException if there are no further Valves
     *  configured in the Pipeline currently being processed
     */
    public void invokeNext(Request request, Response response) throws IOException, ServletException { 
        int subscript = stage;
        stage = stage + 1; 
        // Invoke the requested Valve for the current request thread
        if (subscript < valves.length) {
            valves[subscript].invoke(request, response, this);
        } else if ((subscript == valves.length) && (basic != null)) {
            basic.invoke(request, response, this);
        } else {
            throw new ServletException (sm.getString("standardPipeline.noValve"));
        } 
    }
}

The invokeNext method uses subscript and stage to remember which valve is being invoke. When first invoked from pipeline’s invoke method, the value of subscript is 0 and the value of stage is 1. Therefore, the first valve(array index 0) is invoked. The first valve in the pipeline receives the ValveContext instance and invokes its invokeNext method. This time, the value of subscript is 1 so that the second valve is invoked, and so on.

When the invokeNext method is called from the last valve, the value of subscript is equal to the number of valves. As a result, the basic valve is invoked.

Tomcat 5 remove the StandardPipelineValveContext class from the StandardPipeline and instead relies on the org.apache.catalina.core.StandarValveContext class.

import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.catalina.Request;
import org.apache.catalina.Response;
import org.apache.catalina.Valve;
import org.apache.catalina.ValveContext;
import org.apache.catalina.util.StringManager;

public final class StandardValveContext implements ValveContext {
    protected static StringManager sm = StringManager.getManager(Constants.Package);
    protected String info = "org.apache.catalina.core.StandardValveContext/1.0";
    protected int stage = 0;
    protected Valve basic = null;
    protected Valve valves[] = null;
    public String getInfo() {
        return info;
    }

    public final void invokeNext(Request request, Response response) throws IOException, ServletException {
        int subscript = stage;
        stage = stage + 1;
        // Invoke the requested Valve for the current request thread
        if (subscript < valves.length) {
            valves[subscript].invoke(request, response, this);
        } else if ((subscript == valves.length) && (basic != null)) {
            basic.invoke(request, response, this);
        } else {
            throw new ServletException (sm.getString("standardPipeline.noValve"));
        }
    }
    void set(Valve basic, Valve valves[]) {
        stage = 0;
        this.basic = basic;
        this.valves = valves;
    }
}

Can you see similarities between the standardPipelineValveContext class in Tomcat 4 and the StandardValveContext class in Tomcat 5?

We will now explain the Pipeline, Valve, and ValveContext interfaces in more detail. Also discussed is the org.apache.catalina.Contained interface that a valve class normally implements.

The Pipeline Interface

The first method of the Pipeline interface that we mentioned was the invoke method, which a container calls to start invoking the valves in the pipeline and the basic valve. The Pipeline interface allows you to add a new valve through its addValve method and remove a valve by calling its removeValve method. The basic valve, which is invoked last, is responsible for processing the request and corresponding response. The Pipeline interface code is given as follows:

//The Pipeline interface
package org.apache.catalina;
import java.io.IOException;
import javax.servlet.ServletException;

public interface Pipeline {
    public Valve getBasic();
    public void setBasic(Valve valve);
    public void addValve(Valve valve);
    public Valve[] getValves();
    public void invoke(Request request, Response response) throws IOException, ServletException;
    public void removeValve(Valve valve);
}

The Valve interface

The Valve interface represents a valve, the components responsible for processing a request. This interface has two methods: invoke, getInfo. The invoke method has been discussed above. The getInfo method returns information about the valve implementation. The Valve interface source code is given as the following:

//The Valve interface
package org.apache.catalina;
import java.io.IOException;
import javax.servlet.ServletException;

public interface Valve {
    public String getInfo();
    public void invoke(Request request, Response response, ValveContext context) throws IOException, ServletException;
}

The ValveContext interface

This interface has two methods: the invokeNext method which has been discussed above, and getInfo method, which returns information about ValveContext implementation. The ValveContext interface source code is given in the following:

//The ValveContext interface

package org.apache.catalina;
import java.io.IOException;
import javax.servlet.ServletException;

public interface ValveContext {
    public String getInfo();
    public void invokeNext(Request request, Response response) throws IOException, ServletException;
}

The Contained interface

A valve class can optionally implement the org.apache.catalina.Contained interface. This interface specifies that the implementing class is associated with at most one container instance.

//The Contained interface
package org.apache.catalina;
public interface Contained {
    public Container getContainer();
    public void setContainer(Container container);
}

The Wrapper class

The org.apache.catalina.Wrapper interface represents a wrapper. A Wrapper is a container representing an individual servlet definition. The Wrapper interface extends the Container interface and adds a number of methods. Implementations of Wrapper are responsible for managing the servlet life cycle for their underlying servlet class, i.e. calling the init, service, and destroy methods of servlet. Since a wrapper is the lowest container you must not add a child to it. A Wrapper throws an IllegalArgumentException if its addChild method is called.

Important methods in the Wrapper interface include allocate and load. The allocate method allocates an initialized instance of the servlet wrapper represents. The allocated method must also take into account whether or not the servlet implements javax.servlet.singleThreadModel interface, but we will discuss later. The load method load and initializes an instance of the servlet the wrapper represents. The signatures of the allocated and load methods are as follows:

public javax.servlet.Servlet allocate() throws
javax.servlet.ServletException;
public void load() throws javax.servlet.ServletException;

The Context Interface

A context is a container that represents a web application. A context usually has one or more wrapper containers. Important methods include a addWrapper, createWrapper, etc. This interface will be covered in more detail in chapter 12.

Sequence Diagram

Container-Interface-sequence

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章