【Tomcat】《How Tomcat Works》英文版GPT翻译(第十二章)

2024-01-19 17:19:56 浏览数 (2)

Chapter 12: StandardContext

Overview(概述)

As you have witnessed in the previous chapters, a context represents a web application and contains one or more wrappers, each of which represents a servlet definition. However, a context requires other components as well, notably a loader and a manager. This chapter explains the org.apache.catalina.core.StandardContext class, which represents Catalina's standard implementation of the Context interface.

正如您在前几章中所见,上下文表示一个Web应用程序,并包含一个或多个包装器,每个包装器表示一个 servlet 定义。

但是,上下文还需要其他组件,特别是加载器和管理器。

本章介绍了org.apache.catalina.core.StandardContext类,它表示Catalina对上下文接口的标准实现。

We first have a look at the StandardContext object instantiation and configuration. We then discuss the related classes StandardContextMapper (in Tomcat 4) and ContextConfig. Next, we look at the sequence of method invocation for each incoming HTTP request. Then, we revisit the StandardContext class by discussing its important properties. Finally, the last section of this chapter discusses the backgroundProcess method in Tomcat 5.

我们首先看一下 StandardContext 对象的实例化和配置。

然后,我们讨论相关的类 StandardContextMapper(在Tomcat 4中)和 ContextConfig

接下来,我们查看每个传入HTTP请求的方法调用顺序。

然后,我们通过讨论其重要属性来重新访问 StandardContext 类。

最后,本章的最后一节讨论了Tomcat 5中的backgroundProcess方法。

Note There is no application accompanying this chapter. StandardContext is used in Chapter 11 and previous chapters. 注意 本章没有附带应用程序。StandardContext在第11章和前几章中使用。

StandardContext Configuration(标准上下文配置)

After a StandardContext instance is constructed, its start method must be called to make the instance available to service incoming HTTP requests. For one reason or another, a StandardContext object might fail to start. If this happens, the available property of the StandardContext object will be set to false. The available property of course indicates the availability of the StandardContext.

构造 StandardContext 实例后,必须调用其 start 方法,使该实例能够为传入的HTTP请求提供服务。

由于某种原因,StandardContext 对象可能无法启动。

如果发生这种情况,StandardContext 对象的 available 属性将被设置为 false

当然,available 属性表示 StandardContext 的可用性。

For the start method to succeed, the StandardContext object must be configured properly. In a Tomcat deployment, the configuration of StandardContext does a number of things. It prepares the StandardContext so that the context can read and parse the default web.xml file, which is located in the �TALINA_HOME%/conf directory and applies to all applications deployed. It also makes sure that the StandardContext instance can process application-level web.xml files. In addition, the configuration installs an authenticator valve and a certificate valve.

为了使 start 方法成功,必须正确配置 StandardContext 对象。

Tomcat 部署中,配置 StandardContext 执行了一些操作。

它准备 StandardContext 以便上下文能够读取和解析位于�TALINA_HOME%/conf目录中的默认web.xml文件,并适用于所有已部署的应用程序。

它还确保 StandardContext 实例能够处理应用程序级别的web.xml文件。

此外,配置还安装了一个身份验证阀门和一个证书阀门。

Note More details on StandardContext configuration are discussed in Chapter 15.

One of the properties in the StandardContext class is the configured property, which is a boolean that indicates whether or not a StandardContext instance is configured properly. StandardContext uses an event listener as its configurator. When the start method on a StandardContext instance is called, one of the things it does is fire a lifecycle event. This event invokes the listener that in turn will configure the StandardContext instance. If configuration is successful, the listener will set the configured property to true. Otherwise, the StandardContext instance will refuse to start and thus will be unavailable to service HTTP requests.

StandardContext 类中的一个属性是 configured 属性,它是一个布尔值,指示一个 StandardContext 实例是否被正确配置。

StandardContext 使用一个事件监听器作为其配置器。

当调用 StandardContext 实例的 start 方法时,其中一件事情是触发一个生命周期事件。

这个事件调用监听器,然后配置 StandardContext 实例。如果配置成功,监听器将把 configured 属性设置为 true

否则,StandardContext 实例将拒绝启动,因此将无法为 HTTP 请求提供服务。

In Chapter 11 you have seen an implementation of a lifecycle listener added to the StandardContext instance. Its type is ch11.pyrmont.core.SimpleContextConfig and it simply sets the configured property of the StandardContext to true without doing anything else, just to fool the StandardContext into thinking that it has been configured correctly. In a Tomcat deployment, the lifecycle listener that configures the StandardContext is of type org.apache.catalina.startup.ContextConfig, which will be explained in detail in Chapter 15.

