本文接下来分析Context容器,Context容器实例表示一个具体的Web应用程序,其中包括一个或多个Wrapper实例;不过Context容器还需要其他的组件支持,典型的如载入器和Session管理器等。

在创建StandardContext实例后,必须调用其start()方法来为引入的每个HTTP请求服务;其中包括读取和解析默认的web.xml文件(该文件位于%CATALINA_HOME%/conf目录),该文件的内容会应用到所有部署到tomcat中的应用程序中;此外,还会配置验证器阀和许可阀。

StandardContext类使用一个事件监听器来作为其配置器(前面我们已经学过在SimpleContextConfig事件监听器中配置验证器阀)

public synchronized void start() throws LifecycleException {
if (started)
throw new LifecycleException
(sm.getString("containerBase.alreadyStarted", logName())); if (debug >= 1)
log("Starting"); // Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); if (debug >= 1)
log("Processing start(), current available=" + getAvailable());
setAvailable(false);
setConfigured(false);
boolean ok = true; // Add missing components as necessary
if (getResources() == null) { // (1) Required by Loader
if (debug >= 1)
log("Configuring default Resources");
try {
if ((docBase != null) && (docBase.endsWith(".war")))
setResources(new WARDirContext());
else
setResources(new FileDirContext());
} catch (IllegalArgumentException e) {
log("Error initializing resources: " + e.getMessage());
ok = false;
}
}
if (ok && (resources instanceof ProxyDirContext)) {
DirContext dirContext =
((ProxyDirContext) resources).getDirContext();
if ((dirContext != null)
&& (dirContext instanceof BaseDirContext)) {
((BaseDirContext) dirContext).setDocBase(getBasePath());
((BaseDirContext) dirContext).allocate();
}
}
if (getLoader() == null) { // (2) Required by Manager
if (getPrivileged()) {
if (debug >= 1)
log("Configuring privileged default Loader");
setLoader(new WebappLoader(this.getClass().getClassLoader()));
} else {
if (debug >= 1)
log("Configuring non-privileged default Loader");
setLoader(new WebappLoader(getParentClassLoader()));
}
}
if (getManager() == null) { // (3) After prerequisites
if (debug >= 1)
log("Configuring default Manager");
setManager(new StandardManager());
} // Initialize character set mapper
getCharsetMapper(); // Post work directory
postWorkDirectory(); // 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.setDebug(getDebug());
namingContextListener.setName(getNamingContextName());
addLifecycleListener(namingContextListener);
}
} // Binding thread
ClassLoader oldCCL = bindThread(); // Standard container startup
if (debug >= 1)
log("Processing standard container startup"); if (ok) { try { addDefaultMapper(this.mapperClass);
started = true; // Start our subordinate components, if any
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();
if ((logger != null) && (logger instanceof Lifecycle))
((Lifecycle) logger).start(); // Unbinding thread
unbindThread(oldCCL); // Binding thread
oldCCL = bindThread(); 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 Mappers, if any
Mapper mappers[] = findMappers();
for (int i = 0; i < mappers.length; i++) {
if (mappers[i] instanceof Lifecycle)
((Lifecycle) mappers[i]).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); if ((manager != null) && (manager instanceof Lifecycle))
((Lifecycle) manager).start(); } finally {
// Unbinding thread
unbindThread(oldCCL);
} }
if (!getConfigured())
ok = false; // We put the resources into the servlet context
if (ok)
getServletContext().setAttribute
(Globals.RESOURCES_ATTR, getResources()); // Binding thread
oldCCL = bindThread(); // Create context attributes that will be required
if (ok) {
if (debug >= 1)
log("Posting standard context attributes");
postWelcomeFiles();
} // Configure and call application event listeners and filters
if (ok) {
if (!listenerStart())
ok = false;
}
if (ok) {
if (!filterStart())
ok = false;
} // Load and initialize all "load on startup" servlets
if (ok)
loadOnStartup(findChildren()); // Unbinding thread
unbindThread(oldCCL); // Set available status depending upon startup success
if (ok) {
if (debug >= 1)
log("Starting completed");
setAvailable(true);
} else {
log(sm.getString("standardContext.startFailed"));
try {
stop();
} catch (Throwable t) {
log(sm.getString("standardContext.startCleanup"), t);
}
setAvailable(false);
} // Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); }

