StandardContext分析-tomcat6.x源碼閱讀

2013-10-06java

StandardContext 是什麼

是org.apache.catalina.Context的標準實現,繼承自ContainerBase基容器,具有容器的功能,在tomcat結構層次圖中位於Host內部,包含ServletWrapper容器,它的角色是管理在其內部的ServletWrapper,從Host那裏接收請求信息,加載實例化Servlet,而後選擇一個合適的ServletWrapper處理,同一個Context內部中Servlet數據共享,不一樣Context之間數據隔離。在Context層面上,Filter的做用就出來了,Filter是用來過濾Servlet的組件,Context負責在每一個Servlet上面調用Filter過濾,Context與開發Servlet息息相關,跟前面的幾個組件相比,Context可以提供更多的信息給Servlet。一個webapp有一個Context,Context負責管理在其內部的組件:Servlet,Cookie,Session等等。web

Context
tomcat定義的Servlet上下文接口,也即一個webapp的應用環境。tomcat定義了一個Context所必須具有的功能,定義對Cookie,Session,ServletWrapper等組件的管理,爲一個請求選項合適的Servlet方法,他要解決的問題是爲請求選擇Servlet,Servlet之間的數據共享問題。apache

ContainerBase
容器基類,StandardContext繼承該類,具有了基容器的功能和方法。緩存

StandardContext()
默認構造器,設定basicValve, 初始化NotificationBroadcasterSupport,用戶支持j2ee企業功能。tomcat

StandardContextValve
Context的標準BasicValve,做用鏈接Context和ServletWrapper的工具。StandardContextValve主要完成的功能是:安全

  • WEB-INF和META-INF目錄的訪問選項判斷,tomcat禁止直接訪問這兩個目錄
  • 選定Wrapper處理請求

URLEncoder
跟URL相關,具備對URL編碼的功能,替換特殊字符功能。cookie

charsetMapper : CharsetMapper
Context多支持的字符集,跟本地化有關,負責處理本地字符問題。session

context : ApplicationContext
Servlet的上下文,一個wepapp中提供一個全局的servlet上下,提供了一些列的訪問資源的接口,servlet經過ApplicationContext獲取servlet的運行環境信息,加載資源,獲取資源。與StandardContext的做用是相輔相成的關係,兩種的角色不一樣,ApplicationContext負責的是servlet運行環境上下信息,不關心session管理,cookie管理,servlet的加載,servlet的選擇問題,請求信息,一句話就是:他是servlet管家。StandardContext須要負責管理session,Cookie,Servlet的加載和卸載,負責請求信息的處理,掌握控制權,他是servlet的爹媽。app

exceptionPages : HashMap
錯誤頁面信息匹配,對於404,403等HTTP錯誤碼,tomcat經過映射頁面來定製提示信息。webapp

filterConfigs : HashMap
k/v的方式保存Filter與Fifter配置信息,在過濾請求信息是使用到。

filterDefs : HashMap
k/v的方式保存Filter與Filter的定義信息。

filterMaps : FilterMap[]
webapp中全部的Filter集合。別名映射關係。

mapper : Mapper
Servlet與訪問url匹配集合。

namingContextListener : NamingContextListener
是JNDI中的內容,不瞭解,比較複雜,只知道是負責監聽javax.naming.Context資源事件。NamingContextListener實現了LifecycleListener、ContainerListener、PropertyChangeListener3個接口,具有監聽Lifecycle組件,Container組件、PropertyChange的事件能力。

namingResources : NamingResources
是JNDI中的內容,不瞭解,比較複雜,知道它管理命名資源,將要加載的資源封裝成對象,能夠直接從NamingResources獲取對象了。

servletMappings : HashMap
訪問url匹配與Servlet類映射。

wrapperClassName : String
用來處理Servlet包裝的Wrapper類類名,負責加載管理Servlet。

wrapperClass : Class
用來處理Servlet包裝的Wrapper類,負責加載管理Servlet。

addServletMapping(String, String, boolean)
添加servlet和url模式映射關係。從web.xml或者註解中獲取Servlet與url的關係,而後保存到Context中。

