How Tomcat Works(十五)

系统 1647 0

本文接下来分析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(十五)


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论