StandardContext类
Context实例代表着一个具体的web应用程序,其中包含一个或者多个Wrapper实例,每个Wrapper实例代表着具体的servlet定义。但是,Context容器还需要其他组件支持,典型的如载入器和session管理器。
在创建了StandardContext实例后,必须调用其startInternal方法。
(1)发送启动状态通知。
// Send j2ee.state.starting notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.starting", this.getObjectName(), sequenceNumber.getAndIncrement()); broadcaster.sendNotification(notification); }
(2)设置配置状态
setConfigured(false); public void setConfigured(boolean configured) { boolean oldConfigured = this.configured; this.configured = configured; support.firePropertyChange("configured", oldConfigured, this.configured); }
(3)加载资源
if (webappResources == null) { // (1) Required by Loader if (log.isDebugEnabled()) log.debug("Configuring default Resources"); try { if (getDocBase() == null) setResources(new EmptyDirContext()); else if ((getDocBase() != null) && (getDocBase().endsWith(".war")) && (!(new File(getBasePath())).isDirectory())) setResources(new WARDirContext()); else setResources(new FileDirContext()); } catch (IllegalArgumentException e) { log.error(sm.getString("standardContext.resourcesInit"), e); ok = false; } }
(4)初始化字符集mapper
// Initialize character set mapper getCharsetMapper();
(5)设置工作目录
// Post work directory postWorkDirectory();
6)绑定线程
// Binding thread ClassLoader oldCCL = bindThread();
(7)通知监听器,启动子容器,开启管道中的阀门
// Notify our interested LifecycleListeners fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); // Start our child containers, if not already started for (Container child : findChildren()) { if (!child.getState().isAvailable()) { child.start(); } } // Start the Valves in our pipeline (including the basic), // if any if (pipeline instanceof Lifecycle) { ((Lifecycle) pipeline).start(); }
(8)加载资源到servletContext和资源映射
// We put the resources into the servlet context if (ok) getServletContext().setAttribute (Globals.RESOURCES_ATTR, getResources()); // Initialize associated mapper mapper.setContext(getPath(), welcomeFiles, resources);
(9)创建context属性设置初始参数
// Create context attributes that will be required if (ok) { getServletContext().setAttribute( JarScanner.class.getName(), getJarScanner()); } // Set up the context init params mergeParameters();
(10)servlet容器初始化,配置事件监听
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry : initializers.entrySet()) { try { entry.getKey().onStartup(entry.getValue(), getServletContext()); } catch (ServletException e) { log.error(sm.getString("standardContext.sciFail"), e); ok = false; break; } } // Configure and call application event listeners if (ok) { if (!listenerStart()) { log.error(sm.getString("standardContext.listenerFail")); ok = false; } }
(11)启动Manager
try { // Start manager if ((manager != null) && (manager instanceof Lifecycle)) { ((Lifecycle) getManager()).start(); } } catch(Exception e) { log.error(sm.getString("standardContext.managerFail"), e); ok = false; } // Configure and call application filters if (ok) { if (!filterStart()) { log.error(sm.getString("standardContext.filterFail")); ok = false; } } // Load and initialize all "load on startup" servlets if (ok) { if (!loadOnStartup(findChildren())){ log.error(sm.getString("standardContext.servletFail")); ok = false; } } // Start ContainerBackgroundProcessor thread super.threadStart();
(12)通知启动状态,重新检查是否启动成功。
// Send j2ee.state.running notification if (ok && (this.getObjectName() != null)) { Notification notification = new Notification("j2ee.state.running", this.getObjectName(), sequenceNumber.getAndIncrement()); 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) { setState(LifecycleState.FAILED); } else { setState(LifecycleState.STARTING); }
StandardContextValve类
实现了一个默认的阀门类,对于每一个HTTP请求可能是有用的。重要的方法invoke。
(1)WEB-INF或META-INF目录下的资源访问不了的原因
// Disallow any direct access to resources under WEB-INF or META-INF MessageBytes requestPathMB = request.getRequestPathMB(); if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/META-INF")) || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; }
(2)获取一个Wrapper
// Select the Wrapper to be used for this Request Wrapper wrapper = request.getWrapper(); if (wrapper == null || wrapper.isUnavailable()) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; }
(3)认证request请求,调用Wrapper的阀门(valve)
// Acknowledge the request try { response.sendAcknowledgement(); } catch (IOException ioe) { container.getLogger().error(sm.getString( "standardContextValve.acknowledgeException"), ioe); request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } if (request.isAsyncSupported()) { request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported()); } wrapper.getPipeline().getFirst().invoke(request, response);
StandardContext对重载的支持
在类加载的指定的仓库下,类发生了改变或者web.xml文件发生了改变将会被重新加载,然而,不能处理任何context.xml文件的改变,如果context.xml发生了改变,应该停止指定的上下文(Context),并且创建和启动一个新的Context实例代替。
在StandardContext中的reload方法中
(1)检查组件是否可用
// Validate our current component state if (!getState().isAvailable()) throw new IllegalStateException (sm.getString("standardContext.notStarted", getName())); if(log.isInfoEnabled()) log.info(sm.getString("standardContext.reloadingStarted", getName()));
(2)设置StandardContext暂停状态
// Stop accepting requests temporarily. setPaused(true);
(3)停止并启动
try { stop(); } catch (LifecycleException e) { log.error( sm.getString("standardContext.stoppingContext", getName()), e); } try { start(); } catch (LifecycleException e) { log.error( sm.getString("standardContext.startingContext", getName()), e); }
(4)设置StandardContext暂停状态
setPaused(false);//设置非暂停状态
ContainerBackgroundProcessor类
Context容器运行还需要其他的组件支持,例如载入器和session管理器。通常这些组件都需要各自的线程执行一些后台处理任务。例如:载入器使用线程定时的检查类和jar的时间戳是否发生改变;session管理器使用线程定时检查session是否过期。
为了节省资源,所有后台共享同一个线程。若某个组件或者servlet容器需要周期性的执行一个操作,只需要将代码写到backgroundProcess方法中。
ContainerBackgroundProcessor是ContainerBase的内部类,下面是代码:
protected class ContainerBackgroundProcessor implements Runnable { @Override public void run() { Throwable t = null; String unexpectedDeathMessage = sm.getString( "containerBase.backgroundProcess.unexpectedThreadDeath", Thread.currentThread().getName()); try { while (!threadDone) { try { //sleep指定时间 Thread.sleep(backgroundProcessorDelay * 1000L); } catch (InterruptedException e) { // Ignore } if (!threadDone) { Container parent = (Container) getMappingObject(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (parent.getLoader() != null) { cl = parent.getLoader().getClassLoader(); } processChildren(parent, cl); } } } catch (RuntimeException e) { t = e; throw e; } catch (Error e) { t = e; throw e; } finally { if (!threadDone) { log.error(unexpectedDeathMessage, t); } } }
processChildren方法
protected void processChildren(Container container, ClassLoader cl) { try { if (container.getLoader() != null) { Thread.currentThread().setContextClassLoader (container.getLoader().getClassLoader()); } container.backgroundProcess(); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.error("Exception invoking periodic operation: ", 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); } } }