1. 程式人生 > >How tomcat works——17 啟動Tomcat

How tomcat works——17 啟動Tomcat

概述

本章關注的重點是如何使用 org.apache.catalina.startup下面的Catalina和 Bootstrap 類來啟動Tomcat。Catalina類用於解析Tomcat配置檔案server.xml以及啟動和停止Server。Bootstrap 類建立一Catalina例項並呼叫它的 process()方法。理論上,這兩個類可以合成一個類。但是,為了支援 Tomcat 的多模式啟動,提供了多個引導類。例如前述的 Bootstrap 類是將 Tomcat 作為一個獨立的程式執行,而org.apache.catalina.startup.BootstrapService 則是將 Tomcat 作為一個Windows NT 系統的服務來執行。

為了使用方便,Tomcat 提供了批處理檔案及Shell 指令碼來啟動和停止servlet 容器。有了這些批處理檔案和 Shell 指令碼的幫助,使用者則就不需要記住java.exe 的選項來執行 Bootstrap 類,簡單地執行批處理檔案或Shell 指令碼即可。

本章第1節討論 Catalina 類,第2節討論 Bootstrap 類。要理解本章內容,首先要確保已經讀過 14、15、16 章的內容。本章還討論瞭如何在 Windows 以及 Unix/Linux 下面執行 Tomcat。一節介紹 Windows 環境下的批處理檔案,一節介紹 Unix/Linux 下面的 Shell 指令碼。

17.1 Catalina類

org.apache.catalina.startup.Catalina 是 Tomcat 的啟動類。它包含一個用於解析%CATALINE_HOME%/conf 目錄下面 server.xml配置檔案的 Digester。通過理解新增到此Digester的規則,我們可以按需配置Tomcat。

Catalina 類還封裝了一個 Server 物件用來提供服務。如第 15 章所介紹,一個Service物件包括一個容器以及一個或多個聯結器。我們可以使用Catalina 來啟動或停止 Server 物件。

可以首先初始化一個 Catalina 物件並呼叫它的 process()方法來啟動 Tomcat,在呼叫該方法時必須傳遞合適的引數。如果要啟動Tomcat,則第一個引數為startl;如果關閉,則傳送shutdown命令停止它。 還有其它可接受的命令,如-help,-config,-debug和-nonaming。

注意:nonaming 引數出現時,說明不支援 JNDI 名稱空間。有關更多的 Tomcat 對於 JNDI 名稱空間的支援請看 org.apache.naming 包。

通常,需要 Bootstrap 類來初始化 Catalina 物件並呼叫它的 process()方法,即使 Catalina 有自己的 main()方法。下一節裡將會介紹Bootstrap類,本節也會對它是如何工作的略做介紹。

Tomcat4 中的 process ()方法如 Listing17.1 所示:

Listing 17.1: The process method of the Catalina class

public void process(String args[]) {
    setCatalinaHome();
    setCatalinaBase();
    try {
        if (arguments(args))
        execute();
    }catch (Exception e) {
        e.printStackTrace(System.out);
    }
}

該方法設定2個系統屬性 catalina.home 和 catalina.base,catalina.home預設值是 user.dir 屬性的值。Catalina.base 被設定為 catalina.home 的值。因此這2個屬性的值都跟 user.dir 屬性值相同。

注意:user.dir 系統屬性是指當前使用者工作空間,即執行 Java 命令的目錄。可以通過java.lang.System 中getProperties()獲取到系統屬性列表。

然後 process()方法呼叫了arguments()方法,如Listing17.2。該方法處理引數列表,如果 Catalina 物件能繼續工作則返回 true。

Listing 17.2: The arguments method