createWrapper()
實例化Wrapper類,建立一個容納Servlet的容器,並在Wrapper上註冊監聽器。主要完成如下步驟:

  • 建立Wrapper類實例
  • 在Wrapper類實例上註冊組件實例化監聽
  • 在Wrapper類實例上註冊組件生命週期監聽
  • 在Wrapper類實例上註冊容器事件期監聽
/**
	 * Factory method to create and return a new Wrapper instance, of the Java
	 * implementation class appropriate for this Context implementation. The
	 * constructor of the instantiated Wrapper will have been called, but no
	 * properties will have been set.
	 */
	public Wrapper createWrapper() {

		Wrapper wrapper = null;
		if (wrapperClass != null) {
			try {
				wrapper = (Wrapper) wrapperClass.newInstance();
			} catch (Throwable t) {
				log.error("createWrapper", t);
				return (null);
			}
		} else {
			wrapper = new StandardWrapper();
		}

		synchronized (instanceListenersLock) {
			for (int i = 0; i < instanceListeners.length; i++) {
				try {
					Class clazz = Class.forName(instanceListeners[i]);
					InstanceListener listener = (InstanceListener) clazz
							.newInstance();
					wrapper.addInstanceListener(listener);
				} catch (Throwable t) {
					log.error("createWrapper", t);
					return (null);
				}
			}
		}

		synchronized (wrapperLifecyclesLock) {
			for (int i = 0; i < wrapperLifecycles.length; i++) {
				try {
					Class clazz = Class.forName(wrapperLifecycles[i]);
					LifecycleListener listener = (LifecycleListener) clazz
							.newInstance();
					if (wrapper instanceof Lifecycle)
						((Lifecycle) wrapper).addLifecycleListener(listener);
				} catch (Throwable t) {
					log.error("createWrapper", t);
					return (null);
				}
			}
		}

		synchronized (wrapperListenersLock) {
			for (int i = 0; i < wrapperListeners.length; i++) {
				try {
					Class clazz = Class.forName(wrapperListeners[i]);
					ContainerListener listener = (ContainerListener) clazz
							.newInstance();
					wrapper.addContainerListener(listener);
				} catch (Throwable t) {
					log.error("createWrapper", t);
					return (null);
				}
			}
		}

		return (wrapper);

	}

reload()
從新加載數據,用於當類或者配置文件發送變化時從新加載,從代碼中能夠看出來,tomcat是經過重啓來完成的,主要有幾個步驟

  • 設置暫停標記
  • 中止Context
  • 啓動Context
  • 清楚暫停標記

filterStart()
配置和初始化過濾Servlet的Filter集合。

filterStop()
釋放和清除Filter集合配置。

listenerStart()
配置並實例化註冊在Context上的監聽器集合,用於監聽在Context上面觸發的事件,監聽器用於監聽Conext事件和Servlet事件。分下面幾個步驟:

  • 獲取類加載器,原因:出於安全考慮,不一樣Context使用不一樣類加載器,此外獲取類加載器用於實例化監聽器
  • 獲取監聽器類列表,在第一步取得的類加載器中實例化
  • 區分監聽器類型(分ContextEvent監聽器和生命週期監聽器),註冊在Context組件上,分別在不一樣的監聽器集合列表中
  • 獲取ContextEvent監聽器,觸發Context實例化事件