在第 11 章中,你已经看到了一个生命周期监听器的实现,它被添加到了 StandardContext 实例中。

它的类型是 ch11.pyrmont.core.SimpleContextConfig,它只是将 StandardContextconfigured 属性设置为 true,而不做其他任何事情,只是为了让 StandardContext 以为它已经被正确配置了。

Tomcat 部署中,配置 StandardContext 的生命周期监听器的类型是 org.apache.catalina.startup.ContextConfig,在第 15 章中将对其进行详细解释。

Now that you understand the importance of configuring a StandardContext, let's look at the StandardContext class in more detail, starting from its constructor.

现在你已经了解了配置 StandardContext 的重要性,让我们更详细地了解 StandardContext 类,从它的构造函数开始。

StandardContext Class's Constructor(StandardContext 类的构造函数)

Here is the StandardContext class's constructor:

下面是 StandardContext 类的构造函数:

代码语言:javascript复制
public StandardContext() {
 super();
 pipeline.setBasic(new StandardContextValve());
 namingResources.setContainer(this);
}

The most important thing to note in the constructor is that the StandardContext's pipeline is given a basic valve of type org.apache.catalina.core.StandardContextValve. This valve will handle every HTTP request that comes through the connector.

在构造函数中需要注意的最重要的一点是,StandardContext 的管道被赋予了一个类型为org.apache.catalina.core.StandardContextValve的基本阀门。

这个阀门将处理通过连接器传入的每个HTTP请求。

Starting StandardContext(启动 StandardContext)

The start method initializes the StandardContext instance and gives the lifecycle listener a chance to configure the StandardContext instance. The listener will set the configured property to true upon a successful configuration. At the end of the day, the start method sets the available property to either true or false. A value of true means the StandardContext instance has been configured properly and all related child containers and components have been started successfully and therefore the StandardContext instance is ready to service incoming HTTP requests. A value of false indicates otherwise.

start方法初始化StandardContext实例,并让生命周期监听器有机会配置StandardContext实例。

监听器将在成功配置后将configured属性设置为true。

最终,start方法将available属性设置为true或false。

true表示StandardContext实例已经被正确配置,并且所有相关的子容器和组件已经成功启动,因此StandardContext实例已经准备好为传入的HTTP请求提供服务。

false表示相反的情况。

The StandardContext class employs a boolean called configured that is initially set to false. If the lifecycle listener is successful in performing its tasks of configuring a StandardContext instance, it will set the StandardContext's configured property to true. Towards the end of the start method, the StandardContext checks the value of the configured variable. If it is true, the StandardContext has started successfully. Otherwise, the stop method is called to stop all the components that have been started by the start method.

StandardContext类使用一个名为configured的布尔变量,最初设置为false。

如果生命周期监听器成功执行其配置StandardContext实例的任务,它将把StandardContext的configured属性设置为true。

在start方法的末尾,StandardContext检查configured变量的值。

如果为true,表示StandardContext已经成功启动。

否则,将调用stop方法停止所有被start方法启动的组件。

The start method of the StandardContext class in Tomcat 4 is given in Listing 12.1.

Tomcat 4中StandardContext类的start方法如下所示:

Listing 12.1: The start method of the StandardContext class in Tomcat 4

列表12.1:Tomcat 4中StandardContext类的start方法

代码语言:javascript复制
  
public synchronized void start() throws LifecycleException {  
    if (started)  
        throw new LifecycleException  
                (sm.getString("containerBase.alreadyStarted", logName()));  
    if (debug >= 1)  
        log("Starting");  
    // Notify our interested LifecycleListeners    
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);  
    if (debug >= 1)  
        log("Processing start(), current available="   getAvailable());  
    setAvailable(false);  
    setConfigured(false);  
    boolean ok = true;  
    // Add missing components as necessary    
