Chapter 18: Deployer

Overview(概述)

For a web application to be available, the context representing it must first be deployed to a host. In Tomcat, a context can be deployed as a WAR file or by copying the whole application to the webapps directory under the Tomcat installation directory. For each application you deploy, you can optionally have a descriptor file that contains the configuration settings for the context. A descriptor file takes the form of an XML document.

要使一个Web应用程序可用,首先必须将代表它的上下文部署到主机上。

在Tomcat中,上下文可以以WAR文件的形式部署,也可以通过将整个应用程序复制到Tomcat安装目录下的webapps目录中进行部署。

对于每个部署的应用程序,可以选择性地拥有一个包含上下文配置设置的描述符文件。

描述符文件采用XML文档的形式。

Note Tomcat 4 and 5 come with two applications for managing Tomcat and applications deployed to it, the manager application and the admin application. The class files for both applications are in the %CATALINA_HOME%/server/webapps directory. Both applications come with descriptor files, which are manager.xml and admin.xml, respectively. In Tomcat 4, these descriptors reside in the %CATALINA_HOME%/webapps directory; in Tomcat 5 they live in the corresponding application directories, i.e. in the %CATALINA_HOME%/server/webapps/admin and %CATALINA_HOME%/server/webapps/manager, respectively.

注意:Tomcat 4和5都附带了两个用于管理Tomcat和部署到Tomcat上的应用程序的应用程序,即管理器应用程序和管理员应用程序。

这两个应用程序的类文件位于%CATALINA_HOME%/server/webapps目录中。

这两个应用程序都附带了描述符文件,分别是manager.xml和admin.xml。

在Tomcat 4中,这些描述符位于%CATALINA_HOME%/webapps目录中;

在Tomcat 5中,它们位于相应的应用程序目录中,

即%CATALINA_HOME%/server/webapps/admin

和%CATALINA_HOME%/server/webapps/manager。

This chapter discusses web application deployment using a deployer, which is represented by the org.apache.catalina.Deployer interface. A deployer is associated with a host and is used to install child contexts. Installing a context to a host means creating an instance of the StandardContext class and adding that instance to the host. A child context is started when the parent host is started (because a container's start method always calls the start method of its child containers, except for a wrapper). However, a deployer can also be used to start and stop each context individually.

本章讨论了使用部署器进行Web应用程序部署的方法,部署器由org.apache.catalina.Deployer接口表示。

部署器与主机关联,并用于安装子上下文。

将上下文安装到主机意味着创建StandardContext类的实例,并将该实例添加到主机中。

当父主机启动时,将启动子上下文(因为容器的start方法总是调用其子容器的start方法,除了包装器)。

然而,部署器也可以用于单独启动和停止每个上下文。

In this chapter you will first learn how a Tomcat deployment deploys web applications in a host. This chapter will then explain about the Deployer interface and its standard implementation, the org.apache.catalina.core.StandardHostDeployer class.

在本章中,您将首先了解Tomcat部署如何在主机中部署Web应用程序。

然后,本章将解释Deployer接口及其标准实现org.apache.catalina.core.StandardHostDeployer类。

Deploying A Web Context(部署网络情境)

In Chapter 15, you used the following code to instantiate the StandardHost class and add a Context instance as the host's child container.

在第15章,您使用以下代码来实例化StandardHost类,并将一个Context实例作为主机的子容器添加进去。

Context context = new StandardContext();
context.setPath("/app1");
context.setDocBase("app1");
LifecycleListener listener = new ContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
Host host = new StandardHost();
host.addChild(context);

That was how we deployed our application. However, such code does not exist in Tomcat. Then, how does a context get added to a host in a real life deployment? The answer lies in a lifecycle listener of type org.apache.catalina.startup.HostConfig in the StandardHost instance.

这就是我们部署应用程序的方式。

然而,在Tomcat中并不存在这样的代码。

那么,在实际部署中,一个上下文是如何添加到主机中的呢?

答案在于StandardHost实例中的类型为org.apache.catalina.startup.HostConfig的生命周期监听器。

When the start method on the StandardHost instance is called, it triggers a START event. The HostConfig instance responds to it by calling its own start method, which in turn deploys and installs all web applications available in the specified directories. Here are the details.

当调用StandardHost实例的start方法时,会触发一个START事件。

HostConfig实例会响应该事件,通过调用自己的start方法来部署和安装指定目录中的所有Web应用程序。以下是详细信息。

Recall that Chapter 15, "Digester", explained how you could use a Digester object to parse an XML file. However it did not discuss all the rules in the Digester object. One subject of discussion that it skipped was deployer, which is the topic of this chapter.

回顾一下第15章中的“Digester”部分,它解释了如何使用Digester对象来解析XML文件。

然而,它并未讨论Digester对象中的所有规则。

它跳过的一个讨论主题是部署者(deployer),这也是本章的主题。

The org.apache.catalina.startup.Catalina class is a startup class that employs a Digester to convert XML elements in the server.xml file to Java objects. The Catalina class defines the createStartDigester method for adding rules to the Digester. One line in the createStartDigester method is this:

org.apache.catalina.startup.Catalina类是一个启动类,它使用Digester将server.xml文件中的XML元素转换为Java对象。

Catalina类定义了createStartDigester方法,用于向Digester添加规则。

在createStartDigester方法中的一行代码是这样的:

digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));

