zoukankan      html  css  js  c++  java
  • How Tomcat Works(十九)

    本文重点关注启动tomcat时会用到的两个类,分别为Catalina类和Bootstrap类,它们都位于org.apachae.catalina.startup包下;Catalina类用于启动或关闭Server对象,并负责解析server.xml配置文件;Bootstrap类是一个入口点,负责创建Catalina实例,并调用其process()方法。

    org.apachae.catalina.startup.Catalina类是启动类,它包含一个Digester对象,用于解析位于%CATALINE_HOME%/conf目录的server.xml文件

    Catalina类还封装了一个Server对象,该对象持有一个Service对象(Service对象包含一个Servlet容器和一个或多个连接器)。可以使用Catalina类来启动/关闭Server对象

    可以通过实例化Catalina类,并调用其process()方法来运行Tomcat,但在调用该方法时,需要传入适当的参数(如start或stop -help -config -debug -nonaming等)。

    /**
         * The instance main program.
         *
         * @param args Command line arguments
         */
        public void process(String args[]) {
    
            setCatalinaHome();
            setCatalinaBase();
            try {
                if (arguments(args))
                    execute();
            } catch (Exception e) {
                e.printStackTrace(System.out);
            }
        }

    上面方法会进一步调用execute()方法,execute()方法会根据传入参数调用start()方法或stop()方法

    start方法会创建一个Digester对象来解析server.xml文件(Tomcat配置文件)。在解析server.xml文件之前,start方法会调用Digester对象的push方法,传入当前的Catalina对象为参数。这样,Catalina对象就成了Digester对象内部对象栈的第一个对象。解析server.xml文件后,会将变量server指向一个Server对象(默认是org.apache.catalina.core.StandardServer类型的对象)。然后,start方法会调用server的initialize和start方法。Catalina对象的start方法会调用Server对象的await方法,server对象会使用一个专用的线程来等待关闭命令。await方法会循环等待,知道接收到正确的关闭命令。当await方法返回时,Catalina对象的start方法会调用server对象的stop方法,从而关闭server对象和其他的组件。此外,start方法还会注册shutdown hook,确保服务器关闭时会执行Server对象的stop方法。

    start方法的实现如下:

    /**
         * Start a new server instance.
         */
        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);
                    }
                }
            }
    
        }

    其中的CatalinaShutdownHook类为关闭钩子

     /**
         * Shutdown hook which will perform a clean shutdown of Catalina if needed.
         */
        protected class CatalinaShutdownHook extends Thread {
    
            public void run() {
    
                if (server != null) {
                    try {
                        ((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);
                        }
                    }
                }
    
            }
        }

    Catalina对象的stop方法会关闭Server对象,其实现如下:

    /**
         * Stop an existing server instance.
         */
        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对象push到Digester对象的内部对象栈中。

    createStartDigester方法创建了一个Digester对象,然后将规则添加到其中,解析server.xml文件。添加到Digester对象中的规则是理解tomcat配置的关键。

    createStartDigester方法的实现如下:

    /**
         * Create and configure the Digester we will be using for startup.
         */
        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);
    
        }

    这里需要注意的是 digester.addSetNext("Server", "setServer","org.apache.catalina.Server")方法,该方法将Server对象压入到Digester对象的内部栈中,并与栈中的下一个对象相关联;在这里,下一个对象是Catalina实例,调用其setServer()方法与Server对象相关联。

    createStopDigester方法返回一个Digester对象来关闭Server对象。createStopDigester方法实现如下:

    /**
         * Create and configure the Digester we will be using for shutdown.
         */
        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对象只对XML文件的根元素感兴趣

     org.apache.catalina.startup.Bootstrap类提供了启动tomcat的切入点(还有一些其他的类也有此功能)。当使用bat或sh启动tomcat时,实际上会调用该类的main方法。在main方法中会创建三个loader,并实例化Catalina对象,然后调用Catalina对象的process方法。

    Bootstrap类的定义如下所示:

    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类定义了四个静态方法,其中getCatalinaHome返回catalina.home属性的值,若没有,则返回user.dir属性的值。getCatalinaBase方法与getCatalinaHome方法类似。

    Bootstrap类的main方法构造了三个loader,之所以如此做是为了防止运行WEB-INF/classes和WEB-INF/lib目录外的类。

    其中commonLoader允许从%CATALINA_HOME%/common/classes,%CATALINA_HOME%/common/endorsed和%CATALINA_HOME%/common/lib目录下载入类。

    catalinaLoader负责载入servlet容器需要使用的类,它只会从%CATALINA_HOME%/server/classes和%CATALINA_HOME%/server/lib目录下查找。

    sharedLoader会从%CATALINA_HOME%/shared/classes和%CATALJNA_HOME%/shared/lib目录,以及对commonLoader可用的目录下查找需要的类。

    然后,将sharedLoader设置为每个web应用的类载入器的父类载入器。(注意,sharedLoader并不访问catalina的内部类,或CLASSPATH中的类)

    在创建了三个loader之后,main方法会载入Catalina类,实例化,并将之赋值给startupInstance变量。然后调用setParentClassLoader方法。

    最后,main方法调用Catalina对象的process对象。

    --------------------------------------------------------------------------- 

    本系列How Tomcat Works系本人原创 

    转载请注明出处 博客园 刺猬的温驯 

    本人邮箱: chenying998179#163.com (#改为@

    本文链接http://www.cnblogs.com/chenying99/p/3250907.html

  • 相关阅读:
    在Win8下无法打开 hlp 帮助文件的问题
    ftp 终端命令
    为 Macbook 增加锁屏热键技巧
    苹果系统直接读写 ntfs 磁盘
    div 绝对布局居中
    2015-1-11
    unable to load default svn client
    ubuntu eclipse 安装svn
    centos mysq table is read only
    centos ssh 乱码
  • 原文地址:https://www.cnblogs.com/chenying99/p/3250907.html
Copyright © 2011-2022 走看看