/**
	 * Configure the set of instantiated application event listeners for this
	 * Context. Return <code>true</code> if all listeners wre initialized
	 * successfully, or <code>false</code> otherwise.
	 */
	public boolean listenerStart() {

		if (log.isDebugEnabled())
			log.debug("Configuring application event listeners");

		// Instantiate the required listeners
		//取當前Context的類加載器,用於實例化監聽器須要。不一樣Context的類加載可能不一樣,出於安全考慮
		ClassLoader loader = getLoader().getClassLoader();
		String listeners[] = findApplicationListeners();
		Object results[] = new Object[listeners.length];
		boolean ok = true;
		//遍歷全部監聽器,並實例化到results列表中
		for (int i = 0; i < results.length; i++) {
			if (getLogger().isDebugEnabled())
				getLogger().debug(
						" Configuring event listener class '" + listeners[i]
								+ "'");
			try {
				Class clazz = loader.loadClass(listeners[i]);
				results[i] = clazz.newInstance();
				// Annotation processing
				if (!getIgnoreAnnotations()) {
					getAnnotationProcessor().processAnnotations(results[i]);
					getAnnotationProcessor().postConstruct(results[i]);
				}
			} catch (Throwable t) {
				getLogger().error(
						sm.getString("standardContext.applicationListener",
								listeners[i]), t);
				ok = false;
			}
		}
		if (!ok) {
			getLogger().error(
					sm.getString("standardContext.applicationSkipped"));
			return (false);
		}

		// Sort listeners in two arrays
		ArrayList eventListeners = new ArrayList();
		ArrayList lifecycleListeners = new ArrayList();
		//遍歷監聽器,區分ContextEvent監聽器和LifecycleEvent監聽器
		for (int i = 0; i < results.length; i++) {
			if ((results[i] instanceof ServletContextAttributeListener)
					|| (results[i] instanceof ServletRequestAttributeListener)
					|| (results[i] instanceof ServletRequestListener)
					|| (results[i] instanceof HttpSessionAttributeListener)) {
				eventListeners.add(results[i]);
			}
			if ((results[i] instanceof ServletContextListener)
					|| (results[i] instanceof HttpSessionListener)) {
				lifecycleListeners.add(results[i]);
			}
		}

		//註冊監聽器
		setApplicationEventListeners(eventListeners.toArray());
		//註冊監聽器
		setApplicationLifecycleListeners(lifecycleListeners.toArray());

		// Send application start events

		if (getLogger().isDebugEnabled())
			getLogger().debug("Sending application start events");

		Object instances[] = getApplicationLifecycleListeners();
		if (instances == null)
			return (ok);
		ServletContextEvent event = new ServletContextEvent(getServletContext());
		//觸發Context實例化事件,通知監聽
		for (int i = 0; i < instances.length; i++) {
			if (instances[i] == null)
				continue;
			if (!(instances[i] instanceof ServletContextListener))
				continue;
			ServletContextListener listener = (ServletContextListener) instances[i];
			try {
				fireContainerEvent("beforeContextInitialized", listener);
				listener.contextInitialized(event);
				fireContainerEvent("afterContextInitialized", listener);
			} catch (Throwable t) {
				fireContainerEvent("afterContextInitialized", listener);
				getLogger().error(
						sm.getString("standardContext.listenerStart",
								instances[i].getClass().getName()), t);
				ok = false;
			}
		}
		return (ok);

	}

listenerStop()
在Conext上中止監聽器的監聽,步驟與啓動時相反,首先觸發中止監聽事件,而後再清除資源。

resourcesStart()
分配Context的目錄資源,這裏涉及到目錄服務。

resourcesStop()
中止Context的目錄資源,這裏涉及到目錄服務。

loadOnStartup(Container[])
這個方法是用來處理在web.xml中配置的loadonstartup Servlet,須要在Context啓動時加載實例化,而不是要等待請求觸發才實例化。特殊servlet提早實例化加快了第一次訪問速度。主要有兩個步驟:

  • 掃描Servlet,標記loadonstartup 大於0的Servlet
  • 加載並實例化Servlet,標記爲可用
