IThaiのブログ

IT関連の話題やタイに関する様々なことを書いていきます。

Tomcatのソースを読んでみる - Catalinaの初期化

前回、Digesterオブジェクトを作成するところまで書きました。

Apache Commons Digesterとは、XMLをJavaオブジェクトにマッピングするクラスです。

public class Catalina extends Embedded {
//------------------------------------------------中略
    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.setClassLoader(StandardServer.class.getClassLoader());

        // 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");
//---------------------------------------中略
        // 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/"));
        digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("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));
        digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("Server/Service/Engine/Cluster/"));

server.xmlのDOM構造に合わせてXML解析ルールを設定しています。

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
            file = configFile();
            inputStream = new FileInputStream(file);
            inputSource = new InputSource("file://" + file.getAbsolutePath());
        } catch (Exception e) {
            ;
        }
        if (inputStream == null) {
            try {
                inputStream = getClass().getClassLoader()
                    .getResourceAsStream(getConfigFile());
                inputSource = new InputSource
                    (getClass().getClassLoader()
                     .getResource(getConfigFile()).toString());
            } catch (Exception 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 ((inputStream == null) && (file != null)) {
            log.warn("Can't load server.xml from " + file.getAbsolutePath());
            if (file.exists() && !file.canRead()) {
                log.warn("Permissions incorrect, read permission is not allowed on the file.");
            }
            return;
        }

        try {
            inputSource.setByteStream(inputStream);
            digester.push(this);
            digester.parse(inputSource);
            inputStream.close();
        } catch (Exception e) {
            log.warn("Catalina.start using "
                               + getConfigFile() + ": " , e);
            return;
        }

        // Stream redirection
        initStreams();

        // Start the new server
        if (getServer() instanceof Lifecycle) {
            try {
                getServer().initialize();
            } 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");

    }

Digesterオブジェクトに解析ルールを設定した後、
digester.push()でcatalinaオブジェクトをのせて、
digester.parse()で設定したルールに基づいてそれぞれオブジェクトにマッピングします。

次にgetServer().initialize()でStandardServer#initialize()を呼び出しています。

ここからの処理はデザインパターンの一つである
Template Methodパターンを使っています。
処理の大概を親クラスで実装し、異なる部分だけをそれぞれの子クラスで実装するようです。

ここからの処理は分かりにくいので、下記シーケンスを参考にします。
http://tomcat.apache.org/tomcat-7.0-doc/architecture/startup/serverStartup.pdf

public final class StandardServer
    implements Lifecycle, Server, MBeanRegistration 
 {
    /**
     * Invoke a pre-startup initialization. This is used to allow connectors
     * to bind to restricted ports under Unix operating environments.
     */
    public void initialize()
        throws LifecycleException 
    {
        if (initialized) {
                log.info(sm.getString("standardServer.initialize.initialized"));
            return;
        }
        lifecycle.fireLifecycleEvent(INIT_EVENT, null);
        initialized = true;

        if( oname==null ) {
            try {
                oname=new ObjectName( "Catalina:type=Server");
                Registry.getRegistry(null, null)
                    .registerComponent(this, oname, null );
            } catch (Exception e) {
                log.error("Error registering ",e);
            }
        }
        
        // Register global String cache
        try {
            ObjectName oname2 = 
                new ObjectName(oname.getDomain() + ":type=StringCache");
            Registry.getRegistry(null, null)
                .registerComponent(new StringCache(), oname2, null );
        } catch (Exception e) {
            log.error("Error registering ",e);
        }

        // Initialize our defined Services
        for (int i = 0; i < services.length; i++) {
            services[i].initialize();
        }
    }

アプリケーション管理技術のJMXでCatalinaのtype属性のserverのオブジェクト(StandardServer)を作成し、MBeansに登録しています。 StandardServer#initialize()の中でStandardService#initialize()を呼び出しています。

public class StandardService
        implements Lifecycle, Service, MBeanRegistration 
 {
    /**
     * Invoke a pre-startup initialization. This is used to allow connectors
     * to bind to restricted ports under Unix operating environments.
     */
    public void initialize()
            throws LifecycleException
    {
//--------------------------------------------------------------中略
        // Initialize our defined Connectors
        synchronized (connectors) {
            for (int i = 0; i < connectors.length; i++) {
                try {
                    connectors[i].initialize();
                } catch (Exception e) {
                    String message = sm.getString(
                            "standardService.connector.initFailed",
                            connectors[i]);
                    log.error(message, e);

                    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                        throw new LifecycleException(message);
                }
            }
        }
    }

そして、StandardService#initialize()の中でConnector#initialize()を呼び出しています。

/**
 * Implementation of a Coyote connector.
 *
 * @author Craig R. McClanahan
 * @author Remy Maucherat
 *
 */
public class Connector
    implements Lifecycle, MBeanRegistration
{
    public Connector(String protocol)
        throws Exception {
        setProtocol(protocol);
        // Instantiate protocol handler
        try {
            Class clazz = Class.forName(protocolHandlerClassName);
            this.protocolHandler = (ProtocolHandler) clazz.newInstance();
        } catch (Exception e) {
            log.error
                (sm.getString
                 ("coyoteConnector.protocolHandlerInstantiationFailed", e));
        }
    }

    /**
     * Initialize this connector (create ServerSocket here!)
     */
    public void initialize()
        throws LifecycleException
    {
//------------------------------------------------中略
       // Initializa adapter
        adapter = new CoyoteAdapter(this);
        protocolHandler.setAdapter(adapter);
//------------------------------------------------中略
        try {
            protocolHandler.init();
        } catch (Exception e) {
            throw new LifecycleException
                (sm.getString
                 ("coyoteConnector.protocolHandlerInitializationFailed", e));
        }
    }

ConnectorでCoyoteAdapterを作成し、Connectorのコンストラクタで
生成されたprotocolHandlerにセットします。
その後、protocolHandler.init()で初期化します。

Connectorの中では、初期設定はHTTP/1.1コネクタが設定されます
org.apache.coyote.http11.Http11Protocol - HTTP/1.1コネクタorg.apache.coyote.http11.Http11NioProtocol - 非ブロックJavaコネクタorg.apache.coyote.http11.Http11AprProtocol - APR(Apache Portable Runtime)コネクタ があります。

public interface ProtocolHandler {
//------------------------------------------------中略
}
public abstract class AbstractProtocol implements ProtocolHandler {
//------------------------------------------------中略
}
public class Http11Protocol extends AbstractProtocol
    implements MBeanRegistration {
    public void init() throws Exception {
        endpoint.setName(getName());
        endpoint.setHandler(cHandler);
//------------------------------------------------中略
       try {
            endpoint.init();
        } catch (Exception ex) {
            log.error(sm.getString("http11protocol.endpoint.initerror"), ex);
            throw ex;
        }
        if (log.isInfoEnabled())
            log.info(sm.getString("http11protocol.init", getName()));

    }

Http11Protocol#init()の中でJloEndpointを初期化します。
ようやくCatalina関連の初期化処理が終わりましたので、
次回はサーバの開始処理を見てみます。

Javaオープンソース徹底攻略―Eclipse,JBoss,Tomcat,Strutsから最新のXML/WebServicesまで

Javaオープンソース徹底攻略―Eclipse,JBoss,Tomcat,Strutsから最新のXML/WebServicesまで