Tomcat 소스 코드 분석의 서버 와 Lifecycle 의 디자인 과 구현

최근 에 Tomcat 의 소스 코드 를 보 는 리듬 은 매우 치밀 한 편 이 고 사람들 에 게 느낌 을 준다. tomcat 의 코드 는 예전 에 읽 었 던 Jetty 의 코드 에 비해 더욱 조리 있 게 보인다.물론 이것 도 자신 이 본 Jetty 버 전이 비교적 오래된 것 일 수 있 지만 본 Tomcat 의 코드 는 비교적 새로운 Tomcat 8 의 코드 이기 때문에...
됐어. 잡담 은 그만 하고...
라 이 프 사이클 의 개념 부터...
이것 은 Jetty 에 도 있 습 니 다. 그룹 이 하나의 구성 요소 의 생명 주 기 를 유지 하 는 데 사용 된다 면, 예 를 들 어 start, stop 같은 것 입 니 다.
또한 lifecycle 개념 이 있 는 구성 요소 에 대해 서도 일반적으로 listener 개념 이 있 으 며, 구성 요소 의 상태 가 바 뀌 었 을 때 listener 가 응답 할 수 있 습 니 다.
자, 우선 최상 위 라 이 프 사이클 인터페이스의 정 의 를 살 펴 보 자.
//         
public interface Lifecycle {

	
	//            
    public static final String BEFORE_INIT_EVENT = "before_init";


    /**
     * The LifecycleEvent type for the "component after init" event.
     */
    public static final String AFTER_INIT_EVENT = "after_init";


    /**
     * The LifecycleEvent type for the "component start" event.
     */
    public static final String START_EVENT = "start";


    /**
     * The LifecycleEvent type for the "component before start" event.
     */
    public static final String BEFORE_START_EVENT = "before_start";


    /**
     * The LifecycleEvent type for the "component after start" event.
     */
    public static final String AFTER_START_EVENT = "after_start";


    /**
     * The LifecycleEvent type for the "component stop" event.
     */
    public static final String STOP_EVENT = "stop";


    /**
     * The LifecycleEvent type for the "component before stop" event.
     */
    public static final String BEFORE_STOP_EVENT = "before_stop";


    /**
     * The LifecycleEvent type for the "component after stop" event.
     */
    public static final String AFTER_STOP_EVENT = "after_stop";


    /**
     * The LifecycleEvent type for the "component after destroy" event.
     */
    public static final String AFTER_DESTROY_EVENT = "after_destroy";


    /**
     * The LifecycleEvent type for the "component before destroy" event.
     */
    public static final String BEFORE_DESTROY_EVENT = "before_destroy";


    /**
     * The LifecycleEvent type for the "periodic" event.
     */
    public static final String PERIODIC_EVENT = "periodic";


  
    public static final String CONFIGURE_START_EVENT = "configure_start";

    public static final String CONFIGURE_STOP_EVENT = "configure_stop";


    // --------------------------------------------------------- Public Methods


    /**
     * Add a LifecycleEvent listener to this component.
     *
     * @param listener The listener to add
     */
    //       
    public void addLifecycleListener(LifecycleListener listener);


    /**
     * Get the life cycle listeners associated with this life cycle. If this
     * component has no listeners registered, a zero-length array is returned.
     */
    //        
    public LifecycleListener[] findLifecycleListeners();


    /**
     * Remove a LifecycleEvent listener from this component.
     *
     * @param listener The listener to remove
     */
    //       
    public void removeLifecycleListener(LifecycleListener listener);


  
    //   
    public void init() throws LifecycleException;

    //  
    public void start() throws LifecycleException;


    //  
    public void stop() throws LifecycleException;

    /**
     * Prepare to discard the object. The following {@link LifecycleEvent}s will
     * be fired in the following order:
     * <ol>
     *   <li>DESTROY_EVENT: On the successful completion of component
     *                      destruction.</li>
     * </ol>
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    //  
    public void destroy() throws LifecycleException;


    /**
     * Obtain the current state of the source component.
     *
     * @return The current state of the source component.
     */
    //           
    public LifecycleState getState();


    /**
     * Obtain a textual representation of the current component state. Useful
     * for JMX.
     */
    //       
    public String getStateName();
}