/**
	 * Load and initialize all servlets marked "load on startup" in the web
	 * application deployment descriptor.
	 * 
	 * @param children
	 *            Array of wrappers for all currently defined servlets
	 *            (including those not declared load on startup)
	 */
	public void loadOnStartup(Container children[]) {

		// Collect "load on startup" servlets that need to be initialized
		TreeMap map = new TreeMap();
		for (int i = 0; i < children.length; i++) {
			Wrapper wrapper = (Wrapper) children[i];
			int loadOnStartup = wrapper.getLoadOnStartup();
			if (loadOnStartup < 0)
				continue;
			Integer key = Integer.valueOf(loadOnStartup);
			ArrayList list = (ArrayList) map.get(key);
			if (list == null) {
				list = new ArrayList();
				map.put(key, list);
			}
			list.add(wrapper);
		}

		// Load the collected "load on startup" servlets
		Iterator keys = map.keySet().iterator();
		while (keys.hasNext()) {
			Integer key = (Integer) keys.next();
			ArrayList list = (ArrayList) map.get(key);
			Iterator wrappers = list.iterator();
			while (wrappers.hasNext()) {
				Wrapper wrapper = (Wrapper) wrappers.next();
				try {
					wrapper.load();
				} catch (ServletException e) {
					getLogger()
							.error(sm.getString(
									"standardWrapper.loadException", getName()),
									StandardWrapper.getRootCause(e));
					// NOTE: load errors (including a servlet that throws
					// UnavailableException from tht init() method) are NOT
					// fatal to application startup
				}
			}
		}

	}

init()
負責初始化Context,爲Context啓動作準備工做。在方法中完成了Context配置類的實例化,並將其註冊到生命週期監聽器集合中,調用父類初始化函數super.init(),而後出發初始化事件。

start()
負責啓動Context,啓動web app應用,在啓動方法中要完成不少步驟:

  • 斷定是否已經啓動,是否初始化
  • 註冊JMX,觸發BEFORE_START_EVENT事件
  • 標記狀態(不可用和未配置)
  • 配置Context的目錄上下文,用於監控應用文件
  • 啓動Context關聯的目錄服務,監控文件
  • 配置realm權限控制
  • 設置類加載器
  • 設置字符集,工做目錄
  • 校驗目錄文件合法性
  • 註冊命名服務監聽器
  • 線程綁定,爲CL和JNDI在啓動,從新加載,中止時提供支持
  • 啓動loader類加載器
  • 啓動Context中的組件
  • 觸發啓動事件
  • 解綁線程
  • 配置mapper(serlvet與url匹配規則)
  • 綁定線程
  • 處理歡迎頁面
  • 合併Context參數
  • 觸發AFTER_START_EVENT事件
  • 啓動manager session管理器
  • 啓動後臺任務處理線程
  • 開啓Filter過濾功能
  • 加載實例化標記爲loadOnStartup的Servlet
  • 解綁線程
  • 判斷是否啓動成功,設置標記
  • 註冊JMX
  • 釋放jars包
  • 判斷是否啓動成功
