tomcat中StandardContext

一個上下文容器(Context)代表一個web應用,每一個上下文包括多個包裝器(Wrapper),每個包裝器代表一個Servlet
上下文還需要其它的一些組件如加載器和管理器
Context接口的標準實現,org.apache.catalina.core.StandardContext類

StandardContext配置
創建一個StandardContext實例之後,必須調用它的start方法,這樣它就能爲受到的HTTP請求服務了。一個StandardContext對象可能啓動失敗,
這時候屬性available被設置爲false,屬性available表示了StandardContext對象的可用性。
在一個Tomcat部署中,StandardContext的配置過程做了以下事情:準備讀取和解析%CATALINA_HOME%/conf 目錄下面的web.xml,
部署所有應用程序,確保StandardContext實例可以處理應用級別的web.xml。
另外,配置需要添加一個驗證器閥門和證書閥門(authenticator valve and a certificate valve)
StandardContext的屬性之一是它屬性configured,用來表示該StandardContext是否已經配置了
StandardContext使用一個事件監聽器來作爲它的配置器
當StandardContext實例的start方法被調用的時候,首先觸發一個生命週期事件
。該事件喚醒一個監聽器來配置該StandardContext實例。配置成功後,該監聽器將configured屬性設置爲true。
否則,StandardContext對象拒絕啓動,這樣就不能對HTTP請求進行服務了。

StandardContext構造函數
public StandardContext() {
 super(); 
 pipeline.setBasic(new StandardContextValve()); 
 namingResources.setContainer(this); 
}
在構造函數中,最重要的事情是在StandardContext的流水線上添加了一個類型爲StandardContextValve的基本閥門

 


啓動StandardContext
Start方法初始化StandardContext對象並讓生命週期監聽器配置該StandardContext實例。如果配置成功,
生命週期監聽器會將configured屬性設置爲true。最後start方法,將available屬性設置爲true或者false。
如果是true的話表示該StandardContext屬性配置完畢並且所有相關子容器和組件已經成功啓動,
這樣就能對HTTP請求進行服務了,如果是false則表示出現了錯誤。

Java代碼  收藏代碼
  1. public synchronized void start() throws LifecycleException {  
  2.     if (started)  
  3.         throw new LifecycleException (sm.getString("containerBase.alreadyStarted", logName()));  
  4.     lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);  
  5.     setAvailable(false);  
  6.     setConfigured(false);  
  7.     boolean ok = true;  
  8.     if (getResources() == null) {  
  9.         try {  
  10.             if ((docBase != null) && (docBase.endsWith(".war")))   
  11.                 setResources(new WARDirContext());  
  12.             else   
  13.                 setResources(new FileDirContext());  
  14.         }catch (IllegalArgumentException e) {  
  15.             ok = false;  
  16.         }  
  17.     }  
  18.     if (ok && (resources instanceof ProxyDirContext)) {  
  19.         DirContext dirContext = ((ProxyDirContext) resources).getDirContext();  
  20.         if ((dirContext != null) && (dirContext instanceof BaseDirContext)) {  
  21.             ((BaseDirContext) dirContext).setDocBase(getBasePath());   
  22.             ((BaseDirContext) dirContext).allocate();  
  23.         }  
  24.     }  
  25.     if (getLoader() == null) {  
  26.         if (getPrivileged()) {  
  27.             setLoader(new WebappLoader(this.getClass().getClassLoader()));  
  28.         }else{  
  29.             setLoader(new WebappLoader(getParentClassLoader()));  
  30.         }  
  31.     }  
  32.     if (getManager() == null) {  
  33.         setManager(new StandardManager());  
  34.     }  
  35.     // Initialize character set mapper   
  36.     getCharsetMapper();  
  37.     // Post work directory   
  38.     postWorkDirectory();  
  39.     String useNamingProperty = System.getProperty("catalina.useNaming");  
  40.     if ((useNamingProperty != null) && (useNamingProperty.equals("false"))) {  
  41.         useNaming = false;  
  42.     }  
  43.     if (ok && isUseNaming()) {  
  44.         if (namingContextListener == null) {  
  45.             namingContextListener = new NamingContextListener();   
  46.             namingContextListener.setDebug(getDebug());   
  47.             namingContextListener.setName(getNamingContextName()); addLifecycleListener(namingContextListener);  
  48.         }  
  49.     }  
  50.     ClassLoader oldCCL = bindThread();  
  51.     if (ok) {  
  52.         try {   
  53.             addDefaultMapper(this.mapperClass);   
  54.             started = true;  
  55.             if ((loader != null) && (loader instanceof Lifecycle))   
  56.                 ((Lifecycle) loader).start();  
  57.             if ((logger != null) && (logger instanceof Lifecycle))  
  58.                 ((Lifecycle) logger).start();  
  59.             // Unbinding thread  
  60.             unbindThread(oldCCL);  
  61.             oldCCL = bindThread();  
  62.             if ((cluster != null) && (cluster instanceof Lifecycle))   
  63.                 ((Lifecycle) cluster).start();  
  64.             if ((realm != null) && (realm instanceof Lifecycle))   
  65.                 ((Lifecycle) realm).start();  
  66.             if ((resources != null) && (resources instanceof Lifecycle))   
  67.                 ((Lifecycle) resources).start();  
  68.               
  69.             Mapper mappers[] = findMappers();  
  70.             for (int i = 0; i < mappers.length; i++) {  
  71.                 if (mappers[i] instanceof Lifecycle)   
  72.                     ((Lifecycle) mappers[i]).start();  
  73.             }  
  74.             Container children[] = findChildren();  
  75.             for (int i = 0; i < children.length; i++) {  
  76.                 if (children[i] instanceof Lifecycle)  
  77.                     ((Lifecycle) children[i]).start();  
  78.             }  
  79.             if (pipeline instanceof Lifecycle)   
  80.                 ((Lifecycle) pipeline).start();  
  81.             // Notify our interested LifecycleListeners  
  82.             lifecycle.fireLifecycleEvent(START_EVENT, null);  
  83.             if ((manager != null) && (manager instanceof Lifecycle))  
  84.                 ((Lifecycle) manager).start();  
  85.         } finally {  
  86.             // Unbinding thread   
  87.             unbindThread(oldCCL);  
  88.         }  
  89.     }  
  90.     if (!getConfigured())  
  91.         ok = false;  
  92.     if (ok)   
  93.         getServletContext().setAttribute (Globals.RESOURCES_ATTR, getResources());  
  94.     if (ok) {  
  95.         postWelcomeFiles();  
  96.     }  
  97.     if (ok) {   
  98.         if (!listenerStart())  
  99.             ok = false;  
  100.     }  
  101.     if (ok) {  
  102.         if (!filterStart())   
  103.             ok = false;  
  104.     }  
  105.     // Load and initialize all "load on startup" servlets   
  106.     if (ok)   
  107.         loadOnStartup(findChildren());  
  108.     unbindThread(oldCCL);  
  109.     if (ok){  
  110.         setAvailable(true);  
  111.     }else{  
  112.         stop();  
  113.         setAvailable(false);  
  114.     }  
  115.     lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);  
  116. }  

 

 