protected boolean arguments(String args[]) {

        boolean isConfig = false;

        if (args.length < 1) {
            usage();
            return (false);
        }

        for (int i = 0; i < args.length; i++) {
            if (isConfig) {
                configFile = args[i];
                isConfig = false;
            } else if (args[i].equals("-config")) {
                isConfig = true;
            } else if (args[i].equals("-debug")) {
                debug = true;
            } else if (args[i].equals("-nonaming")) {
                useNaming = false;
            } else if (args[i].equals("-help")) {
                usage();
                return (false);
            } else if (args[i].equals("start")) {
                starting = true;
            } else if (args[i].equals("stop")) {
                stopping = true;
            } else {
                usage();
                return (false);
            }
        }

        return (true);

    }

process()方法檢查 arguments()方法的返回值,如果為 true 就呼叫 execute()方法。該方法如 Listing17.3 所示:

Listing 17.3: The execute method

protected void execute() throws Exception {
    if (starting)
        start();
    else if (stopping)
        stop();
}

方法execute()用於呼叫 start()或stop() 方法來啟動或者停止 Tomcat。這2個方法將在下面的子章節中介紹。

注意:在 Tomcat5 中,沒有 execute() 方法,process()方法直接呼叫 start() 方法和stop()方法。

17.1.1 start()方法

start()方法建立一個 Digester 例項來處理 server.xml 檔案(Tomcat 的配置檔案)。在解析 XML 檔案之前,start()方法呼叫Digester 的 push()方法,傳遞當前Catalina 物件。這意味著Catalina 物件是Digester內部堆疊中第1個物件。解析檔案可以設定Server物件的變數,Server預設型別是org.apache.catalina.core.StandardServer。然後 start()方法呼叫Server物件的 initialize()和start()方法。然後再呼叫Server的await()方法,伺服器分配一個執行緒等待停止命令。該方法不返回,直到收到停止命令。當 await()方法返回後,則繼續呼叫Server物件的 stop()方法用於停止伺服器以及所有元件。start()方法也採用了關閉鉤子的方法以此確保Server物件的 stop()方法即使在使用者突然退出該應用程式情況下也總會執行。start()方法如 Listing17.4 所示。

Listing 17.4: The start method

protected void start() {

        // Create and execute our Digester
        Digester digester = createStartDigester();
        File file = configFile();
        try {
            InputSource is =
                new InputSource("file://" + file.getAbsolutePath());
            FileInputStream fis = new FileInputStream(file);
            is.setByteStream(fis);
            digester.push(this);
            digester.parse(is);
            fis.close();
        } catch (Exception e) {
            System.out.println("Catalina.start: " + e);
            e.printStackTrace(System.out);
            System.exit(1);
        }

        // Setting additional variables
        if (!useNaming) {
            System.setProperty("catalina.useNaming", "false");
        } else {
            System.setProperty("catalina.useNaming", "true");
            String value = "org.apache.naming";
            String oldValue =
                System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
            if (oldValue != null) {
                value = value + ":" + oldValue;
            }
            System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
            value = System.getProperty
                (javax.naming.Context.INITIAL_CONTEXT_FACTORY);
            if (value == null) {
                System.setProperty
                    (javax.naming.Context.INITIAL_CONTEXT_FACTORY,
                     "org.apache.naming.java.javaURLContextFactory");
            }
        }

        // If a SecurityManager is being used, set properties for
        // checkPackageAccess() and checkPackageDefinition
        if( System.getSecurityManager() != null ) {
            String access = Security.getProperty("package.access");
            if( access != null && access.length() > 0 )
                access += ",";
            else
                access = "sun.,";
            Security.setProperty("package.access",
                access + "org.apache.catalina.,org.apache.jasper.");
            String definition = Security.getProperty("package.definition");
            if( definition != null && definition.length() > 0 )
                definition += ",";
            else
                definition = "sun.,";
            Security.setProperty("package.definition",
                // FIX ME package "javax." was removed to prevent HotSpot
                // fatal internal errors
                definition + "java.,org.apache.catalina.,org.apache.jasper.");
        }

        // Replace System.out and System.err with a custom PrintStream
        SystemLogHandler log = new SystemLogHandler(System.out);
        System.setOut(log);
        System.setErr(log);

        Thread shutdownHook = new CatalinaShutdownHook();

        // Start the new server
        if (server instanceof Lifecycle) {
            try {
                server.initialize();
                ((Lifecycle) server).start();
                try {
                    // Register shutdown hook
                    Runtime.getRuntime().addShutdownHook(shutdownHook);
                } catch (Throwable t) {
                    // This will fail on JDK 1.2. Ignoring, as Tomcat can run
                    // fine without the shutdown hook.
                }
                // Wait for the server to be told to shut down
                server.await();
            } catch (LifecycleException e) {
                System.out.println("Catalina.start: " + e);
                e.printStackTrace(System.out);
                if (e.getThrowable() != null) {
                    System.out.println("----- Root Cause -----");
                    e.getThrowable().printStackTrace(System.out);
                }
            }
        }

        // Shut down the server
        if (server instanceof Lifecycle) {
            try {
                try {
                    // Remove the ShutdownHook first so that server.stop()
                    // doesn't get invoked twice
                    Runtime.getRuntime().removeShutdownHook(shutdownHook);
                } catch (Throwable t) {
                    // This will fail on JDK 1.2. Ignoring, as Tomcat can run
                    // fine without the shutdown hook.
                }
                ((Lifecycle) server).stop();
            } catch (LifecycleException e) {
                System.out.println("Catalina.stop: " + e);
                e.printStackTrace(System.out);
                if (e.getThrowable() != null) {
                    System.out.println("----- Root Cause -----");
                    e.getThrowable().printStackTrace(System.out);
                }
            }
        }

    }

