美文网首页
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