if (getResources() == null) { // (1) Required by Loader    
if (debug >= 1)  
            log("Configuring default Resources");  
        try {  
            if ((docBase != null) && (docBase.endsWith(".war")))  
                setResources(new WARDirContext());  
            else                setResources(new FileDirContext());  
        } catch (IllegalArgumentException e) {  
            log("Error initializing resources: "   e.getMessage());  
            ok = false;  
        }  
    }  
    if (ok && (resources instanceof ProxyDirContext)) {  
        DirContext dirContext =  
                ((ProxyDirContext) resources).getDirContext();  
        if ((dirContext != null)  
                && (dirContext instanceof BaseDirContext)) {  
            ((BaseDirContext) dirContext).setDocBase(getBasePath());  
            ((BaseDirContext) dirContext).allocate();  
        }  
    }  
    if (getLoader() == null) { // (2) Required by Manager  
        if (getPrivileged()) {  
            if (debug >= 1)  
                log("Configuring privileged default Loader");  
            setLoader(new WebappLoader(this.getClass().getClassLoader()));  
        }  
        else {  
            if (debug >= 1)  
                log("Configuring non-privileged default Loader");  
            setLoader(new WebappLoader(getParentClassLoader()));  
        }  
    }  
    if (getManager() == null) { // (3) After prerequisites  
        if (debug >= 1)  
            log("Configuring default Manager");  
        setManager(new StandardManager());  
    }  
    // Initialize character set mapper  
    getCharsetMapper();  
    // Post work directory  
    postWorkDirectory();  
    // Reading the "catalina.useNaming" environment variable  
    String useNamingProperty = System.getProperty("catalina.useNaming");  
    if ((useNamingProperty != null)  
            && (useNamingProperty.equals("false"))) {  
        useNaming = false;  
    }  
    if (ok && isUseNaming()) {  
        if (namingContextListener == null) {  
            namingContextListener = new NamingContextListener();  
            namingContextListener.setDebug(getDebug());  
            namingContextListener.setName(getNamingContextName());  
            addLifecycleListener(namingContextListener);  
        }  
    }  
    // Binding thread  
    ClassLoader oldCCL = bindThread();  
    // Standard container startup  
    if (debug >= 1)  
        log("Processing standard container startup");  
    if (ok) {  
        try {  
            addDefaultMapper(this.mapperClass);  
            started = true;  
            // Start our subordinate components, if any  
            if ((loader != null) && (loader instanceof Lifecycle))  
                ((Lifecycle) loader).start();  
            if ((logger != null) && (logger instanceof Lifecycle))  
                ((Lifecycle) logger).start();  
            // Unbinding thread  
            unbindThread(oldCCL);  
            // Binding thread  
            oldCCL = bindThread();  
            if ((cluster != null) && (cluster instanceof Lifecycle))  
                ((Lifecycle) cluster).start();  
            if ((realm != null) && (realm instanceof Lifecycle))  
                ((Lifecycle) realm).start();  
            if ((resources != null) && (resources instanceof Lifecycle))  
                ((Lifecycle) resources).start();  
            // Start our Mappers, if any  
            Mapper mappers[] = findMappers();  
            for (int i = 0; i < mappers.length; i  ) {  
                if (mappers[i] instanceof Lifecycle)  
                    ((Lifecycle) mappers[i]).start();  
            }  
            // Start our child containers, if any  
            Container children[] = findChildren();  
            for (int i = 0; i < children.length; i  ) {  
                if (children[i] instanceof Lifecycle)  
                    ((Lifecycle) children[i]).start();  
            }  
            // Start the Valves in our pipeline (including the basic),  
            // if any            if (pipeline instanceof Lifecycle)  
                ((Lifecycle) pipeline).start();  
            // Notify our interested LifecycleListeners  
            lifecycle.fireLifecycleEvent(START_EVENT, null);  
            if ((manager != null) && (manager instanceof Lifecycle))  
                ((Lifecycle) manager).start();  
        }  
        finally {  
            // Unbinding thread  
            unbindThread(oldCCL);  
        }  
    }  
    if (!getConfigured())  
        ok = false;  
    // We put the resources into the servlet context  
    if (ok)  
        getServletContext().setAttribute  
                (Globals.RESOURCES_ATTR, getResources());  
    // Binding thread  
    oldCCL = bindThread();  
    // Create context attributes that will be required  
    if (ok) {  
        if (debug >= 1)  
            log("Posting standard context attributes");  
        postWelcomeFiles();  
    }  
    // Configure and call application event listeners and filters  
    if (ok) {  
        if (!listenerStart())  
            ok = false;  
    }  
    if (ok) {  
        if (!filterStart())  
            ok = false;  
    }  
    // Load and initialize all "load on startup" servlets  
    if (ok)  
        loadOnStartup(findChildren());  
    // Unbinding thread  
    unbindThread(oldCCL);  
    // Set available status depending upon startup success  
    if (ok) {  
        if (debug >= 1)  
            log("Starting completed");  
        setAvailable(true);  
    }  
    else {  
        log(sm.getString("standardContext.startFailed"));  
        try {  
            stop();  
        }  
        catch (Throwable t) {  
            log(sm.getString("standardContext.startCleanup"), t);  
        }  
        setAvailable(false);  
    }  
    // Notify our interested LifecycleListeners  
    lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);  
}