/**
	 * Start this Context component.
	 * 
	 * @exception LifecycleException
	 *                if a startup error occurs
	 */
	public synchronized void start() throws LifecycleException {
		// if (lazy ) return;
		if (started) {
			if (log.isInfoEnabled())
				log.info(sm
						.getString("containerBase.alreadyStarted", logName()));
			return;
		}
		if (!initialized) {
			try {
				init();
			} catch (Exception ex) {
				throw new LifecycleException("Error initializaing ", ex);
			}
		}
		if (log.isDebugEnabled())
			log.debug("Starting " + ("".equals(getName()) ? "ROOT" : getName()));

		// Set JMX object name for proper pipeline registration
		preRegisterJMX();

		if ((oname != null)
				&& (Registry.getRegistry(null, null).getMBeanServer()
						.isRegistered(oname))) {
			// As things depend on the JMX registration, the context
			// must be reregistered again once properly initialized
			Registry.getRegistry(null, null).unregisterComponent(oname);
		}

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

		setAvailable(false);
		setConfigured(false);
		boolean ok = true;

		// Add missing components as necessary
		if (webappResources == null) { // (1) Required by Loader
			if (log.isDebugEnabled())
				log.debug("Configuring default Resources");
			try {
				if ((docBase != null) && (docBase.endsWith(".war"))
						&& (!(new File(getBasePath())).isDirectory()))
					setResources(new WARDirContext());
				else
					setResources(new FileDirContext());
			} catch (IllegalArgumentException e) {
				log.error("Error initializing resources: " + e.getMessage());
				ok = false;
			}
		}
		if (ok) {
			if (!resourcesStart()) {
				log.error("Error in resourceStart()");
				ok = false;
			}
		}

		// Look for a realm - that may have been configured earlier.
		// If the realm is added after context - it'll set itself.
		// TODO: what is the use case for this ?
		if (realm == null && mserver != null) {
			ObjectName realmName = null;
			try {
				realmName = new ObjectName(getEngineName()
						+ ":type=Realm,host=" + getHostname() + ",path="
						+ getPath());
				if (mserver.isRegistered(realmName)) {
					mserver.invoke(realmName, "init", new Object[] {},
							new String[] {});
				}
			} catch (Throwable t) {
				if (log.isDebugEnabled())
					log.debug("No realm for this host " + realmName);
			}
		}

		if (getLoader() == null) {
			WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
			webappLoader.setDelegate(getDelegate());
			setLoader(webappLoader);
		}

		// Initialize character set mapper
		getCharsetMapper();

		// Post work directory
		postWorkDirectory();

		// Validate required extensions
		boolean dependencyCheck = true;
		try {
			dependencyCheck = ExtensionValidator.validateApplication(
					getResources(), this);
		} catch (IOException ioe) {
			log.error("Error in dependencyCheck", ioe);
			dependencyCheck = false;
		}

		if (!dependencyCheck) {
			// do not make application available if depency check fails
			ok = false;
		}

		// Reading the "catalina.useNaming" environment variable
		String useNamingProperty = System.getProperty("catalina.useNaming");
		if ((useNamingProperty != null) && (useNamingProperty.equals("false"))) {
			useNaming = false;
		}

		if (ok && isUseNaming()) {
			if (namingContextListener == null) {
				namingContextListener = new NamingContextListener();
				namingContextListener.setName(getNamingContextName());
				addLifecycleListener(namingContextListener);
			}
		}

		// Standard container startup
		if (log.isDebugEnabled())
			log.debug("Processing standard container startup");

		// Binding thread
		ClassLoader oldCCL = bindThread();

		boolean mainOk = false;

		try {

			if (ok) {

				started = true;

				// Start our subordinate components, if any
				if ((loader != null) && (loader instanceof Lifecycle))
					((Lifecycle) loader).start();

				// since the loader just started, the webapp classloader is now
				// created.
				// By calling unbindThread and bindThread in a row, we setup the
				// current Thread CCL to be the webapp classloader
				unbindThread(oldCCL);
				oldCCL = bindThread();

				// Initialize logger again. Other components might have used it
				// too early,
				// so it should be reset.
				logger = null;
				getLogger();
				if ((logger != null) && (logger instanceof Lifecycle))
					((Lifecycle) logger).start();

				if ((cluster != null) && (cluster instanceof Lifecycle))
					((Lifecycle) cluster).start();
				if ((realm != null) && (realm instanceof Lifecycle))
					((Lifecycle) realm).start();
				if ((resources != null) && (resources instanceof Lifecycle))
					((Lifecycle) resources).start();

				// Start our child containers, if any
				Container children[] = findChildren();
				for (int i = 0; i < children.length; i++) {
					if (children[i] instanceof Lifecycle)
						((Lifecycle) children[i]).start();
				}

				// Start the Valves in our pipeline (including the basic),
				// if any
				if (pipeline instanceof Lifecycle) {
					((Lifecycle) pipeline).start();
				}

				// Notify our interested LifecycleListeners
				lifecycle.fireLifecycleEvent(START_EVENT, null);

				// Acquire clustered manager
				Manager contextManager = null;
				if (manager == null) {
					if ((getCluster() != null) && distributable) {
						try {
							contextManager = getCluster().createManager(
									getName());
						} catch (Exception ex) {
							log.error("standardContext.clusterFail", ex);
							ok = false;
						}
					} else {
						contextManager = new StandardManager();
					}
				}

				// Configure default manager if none was specified
				if (contextManager != null) {
					setManager(contextManager);
				}

				if (manager != null && (getCluster() != null) && distributable) {
					// let the cluster know that there is a context that is
					// distributable
					// and that it has its own manager
					getCluster().registerManager(manager);
				}

				mainOk = true;

			}

		} finally {
			// Unbinding thread
			unbindThread(oldCCL);
			if (!mainOk) {
				// An exception occurred
				// Register with JMX anyway, to allow management
				registerJMX();
			}
		}

		if (!getConfigured()) {
			log.error("Error getConfigured");
			ok = false;
		}

		// We put the resources into the servlet context
		if (ok)
			getServletContext().setAttribute(Globals.RESOURCES_ATTR,
					getResources());

		// Initialize associated mapper
		mapper.setContext(getPath(), welcomeFiles, resources);

		// Binding thread
		oldCCL = bindThread();

		// Set annotation processing parameter for Jasper (unfortunately, since
		// this can be configured in many places and not just in
		// /WEB-INF/web.xml,
		// there are not many solutions)
		// Initialize annotation processor
		if (ok && !getIgnoreAnnotations()) {
			if (annotationProcessor == null) {
				if (isUseNaming() && namingContextListener != null) {
					annotationProcessor = new DefaultAnnotationProcessor(
							namingContextListener.getEnvContext());
				} else {
					annotationProcessor = new DefaultAnnotationProcessor(null);
				}
			}
			getServletContext().setAttribute(
					AnnotationProcessor.class.getName(), annotationProcessor);
		}

		try {

			// Create context attributes that will be required
			if (ok) {
				postWelcomeFiles();
			}

			// Set up the context init params
			mergeParameters();

			if (ok) {
				// Notify our interested LifecycleListeners
				lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
			}

			// Configure and call application event listeners
			if (ok) {
				if (!listenerStart()) {
					log.error("Error listenerStart");
					ok = false;
				}
			}

			try {
				// Start manager
				if ((manager != null) && (manager instanceof Lifecycle)) {
					((Lifecycle) getManager()).start();
				}

				// Start ContainerBackgroundProcessor thread
				super.threadStart();
			} catch (Exception e) {
				log.error("Error manager.start()", e);
				ok = false;
			}

			// Configure and call application filters
			if (ok) {
				if (!filterStart()) {
					log.error("Error filterStart");
					ok = false;
				}
			}

			// Load and initialize all "load on startup" servlets
			if (ok) {
				loadOnStartup(findChildren());
			}

		} finally {
			// Unbinding thread
			unbindThread(oldCCL);
		}

		// Set available status depending upon startup success
		if (ok) {
			if (log.isDebugEnabled())
				log.debug("Starting completed");
			setAvailable(true);
		} else {
			log.error(sm.getString("standardContext.startFailed", getName()));
			try {
				stop();
			} catch (Throwable t) {
				log.error(sm.getString("standardContext.startCleanup"), t);
			}
			setAvailable(false);
		}

		// JMX registration
		registerJMX();

		startTime = System.currentTimeMillis();

		// Send j2ee.state.running notification
		if (ok && (this.getObjectName() != null)) {
			Notification notification = new Notification("j2ee.state.running",
					this.getObjectName(), sequenceNumber++);
			broadcaster.sendNotification(notification);
		}

		// Close all JARs right away to avoid always opening a peak number
		// of files on startup
		if (getLoader() instanceof WebappLoader) {
			((WebappLoader) getLoader()).closeJARs(true);
		}

		// Reinitializing if something went wrong
		if (!ok && started) {
			stop();
		}

		// cacheContext();
	}

