zoukankan      html  css  js  c++  java
  • How Tomcat Works(十五)

    本文接下来分析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

  • 相关阅读:
    ALinq Dynamic 使用指南——前言
    前端与后端分离的架构实例(三)
    前端与后端分离的架构实例(二)
    启动画面QSplashScreen鼠标点击的时候不退出
    Qt组件屏蔽鼠标激活
    Qt LNK2001错误
    QtDesigner中设定一个组件位于另一个组件上方
    QToolButton设置图片
    osgearth_package切片工具切局部影像或者高程tif无法生成切片问题;切完数据集无法显示问题
    Qt输入框添加搜索按钮,以及自动补全内容
  • 原文地址:https://www.cnblogs.com/chenying99/p/3242279.html
Copyright © 2011-2022 走看看