Bootstrap的load方法是加載Tomcat的server.xml的入口,load方法實際經過反射調用catalinaDaemon(類型爲Catalina)的load方法:java
/** * Load daemon. */ private void load(String[] arguments) throws Exception { // Call the load() method String methodName = "load"; Object param[]; Class<?> paramTypes[]; if (arguments==null || arguments.length==0) { paramTypes = null; param = null; } else { paramTypes = new Class[1]; paramTypes[0] = arguments.getClass(); param = new Object[1]; param[0] = arguments; } Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);//經過反射機制調用Catalina類的load方法。 if (log.isDebugEnabled()) log.debug("Calling startup class " + method); method.invoke(catalinaDaemon, param); }
Catalina類的load方法:express
/* * Load using arguments */ public void load(String args[]) { try { if (arguments(args)) { load();//調用自身的load方法 } } catch (Exception e) { e.printStackTrace(System.out); } } /** * Start a new server instance. */ public void load() { long t1 = System.nanoTime(); initDirs(); //用於對catalina.home和catalina.base的一些檢查工做 // Before digester - it may be needed initNaming();//給系統設置java.naming.factory.url.pkgs和java.naming.factory.initial // Create and execute our Digester Digester digester = createStartDigester();//實例化Digester InputSource inputSource = null; InputStream inputStream = null; File file = null; try { try { file = configFile();//獲取conf/server.xml配置文件 inputStream = new FileInputStream(file);獲取conf/server.xml配置文件輸入流 inputSource = new InputSource(file.toURI().toURL().toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", file), e); } } if (inputStream == null) { try { inputStream = getClass().getClassLoader() .getResourceAsStream(getConfigFile()); inputSource = new InputSource (getClass().getClassLoader() .getResource(getConfigFile()).toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", getConfigFile()), e); } } } // This should be included in catalina.jar // Alternative: don't bother with xml, just create it manually. if( inputStream==null ) { try { inputStream = getClass().getClassLoader() .getResourceAsStream("server-embed.xml"); inputSource = new InputSource (getClass().getClassLoader() .getResource("server-embed.xml").toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", "server-embed.xml"), e); } } } if (inputStream == null || inputSource == null) { if (file == null) { log.warn(sm.getString("catalina.configFail", getConfigFile() + "] or [server-embed.xml]")); } else { log.warn(sm.getString("catalina.configFail", file.getAbsolutePath())); if (file.exists() && !file.canRead()) { log.warn("Permissions incorrect, read permission is not allowed on the file."); } } return; } try { inputSource.setByteStream(inputStream);//將FileInputStream封裝爲InputSource digester.push(this); digester.parse(inputSource);//調用Digester的parse方法進行解析 } catch (SAXParseException spe) { log.warn("Catalina.start using " + getConfigFile() + ": " + spe.getMessage()); return; } catch (Exception e) { log.warn("Catalina.start using " + getConfigFile() + ": " , e); return; } } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { // Ignore } } } getServer().setCatalina(this); // Stream redirection initStreams();//initStreams對輸出流、錯誤流重定向 // Start the new server try { getServer().init();//初始化server } catch (LifecycleException e) { if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new java.lang.Error(e); } else { log.error("Catalina.start", e); } } long t2 = System.nanoTime(); if(log.isInfoEnabled()) { log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms"); } }
下面開始分析整個過程:apache
protected void initDirs() { String catalinaHome = System.getProperty(Globals.CATALINA_HOME_PROP); if (catalinaHome == null) { // Backwards compatibility patch for J2EE RI 1.3 String j2eeHome = System.getProperty("com.sun.enterprise.home"); if (j2eeHome != null) { catalinaHome=System.getProperty("com.sun.enterprise.home"); } else if (System.getProperty(Globals.CATALINA_BASE_PROP) != null) { catalinaHome = System.getProperty(Globals.CATALINA_BASE_PROP); } } // last resort - for minimal/embedded cases. if(catalinaHome==null) { catalinaHome=System.getProperty("user.dir"); } if (catalinaHome != null) { File home = new File(catalinaHome); if (!home.isAbsolute()) { try { catalinaHome = home.getCanonicalPath(); } catch (IOException e) { catalinaHome = home.getAbsolutePath(); } } System.setProperty(Globals.CATALINA_HOME_PROP, catalinaHome); } if (System.getProperty(Globals.CATALINA_BASE_PROP) == null) { System.setProperty(Globals.CATALINA_BASE_PROP, catalinaHome); } else { String catalinaBase = System.getProperty(Globals.CATALINA_BASE_PROP); File base = new File(catalinaBase); if (!base.isAbsolute()) { try { catalinaBase = base.getCanonicalPath(); } catch (IOException e) { catalinaBase = base.getAbsolutePath(); } } System.setProperty(Globals.CATALINA_BASE_PROP, catalinaBase); } String temp = System.getProperty("java.io.tmpdir"); if (temp == null || (!(new File(temp)).exists()) || (!(new File(temp)).isDirectory())) { log.error(sm.getString("embedded.notmp", temp)); } }
protected void initNaming() { // Setting additional variables if (!useNaming) { log.info( "Catalina naming disabled"); 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); if( log.isDebugEnabled() ) { log.debug("Setting naming prefix=" + 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"); } else { log.debug( "INITIAL_CONTEXT_FACTORY already set " + value ); } } }
在建立JNDI上下文時,使用Context.INITIAL _ CONTEXT _ FACTORY("java.naming.factory.initial")屬性,來指定建立JNDI上下文的工廠類;Context.URL _ PKG _ PREFIXES("java.naming.factory.url.pkgs")用在查詢url中包括scheme方法id時建立對應的JNDI上下文.tomcat
/** * Create and configure the Digester we will be using for startup. */ protected Digester createStartDigester() { long t1=System.currentTimeMillis(); // Initialize the digester Digester digester = new Digester(); digester.setValidating(false); digester.setRulesValidation(true); HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<Class<?>, List<String>>(); ArrayList<String> attrs = new ArrayList<String>(); 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.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"); //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繼承了DefaultHandler,而DefaultHandler默認實現了ContentHander、DTDHander、ErrorHandler及EntityResolver 這4個接口,代碼以下:數據結構
public class DefaultHandler implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler
經過源碼能夠發現DefaultHandler的全部實現都是空實現,因此解析還須要Digester。(具體分析後面在說)。app
/** * Return a File object representing our configuration file. */ protected File configFile() { File file = new File(configFile); if (!file.isAbsolute()) { file = new File(System.getProperty(Globals.CATALINA_BASE_PROP), configFile); } return (file); }
inputSource.setByteStream(inputStream); digester.push(this); digester.parse(inputSource);
protected void initStreams() { // Replace System.out and System.err with a custom PrintStream System.setOut(new SystemLogHandler(System.out)); System.setErr(new SystemLogHandler(System.err)); }
// Start the new server try { getServer().init(); } catch (LifecycleException e) { if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new java.lang.Error(e); } else { log.error("Catalina.start", e); } }
當加載server.xml配置文件到內存後,開始對XML文件中的內容進行解析,主要包含兩個步驟:ide
Tomcat將server.xml文件中的全部元素上的屬性都抽象爲Rule,以Server元素爲例,在內存中對應Server實例,Server實例的屬性值就來自於Server元素的屬性值。經過對規則(Rule)的應用,最終改變Server實例的屬性值。Rule是一個抽象類,其中定義瞭如下方法:ui
Rule目前有不少實現類,如:NodeCreateRule、AbsoluteOrderingRule、CallParamRule、ConnectorCreateRule等。下圖展現了Rule的部分實現類:this
Tomcat使用SAX(Simple API for XML)解析XML:url
SAX解析XML採用的是從上而下的基於事件驅動的解析方式,在解析過程當中會視狀況自動調用ContentHandler接口中的startDocument()、startElement()、characters()、endElement()、endDocument()等相關的方法。
由編譯執行的結果分析:
使用SAX解析XML的好處:
因爲SAX是基於事件驅動的,不用解析完整個文檔,在按內容順序解析文檔過程當中, SAX 會判斷當前讀到的字符是否符合 XML 文件語法中的某部分。若是符合某部分,則會觸發事件。所謂觸發事件,就是調用一些回調方法。在用 SAX 解析 xml 文檔時候,在讀取到文檔開始和結束標籤時候就會回調一個事件,在讀取到其餘節點與內容時候也會回調一個事件。在 SAX 接口中,事件源是 org.xml.sax 包中的 XMLReader ,它經過 parser() 方法來解析 XML 文檔,併產生事件。事件處理器是 org.xml.sax 包中 ContentHander 、 DTDHander 、 ErrorHandler ,以及 EntityResolver 這4個接口:
回調方法通常都定義在ContentHandler接口中,上面已經對這些回調方法的加載順序已經說了就不在介紹了。
使用 SAX 解析 XML 文件通常有如下五個步驟:
咱們經過源碼能夠發現DefaultHandler的全部實現都是空實現,因此解析還須要Digester自身,代碼以下:
@Override public void startDocument() throws SAXException { if (saxLog.isDebugEnabled()) { saxLog.debug("startDocument()"); } // ensure that the digester is properly configured, as // the digester could be used as a SAX ContentHandler // rather than via the parse() methods. configure(); } @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); 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 + "'."); } } } @Override public void endDocument() throws SAXException { if (saxLog.isDebugEnabled()) { if (getCount() > 1) { saxLog.debug("endDocument(): " + getCount() + " elements left"); } else { saxLog.debug("endDocument()"); } } while (getCount() > 1) { pop(); } // Fire "finish" events for all defined rules Iterator<Rule> rules = getRules().rules().iterator(); while (rules.hasNext()) { Rule rule = rules.next(); try { rule.finish(); } catch (Exception e) { log.error("Finish event threw exception", e); throw createSAXException(e); } catch (Error e) { log.error("Finish event threw error", e); throw e; } } // Perform final cleanup clear(); } @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { boolean debug = log.isDebugEnabled(); if (debug) { if (saxLog.isDebugEnabled()) { saxLog.debug("endElement(" + namespaceURI + "," + localName + "," + qName + ")"); } log.debug(" match='" + match + "'"); log.debug(" bodyText='" + bodyText + "'"); } // Parse system properties bodyText = updateBodyText(bodyText); // 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; } // Fire "body" events for all relevant rules List<Rule> rules = matches.pop(); if ((rules != null) && (rules.size() > 0)) { String bodyText = this.bodyText.toString(); for (int i = 0; i < rules.size(); i++) { try { Rule rule = rules.get(i); if (debug) { log.debug(" Fire body() for " + rule); } rule.body(namespaceURI, name, bodyText); } catch (Exception e) { log.error("Body event threw exception", e); throw createSAXException(e); } catch (Error e) { log.error("Body event threw error", e); throw e; } } } else { if (debug) { log.debug(" No rules found matching '" + match + "'."); } if (rulesValidation) { log.warn(" No rules found matching '" + match + "'."); } } // Recover the body text from the surrounding element bodyText = bodyTexts.pop(); // Fire "end" events for all relevant rules in reverse order if (rules != null) { for (int i = 0; i < rules.size(); i++) { int j = (rules.size() - i) - 1; try { Rule rule = rules.get(j); if (debug) { log.debug(" Fire end() for " + rule); } rule.end(namespaceURI, name); } catch (Exception e) { log.error("End event threw exception", e); throw createSAXException(e); } catch (Error e) { log.error("End event threw error", e); throw e; } } } // Recover the previous match expression int slash = match.lastIndexOf('/'); if (slash >= 0) { match = match.substring(0, slash); } else { match = ""; } }
當咱們建立好Digester後,會調用addObjectCreate、addSetProperties、addSetNext方法陸續添加不少Rule,這些方法的實現如代碼:
public void addObjectCreate(String pattern, String className, String attributeName) { addRule(pattern, new ObjectCreateRule(className, attributeName)); } public void addSetProperties(String pattern) { addRule(pattern, new SetPropertiesRule()); } public void addSetNext(String pattern, String methodName, String paramType) { addRule(pattern, new SetNextRule(methodName, paramType)); }
這三個方法分別建立ObjectCreateRule、SetPropertiesRule及SetNextRule,爲了便於理解,咱們舉例說明(Server標籤):
digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className"); digester.addSetProperties("Server"); digester.addSetNext("Server","setServer", "org.apache.catalina.Server");
咱們知道最終會建立ObjectCreateRule、SetPropertiesRule及SetNextRule,而且調用addRule方法。
public void addRule(String pattern, Rule rule) { rule.setDigester(this); getRules().add(pattern, rule); }
從代碼能夠看出,addRule方法首先調用getRules方法獲取RulesBase,而後調用RulesBase的add方法。代碼以下:
//getRules()獲取RulesBase public Rules getRules() { if (this.rules == null) { this.rules = new RulesBase(); this.rules.setDigester(this); } return (this.rules); } //RulesBase的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<Rule> list = cache.get(pattern); if (list == null) { list = new ArrayList<Rule>(); 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); } }
其中,cache的數據結構爲HashMap<String,List<Rule>>,每一個鍵值維護一個List<Rule>,由此可知,對Server標籤來講,對應的Rule列表爲ObjectCreateRule、SetPropertiesRule及SetNextRule。
Digester解析XML的入口是其parse方法,其處理步驟以下:
1.建立XMLReader ;
2.使用XMLReader解析XML。
public Object parse(InputSource input) throws IOException, SAXException { configure(); getXMLReader().parse(input); return (root); } //configure() 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"); // Perform lazy configuration as needed initialize(); // call hook method for subclasses that want to be initialized once only // Nothing else required by default // Set the configuration flag to avoid repeating configured = true; }
getXMLReader方法調用getParser建立SAXParser ,而後調用SAXParser 的getXMLReader方法建立XMLReader.
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; }
getParser方法調用getFactory方法建立SAXParserFactory,而後調用SAXParserFactory的newSAXParser方法建立SAXParser
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); }
getFactory方法使用SAX的API生成SAXParserFactory實例.
public SAXParserFactory getFactory() throws SAXNotRecognizedException, SAXNotSupportedException,ParserConfigurationException { if (factory == null) { factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(namespaceAware); // Preserve xmlns attributes if (namespaceAware) { factory.setFeature( "http://xml.org/sax/features/namespace-prefixes", true); } factory.setValidating(validating); if (validating) { // Enable DTD validation factory.setFeature( "http://xml.org/sax/features/validation", true); // Enable schema validation factory.setFeature( "http://apache.org/xml/features/validation/schema", true); } } return (factory); }
XMLReader解析XML時,會生成事件,回調Digester的startDocument方法,解析的第一個元素是Server,此時回調Digester的startElement方法,入參Attributes list即Server上的屬性,如port、shutdown等,入參qName即爲Server.
@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); 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 + "'."); } } }
startElement方法的處理步驟以下:
1.match剛開始爲空字符串,拼接Server後變爲Server。
2.調用RulesBase的match方法,返回cache中按照鍵值Server匹配的ObjectCreateRule、SetPropertiesRule及SetNextRule。
3.循環列表依次遍歷ObjectCreateRule、SetPropertiesRule及SetNextRule,並調用它們的begin方法。
ObjectCreateRule的begin方法將生成Server的實例(默認爲"org.apache.catalina.core.StandardServer",用戶能夠經過給Server標籤指定className使用其它Server實現),最後將Server的實例壓入Digester的棧中.
@Override public void begin(String namespace, String name, Attributes attributes) throws Exception { // Identify the name of the class to instantiate String realClassName = className; if (attributeName != null) { String value = attributes.getValue(attributeName); if (value != null) { realClassName = value; } } if (digester.log.isDebugEnabled()) { digester.log.debug("[ObjectCreateRule]{" + digester.match + "}New " + realClassName); } if (realClassName == null) { throw new NullPointerException("No class name specified for " + namespace + " " + name); } // Instantiate the new object and push it on the context stack Class<?> clazz = digester.getClassLoader().loadClass(realClassName); Object instance = clazz.newInstance(); digester.push(instance); }
SetPropertiesRule的begin方法首先將剛纔壓入棧中的Server實例出棧,而後給Server實例設置各個屬性值,如port、shutdown等:
@Override public void begin(String namespace, String theName, Attributes attributes) throws Exception { // Populate the corresponding properties of the top object Object top = digester.peek(); if (digester.log.isDebugEnabled()) { if (top != null) { digester.log.debug("[SetPropertiesRule]{" + digester.match + "} Set " + top.getClass().getName() + " properties"); } else { digester.log.debug("[SetPropertiesRule]{" + digester.match + "} Set NULL properties"); } } // set up variables for custom names mappings int attNamesLength = 0; if (attributeNames != null) { attNamesLength = attributeNames.length; } int propNamesLength = 0; if (propertyNames != null) { propNamesLength = propertyNames.length; } for (int i = 0; i < attributes.getLength(); i++) { String name = attributes.getLocalName(i); if ("".equals(name)) { name = attributes.getQName(i); } String value = attributes.getValue(i); // we'll now check for custom mappings for (int n = 0; n<attNamesLength; n++) { if (name.equals(attributeNames[n])) { if (n < propNamesLength) { // set this to value from list name = propertyNames[n]; } else { // set name to null // we'll check for this later name = null; } break; } } if (digester.log.isDebugEnabled()) { digester.log.debug("[SetPropertiesRule]{" + digester.match + "} Setting property '" + name + "' to '" + value + "'"); } if (!digester.isFakeAttribute(top, name) && !IntrospectionUtils.setProperty(top, name, value) && digester.getRulesValidation()) { digester.log.warn("[SetPropertiesRule]{" + digester.match + "} Setting property '" + name + "' to '" + value + "' did not find a matching property."); } } }
SetNextRule的begin不作什麼動做。當遇到Server的結束標籤時,還會依次調用ObjectCreateRule、SetPropertiesRule及SetNextRule的end方法,再也不贅述。全部元素的解析都與Server標籤同理,最終將server.xml文件中設置的元素及其屬性值,構造出tomcat中的容器,如:Server、Service、Connector等.