비교적 간단 한 것 이 좋 습 니 다. 먼저 기본 적 인 상태 와 사건 의 정 의 를 내 린 다음 에 기본 적 인 조작 입 니 다. 예 를 들 어 listener 를 추가 하고 listener 를 제거 하 며 시작 하고 멈 추 는 등 입 니 다.다 비교적 일반적인 편 인 데...
다음은 라 이 프 사이클 의 추상 층, 라 이 프 사이클 베이스...
이것 은 구체 적 으로 그것 의 실현 코드 를 배려 하지 않 는 다.LifecycleBase 는 LifeCycle 인 터 페 이 스 를 직접 계승 합 니 다. 여 기 는 주로 중간 에 listener 를 추가 하고 listener 를 제거 하 는 작업 을 실 현 했 습 니 다. 여 기 는 listener 의 용기 로 확장 되 었 음 을 초보 적 으로 이해 할 수 있 습 니 다.
또한 LifecycleBase 의 정의 에 서 는 기본 적 인 시작 을 확장 하고 작업 을 중단 합 니 다.
예 를 들 어 구성 요소 가 시 작 될 때 현재 구성 요소 의 상 태 를 변경 하고 해당 하 는 listener 를 호출 해 야 합 니 다.여기 서 초기 화 하 는 방법 으로 예 를 들 어 보 자.
    //     init  ,             ,
    @Override
    public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }
        setStateInternal(LifecycleState.INITIALIZING, null, false);  //        INITIALIZING

        try {
            initInternal();  //          ,         
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.initFail",toString()), t);
        }

        setStateInternal(LifecycleState.INITIALIZED, null, false);  //      INITIALIZED  
    }

 //         ,      
    protected abstract void initInternal() throws LifecycleException;

이 코드 는 쉽게 이해 할 수 있 을 것 입 니 다. 현재 구성 요소 의 장 착 을 검증 하고 구성 요소 의 상 태 를 수정 하 는 것 이 아 닙 니 다. 물론 구성 요소 의 상 태 를 설정 할 때 listener 호출 도 수반 합 니 다.
마지막 으로 initInternal 방법 을 사용 하여 초기 화 합 니 다. 물론 이 방법 은 구체 적 인 하위 클래스 에서 이 루어 져 야 합 니 다...
또한 여기 에는 비교적 특수 한 LifeCycle 의 추상 층 인 LifecycleMBeanBase 도 소개 해 야 한다. 추상 적 인 LifecycleBase 유형 을 계승 했다. 사실 여기 서 이름 을 보면 이 추상 층 이 무슨 일 을 해 야 하 는 지 알 수 있 을 것 이다.코드 보기:
    protected void initInternal() throws LifecycleException {

        // If oname is not null then registration has already happened via
        // preRegister().
        if (oname == null) {
            mserver = Registry.getRegistry(null, null).getMBeanServer();  //     mbserver

            oname = register(this, getObjectNameKeyProperties());  //       mbserver
        }
    }

음, 사실은 구성 요소 가 초기 화 될 때 현재 구성 요 소 를 JMX 에 등록 하 는 것 입 니 다. 구체 적 인 Tomcat 의 JMX 부분 내용 은 앞의 글 에서 이미 말 했 습 니 다. 여 기 는 자세히 말 하지 않 겠 습 니 다.
즉, 하나의 유형 이 JMX 에 등록 해 야 한다 면 LifeCycleMBeanBase 추상 류 를 계승 한 다음 에 그 중의 몇 가지 추상 적 인 방법 을 실현 해 야 한 다 는 것 이다.
자, 여기까지. 라 이 프 사이클 부분의 내용 은 많 지 않 습 니 다.
전체적인 느낌 으로 Tomcat 의 라 이 프 사이클 부분 디자인 은 비교적 간단 한 편 인 데...
자, 이제 서버 부분 내용 을 살 펴 보 겠 습 니 다...우선 최상 위 인터페이스의 정 의 를 살 펴 보 자.


package org.apache.catalina;

import java.io.File;

import org.apache.catalina.deploy.NamingResourcesImpl;
import org.apache.catalina.startup.Catalina;


//   server     ,         
public interface Server extends Lifecycle {

    // ------------------------------------------------------------- Properties

    /**
     * Return the global naming resources.
     */
    public NamingResourcesImpl getGlobalNamingResources();


    /**
     * Set the global naming resources.
     *
     * @param globalNamingResources The new global naming resources
     */
    public void setGlobalNamingResources
        (NamingResourcesImpl globalNamingResources);


    /**
     * Return the global naming resources context.
     */
    public javax.naming.Context getGlobalNamingContext();


    /**
     * Return the port number we listen to for shutdown commands.
     */
    public int getPort();  //    shutdown     


    /**
     * Set the port number we listen to for shutdown commands.
     *
     * @param port The new port number
     */
    public void setPort(int port);  ////    shutdown     


    /**
     * Return the address on which we listen to for shutdown commands.
     */
    public String getAddress();  //    shutdown     


    /**
     * Set the address on which we listen to for shutdown commands.
     *
     * @param address The new address
     */
    public void setAddress(String address);  //    shutdown     


