1. 程式人生 > >How Tomcat Works 15: Digester

How Tomcat Works 15: Digester

一、概述     前面章節中,使用hard-code來管理各component間的從屬關係,如果需要改變則需要重新編譯Bootstrap類。幸運的是tomcat設計者採用了更優雅的方法來管理配置,即XML檔案server.xml. 這樣我們只需要修改server.xml檔案就可以設定tomcat。如:<context docBase='myApp' path="/myApp"/>     Tomcat採用了開源包Digester來完成xml元素到java物件的轉換。而WebApp的配置則不同,它在app內部WEB-INF下的xml檔案

二、組成   1. Rule規則    用特定的pattern解析xml內容,生成對應的物件和路徑  

        void begin(org.xml.sax.Attribute)
        void end()
        void addRule(String pattern, Rule rule)

    2. 案例     

<employee firstName="Brant" lastName="Kobe">
        <office>
          <address streetName="Los Angle" streetNumber="10" />
        </office>
    </employee>

    3. 建立物件addObjectCreate(…),四種過載方法   

 void addObjectCreate(String pattern, Class clazz);
    void addObjectCreate(String pattern, String className);
    void addObjectCreate(String pattern, String attributeName);
    void addObjectCreate(String pattern, Class clazz, String attributeName); // digester.addObjectCreate("employee", "com.cisco.tomcat.Employee", "streetNumber")

    4. 設定屬性void addSetProperties(String pattern)     

void addProperties(String pattern)// addSetproperties("employee")-->will call setFirstName() and setLastName()

         5. 設定屬性

void addSetNext("employee/office", "addOffice");// 會呼叫employee.addOffice()方法

    解析時,會按照樹形結構依次將employee和office入棧, 然後呼叫addSetNext()構建元素關係

三、ContextConfig介紹    1. 作用      ContextConfig對於standardContext有很重要的作用,比如它關聯了authenticator valve, CertificateValve等,更重要的ContextConfig例項也要讀取解析預設web.xml檔案和webApp下的web.xml檔案,並轉換xml元素成java物件     2. Web.xml的配置     CATALINA_HOME下的預設web.xml檔案定義了預設Servlets的map,MIME型別的拓展名map,定義了預設session超時時間,定義歡迎檔案列表     3. WebApp下的web.xml配置     web.xml是應用配置檔案,放在webapp的WEB-INF下面。不過他們兩都不是必需檔案     4. Contextconfig功能:     為每個servlet元素建立一個StandardWrapper,所以後續配置更方便無需手工例項化Wrapper   5.  案例     在bootStrap類中,可以直接code:     

  lifecyclelistener listener = new ContextConfig();
      ((Lifecycle)context).addLifecycleListener(lisenter);

   在context的啟動和關閉時會fire event, START_EVENT和STOP_EVENT。這時就會觸發ContextConfig的lifecycleEvent(lifecycleEvent event),lifecycleEvent方法如下:   

 @Override
    public void LifecycleEvent(com.cisco.tomcat.lifecycle.LifecycleEvent lifecycleEvent) {
        if(lifecycleEvent.getType().equals(Lifecycle.START_EVENT)) {
            start();
        }else {
            stop();
        }
    }

     Start方法如下:

    

private void start() {
        ok = true;
        // 初始化,設定Engine.defaultContext, Host.defaultContext
        context.setConfigured(false);
        Container container = context.getParent();
        if (container instanceof Host) {
            ((Host) container).importDefaultContext(context);
            container = container.getParent;
        }
        if (container instanceof Engine) {
            ((Engine) container).importDefaultContext(context);
        }

        // 處理預設的web.xml和WebApp的web.xml檔案
        defaultConfig();
        applicationConfig();
        if (ok) {
            validateSecurityRoles();
        }
        // 為新增的listener類掃描標籤庫描述符
        if (ok) {
            tldScan();
        }
        // 若需要,則配置證書暴露valve
        if (ok) {
            certificatesConfig();
        }
        // 若需要,則配置認證
        if (ok) {
            authenticatorConfig();
        }
        // 若請求後,dump下context內部的pipeline的內容
        Pipeline pipeline = context.getPipeline();
        Valve[] valves = pipeline.getValves();
        for (int i = 0; i < valves.length; i++) {
            log(" " + valves.getInfo());
        }
        // 若無其他問題引入,設定我們的App為Available狀態
        if (ok) {
            context.setConfigured(true);
        } else {
            log(sm.getString("contextConfig.unavailable"));
            context.setConfigured(false);
        }
    }

defaultConfig 方法:解析%CATALINA_HOME%/conf資料夾中的web.xml     

private void defaultConfig() {    
        // 開啟預設web.xml檔案
        File file = new File(Constants.DefaultWebXml);
        FileInputStream stream = new FileInputStream(file.getCanonicalPath());
        stream.close();
        InputSource is = new InputSource("file://"+file.getAbsolutePath());
        stream = new FileInputStream(file);
        is.setByteStream(stream);
        webDigester.clear();
        webDigester.push(context);
        webDigetster.parse(is);
    }

applicationConfig方法:解析APP下的WEB-INF/下的web.xml   

 private void applicationConfig() {
         ServletContext servletContext = context.getServletContext();
         InputStream stream = servletContext.getResourceAsStream(Constants.ApplicationWebXml);
         URL url = servletContext.getResource(Constants.ApplicationWebXml);
         InputSource is = new InputSrouce(url.toExternalForm());
         is.setByteStream(stream);
         webDigester.clear();
         webDigester.push(context);
         webDigester.parse(is);         
    }

靜態建立WebDigester的方法     

private static Digester createWebDigester() {
        Digester webDigester = new Digester();
        webDigester.setValidating(true);
        URL url = ContextConfig.class.getResource(Constants.WebDtdResourcePath_22);
        webDigester.register(Constants.WebDtdPublicId_22,  url.toString());
        url = ContextConfig.class.getResource(Constants.WneDtdResourcePath_23);
        webDigester.register(Constants.WebDtdPublicId_23,  url.toString());
        webDigester.addRuleSet(new WebRuleSet());
        return webDigester;
    }