17.1.2 stop()方法

stop()方法用於關閉 Catalina 並且關閉 Server 物件。Stop 方法如 Listing17.5所示。

Listing 17.5: The stop Method

protected void stop() {

        // Create and execute our Digester
        Digester digester = createStopDigester();
        File file = configFile();
        try {
            InputSource is =
                new InputSource("file://" + file.getAbsolutePath());
            FileInputStream fis = new FileInputStream(file);
            is.setByteStream(fis);
            digester.push(this);
            digester.parse(is);
            fis.close();
        } catch (Exception e) {
            System.out.println("Catalina.stop: " + e);
            e.printStackTrace(System.out);
            System.exit(1);
        }

      // Stop the existing server
      try {
          Socket socket = new Socket("127.0.0.1", server.getPort());
          OutputStream stream = socket.getOutputStream();
          String shutdown = server.getShutdown();
          for (int i = 0; i < shutdown.length(); i++)
              stream.write(shutdown.charAt(i));
          stream.flush();
          stream.close();
          socket.close();
      } catch (IOException e) {
          System.out.println("Catalina.stop: " + e);
          e.printStackTrace(System.out);
          System.exit(1);
      }


    }

注意stop()方法通過 createStopDigester()方法建立了一個 Digester 例項。把當前Catalina 物件壓入到 Digester 內部棧中,並解析其配置檔案,Digester 的規則新增將在下一小節中介紹。

在收到關閉命令後,stop()方法則停止伺服器。

17.1.3 啟動Digester

Catalina 的 createStartDigester()用於建立 Digester 例項然後向其新增規則來解析 server.xml。該檔案用於配置Tomcat,位於%CATALINE_HOME%/conf 目錄下面。向 Digester 新增的規則是理解 Tomcat 配置的關鍵。createStartDigester()方法如 Listing17.6 所示。

Listing 17.6: The createStartDigester method