cacheContext()
緩存Context對象,將對象序列化到本地文件中。

mergeParameters()
合併Context初始化參數配置

stop()
負責中止Context,清理Context中組件的狀態,以啓動Context的順序到這來:

  • 觸發BEFORE_STOP_EVENT事件
  • 標記不可用
  • 綁定線程
  • 中止子容器
  • 中止Filter過濾
  • 中止後臺處理線程
  • 中止監聽器
  • 清除Context字符集
  • 觸發STOP_EVENT事件,標記未啓動
  • 中止pipeline數據傳輸線
  • 清除Context屬性
  • 中止資源服務
  • 中止Conext上的組件
  • 解綁線程
  • 重置Conext,觸發AFTER_STOP_EVENT事件
/**
	 * Stop this Context component.
	 * 
	 * @exception LifecycleException
	 *                if a shutdown error occurs
	 */
	public synchronized void stop() throws LifecycleException {

		// Validate and update our current component state
		if (!started) {
			if (log.isInfoEnabled())
				log.info(sm.getString("containerBase.notStarted", logName()));
			return;
		}

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);

		// Send j2ee.state.stopping notification
		if (this.getObjectName() != null) {
			Notification notification = new Notification("j2ee.state.stopping",
					this.getObjectName(), sequenceNumber++);
			broadcaster.sendNotification(notification);
		}

		// Mark this application as unavailable while we shut down
		setAvailable(false);

		// Binding thread
		ClassLoader oldCCL = bindThread();

		try {

			// Stop our child containers, if any
			Container[] children = findChildren();
			for (int i = 0; i < children.length; i++) {
				if (children[i] instanceof Lifecycle)
					((Lifecycle) children[i]).stop();
			}

			// Stop our filters
			filterStop();

			// Stop ContainerBackgroundProcessor thread
			super.threadStop();

			if ((manager != null) && (manager instanceof Lifecycle)) {
				((Lifecycle) manager).stop();
			}

			// Stop our application listeners
			listenerStop();

			// Finalize our character set mapper
			setCharsetMapper(null);

			// Normal container shutdown processing
			if (log.isDebugEnabled())
				log.debug("Processing standard container shutdown");
			// Notify our interested LifecycleListeners
			lifecycle.fireLifecycleEvent(STOP_EVENT, null);
			started = false;

			// Stop the Valves in our pipeline (including the basic), if any
			if (pipeline instanceof Lifecycle) {
				((Lifecycle) pipeline).stop();
			}

			// Clear all application-originated servlet context attributes
			if (context != null)
				context.clearAttributes();

			// Stop resources
			resourcesStop();

			if ((realm != null) && (realm instanceof Lifecycle)) {
				((Lifecycle) realm).stop();
			}
			if ((cluster != null) && (cluster instanceof Lifecycle)) {
				((Lifecycle) cluster).stop();
			}
			if ((logger != null) && (logger instanceof Lifecycle)) {
				((Lifecycle) logger).stop();
			}
			if ((loader != null) && (loader instanceof Lifecycle)) {
				((Lifecycle) loader).stop();
			}

		} finally {

			// Unbinding thread
			unbindThread(oldCCL);

		}

		// Send j2ee.state.stopped notification
		if (this.getObjectName() != null) {
			Notification notification = new Notification("j2ee.state.stopped",
					this.getObjectName(), sequenceNumber++);
			broadcaster.sendNotification(notification);
		}

		// Reset application context
		context = null;

		// This object will no longer be visible or used.
		try {
			resetContext();
		} catch (Exception ex) {
			log.error("Error reseting context " + this + " " + ex, ex);
		}

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);

		if (log.isDebugEnabled())
			log.debug("Stopping complete");

	}