下面是該方法做的事情:
· 觸發BEFORE_START事件
· 設置availability屬性爲false
· 設置configured屬性爲false
· 設置源(resources) 
· 設置加載器
· 設置管理器
· 初始化屬性map
· 啓動跟該上下文相關的組件
· 啓動子容器(包裝器)
· 啓動流水線
· 啓動管理器
· 觸發START事件
。監聽器(ContextConfig)會進行一系列配置操作,配置成功後,將StandardContext實例的configured屬性設置爲true。
· 檢查configured屬性的值,如果爲true:調用postWelcomPages方法,加載子包裝器,並將available屬性設置爲true。
 如果configured屬性爲false調用stop方法
· 觸發AFTER_START事件

 

Invoke方法
StandardContext's方法由相關聯的連接器調用,
如果該上下文是一個主機(host)的子容器,
有該主機的invoke方法調用。StandardContext的invoke方法首先檢查是否正在重加載該應用程序,
是的話,等待知道加載完畢。然後調用它的父類ContainerBase的invoke方法

Java代碼  收藏代碼
  1. public void invoke(Request request, Response response){  
  2.     while (getPaused()) {  
  3.         try {  
  4.             Thread.sleep(1000);   
  5.         } catch (InterruptedException e) { ; }  
  6.     }  
  7.     if (swallowOutput) {   
  8.         try {  
  9.             SystemLogHandler.startCapture();  
  10.             super.invoke(request, response);  
  11.         }  
  12.     }else {   
  13.         super.invoke(request, response);   
  14.     }  
  15. }  

 

 

方法getPaused獲得屬性paused的值,當應用程序正在加載的時候該屬性爲ture。


StandardContextMapper
對於每一個請求,invoke方法都會調用StandarContext流水線基本閥門的invoke方法。
StandarContext的基本閥門用org.apache.catalina.core.StandardContextValve類表示。
StandardContextValve實例查找包含它的StandardContext。StandardContextValve使用上下文容器的map來查找合適的包裝器
StandardContext的父類ContainerBase定義了addDefaultMapper方法來添加

Java代碼  收藏代碼
  1. protected void addDefaultMapper(String mapperClass) {  
  2.     if (mapperClass == null)   
  3.         return;  
  4.     if (mappers.size() >= 1)  
  5.         return;  
  6.     try {   
  7.         Class clazz = Class.forName(mapperClass);  
  8.         Mapper mapper = (Mapper) clazz.newInstance();  
  9.         mapper.setProtocol("http");   
  10.         addMapper(mapper);  
  11.     } catch (Exception e) {}  
  12. }  

 

 

