tomcat源码学习(2)  关于apache digest

  好久不写博文,罪过罪过。因为最近公司比较忙加上琐事有点多,所以隔了好久才来更新博文。

  apache digest本来是struts2框架中来加载xml文件并实例化对象的一个jar包,后来使用的越来越多。
我们都知道tomcat的conf文件夹下有一个server.xml配置文件,我们经常会其中的来进行配置以来运行一个java web项目,也经常修改中的port属性以来实现修改tomcat监听的端口。其实每个标签基本上都对应着一个对象,那tomcat是如何将这些对象实例化到java 虚拟机的运行内存中的呢,这就是apache digest类做的事情。
 
  上篇讲到catalina的load的方法,在load方法中调用了两个函数  
1 initDirs();
2 // Before digester - it may be needed
3 initNaming();

  上述两个函数之后调用createStartDigester()。
  
protected Digester createStartDigester() {
        long t1=System.currentTimeMillis();
        // Initialize the digester
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);
        HashMap, List> fakeAttributes = new HashMap<>();
        ArrayList attrs = new ArrayList<>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        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"}));
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.connector.Connector");
 
 
        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/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);
}
我们可以看到创建了一个digester对象之后,暂时不看对digester对象的各种属性的设置set方法,我们可以看到一大堆的
addObjectCreate("Server","org.apache.catalina.core.StandardServer","className");
digester.addSetProperties("Server");
digester.addSetNext("Server","setServer", "org.apache.catalina.Server");

这里就是digest对象中的所谓的各种规则了,这里看到的server就是我们在tomcat源码学习后期看到的server对象啦,这个以后再说。

 
load()方法中,在创建了digester对象后,接下来调用了digester.parse(inputSource);方法即开始解析xml文件并根据上述规则开始实例化各种对象了。这里的xml文件即为conf文件下的server.xml文件拉。
 
回过头来再看digester对象。以下为digester类的成员变量。
protected StringBuilder bodyText = new StringBuilder();
   
protected ArrayStack bodyTexts = new ArrayStack<>();
 
protected ArrayStack> matches = new ArrayStack<>(10);
   
protected ClassLoader classLoader = null;
   
protected boolean configured = false;
   
protected EntityResolver entityResolver;
   
protected HashMap entityValidator = new HashMap<>();
   
protected ErrorHandler errorHandler = null;
   
protected SAXParserFactory factory = null;

protected Locator locator = null;
   
protected String match = "";
 
protected boolean namespaceAware = false;
 
protected HashMap> namespaces = new HashMap<>();
 
protected ArrayStackparams = new ArrayStack<>();
 
protected SAXParser parser = null;
 
protected String publicId = null;
 
protected XMLReader reader = null;
 
protected Object root = null;
   
protected Rules rules = null;
   
protected ArrayStackstack = new ArrayStack<>();
 
protected boolean useContextClassLoader = false;
 
protected boolean validating = false;
 
protected boolean rulesValidation = false;
 
protected Map, List> fakeAttributes = null;

在讨论digester类是如何实现在解析xml时来实例化相应的类前,我们先看一篇博文,关于java如何解析xml的文件的    http://www.iteye.com/topic/763895

 
原生java包中已经提供了sax来解析源码的api,在解析的时候触发不同的事件,对事件函数进行处理,我们只要继承org.xml.sax.helpers.DefaultHandler类来实现我们的业务需求即可。
最主要的是重写startDocument(),startElement(String uri, String localName, String qName, Attributes attributes),endElement(String uri, String localName, String qName)  ,characters(char[] ch, int start, int length) 函数。
 
我们来看Digetster类的成员变量,果然含有SAXParser类和XMLReader的成员对象,这里tomcat调用的是XMLReader的parse方法。
 
我们从
public void addObjectCreate(String pattern, String className,String attributeName) {
    addRule(pattern,new ObjectCreateRule(className, attributeName));
}
ObjectCreateRule 顾名思义,这是一个创建对象的规则了,继承Rule类,Rule是一个抽象类,成员变量为protected类型,可以被子类获取到,分别为
  protected Digester digester = null;
  protected String namespaceURI = null; 。
注释写的很明白。
 
还可以看到除了set,get方法之外的begin() body() end() finish()方法。这些方法将会被子类重写以实现业务需求。后面我们将看到这些方法将会被上面谈到的startElement()等方法使用到。
 
接着看Digester类的方法
public void addRule(String pattern, Rule rule) {
    rule.setDigester(this);
    getRules().add(pattern, rule);
}

将rule绑定到digester上,getRules()返回的是Digester类的成员变量rules,为Rules类型的成员变量,Rules是一个接口,实现有RulesBase类,成员变量如下

protected HashMap> cache = new HashMap<>();

protected Digester digester = null;

protected String namespaceURI = null;

protected ArrayList rules = new ArrayList<>();