protected Digester createStartDigester() {

        // Initialize the digester
        Digester digester = new Digester();
        if (debug)
            digester.setDebug(999);
        digester.setValidating(false);

        // Configure the actions we will be using
        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

        digester.addObjectCreate("Server/GlobalNamingResources",
                                 "org.apache.catalina.deploy.NamingResources");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResources");

        digester.addObjectCreate("Server/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Listener");
        digester.addSetNext("Server/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        digester.addObjectCreate("Server/Service",
                                 "org.apache.catalina.core.StandardService",
                                 "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service",
                            "addService",
                            "org.apache.catalina.Service");

        digester.addObjectCreate("Server/Service/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        digester.addObjectCreate("Server/Service/Connector",
                                 "org.apache.catalina.connector.http.HttpConnector",
                                 "className");
        digester.addSetProperties("Server/Service/Connector");
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.Connector");

        digester.addObjectCreate("Server/Service/Connector/Factory",
                                 "org.apache.catalina.net.DefaultServerSocketFactory",
                                 "className");
        digester.addSetProperties("Server/Service/Connector/Factory");
        digester.addSetNext("Server/Service/Connector/Factory",
                            "setFactory",
                            "org.apache.catalina.net.ServerSocketFactory");

        digester.addObjectCreate("Server/Service/Connector/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        // Add RuleSets for nested elements
        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Default"));
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/DefaultContext/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/Default"));
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/DefaultContext/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

        digester.addRule("Server/Service/Engine",
                         new SetParentClassLoaderRule(digester,
                                                      parentClassLoader));


        return (digester);

    }

createStartDigester()方法建立一個 org.apache.commons.digester.Digester類的例項,然後新增規則。

在 server.xml 中的前三個規則是針對 server 元素的,server 元素是根元素,如下是 server 模式的規則:

digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer", "className");
digester.addSetProperties("Server");
digester.addSetNext("Server", "setServer","org.apache.catalina.Server");

在遇到 server 元素時,Digester 要建立org.apache.catalina.core.StandardServer 的例項。一種例外是 server 元素配置了className 屬性,此時它表示是要被初始化的類。

第二個規則使用具有相同名稱的元素屬性的值來填充Server物件的屬性。

第三條規則將 Server 物件壓入堆疊並將其與下一個物件(Catalina 物件)相關聯,使用的方法是 setServer()方法。是如何將一個 Catalina 的物件放入 Digester的棧中的?在start()方法中呼叫Digester的push()方法來解析server.xml:

digester.push (this);

上面的程式碼將 Catalina 物件壓入 Digester 的內部棧中。

其餘的規則可以根據方法中的程式碼得出,如果有困難,請重讀 第15 章。

17.1.4 停止Digester

createStopDigester()方法返回一個 Digester 物件用於優雅地停止伺服器物件,該方法如 Listing17.7所示。

Listing 17.7: The stop method

protected Digester createStopDigester() {

        // Initialize the digester
        Digester digester = new Digester();
        if (debug)
            digester.setDebug(999);

        // Configure the rules we need for shutting down
        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

        return (digester);

    }

跟開始 Digester 不同,停止 Digester 僅僅對根元素感興趣。

17.2 Bootstrap類

org.apache.catalina.startup.Bootstrap 類提供了 Tomcat 的啟動入口。當執行 startup.bat 或 startup.sh 時,實際上執行的就是該類中的main()方法。該方法建立3個類載入器並初始化 Catalina 類,然後呼叫 Catalina 的process()方法。

Bootstrap 類如 Listing17.8 所示:

package org.apache.catalina.startup;

import java.io.File;
import java.lang.reflect.Method;


/**
 * Boostrap loader for Catalina.  This application constructs a class loader
 * for use in loading the Catalina internal classes (by accumulating all of the
 * JAR files found in the "server" directory under "catalina.home"), and
 * starts the regular execution of the container.  The purpose of this
 * roundabout approach is to keep the Catalina internal classes (and any
 * other classes they depend on, such as an XML parser) out of the system
 * class path and therefore not visible to application level classes.
 *
 * @author Craig R. McClanahan
 * @version $Revision: 1.36 $ $Date: 2002/04/01 19:51:31 $
 */

public final class Bootstrap {


    // ------------------------------------------------------- Static Variables


    /**
     * Debugging detail level for processing the startup.
     */
    private static int debug = 0;


    // ----------------------------------------------------------- Main Program


    /**
     * The main program for the bootstrap.
     *
     * @param args Command line arguments to be processed
     */
    public static void main(String args[]) {

        // Set the debug flag appropriately
        for (int i = 0; i < args.length; i++)  {
            if ("-debug".equals(args[i]))
                debug = 1;
        }

        // Configure catalina.base from catalina.home if not yet set
        if (System.getProperty("catalina.base") == null)
            System.setProperty("catalina.base", getCatalinaHome());

        // Construct the class loaders we will need
        ClassLoader commonLoader = null;
        ClassLoader catalinaLoader = null;
        ClassLoader sharedLoader = null;
        try {

            File unpacked[] = new File[1];
            File packed[] = new File[1];
            File packed2[] = new File[2];
            ClassLoaderFactory.setDebug(debug);

            unpacked[0] = new File(getCatalinaHome(),
                                   "common" + File.separator + "classes");
            packed2[0] = new File(getCatalinaHome(),
                                  "common" + File.separator + "endorsed");
            packed2[1] = new File(getCatalinaHome(),
                                  "common" + File.separator + "lib");
            commonLoader =
                ClassLoaderFactory.createClassLoader(unpacked, packed2, null);

            unpacked[0] = new File(getCatalinaHome(),
                                   "server" + File.separator + "classes");
            packed[0] = new File(getCatalinaHome(),
                                 "server" + File.separator + "lib");
            catalinaLoader =
                ClassLoaderFactory.createClassLoader(unpacked, packed,
                                                     commonLoader);

            unpacked[0] = new File(getCatalinaBase(),
                                   "shared" + File.separator + "classes");
            packed[0] = new File(getCatalinaBase(),
                                 "shared" + File.separator + "lib");
            sharedLoader =
                ClassLoaderFactory.createClassLoader(unpacked, packed,
                                                     commonLoader);
        } catch (Throwable t) {

            log("Class loader creation threw exception", t);
            System.exit(1);

        }

        Thread.currentThread().setContextClassLoader(catalinaLoader);

        // Load our startup class and call its process() method
        try {

            SecurityClassLoad.securityClassLoad(catalinaLoader);

            // Instantiate a startup class instance
            if (debug >= 1)
                log("Loading startup class");
            Class startupClass =
                catalinaLoader.loadClass
                ("org.apache.catalina.startup.Catalina");
            Object startupInstance = startupClass.newInstance();

            // Set the shared extensions class loader
            if (debug >= 1)
                log("Setting startup class properties");
            String methodName = "setParentClassLoader";
            Class paramTypes[] = new Class[1];
            paramTypes[0] = Class.forName("java.lang.ClassLoader");
            Object paramValues[] = new Object[1];
            paramValues[0] = sharedLoader;
            Method method =
                startupInstance.getClass().getMethod(methodName, paramTypes);
            method.invoke(startupInstance, paramValues);

            // Call the process() method
            if (debug >= 1)
                log("Calling startup class process() method");
            methodName = "process";
            paramTypes = new Class[1];
            paramTypes[0] = args.getClass();
            paramValues = new Object[1];
            paramValues[0] = args;
            method =
                startupInstance.getClass().getMethod(methodName, paramTypes);
            method.invoke(startupInstance, paramValues);

        } catch (Exception e) {
            System.out.println("Exception during startup processing");
            e.printStackTrace(System.out);
            System.exit(2);
        }

    }


    /**
     * Get the value of the catalina.home environment variable.
     */
    private static String getCatalinaHome() {
        return System.getProperty("catalina.home",
                                  System.getProperty("user.dir"));
    }


    /**
     * Get the value of the catalina.base environment variable.
     */
    private static String getCatalinaBase() {
        return System.getProperty("catalina.base", getCatalinaHome());
    }


    /**
     * Log a debugging detail message.
     *
     * @param message The message to be logged
     */
    private static void log(String message) {

        System.out.print("Bootstrap: ");
        System.out.println(message);

    }


    /**
     * Log a debugging detail message with an exception.
     *
     * @param message The message to be logged
     * @param exception The exception to be logged
     */
    private static void log(String message, Throwable exception) {

        log(message);
        exception.printStackTrace(System.out);

    }

}

Bootstrap 類有4個靜態方法:2個 log 方法、getCatalinaHome()以及getCatalinaBase()方法。getCatalinaHome()方法的實現如下:

return System.getProperty("catalina.home",System.getProperty("user.dir"));

它意味著如果在前面沒有提供 catalina.home 的值,它會使用 user.dir 的值。

getCatalinaBase()方法的實現如下:

return System.getProperty("catalina.base", getCatalinaHome());

它返回 catalina.base 的值,如果該值不存在則返回 catalina.home 的值。

getCatalinaHome() 和 getCatalinaBase() 都會被 Bootstrap 類的main()方法呼叫。

Bootstrap 類中main()方法還構造了3個用於不同用處的載入器。擁有不同類載入器的主要原因是阻止應用程式類(Web應用程式中的servlet和其它幫助類)在WEB-INF/classes和WEB-INF/lib目錄之外執行類。部署到%CATALINE_HOME%/common/lib目錄的Jar包也是被允許的。

這3個類載入器定義如下:

// Construct the class loaders we will need
ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;

每個類載入器都給定一個可以訪問的路徑。commonLoader 可以訪問如下3個目錄下的類:%CATALINA_HOME%/common/classes,%CATALINA_HOME%/common/endorsed
和 %CATALINA_HOME%/common/lib。

try {
    File unpacked[] = new File[1];
    File packed[] = new File[1];
    File packed2[] = new File[2];
    ClassLoaderFactory.setDebug(debug);
    unpacked[0] = new File(getCatalinaHome(),"common" + File.separator + "classes");
    packed2[0] = new File(getCatalinaHome(),"common" + File.separator + "endorsed");
    packed2[1] = new File(getCatalinaHome(),"common" + File.separator + "lib");
    commonLoader =ClassLoaderFactory.createClassLoader(unpacked, packed2, null);

catalinaLoader 負責載入 Catalina 容器需要的類,它可以載入%CATALINA_HOME%/server/classes 和%CATALINA_HOME%/server/lib 目錄下面的類。以及commonLoader類載入器可以訪問的所有目錄。

unpacked[0] = new File(getCatalinaHome(),"server" + File.separator + "classes");
packed[0] = new File(getCatalinaHome(),"server" + File.separator + "lib");
catalinaLoader =ClassLoaderFactory.createClassLoader(unpacked, packed,commonLoader);

sharedLoader 可以訪問%CATALINA_HOME%/shared/classes和%CATALJNA_HOME%/shared/lib 目錄下的類以及 commondLoader 類可以訪問的類。然後將sharedLoader類載入器分配為部署在Tomcat中該上下文相關聯的每個Web應用程式的每個類載入器的父類載入器。

    unpacked[0] = new File(getCatalinaBase(),"shared" + File.separator + "classes");
    packed[0] = new File(getCatalinaBase(),"shared" + File.separator + "lib");
    sharedLoader =ClassLoaderFactory.createClassLoader(unpacked, packed,commonLoader);
}catch (Throwable t) {
    log('Class loader creation threw exception", t);
    System.exit(1);
}

請注意,sharedLoader類載入器無法訪問Catalina內部類和CLASSPATH環境中定義的類路徑變數。可以在第8章看到更多的載入器工作原理。

建立完3個類載入器後,載入了 Catalina 類並建立它的例項並將其賦值給 startupInstance 變數:

Class startupClass =catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();

然後呼叫 setParentClassLoader()方法,將 sharedLoader 作為引數:

// Set the shared extensions class loader
if (debug >= 1)
    log("Setting startup class properties");
String methodName = "setParentClassLoader";
Class paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);

最後,呼叫 Catalina 物件的 process ()方法:

// Call the process() method
if (debug >= 1)
    log("Calling startup class process() method");
methodName = "process";
paramTypes = new Class[1];
paramTypes[0] = args.getClass();
paramValues = new Object[1];
paramValues[0] = args;
method = startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);