在它的start()方法里面,包括初始化相关容器组件、触发相关事件等(ContextConfig监听器会执行一些配置操作)

StandardContext类的invoke()方法由与其相关联的连接器调用,或者当StandardContext实例是Host容器的子容器时,由Host实例的invoke()方法调用

public void invoke(Request request, Response response)
throws IOException, ServletException { // Wait if we are reloading
while (getPaused()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
;
}
} // Normal request processing
if (swallowOutput) {
try {
SystemLogHandler.startCapture();
super.invoke(request, response);
} finally {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
log(log);
}
}
} else {
super.invoke(request, response);
} }

对于每个引入的HTTP请求,都会调用StandardContext实例的管道对象的基础阀的invoke()方法来处理,这里是org.apache.catalina.core.StandardContextValve类的实例;StandardContextValve类的invoke()方法要做的第一件事是获取一个要处理当前HTTP请求的Wrapper实例;StandardContextValve实例使用StandardContext实例的映射器找到一个合适的Wrapper实例,找到Wrapper实例后,它就会调用Wrapper实例的invoke()方法

下面是standardContextMapper类的map()方法

public Container map(Request request, boolean update) {

        int debug = context.getDebug();

        // Has this request already been mapped?
if (update && (request.getWrapper() != null))
return (request.getWrapper()); // Identify the context-relative URI to be mapped
String contextPath =
((HttpServletRequest) request.getRequest()).getContextPath();
String requestURI = ((HttpRequest) request).getDecodedRequestURI();
String relativeURI = requestURI.substring(contextPath.length()); if (debug >= 1)
context.log("Mapping contextPath='" + contextPath +
"' with requestURI='" + requestURI +
"' and relativeURI='" + relativeURI + "'"); // Apply the standard request URI mapping rules from the specification
Wrapper wrapper = null;
String servletPath = relativeURI;
String pathInfo = null;
String name = null; // Rule 1 -- Exact Match
if (wrapper == null) {
if (debug >= 2)
context.log(" Trying exact match");
if (!(relativeURI.equals("/")))
name = context.findServletMapping(relativeURI);
if (name != null)
wrapper = (Wrapper) context.findChild(name);
if (wrapper != null) {
servletPath = relativeURI;
pathInfo = null;
}
} // Rule 2 -- Prefix Match
if (wrapper == null) {
if (debug >= 2)
context.log(" Trying prefix match");
servletPath = relativeURI;
while (true) {
name = context.findServletMapping(servletPath + "/*");
if (name != null)
wrapper = (Wrapper) context.findChild(name);
if (wrapper != null) {
pathInfo = relativeURI.substring(servletPath.length());
if (pathInfo.length() == 0)
pathInfo = null;
break;
}
int slash = servletPath.lastIndexOf('/');
if (slash < 0)
break;
servletPath = servletPath.substring(0, slash);
}
} // Rule 3 -- Extension Match
if (wrapper == null) {
if (debug >= 2)
context.log(" Trying extension match");
int slash = relativeURI.lastIndexOf('/');
if (slash >= 0) {
String last = relativeURI.substring(slash);
int period = last.lastIndexOf('.');
if (period >= 0) {
String pattern = "*" + last.substring(period);
name = context.findServletMapping(pattern);
if (name != null)
wrapper = (Wrapper) context.findChild(name);
if (wrapper != null) {
servletPath = relativeURI;
pathInfo = null;
}
}
}
} // Rule 4 -- Default Match
if (wrapper == null) {
if (debug >= 2)
context.log(" Trying default match");
name = context.findServletMapping("/");
if (name != null)
wrapper = (Wrapper) context.findChild(name);
if (wrapper != null) {
servletPath = relativeURI;
pathInfo = null;
}
} // Update the Request (if requested) and return this Wrapper
if ((debug >= 1) && (wrapper != null))
context.log(" Mapped to servlet '" + wrapper.getName() +
"' with servlet path '" + servletPath +
"' and path info '" + pathInfo +
"' and update=" + update);
if (update) {
request.setWrapper(wrapper);
((HttpRequest) request).setServletPath(servletPath);
((HttpRequest) request).setPathInfo(pathInfo);
}
return (wrapper); }