The org.apache.catalina.startup.HostRuleSet class extends the org.apache.commons.digester.RuleSetBase class (also discussed in Chapter 15). As a subclass of RuleSetBase, the HostRuleSet class must provide the implementation of the addRuleInstances method, which defines the rule(s) for the RuleSet. Here is a fragment of the HostRuleSet class's addRuleInstances method.

org.apache.catalina.startup.HostRuleSet类扩展了org.apache.commons.digester.RuleSetBase类(也将在第15章中讨论)。

作为 RuleSetBase 的子类,HostRuleSet 类必须提供 addRuleInstances 方法的实现,该方法定义了 RuleSet 的规则。

下面是 HostRuleSet 类 addRuleInstances 方法的片段。

public void addRuleInstances(Digester digester) {
 digester.addObjectCreate(prefix + "Host",
 "org.apache.catalina.core.StandardHost", "className");
 digester.addSetProperties(prefix + "Host");
 digester.addRule(prefix + "Host",
 new CopyParentClassLoaderRule(digester));
 digester.addRule(prefix + "Host",
 new LifecycleListenerRule (digester,
 "org.apache.catalina.startup.HostConfig", "hostConfigClass"));

What the code says is, the occurrence of the Server/Service/Engine/Host pattern in the server.xml file creates an instance of org.apache.catalina.startup.HostConfig class and adds it to the host as a lifecycle listener. In other words, the HostConfig class handles the events fired by the StandardHost's start and stop methods.

代码所述的是,server.xml文件中的Server/Service/Engine/Host模式的出现会创建org.apache.catalina.startup.HostConfig类的一个实例,并将其作为生命周期监听器添加到主机中。

换句话说,HostConfig类处理StandardHost的start和stop方法触发的事件。

Listing 18.1 offers the lifecycleEvent method of the HostConfig class. This method is an event handler. Because the HostConfig is a listener for a StandardHost instance, every time the StandardHost is started or stopped, the lifecycleEvent method will be invoked.

清单18.1提供了HostConfig类的lifecycleEvent方法。

这个方法是一个事件处理程序。

因为HostConfig是StandardHost实例的监听器,所以每当StandardHost启动或停止时,lifecycleEvent方法都会被调用。

Listing 18.1: The lifecycleEvent method of the HostConfig class.

清单18.1:HostConfig类的lifecycleEvent方法。

public void lifecycleEvent(LifecycleEvent event) {
    // Identify the host we are associated with
    try {
        host = (Host) event.getLifecycle();
        if (host instanceof StandardHost) {
            int hostDebug = ((StandardHost) host).getDebug();
            if (hostDebug > this.debug) {
                this.debug = hostDebug;
            }
            setDeployXML(((StandardHost) host).isDeployXML());
            setLiveDeploy(((StandardHost) host).getLiveDeploy());
            setUnpackWARs(((StandardHost) host).isUnpackWARs());
        }
    }
    catch (ClassCastException e) {
        log(sm.getString("hostConfig.cce", event.getLifecycle()), e);
        return;
    }
    // Process the event that has occurred
    if (event.getType().equals(Lifecycle.START_EVENT))
        start ();
    else if (event.getType().equals(Lifecycle.STOP_EVENT))
        stop();
}

If the host is an instance of org.apache.catalina.core.StandardHost, the setDeployXML,setLiveDeploy, and setUnpackWARs methods are called.

如果主机是 org.apache.catalina.core.StandardHost 的实例,则会调用 setDeployXML、setLiveDeploy 和 setUnpackWARs 方法。

setDeployXML(((StandardHost) host).isDeployXML());
 setLiveDeploy(((StandardHost) host).getLiveDeploy());
 setUnpackWARs(((StandardHost) host).isUnpackWARs());

The isDeployXML method of the StandardHost class indicates whether or not the host should deploy a context's descriptor file. By default the value of the deployXML property is true. The liveDeploy property indicates whether or not the host should periodically check for a new deployment, and the unpackWARs property specifies whether or not to unpack applications deployed as WAR files.

StandardHost类的isDeployXML方法指示主机是否应该部署上下文的描述符文件。

默认情况下,deployXML属性的值为true。

liveDeploy属性指示主机是否应定期检查新的部署,而unpackWARs属性指定是否解压作为WAR文件部署的应用程序。

Upon receiving a START event notification, the HostConfig object's lifecycleEvent method calls the start method to deploy applications. This method is given in Listing 18.2.

Upon receiving a START event notification, the HostConfig object's lifecycleEvent method calls the start method to deploy applications. This method is given in Listing 18.2.

在接收到START事件通知后,HostConfig对象的lifecycleEvent方法调用start方法来部署应用程序。

该方法在代码清单18.2中给出。

Listing 18.2: The start method of the HostConfig class

代码清单18.2:HostConfig类的start方法

  
protected void start() {  
    if (debug >= 1)  
        log(sm.getString("hostConfig.start"));  
    if (host.getAutoDeploy()) {  
        deployApps();  
    }  
    if (isLiveDeploy ()) {  
        threadStart();  
    }  
}

The start method calls the deployApps method if the autoDeploy property is true (by default, its value is true). Also, it spawns a new thread by calling the threadStart method if liveDeploy is true (which it is, by default). Live deploy is discussed further in the subsection "Live Deploy" later in this section.

start方法在autoDeploy属性为true(默认为true)时调用deployApps方法。

此外,如果liveDeploy属性为true(默认为true),它还通过调用threadStart方法生成一个新线程。

关于live deploy的详细信息将在本节的子部分"Live Deploy"中进一步讨论。

The deployApps method obtains the appBase property of the host. appBase by default has the value of webapps (See Tomcat's server.xml file). The deployment process regards all directories under the %CATALINE_HOME%/webapps directory as application directories to be deployed. In addition, all WAR and descriptor files found in this directory are to be deployed as well.

deployApps方法获取主机的appBase属性。默认情况下,appBase的值为webapps(请参阅Tomcat的server.xml文件)。

部署过程将所有位于%CATALINE_HOME%/webapps目录下的目录视为要部署的应用程序目录。

此外,该目录中找到的所有WAR和描述符文件也将被部署。

The deployApps method is given in Listing 18.3.

Listing 18.3中给出了deployApps方法的代码。

Listing 18.3: The deployApps method

清单 18.3: deployApps 方法

  
protected void deployApps() {  
    if (!(host instanceof Deployer))  
        return;  
    if (debug >= 1)  
        log(sm.getString("hostConfig.deploying"));  
    File appBase = appBase();  
    if (!appBase.exists() || !appBase.isDirectory())  
        return;  
    String files[] = appBase.list();  
    deployDescriptors(appBase, files);  
    deployWARs(appBase, files);  
    deployDirectories(appBase, files);  
}

The deployApps method calls three other methods, deployDescriptors, deployWARs, and deployDirectories. To all methods, deployApps passes the appBase File and the array of files in the webapps directory. A context is identified by its path, and all deployed contexts must have unique paths. A context that has been deployed is added to the deployed ArrayList in the HostConfig object. Therefore, before deploying a context, the deployDescriptors, deployWARs, and deployDirectories methods make sure that the deployed ArrayList does not contain a context having an identical path.

deployApps方法调用了三个其他方法:deployDescriptors、deployWARs和deployDirectories。

对于所有方法,deployApps传递了appBase文件和webapps目录中的文件数组。

一个上下文通过其路径进行标识,所有部署的上下文必须具有唯一的路径。

已部署的上下文将添加到HostConfig对象的deployed ArrayList中。

因此,在部署上下文之前,deployDescriptors、deployWARs和deployDirectories方法确保deployed ArrayList不包含具有相同路径的上下文。

We now look at each of the three deployment methods in turn. After reading the three subsections below, you should be able to answer this question: Is the order the three methods are called important? (The answer is yes)

现在我们依次查看这三个部署方法。

阅读下面的三个小节后,您应该能够回答这个问题:这三个方法的调用顺序重要吗?(答案是肯定的)

Deploying a Descriptor(部署描述符)

You can write an XML file that describes the context object. For instance, the admin and manager applications that accompany Tomcat 4 and 5 have the descriptors given in Listings 18.4 and 18.5, respectively.

您可以编写一个 XML 文件来描述上下文对象。

例如,Tomcat 4 和 5 中的管理员和管理程序的描述符分别如清单 18.4 和 18.5 所示。

Listing 18.4: The descriptor for the admin application (admin.xml)

清单 18.4:管理应用程序的描述符 (admin.xml)

<Context path="/admin" docBase="../server/webapps/admin"
 debug="0" privileged="true">
 <!-- Uncomment this Valve to limit access to the Admin app to
 localhost for obvious security reasons. Allow may be a comma-
 separated list of hosts (or even regular expressions).
 <Valve className="org.apache.catalina.valves.RemoteAddrValve"
 allow="127.0.0.1"/>
 -->
 <Logger className="org.apache.catalina.logger.FileLogger"
 prefix="localhost_admin_log." suffix=".txt"
 timestamp="true"/>
</Context>

Listing 18.5: The descriptor for the manager application (manager.xml)

清单 18.5:管理程序的描述符(manager.xml)`

<Context path="/manager" docBase="../server/webapps/manager"
 debug="0" privileged="true">
 <!-- Link to the user database we will get roles from -->
 <ResourceLink name="users" global="UserDatabase"
 type="org.apache.catalina.UserDatabase"/>
</Context>

Note that both descriptors have a Context element and the docBase attributes refer to %CATALINA_HOME%/server/webapps/admin and %CATALINA_HOME%/server/webapps/manager, respectively, which indicate that the admin and manager applications are not deployed to the usual place.

请注意,这两个描述符都有一个Context元素,而docBase属性分别引用了%CATALINA_HOME%/server/webapps/admin和%CATALINA_HOME%/server/webapps/manager,这表明admin和manager应用程序没有部署到通常的位置。

The HostConfig class uses the deployDescriptors method in Listing 18.6 to deploy all XML files found in %CATALINA_HOME%/webapps in Tomcat 4 and in the subdirectories of %CATALINA_HOME%/server/webapps/ in Tomcat 5.

HostConfig类在Tomcat 4中使用deployDescriptors方法(见清单18.6)来部署在%CATALINA_HOME%/webapps中找到的所有XML文件,在Tomcat 5中使用%CATALINA_HOME%/server/webapps/的子目录。

Listing 18.6: The deployDescriptors method in HostConfig

清单18.6:HostConfig中的deployDescriptors方法


    protected void deployDescriptors(File appBase, String[] files) {
        if (!deployXML)
            return;
        for (int i = 0; i < files.length; i++) {
            if (files[i].equalsIgnoreCase("META-INF"))
                continue;
            if (files[i].equalsIgnoreCase("WEB-INF"))
                continue;
            if (deployed.contains(files[i]))
                continue;
            File dir = new File(appBase, files[i]);
            if (files[i].toLowerCase().endsWith(".xml")) {
                deployed.add(files[i]);
                // Calculate the context path and make sure it is unique
                String file = files[i].substring(0, files[i].length() - 4);
                String contextPath = "/" + file;
                if (file.equals("ROOT")) {
                    contextPath = "";
                }
                if (host.findChild(contextPath) != null) {
                    continue;
                }
                // Assume this is a configuration descriptor and deploy it
                log(sm.getString("hostConfig.deployDescriptor", files[i]));
                try {
                    URL config =
                            new URL("file", null, dir.getCanonicalPath());
                    ((Deployer) host).install(config, null);
                }
                catch (Throwable t) {
                    log(sm.getString("hostConfig.deployDescriptor.error",
                            files[i]), t);
                }
            }
        }
    }

Deploying a WAR File(部署 WAR 文件)

You can deploy a web application as a WAR file. The HostConfig class employs the deployWARs method in Listing 18.7 to deploy any WAR files in the %CATALINA_HOME%/webapps directory.

您可以将Web应用程序部署为WAR文件。

HostConfig类使用List 18.7中的deployWARs方法来部署%CATALINA_HOME%/webapps目录中的任何WAR文件。

Listing 18.7: The deployWARs method in HostConfig

List 18.7:HostConfig中的deployWARs方法。


protected void deployWARs(File appBase, String[] files) {
    for (int i = 0; i < files.length; i++) {
        if (files[i].equalsIgnoreCase("META-INF"))
            continue;
        if (files[i].equalsIgnoreCase("WEB-INF"))
            continue;
        if (deployed.contains(files [i]))
            continue;
        File dir = new File(appBase, files [i]);
        if (files[i].toLowerCase().endsWith(".war")) {
            deployed.add(files [i]);
            // Calculate the context path and make sure it is unique
            String contextPath = "/" + files[i];
            int period = contextPath.lastIndexOf(".");
            if (period >= 0)
                contextPath = contextPath.substring(0, period);
            if (contextPath.equals("/ROOT"))
                contextPath = "";
            if (host.findChild(contextPath) != null)
                continue;
            if (isUnpackWARs()) {
                // Expand and deploy this application as a directory
                log(sm.getString("hostConfig.expand", files[i]));
                try {
                    URL url = new URL("jar:file:" +
                            dir.getCanonicalPath() + "!/");
                    String path = expand(url);
                    url = new URL("file:" + path);
                    ((Deployer) host).install(contextPath, url);
                }
                catch (Throwable t) {
                    log(sm.getString("hostConfig.expand.error", files[i]), t);
                }
            }
            else {
                // Deploy the application in this WAR file
                log(sm.getString("hostConfig.deployJar", files[i]));
                try {
                    URL url = new URL("file", null,
                            dir.getCanonicalPath());
                    url = new URL("jar:" + url.toString() + "!/");
                    ((Deployer) host).install(contextPath, url);
                }
                catch (Throwable t) {
                    log(sm.getString("hostConfig.deployJar.error",
                            files[i]), t);
                }
            }
        }
    }
}

Deploying a Directory(部署目录)

Alternatively, you can deploy an application as it is by copying the whole directory to the %CATALINA_HOME%/webapps directory. HostConfig uses the deployDirectories in Listing 18.8 to deploy these applications.

另一种方法是将整个目录复制到%CATALINA_HOME%/webapps目录中,将应用程序部署为它本身。

HostConfig使用清单18.8中的deployDirectories方法来部署这些应用程序。

Listing 18.8: The deployDirectories method in HostConfig

清单18.8:HostConfig中的deployDirectories方法


protected void deployDirectories (File appBase, string[] files){
    for (int i = 0; i < files.length; i++) {
        if (files[i].equalsIgnoreCase("META-INF"))
            continue;
        if (files[i].equalsIgnoreCase("WEB-INF"))
            continue;
        if (deployed.contains(files[i]))
            continue;
        File dir = new File(appBase, files[i]);
        if (dir.isDirectory()) {
            deployed.add(files[i]);
            // Make sure there is an application configuration directory
            // This is needed if the Context appBase is the same as the
            // web server document root to make sure only web applications
            // are deployed and not directories for web space.
            File webInf = new File(dir, "/WEB-INF");
            if (!webInf.exists() || !webInf.isDirectory() ||
                    !webInf.canRead())
                continue;
            // Calculate the context path and make sure it is unique
            String contextPath = "/" + files[i];
            if (files[i].equals("ROOT"))
                contextPath = "";
            if (host.findChild(contextPath) != null)
                continue;
            // Deploy the application in this directory
            log(sm.getString("hostConfig.deployDir", files[i]));
            try {
                URL url = new URL("file", null, dir.getCanonicalPath());
                ((Deployer) host).install(contextPath, url);
            }
            catch (Throwable t) {
                log(sm.getString("hostConfig.deployDir.error", files[i]), t);
            }
        }
    }
}

Live Deploy(实时部署)

As mentioned previously, a StandardHost instance uses a HostConfig object as a lifecycle listener. When the StandardHost object is started, its start method fires a START event. In response to this START event, the lifecycleEvent method in HostConfig, the event handler in HostConfig, calls the start method. In Tomcat 4 the last line of this start method calls the threadStart method if the liveDeploy property is true (by default, this property is true).

如前所述,StandardHost实例使用HostConfig对象作为生命周期监听器。

当StandardHost对象启动时,它的start方法触发一个START事件。

作为对此START事件的响应,HostConfig中的lifecycleEvent方法,即HostConfig中的事件处理程序,调用start方法。

在Tomcat 4中,如果liveDeploy属性为true(默认情况下为true),则start方法的最后一行调用threadStart方法。

if (isLiveDeploy()) {
 threadStart();
 }

The threadStart method spawns a new thread that invokes the run method. The run method regularly checks for a new deployment and if any of the web.xml files of the existing deployed applications has been modified. The run method is presented in Listing 18.9.

threadStart 方法会生成一个调用运行方法的新线程。

运行方法会定期检查是否有新的部署,以及现有部署应用程序的 web.xml 文件是否被修改。

运行方法如清单 18.9 所示。

Listing 18.9: The run method in HostConfig in Tomcat 4

清单 18.9:Tomcat 4 中 HostConfig 的运行方法

  
/**  
 * The background thread that checks for web application autoDeploy     * and changes to the web.xml config.     */    
public void run() {  
    if (debug >= 1)  
        log("BACKGROUND THREAD Starting");  
    // Loop until the termination semaphore is set  
    while (!threadDone) {  
        // Wait for our check interval  
        threadSleep();  
        // Deploy apps if the Host allows auto deploying  
        deployApps();  
        // Check for web.xml modification  
        checkWebXmlLastModified();  
    }  
    if (debug >= 1)  
        log("BACKGROUND THREAD Stopping");  
}

The threadSleep method sends the thread to sleep for the duration indicated by the checkInterval property. By default, the value of this property is 15, meaning that checking is conducted every 15 seconds.

threadSleep方法会使线程休眠一段时间,时间长度由checkInterval属性指定。默认情况下,该属性的值为15,意味着每隔15秒进行一次检查。

In Tomcat 5, the HostConfig class does not utilize a special thread. Instead, the backgroundProcess method of the StandardHost class fires a "check" event periodically:

在Tomcat 5中,HostConfig类不使用特殊的线程。

相反,StandardHost类的backgroundProcess方法会定期触发一个“检查”事件。

public void backgroundProcess() {
 lifecycle.fireLifecycleEvent("check", null);
}

Note The backgroundProcess method is periodically invoked by a special thread that serves all background processings in the containers.

注意 backgroundProcess 方法由一个特殊线程定期调用,该线程负责容器中的所有后台处理。

Upon receiving a "check" event, the lifecycle listener (the HostConfig object) calls its check method that performs the checking.

收到 "检查 "事件后,生命周期监听器(HostConfig 对象)会调用其检查方法执行检查。

public void lifecycleEvent(LifecycleEvent event) {
     if (event.getType().equals("check"))
     check();
     ...

The check method in the HostConfig class in Tomcat 5 is given in Listing 18.10.

Tomcat 5 中 HostConfig 类的检查方法见清单 18.10。

Listing 18.10: The check method in HostConfig in Tomcat 5

清单 18.10:Tomcat 5 中 HostConfig 的检查方法

protected void check() {
 if (host.getAutoDeploy()) {
 // Deploy apps if the Host allows auto deploying
 deployApps();
 // Check for web.xml modification
 checkContextLastModified();
 }
}

As you can see, the check method calls the deployApps method. The deployApps method in both Tomcat 4 and Tomcat 5 deploys the web applications and was given in Listing 18.3. As previously discussed, this method calls the deployDescriptors, deployWARs, and deployDirectories methods.

如您所见,check方法调用了deployApps方法。

在Tomcat 4和Tomcat 5中,deployApps方法部署了Web应用程序,并在第18.3节中给出了具体实现。

正如之前讨论的那样,该方法调用了deployDescriptors、deployWARs和deployDirectories方法。

The check method in Tomcat 5 also calls the checkContextLastModified method that iterates all deployed contexts and checks the timestamps of the web.xml file and the contents of the WEB-INF directory in each context. If any of the checked resources has changed, the context is restarted. In addition, the checkContextLastModified method also checks the timestamps of all deployed WAR files and redeploys an application whose WAR file has been modified.

在Tomcat 5中,check方法还调用了checkContextLastModified方法,该方法迭代了所有已部署的上下文,并检查每个上下文中的web.xml文件和WEB-INF目录的时间戳。

如果任何被检查的资源发生了变化,上下文将会被重启。

此外,checkContextLastModified方法还检查了所有已部署的WAR文件的时间戳,并重新部署那些WAR文件发生了修改的应用程序。

In Tomcat 4, the run method of the background thread calls the checkWebXmlLastModified method that performs a similar task to the checkContextLastModified method in Tomcat 5.

在Tomcat 4中,后台线程的run方法调用了checkWebXmlLastModified方法,该方法执行了与Tomcat 5中的checkContextLastModified方法类似的任务。

The Deployer Interface(部署界面)

A deployer is represented by the org.apache.catalina.Deployer interface. The StandardHost class implements the Deployer interface. Therefore, a StandardHost instance is also a deployer and it is a container into which web applications can be deployed and undeployed. The Deployer interface is presented in Listing 18.11.

部署者由org.apache.catalina.Deployer接口表示。

StandardHost类实现了Deployer接口。

因此,StandardHost实例也是一个部署者,它是一个可以部署和卸载Web应用程序的容器。

Deployer接口的定义如下(见第18.11节):

Listing 18.11: The Deployer interface

第18.11节:部署者接口


package org.apache.catalina;
        import java.io.IOException;
        import java.net.URL;
/**
 * A <b>Deployer</b> is a specialized Container into which web
 * applications can be deployed and undeployed. Such a Container
 * will create and install child Context instances for each deployed
 * application. The unique key for each web application will be the
 * context path to which it is attached.
 *
 * @author Craig R. McClanahan
 * @version $Revision: 1.6 $ $Date: 2002/04/09 23:48:21 $
 */
public interface Deployer {
    /**
     * The ContainerEvent event type sent when a new application is
     * being installed by <code>install()</code>, before it has been
     * started.
     */
    public static final String PRE_INSTALL_EVENT = "pre-install";
    /**
     * The ContainerEvent event type sent when a new application is
     * installed by <code>install()</code>, after it has been started.
     */
    public static final String INSTALL_EVENT = "install";
    /**
     * The ContainerEvent event type sent when an existing application is
     * removed by <code>remove()</code>.
     */
    public static final String REMOVE_EVENT = "remove";
    /**
     * Return the name of the Container with which this Deployer is
     * associated.
     */
    public String getName();
    /**
     * Install a new web application, whose web application archive is at
     * the specified URL, into this container with the specified context.
     * path. A context path of "" (the empty string) should be used for
     * the root application for this container. Otherwise, the context
     * path must start with a slash.
     * <p>
     * If this application is successfully installed, a ContainerEvent of
     * type <code>INSTALL_EVENT</code> will be sent to all registered
     * listeners,
     * with the newly created <code>Context</code> as an argument.
     *
     * @param contextPath The context path to which this application
     * should be installed (must be unique)
     * @param war A URL of type "jar:" that points to a WAR file, or type
     * "file:" that points to an unpacked directory structure containing
     * the web application to be installed
     *
     * @exception IllegalArgumentException if the specified context path
     * is malformed (it must be "" or start with a slash)
     * @exception IllegalStateException if the specified context path
     * is already attached to an existing web application
     * @exception IOException if an input/output error was encountered
     * during installation
     */
    public void install(String contextPath, URL war) throws IOException;
    /**
     * <p>Install a new web application, whose context configuration file
     * (consisting of a <code>&lt;Context&gt;</code> element) and web
     * application archive are at the specified URLs.</p>
     *
     * <p>If this application is successfully installed, a ContainerEvent
     * of type <code>INSTALL_EVENT</code> will be sent to all registered
     * listeners, with the newly created <code>Context</code> as an
     * argument.
     * </p>
     *
     * @param config A URL that points to the context configuration file
     * to be used for configuring the new Context
     * @param war A URL of type "jar:" that points to a WAR file, or type
     * "file:" that points to an unpacked directory structure containing
     * the web application to be installed
     *
     * @exception IllegalArgumentException if one of the specified URLs
     * is null
     * @exception IllegalStateException if the context path specified in
     * the context configuration file is already attached to an existing
     * web application
     * @exception IOException if an input/output error was encountered
     * during installation
     */
    public void install(URL config, URL war) throws IOException;
    /**
     * Return the Context for the deployed application that is associated
     * with the specified context path (if any); otherwise return
     * <code>null</code>.
     *
     * @param contextPath The context path of the requested web
     * application
     */
    public Context findDeployedApp(String contextPath);
    /**
     * Return the context paths of all deployed web applications in this
     * Container. If there are no deployed applications, a zero-length
     * array is returned.
     */
    public String[] findDeployedApps();
    /**
     * Remove an existing web application, attached to the specified
     * context path. If this application is successfully removed, a
     * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to
     * all registered listeners, with the removed <code>Context</code> as
     * an argument.
     *
     * @param contextPath The context path of the application to be
     * removed
     *
     * @exception IllegalArgumentException if the specified context path
     * is malformed (it must be "" or start with a slash)
     * @exception IllegalArgumentException if the specified context path
     * does not identify a currently installed web application
     * @exception IOException if an input/output error occurs during
     * removal
     */
    public void remove(String contextPath) throws IOException;
    /**
     * Start an existing web application, attached to the specified
     * context path. Only starts a web application if it is not running.
     *
     * @param contextPath The context path of the application to be
     * started
     * @exception IllegalArgumentException if the specified context path
     * is malformed (it must be "" or start with a slash)
     * @exception IllegalArgumentException if the specified context path
     * does not identify a currently installed web application
     * @exception IOException if an input/output error occurs during
     * startup
     */
    public void start(String contextPath) throws IOException;
    /**
     * Stop an existing web application, attached to the specified
     * context path. Only stops a web application if it is running.
     *
     * @param contextPath The context path of the application to be
     * stopped
     * @exception IllegalArgumentException if the specified context path
     * is malformed (it must be "" or start with a slash)
     * @exception IllegalArgumentException if the specified context path
     * does not identify a currently installed web application
     * @exception IOException if an input/output error occurs while
     * stopping the web application
     */
    public void stop(String contextPath) throws IOException;
}

The StandardHost class employs a helper class (org.apache.catalina.core.StandardHostDeployer) for performing tasks related to deploying and installing a Web application. You can see in the following fragment of StandardHost how it delegates the task of deploying and installing web applications to an instance of StandardHostDeployer:

StandardHost 类使用一个辅助类(org.apache.catalina.core.StandardHostDeployer)来执行与部署和安装 Web 应用程序相关的任务。

在 StandardHost 的以下片段中,您可以看到它是如何将部署和安装 Web 应用程序的任务委托给 StandardHostDeployer 实例的:

/**
     * The <code>Deployer</code> to whom we delegate application
     * deployment requests.
     */
    private Deployer deployer = new StandardHostDeployer(this);
    public void install(String contextPath, URL war) throws IOException {
        deployer.install(contextPath, war);
    }
    public synchronized void install(URL config, URL war) throws
            IOException {
        deployer.install(config, war);
    }
    public Context findDeployedApp(String contextPath) {
        return (deployer.findDeployedApp(contextPath));
    }
    public String[] findDeployedApps() {
        return (deployer.findDeployedApps());
    }
    public void remove(String contextPath) throws IOException {
        deployer.remove(contextPath);
    }
    public void start(String contextPath) throws IOException {
        deployer.start(contextPath);
    }
    public void stop(String contextPath) throws IOException {
        deployer.stop(contextPath);
    }

StandardHostDeployer is discussed in the next section.

The StandardHostDeployer Class(标准主机部署程序类)

The org.apache.catalina.core.StandardHostDeployer class is a helper class that helps deploy and install web applications in a StandardHost. StandardHostDeployer was designed to be used by a StandardHost object, as indicated by its constructor that accepts a StandardHost instance.

org.apache.catalina.core.StandardHostDeployer 类是一个辅助类,可帮助在 StandardHost 中部署和安装网络应用程序。

StandardHostDeployer 设计用于 StandardHost 对象,其构造函数接受一个 StandardHost 实例。

public StandardHostDeployer(StandardHost host) {
 super();
 this.host = host;
}

The methods in this class are explained in the following sub-sections.

这个类中的方法在下面的小节中进行了解释。

Installing a Descriptor(安装描述符)

The StandardHostDeployer class has two install methods. The first one, which is the topic of this subsection, is used to install a descriptor. The second one, discussed in the next subsection, is used to install a WAR file or a directory.

StandardHostDeployer类有两个安装方法。

第一个方法,即本小节的主题,用于安装描述符。

第二个方法,在下一个小节中讨论,用于安装WAR文件或目录。

The install method for installing descriptors is given in Listing 18.12. A StandardHost instance calls this method after its install method is called by the deployDescriptors method in a HostConfig object.

安装描述符的方法如清单18.12所示。

在HostConfig对象的deployDescriptors方法调用StandardHost实例的安装方法之后,会调用这个方法。

Listing 18.12: The install method for installing descriptors

清单18.12:安装描述符的方法


    public synchronized void install(URL config, URL war)
            throws IOException {
        // Validate the format and state of our arguments
        if (config == null)
            throw new IllegalArgumentException
                    (sm.getString("StandardHost.configRequired"));
        if (!host.isDeployXML())
            throw new IllegalArgumentException
                    (sm.getString("StandardHost.configNotAllowed"));
        // Calculate the document base for the new web application (if
        // needed)
        String docBase = null; // Optional override for value in config file
        if (war != null) {
            String url = war.toString();
            host.log(sm.getString("StandardHost.installingWAR", url));
            // Calculate the WAR file absolute pathname
            if (url.startsWith("jar:")) {
                url = url.substring(4, url.length() - 2);
            }
            if (url.startsWith("file://"))
                docBase = url.substring(7);
            else if (url.startsWith("file:"))
                docBase = url.substring(5);
            else
                throw new IllegalArgumentException
                        (sm.getString("standardHost.warURL", url));
        }
        // Install the new web application
        this.context = null;
        this.overrideDocBase = docBase;
        InputStream stream = null;
        try {
            stream = config.openStream();
            Digester digester = createDigester();
            digester.setDebug(host.getDebug());
            digester.clear();
            digester.push(this);
            digester.parse(stream);
            stream.close();
            stream = null;
        }
        catch (Exception e) {
            host.log
                    (sm.getString("standardHost.installError", docBase), e);
            throw new IOException(e.toString());
        }
        finally {
            if (stream != null) {
                try {
                    stream.close();
                }
                catch (Throwable t) {
                    ;
                }
            }
        }
    }

Installing a WAR file and a Directory(部署 WAR 文件和目录)

The second install method accepts a String representation of a context path and a URL representing a WAR file. This install method is given in Listing 18.13.

第二个安装方法接受上下文路径的字符串表示和表示 WAR 文件的 URL。

清单 18.13 给出了这种 install 方法。

Listing 18.13: The install method for installing a WAR file or a directory

清单 18.13:安装 WAR 文件或目录的 install 方法


    public synchronized void install(String contextPath, URL war)
            throws IOException {
        // Validate the format and state of our arguments
        if (contextPath == null)
            throw new IllegalArgumentException
                    (sm.getString("standardHost.pathRequired"));
        if (!contextPath.equals("") && !contextPath.startsWith("/"))
            throw new IllegalArgumentException
                    (sm.getString("standardHost.pathFormat", contextPath));
        if (findDeployedApp(contextPath) != null)
            throw new IllegalStateException
                    (sm.getString("standardHost.pathUsed", contextPath));
        if (war == null)
            throw new IllegalArgumentException
                    (sm.getString("standardHost.warRequired"));
        // Calculate the document base for the new web application
        host.log(sm.getString("standardHost.installing",
                contextPath, war.toString()));
        String url = war.toString();
        String docBase = null;
        if (url.startsWith("jar:")) {
            url = url.substring(4, url.length() - 2);
        }
        if (url.startsWith("file://"))
            docBase = url.substring(7);
        else if (url.startsWith("file:"))
            docBase = url.substring(5);
        else
            throw new IllegalArgumentException
                    (sm.getString("standardHost.warURL", url));
        // Install the new web application
        try {
            Class clazz = Class.forName(host.getContextClass());
            Context context = (Context) clazz.newInstance();
            context.setPath(contextPath);
            context.setDocBase(docBase);
            if (context instanceof Lifecycle) {
                clazz = Class.forName(host.getConfigClass());
                LifecycleListener listener =
                        (LifecycleListener) clazz.newInstance();
                ((Lifecycle) context).addLifecycleListener(listener);
            }
            host.fireContainerEvent(PRE_INSTALL_EVENT, context);
            host.addChild(context);
            host.fireContainerEvent(INSTALL_EVENT, context);
        }
        catch (Exception e) {
            host.log(sm.getString("standardHost.installError", contextPath),
                    e);
            throw new IOException(e.toString());
        }
    }

Note that once a context is installed, it is added to the StandardHost.

请注意,一旦部署了上下文,它就会被添加到 StandardHost 中。

Starting A Context(启动A环境)

The start method in StandardHostDeployer is used to start a context. It is given in Listing 18.14.

StandardHostDeployer 中的 start 方法用于启动上下文。该方法在清单 18.14 中给出。

Listing 18.14: The start method of the StandardHostDeployer class

清单 18.14:StandardHostDeployer 类的 start 方法


    public void start(String contextPath) throws IOException {
        // Validate the format and state of our arguments
        if (contextPath == null)
            throw new IllegalArgumentException
                    (sm.getString("standardHost.pathRequired"));
        if (!contextPath.equals("") && !contextPath.startsWith("/"))
            throw new IllegalArgumentException
                    (sm.getString("standardHost.pathFormat", contextPath));
        Context context = findDeployedApp(contextPath);
        if (context == null)
            throw new IllegalArgumentException
                    (sm.getstring("standardHost.pathMissing", contextPath));
        host.log("standardHost.start " + contextPath);
        try {
            ((Lifecycle) context).start();
        }
        catch (LifecycleException e) {
            host.log("standardHost.start " + contextPath + ": ", e);
            throw new IllegalStateException
                    ("standardHost.start " + contextPath + ": " + e);
        }
    }

Stopping A Context(停止A环境)

To stop a context, you call the stop method of StandardHostDeployer in Listing 18.15.

要停止上下文,请调用清单 18.15 中 StandardHostDeployer 的 stop 方法。

Listing 18.15: The stop method in the StandardHostDeployer class

清单 18.15:StandardHostDeployer 类中的 stop 方法



public void stop(String contextPath) throws IOException {
    // Validate the format and state of our arguments
    if (contextPath == null)
        throw new IllegalArgumentException
                (sm.getstring("standardHost.pathRequired"));
    if (!contextPath.equals("") && !contextPath.startsWith("/"))
        throw new IllegalArgumentException
                (sm.getstring("standardHost.pathFormat", contextPath));
    Context context = findDeployedApp(contextPath);
    if (context == null)
        throw new IllegalArgumentException
                (sm.getstring("standardHost.pathMissing", contextPath));
    host.log("standardHost.stop " + contextPath);
    try {
        ((Lifecycle) context).stop();
    }
    catch (LifecycleException e) {
        host.log("standardHost.stop " + contextPath + ": ", e);
        throw new IllegalStateException
                ("standardHost.stop " + contextPath + ": " + e);
    }
}

Summary

Deployers are components for deploying and installing web applications. A deployer is represented by the org.apache.catalina.Deployer interface. The StandardHost class implements Deployer to make it a special container to which web applications can be deployed. The StandardHost class delegates the tasks of deploying and installing applications to a helper class, org.apache.catalina.core.StandardHostDeployer. The StandardHostDeployer class provides the code that performs the task of deploying and installing applications, as well as starting and stopping a context.

部署器是用于部署和安装Web应用程序的组件。

一个部署器由org.apache.catalina.Deployer接口表示。

StandardHost类实现了Deployer接口,使其成为一个特殊的容器,可以部署Web应用程序。

StandardHost类将部署和安装应用程序的任务委托给一个辅助类org.apache.catalina.core.StandardHostDeployer。

StandardHostDeployer类提供了执行部署和安装应用程序的代码,以及启动和停止上下文的任务。


Xander
198 声望51 粉丝