17.3 Windows平臺執行Tomcat

如在前面的小節中介紹的,可以使用 Bootstrap 類將 Tomcat 作為一個獨立程式執行。在 Windows 平臺下,可以使用 starup.bat和 shutdown.bat來啟動、停止Tomcat。這兩個批處理檔案都在%CATALINA_HOME%/bin 目錄下。本節主要討論批處理檔案,對於不熟悉 DOS命令的,首先閱讀“批處理檔案簡介”小節。

17.3.1 批處理檔案簡介

本節主要介紹批處理檔案,這樣就能理解用於啟動和停止 Tomcat 的批處理檔案。尤其,解釋瞭如下命令:rem, if, echo, goto, label 等。這裡並沒有做全面介紹,如果需要更多內容可以查閱其它資源。

首先,一個批處理檔案的副檔名必須為.bat。可以雙擊啟動或在DOS控制檯命令列中呼叫它。一旦被呼叫,會從頭至尾一行行的執行。在Tomcat的批處理檔案中用到的元素會在下面介紹到。

注意:DOS 命令和環境變數不區分大小寫。

rem:

命令 rem 用作註釋,rem 開頭的行會被忽略不做處理。

pause:

命令 pause 暫停批處理檔案處理並要求使用者按鍵,使用者按鍵後會繼續執行。