    /**
     * Return the shutdown command string we are waiting for.
     */
    public String getShutdown();


    /**
     * Set the shutdown command we are waiting for.
     *
     * @param shutdown The new shutdown command
     */
    public void setShutdown(String shutdown);


    public ClassLoader getParentClassLoader();   //  server    classLoader,   catalina  loader


    public void setParentClassLoader(ClassLoader parent);


 
    public Catalina getCatalina();  //  catalina  

 
    public void setCatalina(Catalina catalina);  //    catalina  

    public File getCatalinaBase();    //       tomcat    

    public void setCatalinaBase(File catalinaBase);  

    public File getCatalinaHome();

    public void setCatalinaHome(File catalinaHome);


    public void addService(Service service);   //  service  


    public void await();


    public Service findService(String name);    //        service

    public Service[] findServices();  //     service

    public void removeService(Service service);  //    service
}

음, 사실 최상 위 서버 인터페이스의 정의 도 간단 합 니 다. lifecycle 인터페이스 에서 가장 중요 한 부분 은 service 에 대한 추가 와 제거 가 아 닙 니 다.
가장 큰 소 리 를 지 르 는 것 은 바로 그것 이 service 용기 라 는 것 이다.
자, 다음은 가장 많이 사용 되 는 유형 인 StandardServer 를 살 펴 보 겠 습 니 다. 여기 에는 Server 인터페이스 뿐만 아니 라 LifecycleMBeanBase 유형 도 계승 되 었 습 니 다. 여기 에는 StandardServer 가 JMX 에 등 록 될 것 임 을 나타 냅 니 다.
그것 의 코드 가 비교적 길 기 때문에, 여 기 는 직접 붙 이지 않 는 다.비교적 중요 한 속성 정 의 를 내 립 시다:
    private Service services[] = new Service[0];   //       service,      
    private final Object servicesLock = new Object();

응, service 의 배열...응, 용 기 잖 아...
자, 여기 서도 초기 화 와 시동 두 가지 방법의 실현 을 살짝 살 펴 보 자.
    protected void initInternal() throws LifecycleException {

        super.initInternal();   //   init,           mbserver        

        // Register global String cache
        // Note although the cache is global, if there are multiple Servers
        // present in the JVM (may happen when embedding) then the same cache
        // will be registered under multiple names
        onameStringCache = register(new StringCache(), "type=StringCache");

        // Register the MBeanFactory
        MBeanFactory factory = new MBeanFactory();  //  MBeanFactory
        factory.setContainer(this);   //    mbeanfactory container
        onameMBeanFactory = register(factory, "type=MBeanFactory");  //  mbeanfactory

        // Register the naming resources
        globalNamingResources.init(); //        

        // Populate the extension validator with JARs from common and shared
        // class loaders
        if (getCatalina() != null) {   //  catalina  
            ClassLoader cl = getCatalina().getParentClassLoader();  // bootstrap      shareloader
            // Walk the class loader hierarchy. Stop at the system class loader.
            // This will add the shared (if present) and common class loaders
            //  classLoader        ,       classLoader
            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
                if (cl instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader) cl).getURLs();
                    for (URL url : urls) {
                        if (url.getProtocol().equals("file")) {
                            try {
                                File f = new File (url.toURI());
                                if (f.isFile() &&
                                        f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                                }
                            } catch (URISyntaxException e) {
                                // Ignore
                            } catch (IOException e) {
                                // Ignore
                            }
                        }
                    }
                }
                cl = cl.getParent();
            }
        }
        // Initialize our defined Services
        for (int i = 0; i < services.length; i++) {  //   service
            services[i].init();
        }
    }

우선 초기 화 입 니 다. 여기 서 가장 중요 한 것 은 현재 포 함 된 모든 서 비 스 를 초기 화 하 는 것 입 니 다. 그리고 다른 조작 도 있 습 니 다. 주석 도 설명 하 는 것 이 비교적 명확 한 것 이 라 고 할 수 있 습 니 다.
    //             lifecycle start  ,         ,       tomcat,     server service
    @Override
    protected void startInternal() throws LifecycleException {

        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);

        globalNamingResources.start();

        // Start our defined Services
        synchronized (servicesLock) {
            for (int i = 0; i < services.length; i++) {   //     service,      
                services[i].start();
            }
        }
    }

시작 방법, 여기 서 가장 중요 한 것 도 역시 Service 에 대한 시작 입 니 다...
자, 여기까지. 서버 의 대체적인 물건 은 여기까지 입 니 다. 사실은 건어물 이 많 지 않 습 니 다.
일 을 가장 많이 하 는 곳 은 커 넥 터 부분 에 있 을 텐 데 이 건 나중에 쓰 자...

좋은 웹페이지 즐겨찾기