• Tomcat学习笔记(十一)


    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);
                    }
                }
            }
  • 相关阅读:
    第二个周六——3.9
    女王节,很开心——3.8
    女生节——3.7
    尴尬的一批——3.6
    周二——3.5
    周一——3.4
    Java基本语法_循环练习系列(二)——万年历
    Java基本语法_循环练习系列(一)——模拟双色球
    《剩女郎》的艺术魅力
    剩女郎剧评
  • 原文地址:https://www.cnblogs.com/lzeffort/p/7135379.html
Copyright © 2020-2023  润新知