StandardContext分析-tomcat6.x源码阅读

2013-10-06

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等等。

Context
tomcat定义的Servlet上下文接口,也即一个webapp的应用环境。tomcat定义了一个Context所必须具备的功能,定义对Cookie,Session,ServletWrapper等组件的管理,为一个请求选项合适的Servlet方法,他要解决的问题是为请求选择Servlet,Servlet之间的数据共享问题。

ContainerBase
容器基类,StandardContext继承该类,具备了基容器的功能和方法。

StandardContext()
默认构造器,设定basicValve, 初始化NotificationBroadcasterSupport,用户支持j2ee企业功能。

StandardContextValve
Context的标准BasicValve,作用连接Context和ServletWrapper的工具。StandardContextValve主要完成的功能是:

  • WEB-INF和META-INF目录的访问选项判断,tomcat禁止直接访问这两个目录
  • 选定Wrapper处理请求

URLEncoder
跟URL相关,具有对URL编码的功能,替换特殊字符功能。

charsetMapper : CharsetMapper
Context多支持的字符集,跟本地化有关,负责处理本地字符问题。

context : ApplicationContext
Servlet的上下文,一个wepapp中提供一个全局的servlet上下,提供了一些列的访问资源的接口,servlet通过ApplicationContext获取servlet的运行环境信息,加载资源,获取资源。与StandardContext的作用是相辅相成的关系,两种的角色不同,ApplicationContext负责的是servlet运行环境上下信息,不关心session管理,cookie管理,servlet的加载,servlet的选择问题,请求信息,一句话就是:他是servlet管家。StandardContext需要负责管理session,Cookie,Servlet的加载和卸载,负责请求信息的处理,掌握控制权,他是servlet的爹妈。

exceptionPages : HashMap
错误页面信息匹配,对于404,403等HTTP错误码,tomcat通过映射页面来定制提示信息。

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><url-pattern></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中的部分内容,其他内容还请自己查看。

有不足之处请斧正,欢迎吐槽…

坚持,骚年,你可以的

你可能感兴趣的:(StandardContext分析-tomcat6.x源码阅读)