destroy()
負責銷燬Context任務,在方法中主要是調用基類的destroy()方法銷燬Context。

/**
	 * Destroy needs to clean up the context completely.
	 * 
	 * The problem is that undoing all the config in start() and restoring a
	 * 'fresh' state is impossible. After stop()/destroy()/init()/start() we
	 * should have the same state as if a fresh start was done - i.e read
	 * modified web.xml, etc. This can only be done by completely removing the
	 * context object and remapping a new one, or by cleaning up everything.
	 * 
	 * XXX Should this be done in stop() ?
	 * 
	 */
	public void destroy() throws Exception {
		if (oname != null) {
			// Send j2ee.object.deleted notification
			Notification notification = new Notification("j2ee.object.deleted",
					this.getObjectName(), sequenceNumber++);
			broadcaster.sendNotification(notification);
		}
		super.destroy();

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(DESTROY_EVENT, null);

		synchronized (instanceListenersLock) {
			instanceListeners = new String[0];
		}

	}

resetContext()
負責重置Context容器,將恢復至原始狀態。

adjustURLPattern(String)
負責將URL規範化。

/**
	 * Adjust the URL pattern to begin with a leading slash, if appropriate
	 * (i.e. we are running a servlet 2.2 application). Otherwise, return the
	 * specified URL pattern unchanged.
	 * 
	 * @param urlPattern
	 *            The URL pattern to be adjusted (if needed) and returned
	 */
	protected String adjustURLPattern(String urlPattern) {

		if (urlPattern == null)
			return (urlPattern);
		if (urlPattern.startsWith("/") || urlPattern.startsWith("*."))
			return (urlPattern);
		if (!isServlet22())
			return (urlPattern);
		if (log.isDebugEnabled())
			log.debug(sm.getString("standardContext.urlPattern.patternWarning",
					urlPattern));
		return ("/" + urlPattern);

	}