echo:

該命令將它後面的文字顯示到 DOS 控制檯上面。例如,下面的語句列印 Hello World 到控制檯上並暫停。需要 pause 命令的原因是這樣才能看到控制檯上顯示的資訊:

echo Hello World
pause

要打印出一個環境變數的值,需要使用%將該變數括起來。例如,下面的命令打印出 myVar 的值:echo %myVar%

要打印出作業系統的名字,可以使用如下命令:echo %OS%

echo off:

echo off 防止批處理檔案中的命令被顯示,只顯示執行結果。但是 echo off 命令仍然會顯示,要禁止 echo off 命令可以使用@echo off。

@echo off:

@echo off 跟 echo off 相似,但是它也禁止 echo off 命令本身。

set:

該命令用於設定使用者定義的或環境變數。設定的變數值臨時存放在記憶體中,在處理完成後被拋棄。例如,下面的命令建立一個名為 THE_KING 的環境變數,值為 Elvis 並將其顯示
在控制檯上:

set THE_KING=Elvis
echo %THE_KING%
pause

注意:要引用變數的值,使用%符號將變數名括起來。例如,echo %the_king%表示顯示 THE_KING 的值。

label:

使用冒號來表示一個標籤,你可以將標籤傳遞給 goto 命令,讓處理過程跳到該標籤處。下面是一個名為 end 的標籤:end。接下來看 goto 命令使用。