standardContextMapper实例必须与一个Context级的容器相关联(在它的map()方法中调用了Context容器实例的相关方法)

standardContext类定义了reloadable属性来指明该应用程序是否启用了重载功能,当启用重载功能后,当web.xml文件发生变化或WEB-INF/classes目录下的文件被重新编译后,应用程序会重载。

standardContext类是通过其载入器实现应用程序重载的,在tomcat4中,standardContext对象中的WebappLoader类实现了Loader接口,并使用另一个线程检查WEB-INF目录中的所有类和JAR文件的时间戳。只需要调用其setContainer()方法将WebappLoader对象与standardContext对象相关联就可以启动该检查线程

下面是tomcat4中WebappLoader类的setContainer()方法的实现代码

public void setContainer(Container container) {

        // Deregister from the old Container (if any)
if ((this.container != null) && (this.container instanceof Context))
((Context) this.container).removePropertyChangeListener(this); // Process this property change
Container oldContainer = this.container;
this.container = container;
support.firePropertyChange("container", oldContainer, this.container); // Register with the new Container (if any)
if ((this.container != null) && (this.container instanceof Context)) {
setReloadable( ((Context) this.container).getReloadable() );
((Context) this.container).addPropertyChangeListener(this);
} }

WebappLoader实例的reloadable属性值与standardContext实例的reloadable属性值是一致的

下面是WebappLoader类的setReloadable()方法的实现代码:

public void setReloadable(boolean reloadable) {

        // Process this property change
boolean oldReloadable = this.reloadable;
this.reloadable = reloadable;
support.firePropertyChange("reloadable",
new Boolean(oldReloadable),
new Boolean(this.reloadable)); // Start or stop our background thread if required
if (!started)
return;
if (!oldReloadable && this.reloadable)
threadStart();
else if (oldReloadable && !this.reloadable)
threadStop(); }

里面的threadStart()方法会启动一个专用的线程来不断地检查WEB-INF目录下的类和JAR文件的时间戳,而threadStop()方法则会终止该线程。

---------------------------------------------------------------------------

本系列How Tomcat Works系本人原创

转载请注明出处 博客园 刺猬的温驯

本人邮箱: chenying998179#163.com (#改为@)

本文链接http://www.cnblogs.com/chenying99/p/3242279.html

