17,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。
另外,配置需要添加一个验证器阀门和证书阀门(authenticatorvalveandacertificatevalve)
StandardContext的属性之一是它属性configured,用来表示该StandardContext是否已经配置了
StandardContext使用一个事件监听器来作为它的配置器
当StandardContext实例的start方法被调用的时候,首先触发一个生命周期事件
。该事件唤醒一个监听器来配置该StandardContext实例。配置成功后,该监听器将configured属性设置为true。
否则,StandardContext对象拒绝启动,这样就不能对HTTP请求进行服务了。StandardContext构造函数
publicStandardContext(){
super();
pipeline.setBasic(newStandardContextValve());
namingResources.setContainer(this);
}
在构造函数中,最重要的事情是在StandardContext的流水线上添加了一个类型为StandardContextValve的基本阀门启动StandardContext
Start方法初始化StandardContext对象并让生命周期监听器配置该StandardContext实例。如果配置成功,
生命周期监听器会将configured属性设置为true。最后start方法,将available属性设置为true或者false。
如果是true的话表示该StandardContext属性配置完毕并且所有相关子容器和组件已经成功启动,
这样就能对HTTP请求进行服务了,如果是false则表示出现了错误。public synchronized void start() throws LifecycleException { if (started) throw new LifecycleException (sm.getString("containerBase.alreadyStarted", logName())); lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); setAvailable(false); setConfigured(false); boolean ok = true; if (getResources() == null) { try { if ((docBase != null) && (docBase.endsWith(".war"))) setResources(new WARDirContext()); else setResources(new FileDirContext()); }catch (IllegalArgumentException e) { 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) { if (getPrivileged()) { setLoader(new WebappLoader(this.getClass().getClassLoader())); }else{ setLoader(new WebappLoader(getParentClassLoader())); } } if (getManager() == null) { setManager(new StandardManager()); } // Initialize character set mapper getCharsetMapper(); // Post work directory postWorkDirectory(); 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); } } ClassLoader oldCCL = bindThread(); if (ok) { try { addDefaultMapper(this.mapperClass); started = true; if ((loader != null) && (loader instanceof Lifecycle)) ((Lifecycle) loader).start(); if ((logger != null) && (logger instanceof Lifecycle)) ((Lifecycle) logger).start(); // Unbinding thread unbindThread(oldCCL); 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(); Mapper mappers[] = findMappers(); for (int i = 0; i < mappers.length; i++) { if (mappers[i] instanceof Lifecycle) ((Lifecycle) mappers[i]).start(); } Container children[] = findChildren(); for (int i = 0; i < children.length; i++) { if (children[i] instanceof Lifecycle) ((Lifecycle) children[i]).start(); } 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; if (ok) getServletContext().setAttribute (Globals.RESOURCES_ATTR, getResources()); if (ok) { postWelcomeFiles(); } if (ok) { if (!listenerStart()) ok = false; } if (ok) { if (!filterStart()) ok = false; } // Load and initialize all "load on startup" servlets if (ok) loadOnStartup(findChildren()); unbindThread(oldCCL); if (ok){ setAvailable(true); }else{ stop(); setAvailable(false); } lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); }
下面是该方法做的事情:
·触发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方法public void invoke(Request request, Response response){ while (getPaused()) { try { Thread.sleep(1000); } catch (InterruptedException e) { ; } } if (swallowOutput) { try { SystemLogHandler.startCapture(); super.invoke(request, response); } }else { super.invoke(request, response); } }
方法getPaused获得属性paused的值,当应用程序正在加载的时候该属性为ture。
StandardContextMapper
对于每一个请求,invoke方法都会调用StandarContext流水线基本阀门的invoke方法。
StandarContext的基本阀门用org.apache.catalina.core.StandardContextValve类表示。
StandardContextValve实例查找包含它的StandardContext。StandardContextValve使用上下文容器的map来查找合适的包装器
StandardContext的父类ContainerBase定义了addDefaultMapper方法来添加protected void addDefaultMapper(String mapperClass) { if (mapperClass == null) return; if (mappers.size() >= 1) return; try { Class clazz = Class.forName(mapperClass); Mapper mapper = (Mapper) clazz.newInstance(); mapper.setProtocol("http"); addMapper(mapper); } catch (Exception e) {} }
重加载支持
StandardContext定义了reloadable属性来标识是否支持应用程序的重加载。
当允许重加载的时候,当web.xml或者WEB-INF/classes目录下的文件被改变的时候会重加载。
StandardContext中Loader接口的标准实现WebappLoader类,有一个单独线程来检查WEB-INF目录下面所有类和JAR文件的时间戳
你需要做的是启动该线程,将 WebappLoader关联到StandardContext,使用setContainer方法即可public void setContainer(Container container) { if ((this.container != null) && (this.container instanceof Context)) ((Context) this.container).removePropertyChangeListener(this); 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); } }
注意最后一个if语句块中,如果容器是一个上下文容器,调用setReloadable方法,
也就是说WebappLoader的reloadable属性跟StandardContext的reloadable属性相同。
下面是WebappLoader对setReload方法的实现:public void setReloadable(boolean reloadable) { boolean oldReloadable = this.reloadable; this.reloadable = reloadable; support.firePropertyChange("reloadable", new Boolean(oldReloadable), new Boolean(this.reloadable)); if (!started) return; if (!oldReloadable && this.reloadable) threadStart(); else if (oldReloadable && !this.reloadable) threadStop(); } }
如果将reloadable属性设置为true,调用threadStart方法。如果从true到false,则调用threadStop方法。
threadStart方法启动一个线程持续的检查WEB-INF目录下面的类文件和JAR文件的时间戳。threadStop方法用于停止该线程。
类的时间戳是由backgroundProcess方法调用backgroundProcess方法
一个上下文容器需要其它组件如加载器和管理器的支持。这些组件通常需要一个单独的线程来处理后台过程(backgroundprocessing)
所有的后台过程都分享同一个线程。如果一个组件或者是容器需要定期的来执行操作,
它需要做的是将这些代码写入到backgroundProcess方法即可。protected void threadStart() { if (thread != null) return; if (backgroundProcessorDelay <= 0) return; threadDone = false; String threadName = "ContainerBackgroundProcessor[" + toString() + "]"; thread = new Thread(new ContainerBackgroundProcessor(), threadName); thread.setDaemon(true); thread.start(); }
方法threadStart传递一个ContainerBackgroundProcessor对象创建一个新线程。
ContainerBackgroundProcessor实现了java.lang.Runnable接口protected class ContainerBackgroundProcessor implements Runnable { public void run() { while (!threadDone) { try { Thread.sleep(backgroundProcessorDelay * 1000L); } catch (InterruptedException e) { ; } if (!threadDone) { Container parent = (Container) getMappingObject(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (parent.getLoader() != null) { cl = parent.getLoader().getClassLoader(); } processChildren(parent, cl); } } } protected void processChildren(Container container, ClassLoader cl) { try { if (container.getLoader() != null) { Thread.currentThread().setContextClassLoader (container.getLoader().getClassLoader()); } container.backgroundProcess(); }catch (Throwable t) {} finally { Thread.currentThread().setContextClassLoader(cl); } Container[] children = container.findChildren(); for (int i = 0; i < children.length; i++) { if (children[i].getBackgroundProcessorDelay() <= 0) { processChildren(children[i], cl); } } } }
ContainerBackgroundProcessor是ContainerBase的内部类,
在他的run方法里,有一个while循环定期的调用它的processChildren方法