rules的add方法

 @Override
    public void add(String pattern, Rule rule) {
        // to help users who accidently add ‘/‘ to the end of their patterns
        int patternLength = pattern.length();
        if (patternLength>1 && pattern.endsWith("/")) {
            pattern = pattern.substring(0, patternLength-1);
        }
 
        List list = cache.get(pattern);
        if (list == null) {
            list = new ArrayList<>();
            cache.put(pattern, list);
        }
        list.add(rule);
        rules.add(rule);
        if (this.digester != null) {
            rule.setDigester(this.digester);
        }
        if (this.namespaceURI != null) {
            rule.setNamespaceURI(this.namespaceURI);
        }
}

即将需要匹配的pattern和rule放置到cache的hashmap中,并绑定digester和namespaceURI

后面还有两种
digester.addSetProperties("Server");
digester.addSetNext("Server","setServer","org.apache.catalina.Server");

分别是两种不同的规则,就不再一一赘述了。

 
后面继续还有addRuleSet,我们会发现该函数设置了一下namespace,还有增加了一堆以上的规则,比如说NamingRuleSet就增加了一堆EJB相关的规则以对EJB进行支持等等,EngineRuleSet,HostRuleSet,ContextRuleSet 应该分别对应tomcat的几大组件engine,host,context,当然还有wrapper,也就是servlet相关的没有出现。
 
其中有一个addClusterRuleSet 函数是调用了反射,通过Class.forName来动态加载一个RuleSet,不知道为什么,这里先记下。
 
上面说到是XML的parse方法。在catalina类的load方法中调用了digester的parse方法
public Object parse(InputSource input) throws IOException,SAXException {
        configure();
        getXMLReader().parse(input);
        return (root);
}

protected void configure() {

        // Do not configure more than once
        if (configured) {
            return;
        }

        log = LogFactory.getLog("org.apache.tomcat.util.digester.Digester");
        saxLog = LogFactory.getLog("org.apache.tomcat.util.digester.Digester.sax");

        // Set the configuration flag to avoid repeating
        configured = true;
}

public XMLReader getXMLReader() throws SAXException {
        if (reader == null){
            reader = getParser().getXMLReader();
        }

        reader.setDTDHandler(this);
        reader.setContentHandler(this);

        if (entityResolver == null){
            reader.setEntityResolver(this);
        } else {
            reader.setEntityResolver(entityResolver);
        }

        reader.setProperty(
                "http://xml.org/sax/properties/lexical-handler", this);

        reader.setErrorHandler(this);
        return reader;
}

public SAXParser getParser() {

        // Return the parser we already created (if any)
        if (parser != null) {
            return (parser);
        }

        // Create a new parser
        try {
            parser = getFactory().newSAXParser();
        } catch (Exception e) {
            log.error("Digester.getParser: ", e);
            return (null);
        }

        return (parser);

}

可以看到 getXMLReader 方法中,设置XMLReader的解析处理handler均为digester类。主要看

reader.setContentHandler(this);

即在解析xml文件中将调用digester类中的startElement,endElement,endElement方法等。  

我们先来看startElement方法

 @Override
    public void startElement(String namespaceURI, String localName,
                             String qName, Attributes list)
            throws SAXException {
        boolean debug = log.isDebugEnabled();

        if (saxLog.isDebugEnabled()) {
            saxLog.debug("startElement(" + namespaceURI + "," + localName + "," +
                    qName + ")");
        }

        // Parse system properties
        list = updateAttributes(list);

        // Save the body text accumulated for our surrounding element
        bodyTexts.push(bodyText);
        if (debug) {
            log.debug("  Pushing body text ‘" + bodyText.toString() + "‘");
        }
        bodyText = new StringBuilder();

        // the actual element name is either in localName or qName, depending
        // on whether the parser is namespace aware
        String name = localName;
        if ((name == null) || (name.length() < 1)) {
            name = qName;
        }

        // Compute the current matching rule
        StringBuilder sb = new StringBuilder(match);
        if (match.length() > 0) {
            sb.append(‘/‘);
        }
        sb.append(name);
        match = sb.toString();
        if (debug) {
            log.debug("  New match=‘" + match + "‘");
        }

        // Fire "begin" events for all relevant rules
        List<Rule> rules = getRules().match(namespaceURI, match);
        matches.push(rules);
        if ((rules != null) && (rules.size() > 0)) {
            for (int i = 0; i < rules.size(); i++) {
                try {
                    Rule rule = rules.get(i);
                    if (debug) {
                        log.debug("  Fire begin() for " + rule);
                    }
                    rule.begin(namespaceURI, name, list);
                } catch (Exception e) {
                    log.error("Begin event threw exception", e);
                    throw createSAXException(e);
                } catch (Error e) {
                    log.error("Begin event threw error", e);
                    throw e;
                }
            }
        } else {
            if (debug) {
                log.debug("  No rules found matching ‘" + match + "‘.");
            }
        }

    }

 

   
 
还没有写完,明日继续
 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。