bindThread()
綁定線程類加載器,保存現有線程的類加載器,而後將Context的類加載器綁定至當前線程中,將Context資源服務綁定至當前線程,綁定命名服務類加載器,這樣能夠確保在Context中的資源在同一個類加載器上面。

/**
	 * Bind current thread, both for CL purposes and for JNDI ENC support during
	 * : startup, shutdown and realoading of the context.
	 * 
	 * @return the previous context class loader
	 */
	private ClassLoader bindThread() {

		ClassLoader oldContextClassLoader = Thread.currentThread()
				.getContextClassLoader();

		if (getResources() == null)
			return oldContextClassLoader;

		if (getLoader().getClassLoader() != null) {
			Thread.currentThread().setContextClassLoader(
					getLoader().getClassLoader());
		}

		DirContextURLStreamHandler.bindThread(getResources());

		if (isUseNaming()) {
			try {
				ContextBindings.bindThread(this, this);
			} catch (NamingException e) {
				// Silent catch, as this is a normal case during the early
				// startup stages
			}
		}

		return oldContextClassLoader;

	}

unbindThread(ClassLoader)
解綁線程類加載器,與bindThrea()方法的功能相反,還原線程類加載器。

/**
	 * Unbind thread.
	 */
	private void unbindThread(ClassLoader oldContextClassLoader) {

		if (isUseNaming()) {
			ContextBindings.unbindThread(this, this);
		}

		DirContextURLStreamHandler.unbindThread();

		Thread.currentThread().setContextClassLoader(oldContextClassLoader);
	}

validateURLPattern(String)
驗證訪問URL是否合法。

/**
	 * Validate the syntax of a proposed <code>&lt;url-pattern&gt;</code> for
	 * conformance with specification requirements.
	 * 
	 * @param urlPattern
	 *            URL pattern to be validated
	 */
	private boolean validateURLPattern(String urlPattern) {

		if (urlPattern == null)
			return (false);
		if (urlPattern.indexOf('\n') >= 0 || urlPattern.indexOf('\r') >= 0) {
			return (false);
		}
		if (urlPattern.startsWith("*.")) {
			if (urlPattern.indexOf('/') < 0) {
				checkUnusualURLPattern(urlPattern);
				return (true);
			} else
				return (false);
		}
		if ((urlPattern.startsWith("/")) && (urlPattern.indexOf("*.") < 0)) {
			checkUnusualURLPattern(urlPattern);
			return (true);
		} else
			return (false);

	}

getServlets()
返回Context中全部的Servlet。

Context做爲web app的上下文,負責維護和管理在Context中的組件的生命週期,Session管理,Filter過濾,加載Servlet,接收響應請求,應用所需資源加載和維護,Context的數據共享和數據安全隔離等一個webapp所須要的所有功能,上面講到的是Context中的部份內容,其餘內容還請本身查看。

有不足之處請斧正,歡迎吐槽...

堅持,騷年,你能夠的

相關文章
相關標籤/搜索