goto:

命令 goto 強制批處理檔案跳到標籤定義的行,看下面的例子:

echo Start
goto end
echo I can guarantee this line will not be executed
:end
echo End
pause

在第一行打印出 Start 之後,批處理檔案執行 goto 命令,這樣控制器跳到 end標籤。第三行被跳過。

if:

if 用於測試,它有如下三種使用方式
1. 測試一個變數的值
2. 測試檔案的存在性
3. 測試錯誤值

要測試一個變數的值,使用如下的格式:

if variable==value nextCommand

例如,如下的語句測試 myVar 的值是不是 3。如果是則列印Correct 到控制檯上:

set myVar=3
if %myVar%==3 echo Correct

執行上面的命令會測試 myVar 的值並打印出 Correct。

要測試一個檔案是否存在,可以使用如下格式:

if exist c:\temp\myFile.txt goto start

如果在 c:\temp 目錄中存在 myFile.txt 檔案,控制器會跳到 start 標籤。

也可以使用 not 關鍵字來對一個表示式取反。

not:

關鍵字 not 用於對一個表示式取反,例如,如下命令在 myVar 的值不等於 3 時打印出 Correct:

set myVar=3
if not %myVar%==3 echo Correct
pause

如下命令在 c:\temp 目錄中不存在 myFile.txt 檔案時跳到 end 標籤:

if not exist c:\temp\myFile.txt goto end

exist:

關鍵字 exist 跟 if 語句連線用於測試一個檔案是否存在,參考 if 語句的例子。

接收引數:

可以給批處理檔案傳遞引數,可以使用%1 引用第一個引數,%2 引用第二個引數。依次類推。例如,如下命令列印第一個引數到控制檯上:echo %1

如果批處理檔案的名字為 test.bat,可以使用 test Hello 命令來呼叫它,這樣就會在控制檯上顯示 Hello。

下面的批處理檔案檢查第一個引數,如果是 start,就打印出 Starting application。如果是 stop 就打印出 Stopping application。其它情況打印出:Invalid parameter。

echo off
if %1==start goto start
if %1==stop goto stop
goto invalid
:start
echo Starting application
goto end
:stop
echo Stopping application
goto end
:invalid
echo Invalid parameter
:end

可以用“%1”與空字串比較來檢查批處理檔案是否有第一個引數,如果沒有則打印出 No parameter:

if "%1"=="" echo No parameter

上面的語句跟下面的相同:

if ""%1""=="""" echo No parameter

shift:

命令 shift 向後移動引數,意味這%2 指向%1,%3 指向%2,依次類推。例如下面的批處理檔案使用了 shift 命令:

echo off
shift
echo %1
echo %2

如果執行該命令的時候傳遞 3 個引數 a、b、c,會獲得如下輸出:

b
c

第一個引數可以使用%0 引用,最後一個引數丟失。

call:

命令 call 用於呼叫另一個命令。

setLocal:

可以使用 setLocal 命令來指明對於環境變數的改變為本地修改。環境變數的值會在執行完檔案或碰到 endLocal 命令後恢復。

start:

開啟一個新的視窗,可以使用 start 命令,可以傳遞個引數作為視窗的標題:start “Title”
另外,可以傳遞給該視窗要執行的命令,格式如下:start “Title” commandName

17.3.2 catalina.bat批處理檔案

catalina.bat 檔案可以用於啟動和停止 Tomcat。但用 startup.bat 和shutdown.bat 可更簡便地啟動和停止 Tomcat,這兩個批處理檔案都是通過傳遞合適的引數給 catalina.bat 檔案來實現的。

必須在%CATALINA_HOME%下的 bin目錄下面使用如下命令來呼叫 catalina.bat:

catalina  command

或者在%CATALINA_HOME%目錄下使用如下命令:

bin/catalina  command

這兩種情況下,可以傳遞的值如下:
•debug. 在debugger模式下啟動
•debug -security. 安全模式除錯
•embedded.嵌入式啟動
•jpda start.在JPDA 模式下啟動除錯
•run. 在當前視窗啟動
•run -security. 在當前視窗安全模式下啟動
•start.獨立視窗啟動
•start -security. 獨立視窗安全模式啟動
•stop. 停止

例如,要在一個獨立的視窗執行 Catalina,可以使用命令:catalina start

Catalina.bat 如 Listing17.9 所示:

Listing 17.9: The catalina.bat File

@echo off
if "%OS%" == "Windows_NT" setlocal
rem
---------------------------------------------------------------------------
rem Start/Stop Script for the CATALINA Server
rem
rem Environment Variable Prequisites
rem
rem CATALINA_HOME May point at your Catalina "build" directory.
rem
rem CATALINA_BASE (Optional) Base directory for resolving dynamic
portions
rem of a Catalina installation. If not present,
resolves to
rem the same directory that CATALINA_HOME points to.
rem
rem CATALINA_OPTS (Optional) Java runtime options used when the
"start",
rem "stop", or "run" command is executed.
rem
rem CATALINA_TMPDIR (Optional) Directory path location of temporary
directory
rem the JVM should use (java.io.tmpdir). Defaults to
rem %CATALINA_BASE%\temp.
rem
rem JAVA_HOME Must point at your Java Development Kit
installation.
rem
rem JAVA_OPTS (Optional) Java runtime options used