Note In Tomcat 5, the start method is similar to the one in Tomcat 4. However, it includes some JMX-related coding, which will not make sense unless you understand JMX. JMX is discussed in Chapter 20 and if you are new to JMX you are welcomed to visit the start method in Tomcat 5 once you've read that chapter. 注意,在Tomcat 5中,start方法与Tomcat 4中的方法类似。然而,它包含一些与JMX相关的编码,除非你了解JMX,否则这些编码将没有意义。JMX在第20章中有详细讨论,如果你对JMX还不熟悉,欢迎在阅读该章节后再查看Tomcat 5中的start方法。

As you can see in Listing 12.1, here is the list of things that the start method does:

如你在12.1清单中所见,start方法执行以下操作:

  • Fires the BEFORE_START event.
  • Sets the availability property to false.
  • Sets the configured property to false.
  • Sets the resources.
  • Sets a loader
  • Sets a manager
  • Initializes the character set mapper.
  • Starts other components associated with this context
  • Starts child containers (wrappers)
  • Starts the pipeline
  • Starts the manager
  • Fires the START event. Here the listener (ContextConfig) will perform some configuration operations (discussed in Chapter 15). Upon a successful configuration, the ContextConfig will set the StandardContext instance's configured property to true.
  • Checks the value of the configured property. If it is true, do the following: call the postWelcomePages method, load child wrappers that need to be loaded at start-up, and set the availability property to true. If the configured variable is false, call the stop method.
  • Fire the AFTER_START event.
  • 触发BEFORE_START事件。
  • availability 属性设置为 false
  • configured 属性设置为 false
  • 设置资源。
  • 设置加载器。
  • 设置管理器。
  • 初始化字符集映射器。
  • 启动与此上下文相关的其他组件。
  • 启动子容器(包装器)。
  • 启动管道。
  • 启动管理器。
  • 触发START事件。此时监听器(ContextConfig)将执行一些配置操作(在第15章中讨论)。在成功配置后,ContextConfig将将StandardContext实例的configured属性设置为true。
  • 检查configured属性的值。如果为true,则执行以下操作:调用postWelcomePages方法,加载需要在启动时加载的子包装器,并将availability属性设置为true。如果configured变量为false,则调用stop方法。
  • 触发AFTER_START事件。

The Invoke Method(调用方法)

In Tomcat 4 the StandardContext's invoke method is called by the associated connector or, if the StandardContext is a child container of a host, by the host's invoke method. The StandardContext's invoke method first checks if reloading of the application is taking place, and, if so, waits until the application reloading has finished. It then calls the invoke method of its parent class, ContainerBase. Listing 12.2 presents the invoke method of the StandardContext class.

在Tomcat 4中,StandardContextinvoke 方法由关联的连接器调用,或者如果StandardContext是主机的子容器,则由主机的invoke方法调用。

StandardContextinvoke 方法首先检查应用程序是否正在重新加载,如果是,则等待直到应用程序重新加载完成。

然后调用其父类ContainerBase的invoke方法。

清单12.2呈现了 StandardContext 类的 invoke 方法。

Listing 12.2: The invoke method of the StandardContext Class

清单12.2:StandardContext 类的 invoke 方法

代码语言:javascript复制

public void invoke(Request request, Response response)
  throws IOException, ServletException {
 // Wait if we are reloading
 while (getPaused()) {
  try {
   Thread.sleep(1000);
  }
  catch (InterruptedException e) {
   ;
  }
 }
 // Normal request processing
 if (swallowOutput) {
  try {
   SystemLogHandler.startCapture();
   super.invoke(request, response);
  }
  finally {
   String log = SystemLogHandler.stopCapture();
   if (log != null && log.length() > 0) {
    log(log);
   }
  }
 }
 else {
  super.invoke(request, response);
 }
}

The getPaused method returns the value of the paused property, which is true if the application reloading is taking place. Application reloading is discussed in the next sub-section.

getPaused 方法返回 paused 属性的值,如果应用程序正在重新加载,则该值为 true

应用程序重新加载将在下一小节中讨论。

In Tomcat 5, the StandardContext class does not provide an implementation of the invoke method, therefore the ContainerBase class's invoke method is executed. The checking of application reloading has been moved to the invoke method of the StandardContextValve class.

在Tomcat 5中,StandardContext 类没有提供 invoke 方法的实现,因此执行 ContainerBase 类的 invoke 方法。

检查应用程序重新加载的操作已经移动到 StandardContextValve 类的 invoke 方法中。