How Tomcat Works(十五)的更多相关文章

  1. How Tomcat Works(五)

    本文接下来介绍tomcat的默认连接器,Tomcat中的默认连接器必须满足以下要求: 实现org.apache.catalina.Connector接口 负责创建实现org.apache.catali ...

  2. How Tomcat Works(十四)补充

    在How Tomcat Works(十四)中,本人并没有对javax.servlet.Filter及javax.servlet.FilterChain做详细的描述,本文在这里做一下补充 FilterC ...

  3. How Tomcat Works(十四)

    我们已经知道,在tomcat中有四种类型的servlet容器,分别为Engine.Host.Context 和Wrapper,本文接下来对tomcat中Wrapper接口的标准实现进行说明. 对于每个 ...

  4. How Tomcat Works(十)

    本文接下来分析tomcat的日志记录器,日志记录器是用来记录消息的组件,在tomcat中,日志记录器需要与某个servlet容器相关连:在org.apache.catalina.logger包下,to ...

  5. How Tomcat Works(二十)

    要使用一个web应用程序,必须要将表示该应用程序的Context实例部署到一个host实例中.在tomcat中,context实例可以用war文件的形式来部署,也可以将整个web应用拷贝到Tomcat ...

  6. How Tomcat Works(十八)

    在前面的文章中,如果我们要启动tomcat容器,我们需要使用Bootstrap类来实例化连接器.servlet容器.Wrapper实例和其他组件,然后调用各个对象的set方法将它们关联起来:这种配置应 ...

  7. How Tomcat Works(十六)

    本文接下来会介绍Host容器和Engine容器,在tomcat的实际部署中,总是会使用一个Host容器:本文介绍Host接口和Engine接口及其相关类 Host容器是org.apache.catal ...

  8. How Tomcat Works(十二)

    tomcat容器通过一个称为Session管理器的组件来管理建立的Session对象,该组件由org.apache.catalina.Manager接口表示:Session管理器必须与一个Contex ...

  9. How Tomcat Works(十九)

    本文重点关注启动tomcat时会用到的两个类,分别为Catalina类和Bootstrap类,它们都位于org.apachae.catalina.startup包下:Catalina类用于启动或关闭S ...

随机推荐

  1. window+git+AndroidStudio+github

    1. 安装配置git 安装:需要从网上下载一个,然后进行默认安装即可.安装完成后,找到 “Git Bash”,点击: 配置: 注意:name和email 只是用来标识身份,但是一定要配置好 2. St ...

  2. 中文分词系列(一) 双数组Tire树(DART)详解

    1 双数组Tire树简介 双数组Tire树是Tire树的升级版,Tire取自英文Retrieval中的一部分,即检索树,又称作字典树或者键树.下面简单介绍一下Tire树. 1.1 Tire树 Trie ...

  3. php排序测试

    对 http://www.cnblogs.com/kudosharry/articles/2521621.html 这个补充的调用系统sort()函数的测试结果 1000个随机数: 直接插入排序:时间 ...

  4. 数据结构——Java实现二叉树

    相关概念 存储结构: 顺序存储结构:二叉树的顺序存储结构适用于完全二叉树,对完全二叉树进行顺序编号,通过二叉树的性质五(第1个结点为根结点,第i个结点的左孩子为第2i个结点,右孩子为第2i+1个结点) ...

  5. 【Sass初级】开始使用Sass和Compass

    转自:http://www.w3cplus.com/preprocessor/beginner/getting-started-with-sass-and-compass.html 如果你的朋友.同事 ...

  6. 在Ubuntu中用root帐号登录

    一.其实我个人认为这没有多大必要,因为当你需要 root 的权限时,使用 sudo 便可以了.如果你实在需要在 Ubuntu 中启用 root 帐号的话,那么不妨执行下面的操作: 1.重新设置 roo ...

  7. 让 PowerDesigner 支持 SQLite!

    让 PowerDesigner 支持 SQLite!   PowerDesigner是一个功能强大的数据库设计软件,最近正在用其设计新系统的数据库,但由于在项目初级阶段,希望使用轻量级的 SQLite ...

  8. Groovy读取properties及txt

    昨晚帮老同事解决了一个SoapUI的代码问题,好长时间没用SoapUI,好多东西都忘了,今天先总结下Groovy读取properties 首先吐槽下SoapUI的apidocs,我几乎从中看不出什么东 ...

  9. Selenium2Library系列 keywords 之 _SelectElementKeywords 之 select_from_list(self, locator, *items)

    def select_from_list(self, locator, *items): """Selects `*items` from list identified ...

  10. Asp.Net学习进度备忘(第一步:ASP.NET Web Forms)

    书签:“Web Pages”和“MVC”跳过:另外跳过的内容有待跟进 __________________ 学习资源:W3School. _________________ 跳过的内容: 1.ASP. ...