美文网首页
Tomcat源码分析 -- 4

Tomcat源码分析 -- 4

作者: sschrodinger | 来源:发表于2019-03-07 14:55 被阅读0次

    Tomcat源码分析 -- 4

    sschrodinger

    2019/01/07


    参考



    Digester


    tomcat 使用 Digester 解析并创建应用服务。

    Digester 是一款将 XML 转换为 Java 对象的事件驱动型工具,通过流读取 XML 文件,当识别出特定 XML 节点后执行特定的动作:或者创建 Java 对象;或者执行对象的某个方法。

    Digester 使用匹配模式指定相关的约定,匹配模式样例如下:

    匹配模式 XML节点 描述
    a <a> 匹配所有名字为"a"的根节点,注意嵌套的同名子节点无法匹配
    a/b <a><b> 匹配所有父节点为根节点"a",名称为"b"的节点。

    Digester 使用对象栈在匹配模式满足时对对象进行操作。在文件读取中,如果遇到一个 XML 节点开始的部分,则会触发处理规则事件创建 Java 对象,并将其放入栈中。当处理该节点的子节点时,该对象都将维护在栈中,直到该节点结束,该对象才会弹出对象栈。

    Digester 匹配模式确定了合适触发处理操作,处理规则定义了匹配模式时的具体操作,只需实现 org.apache.commons.digester.Rule 接口,就可以自定义事件处理方法。该接口有四个函数,如下表。

    函数名 描述
    begin() 当读取到匹配节点的开始部分时调用,会将该节点的所有属性作为参数传入
    body() 当读取匹配节点的嵌套内容时调用
    end() 当读取到匹配节点的结束部分时调用
    finish() 当整个parse()方法完成后调用

    Digester 实现了部分默认的事件处理函数,部分如下:

    方法及参数 描述
    addObjectCreate(String pattern, Class clazz) 当匹配到 pattern 模式时,将 clazz 实例化并压入栈
    addSetProperties(String pattern) 当匹配到 pattern 模式时,利用 JavaBean 将参数赋值给对象
    addSetNext(String pattern, String methodName) end() 方法调用,找到位于栈顶部对象之后的对象,调用指定的方法,同时将栈顶部对象作为参数传入,用于设置父对象的子对象
    addCallMethod(String pattern, String methodName, int paramCount, Class<?>[] paramTypes) 用于在 end() 执行时调用栈顶部对象的某个方法
    addCallParam(String pattern, int paramIndex, String attributeName) 与 addCallMethod 配合使用,创建参数

    有如下两个个类:

    public class Department {
        public Department() {
            // TODO Auto-generated constructor stub
        }
        
        private String name;
        private String code;
        
        private Map<String, String> extension = new HashMap<>();
        private List<User> users = new ArrayList<>();
        
        public String getName() { return name;}
        public void setName(String name) { this.name = name;}
        
        public String getCode() {return code;}
        public void setCode(String code) {this.code = code;}
        
        public void addUser(User user) { this.users.add(user);}
        
        public void putExtension(String name, String value) {this.extension.put(name, value);}
        
        public void print() {
            System.out.println("department name:" + name + ",code:" + code);
            Set<String> keySet = extension.keySet();
            if (keySet.size() > 0) System.out.println("extension info:");
            for (String string : keySet) {
                System.out.println("key:" + string + ",value:" + extension.get(string));
            }
            if (users.size() > 0) {
                System.out.println("user info:");
                for (User user : users) {
                    System.out.println(user);
                }
            }
            
        }
    }
    
    public class User {
        
        public User() {
            // TODO Auto-generated constructor stub
        }
        
        private String name;
        private String code;
        
        public String getName() { return name;}
        public void setName(String name) { this.name = name;}
        
        public String getCode() {return code;}
        public void setCode(String code) {this.code = code;}
        
        @Override public String toString() {
            return "name:" + name + ",code:" + code;
        }
    
    }
    

    先通过如下 XML 文档将这两个类组织起来:

    <?xml version="1.0" encoding="UTF-8"?>
    <department name="deptname001" code="deptcode001">
        <user name="username001" code="usercode001"></user>
        <user name="username002" code="usercode002"></user>
        <extension>
            <property-name>director</property-name>
            <propertry-value>joke</propertry-value>
        </extension>
    </department>
    

    则我们可以通过如下的 Demo 代码自动生成树状结构的类:

    public class digesterDemo {
        public static void main(String[] args) {
            Digester digester = new Digester();
            digester.setValidating(false);
            digester.addObjectCreate("department", Department.class);
            digester.addSetProperties("department");
            digester.addObjectCreate("department/user", User.class);
            digester.addSetProperties("department/user");
            
            digester.addSetNext("department/user", "addUser");
    
            digester.addCallParam("department/extension/property-name", 0);
            digester.addCallParam("department/extension/propertry-value", 1);
            digester.addCallMethod("department/extension", "putExtension", 2);
            
            try {
                Department department = (Department) digester.parse(new File("test.xml"));
                department.print();
                /**
                output:
                department name:deptname001,code:deptcode001
                    extension info:
                        key:director,value:joke
                    user info:
                        name:username001,code:usercode001
                        name:username002,code:usercode002
    
                **/
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }
    }
    

    tomcat 启动


    Tomcat 启动的主函数隐藏在or.apache.catalina.startup.Bootstrap中。main函数接受一个命令行参数启动或者关闭服务器,我们只关心启动过程,即 daemon.start() 分支。

    public static void main(String args[]) {
    
        if (daemon == null) {
            // Don't set daemon until init() has completed
            Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();
            } catch (Throwable t) {
                handleThrowable(t);
                t.printStackTrace();
                return;
            }
            daemon = bootstrap;
        } else {
            // When running as a service the call to stop will be on a new
            // thread so make sure the correct class loader is used to prevent
            // a range of class not found exceptions.
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }
    
        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }
    
            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else if (command.equals("configtest")) {
                daemon.load(args);
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
                System.exit(0);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t) {
            // Unwrap the Exception for clearer error reporting
            if (t instanceof InvocationTargetException &&
                    t.getCause() != null) {
                t = t.getCause();
            }
            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }
    
    }
    

    主要实现的功能

    • 根据 server.xml 加载整个服务器 (Load()方法)
    • 加载钩子
    • await() 等待
    • 等待结束 调用 stop() 方法 结束

    daemon.start() 利用反射机制调用 org.apache.catalina.startup.Catalina类的 start() 方法,启动整个服务器。

    如下是 Catalina 类的 start() 方法:

    public void start() {
    
        if (getServer() == null) {
            load();
        }
    
        if (getServer() == null) {
            log.fatal("Cannot start server. Server instance is not configured.");
            return;
        }
    
        long t1 = System.nanoTime();
    
        // Start the new server
        try {
            getServer().start();
        } catch (LifecycleException e) {
            log.fatal(sm.getString("catalina.serverStartFail"), e);
            try {
                getServer().destroy();
            } catch (LifecycleException e1) {
                log.debug("destroy() failed for failed Server ", e1);
            }
            return;
        }
    
        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
        }
    
        // Register shutdown hook
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);
    
            // If JULI is being used, disable JULI's shutdown hook since
            // shutdown hooks run in parallel and log messages may be lost
            // if JULI's hook completes before the CatalinaShutdownHook()
            LogManager logManager = LogManager.getLogManager();
            if (logManager instanceof ClassLoaderLogManager) {
                ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                        false);
            }
        }
    
        if (await) {
            await();
            stop();
        }
    }
    

    Tomcat 结合 Digester 和 LifecycleListener 做了大量的初始化工作。具体的执行函数为protected Digester createStartDigester()

    protected Digester createStartDigester() {
        long t1=System.currentTimeMillis();
        // Initialize the digester
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);
        Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
        List<String> objectAttrs = new ArrayList<>();
        objectAttrs.add("className");
        fakeAttributes.put(Object.class, objectAttrs);
        // Ignore attribute added by Eclipse for its internal tracking
        List<String> contextAttrs = new ArrayList<>();
        contextAttrs.add("source");
        fakeAttributes.put(StandardContext.class, contextAttrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setUseContextClassLoader(true);
    
        // 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.NamingResourcesImpl");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                                "setGlobalNamingResources",
                                "org.apache.catalina.deploy.NamingResourcesImpl");
    
        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");
    
        //Executor
        digester.addObjectCreate("Server/Service/Executor",
                             "org.apache.catalina.core.StandardThreadExecutor",
                             "className");
        digester.addSetProperties("Server/Service/Executor");
    
        digester.addSetNext("Server/Service/Executor",
                                "addExecutor",
                                "org.apache.catalina.Executor");
    
    
        digester.addRule("Server/Service/Connector",
                             new ConnectorCreateRule());
        digester.addRule("Server/Service/Connector",
                             new SetAllPropertiesRule(new String[]{"executor", "sslImplementationName"}));
        digester.addSetNext("Server/Service/Connector",
                                "addConnector",
                                "org.apache.catalina.connector.Connector");
    
        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
                                     "org.apache.tomcat.util.net.SSLHostConfig");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig",
                    "addSslHostConfig",
                    "org.apache.tomcat.util.net.SSLHostConfig");
    
        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                             new CertificateCreateRule());
        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                             new SetAllPropertiesRule(new String[]{"type"}));
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
                                "addCertificate",
                                "org.apache.tomcat.util.net.SSLHostConfigCertificate");
    
        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                                     "org.apache.tomcat.util.net.openssl.OpenSSLConf");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                                "setOpenSslConf",
                                "org.apache.tomcat.util.net.openssl.OpenSSLConf");
    
        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                                     "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                                "addCmd",
                                "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
    
        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");
    
        digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
                                      null, // MUST be specified in the element
                                      "className");
        digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
        digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
                                "addUpgradeProtocol",
                                "org.apache.coyote.UpgradeProtocol");
    
            // 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/Host/"));
        addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
    
        // When the 'engine' is found, set the parentClassLoader.
        digester.addRule("Server/Service/Engine",
                             new SetParentClassLoaderRule(parentClassLoader));
        addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");
    
        long t2=System.currentTimeMillis();
        if (log.isDebugEnabled()) {
                log.debug("Digester for server.xml created " + ( t2-t1 ));
        }
        return (digester);
    
    }
    

    getServer().start()启动server的生命周期到LifecycleState.STARTED状态;stop()方法关闭服务器,将生命周期改变为LifecycleState.DESTROYED

    相关文章

      网友评论

          本文标题:Tomcat源码分析 -- 4

          本文链接:https://www.haomeiwen.com/subject/tgvcpqtx.html