class Bootstrap

package org.apache.catalina.startup
2003-03-24

package org.apache.catalina.startup;


// JDK类库

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;


// apache自己的类库

import org.apache.catalina.loader.Extension;
import org.apache.catalina.loader.StandardClassLoader;



/**
 * 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 $
 */



/**
 * 该类的main方法的主要任务:
 * --------------------------
 *
 * 1,创建TOMCAT自己的类载入器(ClassLoader)
 *      +---------------------------+
 *      |         Bootstrap         |
 *      |             |             |
 *      |          System           |
 *      |             |             |
 *      |          Common           |
 *      |         /      \          |
 *      |     Catalina  Shared      |
 *      +---------------------------+
 *    其中:
 *    - Bootstrap - 载入JVM自带的类和$JAVA_HOME/jre/lib/ext/*.jar
 *    - System    - 载入$CLASSPATH/*.class
 *    - Common    - 载入$CATALINA_HOME/common/...,它们对TOMCAT和所有的WEB APP都可见
 *    - Catalina  - 载入$CATALINA_HOME/server/...,它们仅对TOMCAT可见,对所有的WEB APP都不可见
 *    - Shared    - 载入$CATALINA_HOME/shared/...,它们仅对所有WEB APP可见,对TOMCAT不可见(也不必见)
 *    注意:当一个ClassLoader被请求载入一个类时,它首先请求其父ClassLoader完成载入,
 *    仅当其父ClassLoader无法载入该类时,才试图自己载入该类
 * 2,改变本身线程的默认ClassLoader(本线程就是Tomcat Server线程,类载入器是catalinaLoader)
 * 3,让catalinaLoader载入一些类,类的位置在$CATALINA_HOME/server/lib/catalina.jar中
 * 4,创建org.apache.catalina.startup.Catalina类的一个实例startupInstance,并为其调用方法:
 *    startupInstance.setParentClassLoader(sharedLoader);
 *    startupInstance.process(args);
 *
 *
 * 有关ClassLoader的说明:
 * -----------------------
 *
 * 每个被DEPLOY的WEB APP都会被创建一个ClassLoader,用来载入该WEB APP自己的类
 * 这些类的位置是webappX/WEB-INF/classes/*.class和webappX/WEB-INF/lib/*.jar
 *
 * ClassLoader的工作流程是:
 * 1) 收到一个载入类的的请求
 * 2) 请求其父ClassLoader来完成该类的载入
 * 3) 如果父ClassLoader无法载入,则自己试图完成该类的载入
 *
 * 特别注意WEB APP自己的ClassLoader的实现与众不同:
 * 它先试图从WEB APP自己的目录里载入,如果失败则请求父ClassLoader的代理
 * 这样可以让不同的WEB APP之间的类载入互不干扰
 *
 * WEB APP的ClassLoader的层次结构是:
 *     +----------------------------+
 *     |       Shared               |
 *     |      /      \ ...          |
 *     |   Webapp1  Webapp2  ...    |
 *     +----------------------------+
 * 故对于一个WEB APP,其类载入的优先顺序如下:
 * - /WEB-INF/classes/*.class 和 /WEB-INF/lib/*.jar
 * - Bootstrap classes of JVM
 * - System class loader classes
 * - $CATALINA_HOME/common/...
 * - $CATALINA_HOME/shared/...
 *
 *
 * 小结:
 * ------
 *
 * 综上分析
 * - Tomcat Server线程使用的classLoader是Catalina
 * - 每个WEB APP线程使用的classloader是Webapp?
 *
 */


public final class Bootstrap {



    /**
     * DEBUG级别
     */

    private static int debug = 0;



    /**
     * 脚本执行该程序时,提供以下的系统属性:
     * java.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
     * java.security.manager \
     * java.security.policy=="$CATALINA_BASE"/conf/catalina.policy \
     * catalina.base="$CATALINA_BASE" \
     * catalina.home="$CATALINA_HOME" \
     * java.io.tmpdir="$CATALINA_TMPDIR" \
     *
     * @param args Command line arguments to be processed
     */

    public static void main(String args[]) {


        // 设置debug

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


        // 设置好系统属性catalina.base,即保证其有值

        if (System.getProperty("catalina.base") == null)
            System.setProperty("catalina.base", getCatalinaHome());


        // 创建三个ClassLoader
        // 这三个对象是通过ClassLoaderFactory的静态方法创建的
        // 其实际类型是StandardClassLoader,完成tomcat自定义的类载入
        // 这些类对非tomcat及其上的webapp的其它java程序不可见,故用自己的Classloader载入

        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);


            // $CATALINA_HOME/common/classes/*.class - 未压缩的类
            // $CATALINA_HOME/common/endorsed/*.jar - 压缩的类(endorse:支持)
            // $CATALINA_HOME/common/lib/*.jar - 压缩的类
            // 这些类是被tomcat server以及所有的webapp所共享的类,由commonLoader负责载入

            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);



            // $CATALINA_HOME/server/classes/*.class
            // $CATALINA_HOME/server/lib/*.jar
            // 这些类是仅被tomcat server使用而对webapp不可见的类,由catalinaLoader负责载入

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



            // $CATALINA_BASE/shared/classes/*.class
            // $CATALINA_BASE/shared/lib/*.jar
            // 这些类是仅被tomcat的webapp使用的类,由sharedLoader负责载入

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

            // 注意三个自己定置的ClassLoader的层次关系:
            // systemClassLoader (root)
            //   +--- commonLoader
            //          +--- catalinaLoader
            //          +--- sharedLoader

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

        }


        // 为当前的线程更改其contextClassLoader
        // 一般的线程默认的contextClassLoader是系统的ClassLoader(所有其它自定义ClassLoader的父亲)
        // 当该线程需要载入类时,将使用自己的contextClassLoader来寻找并载入类
        // 更改contextClassLoader可以更改该线程的寻找和载入类的行为,但不影响到其它线程
        // 注意!Tomcat Server线程使用的是catalinaLoader

        Thread.currentThread().setContextClassLoader(catalinaLoader);



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

        try {


            // 预载入catalinalLoader的一些类

            SecurityClassLoad.securityClassLoad(catalinaLoader);


            // 获得tomcat的启动类:org.apache.catalina.startup.Catalina,并创建该类的一个实例

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


            // 设置startupInstance的父ClassLoader,相当于执行:
            // Catalina startupInstance = new Catailina();
            // startupInstance.setParentClassLoader(sharedLoader);
            // 详情参考类org.apache.catalina.startup.Catalina

            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);


            // 使用main方法获得的参数args来执行process方法,相当于:
            // startupInstance.process(args);
            // 详情参考类org.apache.catalina.startup.Catalina

            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);
        }

    }



    /**
     * 返回$CATALINA_HOME变量。如果该变量没有定义,则将之赋值为用户的当前工作目录。
     */

    private static String getCatalinaHome() {
        return System.getProperty("catalina.home",
                                  System.getProperty("user.dir"));
    }



    /**
     * 返回$CATALINA_BASE变量。如果该变量没有定义,则将之赋值为$CATALINA_HOME。
     */

    private static String getCatalinaBase() {
        return System.getProperty("catalina.base", getCatalinaHome());
    }



    /**
     * 输出LOG信息。
     */

    private static void log(String message) {

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

    }



    /**
     * 输出由异常引起的LOG信息。
     */

    private static void log(String message, Throwable exception) {

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

    }


}