重加載支持
StandardContext定義了reloadable屬性來標識是否支持應用程序的重加載。
當允許重加載的時候,當web.xml或者WEB-INF/classes目錄下的文件被改變的時候會重加載。
StandardContext 中Loader接口的標準實現WebappLoader類,有一個單獨線程來檢查WEB-INF目錄下面所有類和JAR文件的時間戳
你需要做的是啓動該線程,將 WebappLoader關聯到StandardContext,使用setContainer方法即可

Java代碼  收藏代碼
  1. public void setContainer(Container container) {  
  2.     if ((this.container != null) && (this.container instanceof Context))   
  3.         ((Context) this.container).removePropertyChangeListener(this);  
  4.     Container oldContainer = this.container;  
  5.     this.container = container;  
  6.     support.firePropertyChange("container", oldContainer, this.container);  
  7.     // Register with the new Container (if any)   
  8.     if ((this.container!=null) && (this.container instanceof Context)) {  
  9.         setReloadable( ((Context) this.container).getReloadable() );   
  10.         ((Context) this.container).addPropertyChangeListener(this);   
  11.     }  
  12. }  

 

 


注意最後一個if語句塊中,如果容器是一個上下文容器,調用setReloadable方法,
也就是說WebappLoader的reloadable屬性跟StandardContext的reloadable屬性相同。
下面是WebappLoader對setReload方法的實現:

 

Java代碼  收藏代碼
  1. public void setReloadable(boolean reloadable) {  
  2.     boolean oldReloadable = this.reloadable;  
  3.     this.reloadable = reloadable;  
  4.     support.firePropertyChange("reloadable"new Boolean(oldReloadable), new Boolean(this.reloadable));  
  5.     if (!started)  
  6.         return;  
  7.     if (!oldReloadable && this.reloadable)  
  8.         threadStart();  
  9.     else if (oldReloadable && !this.reloadable)  
  10.         threadStop();  
  11.     }  
  12. }  

 

如果將reloadable屬性設置爲true,調用threadStart方法。如果從true到false,則調用threadStop方法。
threadStart方法啓動一個線程持續的檢查WEB-INF目錄下面的類文件和JAR文件的時間戳。threadStop方法用於停止該線程。
類的時間戳是由backgroundProcess方法調用

backgroundProcess方法
一個上下文容器需要其它組件如加載器和管理器的支持。這些組件通常需要一個單獨的線程來處理後臺過程(background processing)
所有的後臺過程都分享同一個線程。如果一個組件或者是容器需要定期的來執行操作,
它需要做的是將這些代碼寫入到backgroundProcess方法即可。

 

Java代碼  收藏代碼
  1. protected void threadStart() {  
  2.     if (thread != null)   
  3.         return;  
  4.     if (backgroundProcessorDelay <= 0)  
  5.         return;  
  6.     threadDone = false;   
  7.     String threadName = "ContainerBackgroundProcessor[" + toString() + "]";  
  8.     thread = new Thread(new ContainerBackgroundProcessor(), threadName);  
  9.     thread.setDaemon(true);   
  10.     thread.start();  
  11. }  

 


方法threadStart傳遞一個ContainerBackgroundProcessor對象創建一個新線程。
ContainerBackgroundProcessor實現了java.lang.Runnable接口

Java代碼  收藏代碼
  1. protected class ContainerBackgroundProcessor implements Runnable {  
  2.     public void run() {  
  3.         while (!threadDone) {  
  4.             try {  
  5.                 Thread.sleep(backgroundProcessorDelay * 1000L);  
  6.             } catch (InterruptedException e) { ; }  
  7.             if (!threadDone) {  
  8.                 Container parent = (Container) getMappingObject();  
  9.                 ClassLoader cl = Thread.currentThread().getContextClassLoader();  
  10.                 if (parent.getLoader() != null) {   
  11.                     cl = parent.getLoader().getClassLoader();   
  12.                 }  
  13.                 processChildren(parent, cl);  
  14.             }  
  15.         }  
  16.     }  
  17.   
  18.     protected void processChildren(Container container, ClassLoader cl) {  
  19.         try {   
  20.             if (container.getLoader() != null) {  
  21.                 Thread.currentThread().setContextClassLoader (container.getLoader().getClassLoader());  
  22.             }  
  23.             container.backgroundProcess();  
  24.         }catch (Throwable t) {}  
  25.         finally {  
  26.             Thread.currentThread().setContextClassLoader(cl);  
  27.         }  
  28.         Container[] children = container.findChildren();  
  29.         for (int i = 0; i < children.length; i++) {  
  30.             if (children[i].getBackgroundProcessorDelay() <= 0) {  
  31.                 processChildren(children[i], cl);  
  32.             }  
  33.         }  
  34.     }  
  35. }  

 

ContainerBackgroundProcessor是ContainerBase的內部類,
在他的run方法裏,有一個while循環定期的調用它的processChildren方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章