StandardContextMapper(标准上下文映射器)

For each incoming request, the invoke method of the StandardContext's pipeline's basic valve will be called. The basic valve for a StandardContext is represented by the org.apache.catalina.core.StandardContextValve class. The first thing that StandardContextValve.invoke needs to do is obtain a wrapper that will handle the request.

对于每个传入的请求,将调用StandardContext管道的基本阀门的invoke方法。

StandardContext的基本阀门由org.apache.catalina.core.StandardContextValve类表示。

StandardContextValve.invoke需要做的第一件事是获取一个处理请求的包装器。

In Tomcat 4, the StandardContextValve instance looks in its containing StandardContext. The StandardContextValve uses the context's mapper to find a suitable wrapper. Once it obtains the wrapper, it calls the invoke method of the wrapper. Before delving into what the StandardContextValve does, this section presents an introduction to the mapper component.

在Tomcat 4中,StandardContextValve实例在其包含的StandardContext中查找。

StandardContextValve使用上下文的映射器来找到一个合适的包装器。一旦获得包装器,它就调用包装器的invoke方法。

在深入了解StandardContextValve的工作之前,本节将介绍映射器组件。

The ContainerBase class, the parent class of StandardContext, defines the addDefaultMapper method to add a default mapper as follows:

ContainerBase类,即StandardContext的父类,定义了addDefaultMapper方法来添加默认的映射器,如下所示:

代码语言:javascript复制
 protected void addDefaultMapper(String mapperClass) {
        // Do we need a default Mapper?
        if (mapperClass == null)
            return;
        if (mappers.size() >= 1)
            return;
        // Instantiate and add a default Mapper
        try {
            Class clazz = Class.forName(mapperClass);
            Mapper mapper = (Mapper) clazz.newInstance();
            mapper.setProtocol("http");
            addMapper(mapper);
        }
        catch (Exception e) {
            log(sm.getString("containerBase.addDefaultMapper", mapperClass),
                    e);
        }
    }

The StandardContext class calls the addDefaultMapper method from its start method, passing the mapperClass variable:

StandardContext 类在其 start 方法中调用 addDefaultMapper 方法,并传递 mapperClass 变量:

代码语言:javascript复制
public synchronized void start() throws LifecycleException {
 ...
 if (ok) {
 try {
 addDefaultMapper(this.mapperClass);
 ...
}

The StandardContext class defines the mapperClass variable as follows:

StandardContext 类对 mapperClass 变量的定义如下:

代码语言:javascript复制
private String mapperClass =
 "org.apache.catalina.core.StandardContextMapper";

You must then associate a mapper with a container by calling the mapper's setContainer method, passing the instance of the container. The implementation class of the org.apache.catalina.Mapper interface for StandardContextMapper is org.apache.catalina.core.StandardContextMapper. StandardContextMapper can only be associated with a context, as indicated by its setContainer method.

然后,您必须通过调用映射器的setContainer方法,并传递容器的实例来将映射器与容器关联起来。

对于StandardContextMapper的org.apache.catalina.Mapper接口的实现类是org.apache.catalina.core.StandardContextMapper。

StandardContextMapper只能与上下文相关联,如其setContainer方法所示。

代码语言:javascript复制
public void setContainer(Container container) {
 if (!(container instanceof StandardContext))
 throw new IllegalArgumentException
 (sm.getstring("httpContextMapper.container"));
 context = (StandardContext) container;
}

The most important method in a mapper is map, which returns a child container to handle an HTTP request. The signature of this method is as follows:

映射器中最重要的方法是 map,它会返回一个子容器来处理 HTTP 请求。

该方法的签名如下

代码语言:javascript复制
public Container map(Request request, boolean update)

In StandardContextMapper, the map method returns a wrapper that will handle a request. If the appropriate wrapper cannot be found, the map method returns null.

在StandardContextMapper中,map方法返回一个将处理请求的包装器。

如果找不到适当的包装器,map方法将返回null。

Now back to the discussion at the beginning of this section, the StandardContextValve instance calls the context's map method for each incoming HTTP request, passing a org.apache.catalina.Request object. The map method (in the parent class ContainerBase) first obtains a mapper for a particular protocol by calling the findMapper method, and then calling the mapper's map method.

现在回到本节开头的讨论,StandardContextValve实例为每个传入的HTTP请求调用上下文的map方法,并传递一个org.apache.catalina.Request对象。

map方法(在父类ContainerBase中)首先通过调用findMapper方法获取特定协议的映射器,然后调用映射器的map方法。

代码语言:javascript复制


    // Select the Mapper we will use
    Mapper mapper = findMapper(request.getRequest().getProtocol());
if (mapper == null)
            return (null);
// Use this Mapper to perform this mapping
return (mapper.map(request, update));
    The map method in StandardContextMapper first identifies the context-relative URI
    to be mapped:
    // Identify the context-relative URI to be mapped
    String contextPath =
            ((HttpServletRequest) request.getRequest()).getContextPath();
    String requestURI = ((HttpRequest) request).getDecodedRequestURI();
    String relativeURI = requestURI.substring(contextPath.length());
    It then attempts to obtain a wrapper by applying four matching rules:
    // Apply the standard request URI mapping rules from the specification
    Wrapper wrapper = null;
    String servletPath = relativeURI;
    String pathInfo = null;
    String name = null;
// Rule 1 -- Exact Match
if (wrapper == null) {
        if (debug >= 2)
            context.log(" Trying exact match");
        if (!(relativeURI.equals("/")))
            name = context.findServletMapping(relativeURI);
        if (name != null)
            wrapper = (Wrapper) context.findChild(name);
        if (wrapper != null) {
            servletPath = relativeURI;
            pathInfo = null;
        }
    }
// Rule 2 -- Prefix Match
if (wrapper == null) {
        if (debug >= 2)
            context.log(" Trying prefix match");
        servletPath = relativeURI;
        while (true) {
            name = context.findServletMapping(servletPath   "/*");
            if (name != null)
                wrapper = (Wrapper) context.findChild(name);
            if (wrapper != null) {
                pathInfo = relativeURI.substring(servletPath.length());
                if (pathInfo.length() == 0)
                    pathInfo = null;
                break;
            }
            int slash = servletPath.lastIndexOf('/');
            if (slash < 0)
                break;
            servletPath = servletPath.substring(0, slash);
        }
    }
// Rule 3 -- Extension Match
if (wrapper == null) {
        if (debug >= 2)
            context.log(" Trying extension match");
        int slash = relativeURI.lastIndexOf('/');
        if (slash >= 0) {
            String last = relativeURI.substring(slash);
            int period = last.lastIndexOf('.');
            if (period >= 0) {
                String pattern = "*"   last.substring(period);
                name = context.findServletMapping(pattern);
                if (name != null)
                    wrapper = (Wrapper) context.findChild(name);
                if (wrapper != null) {
                    servletPath = relativeURI;
                    pathInfo = null;
                }
            }
        }
    }
// Rule 4 -- Default Match
if (wrapper == null) {
        if (debug >= 2)
            context.log(" Trying default match");
        name = context.findServletMapping("/");
        if (name != null)
            wrapper = (Wrapper) context.findChild(name);
        if (wrapper != null) {
            servletPath = relativeURI;
            pathInfo = null;
        }
    
}

You might ask, how does the context have such information as the servlet mappings? Recall that the Bootstrap class in Chapter 11 adds two servlet mappings to the StandardContext.

你可能会问,上下文是如何获取到servlet映射信息的呢?

回想一下,第11章中的Bootstrap类向StandardContext添加了两个servlet映射。

代码语言:javascript复制
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");

It also adds the wrappers as children of the context:

它还会将包装器添加为上下文的子文件:

代码语言:javascript复制
context.addChild(wrapper1);
context.addChild(wrapper2);

Tomcat 5 removed the Mapper interface and its related classes. In fact, the StandardContextValve class's invoke method obtains the suitable wrapper from the request object:

Tomcat 5 删除了 Mapper 接口及其相关类。

事实上,StandardContextValve 类的 invoke 方法会从请求对象中获取合适的封装器:

代码语言:javascript复制
Wrapper wrapper = request.getWrapper();

which indicates that the mapping information is encapsulated in the request object.

表示映射信息已封装在请求对象中。

Support for Reloading(reload 支持)

The StandardContext class defines the reloadable property to indicate whether reloading of the application is enabled. When reloading is enabled, the application will be reloaded if the web.xml file changes or if one of the files in the WEB-INF/classes directory is recompiled.

StandardContext 类定义了 reloadable 属性,用于指示是否启用应用程序的重新加载。

当启用重新加载时,如果 web.xml 文件更改或者 WEB-INF/classes 目录中的文件重新编译,应用程序将会被重新加载。

The StandardContext class depends on its loader to reload the application. In Tomcat 4 the WebappLoader class, the implementation class of Loader in StandardContext, has a thread that checks the timestamps of all class and JAR files in the WEB-INF directory. All you need to do to start this thread is associate the WebappLoader with the StandardContext by calling its setContainer method. Here is the implementation of the setContainer method of the WebappLoader class in Tomcat 4:

StandardContext 类依赖于它的加载器来重新加载应用程序。

在 Tomcat 4 中,WebappLoader 类(StandardContext 中 Loader 的实现类)有一个线程,用于检查 WEB-INF 目录中所有类和 JAR 文件的时间戳。

要启动这个线程,您只需要通过调用其 setContainer 方法将 WebappLoader 与 StandardContext 关联起来即可。

以下是 Tomcat 4 中 WebappLoader 类的 setContainer 方法的实现:

代码语言:javascript复制
 public void setContainer(Container container) {
        // Deregister from the old Container (if any)
        if ((this.container != null) && (this.container instanceof Context))
            ((Context) this.container).removePropertyChangeListener(this);
        // Process this property change
        Container oldContainer = this.container;
        this.container = container;
        support.firePropertyChange("container", oldContainer,
                this.container);
        // Register with the new Container (if any)
        if ((this.container!=null) && (this.container instanceof Context)) {
            setReloadable( ((Context) this.container).getReloadable() );
            ((Context) this.container).addPropertyChangeListener(this);
        }
    }

Take a look at the last if block. If the container is a context, the setReloadable method will be called. This means, the value of the reloadable property of the WebappLoader instance will be the same as the value of the reloadable property of the StandardContext instance.

看一下最后的 if 块。如果容器是一个上下文,将调用 setReloadable 方法。

这意味着 WebappLoader 实例的 reloadable 属性的值将与 StandardContext 实例的 reloadable 属性的值相同。

Here is the setReloadable method implementation of the WebappLoader class:

以下是 WebappLoader 类的 setReloadable 方法的实现:

代码语言:javascript复制
public void setReloadable(boolean reloadable) {  
    // Process this property change  
    boolean oldReloadable = this.reloadable;  
    this.reloadable = reloadable;  
    support.firePropertyChange("reloadable",  
            new Boolean(oldReloadable), new Boolean(this.reloadable));  
    // Start or stop our background thread if required  
    if (!started)  
        return;  
    if (!oldReloadable && this.reloadable)  
        threadStart();  
    else if (oldReloadable && !this.reloadable)  
        threadStop();  
}

If the reloadable property changes from false to true, the threadStart method is called. If it changes from true to false, the threadStop method is called. The threadStart method starts a dedicated thread that continuously checks the timestamps of the class and JAR files in WEB-INF. The threadStop method stops this thread.

如果可重新加载属性从false更改为true,则调用threadStart方法。

如果从true更改为false,则调用threadStop方法。

threadStart方法启动一个专用线程,

该线程连续检查WEB-INF中类和JAR文件的时间戳。

threadStop方法停止此线程。

In Tomcat 5, the checking of classes' timestamps for reloading is performed by the backgroundProcess method, which is the topic of the next section.

在Tomcat 5中,用于重新加载类时间戳的检查是通过backgroundProcess方法执行的,这是下一节的主题。

The backgroundProcess Method(后台进程方法)

A context needs the help of other components, such as a loader and a manager. Often these components require a separate thread that handles background processing. For instance, a loader that support auto reload needs a thread to periodically check the timestamps of all class and JAR files in WEB-INF. A manager needs to a thread to check the expiration time of the session objects it manages. In Tomcat 4 those components end up having their own threads.

上下文需要其他组件的帮助,例如加载器和管理器。

通常,这些组件需要一个处理后台处理的单独线程。

例如,支持自动重新加载的加载器需要一个线程定期检查WEB-INF中所有类和JAR文件的时间戳。

管理器需要一个线程来检查其管理的会话对象的过期时间。

在Tomcat 4中,这些组件最终拥有自己的线程。

To save resources, Tomcat 5 uses a different approach. All background processes share the same thread. If a component or a container needs to have an operation done periodically, all it needs to do is write the code in its backgroundProcess method.

为了节省资源,Tomcat 5采用了不同的方法。

所有后台处理共享同一个线程。

如果组件或容器需要定期执行操作,它只需要在其backgroundProcess方法中编写代码。

The shared thread is created in a ContainerBase object. The ContainerBase class calls its threadStart method in its start method (i.e. when the container is started):

共享线程是在 ContainerBase 对象中创建的。

ContainerBase 类在其启动方法(即容器启动时)中调用其 threadStart 方法:

代码语言:javascript复制
protected void threadStart() {
 if (thread != null)
 return;
 if (backgroundProcessorDelay <= 0)
 return;
 threadDone = false;
 String threadName = "ContainerBackgroundProcessor["   toString()  
 "]";
 thread = new Thread(new ContainerBackgroundProcessor(), threadName);
 thread.setDaemon(true);
 thread.start();
}

The threadStart method constructs a new thread by passing an instance of ContainerBackgroundProcessor class that implements java.lang.Runnable. The ContainerBackgroundProcessor class is given in Listing 12.3.

threadStart 方法通过传递实现了 java.lang.Runnable 接口的 ContainerBackgroundProcessor 类的实例来构造一个新的线程。

ContainerBackgroundProcessor 类如清单 12.3 中所示。

Listing 12.3: The ContainerBackgroundProcessor class

清单 12.3:ContainerBackgroundProcessor 类

代码语言:javascript复制
 protected class ContainerBackgroundProcessor implements Runnable {
        public void run() {
            while (!threadDone) {
                try {
                    Thread.sleep(backgroundProcessorDelay * 1000L);
                }
                catch (InterruptedException e) {
                    ;
                }
                if (!threadDone) {
                    Container parent = (Container) getMappingObject();
                    ClassLoader cl =
                            Thread.currentThread().getContextClassLoader();
                    if (parent.getLoader() != null) {
                        cl = parent.getLoader().getClassLoader();
                    }
                    processChildren(parent, cl);
                }
            }
        }
        protected void processChildren(Container container, ClassLoader cl) {
            try {
                if (container.getLoader() != null) {
                    Thread.currentThread().setContextClassLoader
                            (container.getLoader().getClassLoader());
                }
                container.backgroundProcess();
            }
            catch (Throwable t) {
                log.error("Exception invoking periodic operation: ", t);
            }
            finally {
                Thread.currentThread().setContextClassLoader(cl);
            }
            Container[] children = container.findChildren();
            for (int i = 0; i < children.length; i  ) {
                if (children[i].getBackgroundProcessorDelay() <= 0) {
                    processChildren(children[i], cl);
                }
            }
        }
    }

The ContainerBackgroundProcessor class is an inner class of ContainerBase. Inside its run method is a while loop that periodically calls its processChildren method. The processChildren method in turn calls the backgroundProcess method and the processChildren method of each of its children. By implementing the backgroundProcess method, a child class of ContainerBase can have a dedicated thread for running periodic tasks, such as checking classes' timestamps or the expiry times of session objects. Listing 12.4 presents the backgroundProcess method in the StandardContext class in Tomcat 5.

ContainerBackgroundProcessor类是ContainerBase的内部类。

在它的run方法中有一个while循环,定期调用它的processChildren方法。

processChildren方法又调用了backgroundProcess方法和它的每个子类的processChildren方法。

通过实现backgroundProcess方法,ContainerBase的子类可以有一个专用的线程来运行周期性任务,例如检查类的时间戳或会话对象的过期时间。

图12.4展示了Tomcat 5中StandardContext类的backgroundProcess方法。

Listing 12.4: The backgroudProccss method of the StandardContext class

图12.4:StandardContext类的backgroundProcess方法

代码语言:javascript复制
  
public void backgroundProcess() {  
    if (!started)  
        return;  
    count = (count   1) % managerChecksFrequency;  
    if ((getManager() != null) && (count == 0)) {  
        try {  
            getManager().backgroundProcess();  
        }  
        catch ( Exception x ) {  
            log.warn("Unable to perform background process on manager", x);  
        }  
    }  
    if (getloader() != null) {  
        if (reloadable && (getLoader().modified())) {  
            try {  
                Thread.currentThread().setContextClassloader  
                        (StandardContext.class.getClassLoader());  
                reload();  
            }  
            finally {  
                if (getLoader() != null) {  
                    Thread.currentThread().setContextClassLoader  
                            (getLoader().getClassLoader());  
                }  
            }  
        }  
        if (getLoader() instanceof WebappLoader) {  
            ((WebappLoader) getLoader()).closeJARs(false);  
        }  
    }  
}

It should be clear how StandardContext helps its associated manager and loader with their periodic tasks.

我们应该清楚 StandardContext 如何帮助其相关的管理器和加载器完成定期任务。

Summary

In this chapter you have learned about the StandardContext class and its related classes. You have also seen how a StandardContext instance is configured and what happens inside it for each incoming HTTP request. The last section of this chapter discusses the backgroundProcess implementation in Tomcat 5.

在本章中,你已经了解了 StandardContext 类及其相关类。

您还看到了 StandardContext 实例是如何配置的,以及它在处理每个传入 HTTP 请求时会发生什么。

本章最后一节将讨论 Tomcat 5 中的 backgroundProcess 实现。

0 人点赞