• context创建过程解析(一)之deployDescriptors


    总结:主要是创建Context对象,并且将默认context配置,host级别配置,context配置的值设置进去,设置docBase,如果是war包就解压到webapp的目录中,重新设置docBase为war包解压后的目录。如果配置文件中没有指定docBase,那么就以webapps为基路径+context的baseName作为docBase
    
    HostConfig.deployApps()
    //在监听到start事件类型,也就是StandardHost调用startInternal
    protected void deployApps() {
    
            File appBase = host.getAppBaseFile();
    //这个值是在触发before_start时间时生成的,默认是tomcat安装目录+engine名+host名
            File configBase = host.getConfigBaseFile();
    //获取host上配置的webapp下的所有文件,默认是webapps目录下的所有文件
            String[] filteredAppPaths = filterAppPaths(appBase.list());
            // Deploy XML descriptors from configBase  发布xml描述文件
            deployDescriptors(configBase, configBase.list());
            // Deploy WARs
            deployWARs(appBase, filteredAppPaths);
            // Deploy expanded folders
            deployDirectories(appBase, filteredAppPaths);
    
        }
    
    
    deployDescriptors
    
    protected void deployDescriptors(File configBase, String[] files) {
    
            if (files == null)
                return;
    
            ExecutorService es = host.getStartStopExecutor();//获取线程池
            List<Future<?>> results = new ArrayList<>();
    
            for (int i = 0; i < files.length; i++) {
                File contextXml = new File(configBase, files[i]);
    
                if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
    //context命名,在构造函数里面进行设置,设置版本,path,命名。
                    ContextName cn = new ContextName(files[i], true);
    //是否已经部署过,如果已经部署过了,就不再进行部署
                    if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
                        continue;
    //异步发布context描述xml
                    results.add(
                            es.submit(new DeployDescriptor(this, cn, contextXml)));
                }
            }
    
            for (Future<?> result : results) {
                try {
    //等待异步部署context描述文件结束
                    result.get();
                } catch (Exception e) {
                    log.error(sm.getString(
                            "hostConfig.deployDescriptor.threaded.error"), e);
                }
            }
        }
    
    ContextName.ContextName()
    
    //name 文件名
    public ContextName(String name, boolean stripFileExtension) {
    //假设文件名为myContext.xml和my/context.xml
            String tmp1 = name;
    
            // Convert Context names and display names to base names
    
            // Strip off any leading "/"  剥离开头的/
            if (tmp1.startsWith("/")) {
                tmp1 = tmp1.substring(1);
            }
    
            // Replace any remaining / 将/替换成#,如果是myContext.xml,那么依然是myContext.xml,如果是my/context.xml,那么就是my#context.xml
            tmp1 = tmp1.replaceAll("/", FWD_SLASH_REPLACEMENT);
    
            // Insert the ROOT name if required  如果需要,插入ROOT名称
    //如果是以##开头或者没有名字的
            if (tmp1.startsWith(VERSION_MARKER) || "".equals(tmp1)) {
                tmp1 = ROOT_NAME + tmp1;//比如##a,就会变成ROOT##a,为空就是ROOT
            }
    
            // Remove any file extensions 去除扩展名
            if (stripFileExtension &&
                    (tmp1.toLowerCase(Locale.ENGLISH).endsWith(".war") ||
                            tmp1.toLowerCase(Locale.ENGLISH).endsWith(".xml"))) {
                tmp1 = tmp1.substring(0, tmp1.length() -4);
            }
    
     baseName = tmp1;//myContext,my#context
    
            String tmp2;
            // Extract version number  提取版本号
            int versionIndex = baseName.indexOf(VERSION_MARKER);
            if (versionIndex > -1) {
    //如果存在##这种的,提取##后面作为版本号
                version = baseName.substring(versionIndex + 2);
    //比如myContext##1.2,这里version就是1.2,tmp2为myContext
                tmp2 = baseName.substring(0, versionIndex);
            } else {
                version = "";
                tmp2 = baseName;
            }
    //如果为ROOT,那么path路径为域名根目录
            if (ROOT_NAME.equals(tmp2)) {
                path = "";
            } else {
    //   /myContext   /my/context
                path = "/" + tmp2.replaceAll(FWD_SLASH_REPLACEMENT, "/");
            }
    //如果存在版本号的应用
            if (versionIndex > -1) {
    // /myContext##1.2   /my/context##1.2
                this.name = path + VERSION_MARKER + version;
            } else {
                this.name = path;
            }
        }
    
    
    DeployDescriptor.run()
    
    public void run() {
                config.deployDescriptor(cn, descriptor);
     }
    
    HostConfig.deployDescriptor(ContextName cn, File contextXml)
    
    protected void deployDescriptor(ContextName cn, File contextXml) {
    //发布应用,用于记录发布的context名,和是否有描述文件,一些可能被修改的文件的修改时间,用于日后检测是否需要重新加载
            DeployedApplication deployedApp =
                    new DeployedApplication(cn.getName(), true);
    
            long startTime = 0;
            // Assume this is a configuration descriptor and deploy it
            if(log.isInfoEnabled()) {
               startTime = System.currentTimeMillis();
               log.info(sm.getString("hostConfig.deployDescriptor",
                        contextXml.getAbsolutePath()));
            }
    
            Context context = null;
    //是否为扩展war包
            boolean isExternalWar = false;
    //是否是扩展web 应用
            boolean isExternal = false;
    //记录扩展web应用的地址
            File expandedDocBase = null;
    
            try (FileInputStream fis = new FileInputStream(contextXml)) {
                synchronized (digesterLock) {
                    try {
    //解析contextxml文件
                        context = (Context) digester.parse(fis);
                    } catch (Exception e) {
                        log.error(sm.getString(
                                "hostConfig.deployDescriptor.error",
                                contextXml.getAbsolutePath()), e);
                    } finally {
    //释放内存
                        digester.reset();
    //如果创建失败,就new出一个失败的context
                        if (context == null) {
                            context = new FailedContext();
                        }
                    }
                }
    //host.getConfigClass() == org.apache.catalina.startup.ContextConfig
                Class<?> clazz = Class.forName(host.getConfigClass());
                LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
                //给context设置ContextConfig生命周期监听器
    context.addLifecycleListener(listener);
    
                context.setConfigFile(contextXml.toURI().toURL());
                context.setName(cn.getName());
                context.setPath(cn.getPath());//一般我们在config/engine名+host名下的配置文件都是
                context.setWebappVersion(cn.getVersion());
                // Add the associated docBase to the redeployed list if it's a WAR
                if (context.getDocBase() != null) {
                    File docBase = new File(context.getDocBase());
                    if (!docBase.isAbsolute()) {
                        docBase = new File(host.getAppBaseFile(), context.getDocBase());
                    }
                    // If external docBase, register .xml as redeploy first,如果是扩展的web应用
    //那么首先进行重发布处理
                    if (!docBase.getCanonicalPath().startsWith(
                            host.getAppBaseFile().getAbsolutePath() + File.separator)) {
                        isExternal = true;
    //设置应用配置xml为重发布资源,记录最后修改的时间
                        deployedApp.redeployResources.put(
                                contextXml.getAbsolutePath(),
                                Long.valueOf(contextXml.lastModified()));
    //设置应用目录为重发布资源,记录最后修改的时间
                        deployedApp.redeployResources.put(docBase.getAbsolutePath(),
                                Long.valueOf(docBase.lastModified()));
    //如果是war包,设置isExternalWar为true
                        if (docBase.getAbsolutePath().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
                            isExternalWar = true;
                        }
                    } else {
                        log.warn(sm.getString("hostConfig.deployDescriptor.localDocBaseSpecified",
                                 docBase));
                        // Ignore specified docBase
                        context.setDocBase(null);
                    }
                }
    
                host.addChild(context);//将context添加到对应host容器中,这里会进行context的初始化,启动生命周期
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString("hostConfig.deployDescriptor.error",
                                       contextXml.getAbsolutePath()), t);
            } finally {
                // Get paths for WAR and expanded WAR in appBase
    
                // default to appBase dir + name 默认是AppBase路径加上容器的继承名称
                expandedDocBase = new File(host.getAppBaseFile(), cn.getBaseName());
    //如果应用的docBase不为空,也就是设置了应用的位置,并且不是war包
                if (context.getDocBase() != null
                        && !context.getDocBase().toLowerCase(Locale.ENGLISH).endsWith(".war")) {
                    // first assume docBase is absolute 重新设置expandedDocBase
                    expandedDocBase = new File(context.getDocBase());
                    if (!expandedDocBase.isAbsolute()) {
                        // if docBase specified and relative, it must be relative to appBase
    //如果是相对路径,都会认为相对appbase
                        expandedDocBase = new File(host.getAppBaseFile(), context.getDocBase());
                    }
                }
    
                boolean unpackWAR = unpackWARs;
                if (unpackWAR && context instanceof StandardContext) {
                    unpackWAR = ((StandardContext) context).getUnpackWAR();
                }
    
                // Add the eventual unpacked WAR and all the resources which will be
                // watched inside it
    //如果是war包,并且允许解压,那么把war加入重新部署检测列表
                if (isExternalWar) {
                    if (unpackWAR) {
                        deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
                                Long.valueOf(expandedDocBase.lastModified()));
    //加入以expandedDocBase为基路径的资源重新加载监控
    //在配置文件中有WatchedResource这样的xml元素,可以配置需要重加载检测文件
                        addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), context);
                    } else {
    //如果不允许解压,那么就直接用他们的相对路径进行资源修改监控
                        addWatchedResources(deployedApp, null, context);
                    }
                } else {
    //如果不是war包,而又不是扩展目录应用,那么自动加上war后缀
                    // Find an existing matching war and expanded folder
                    if (!isExternal) {
                        File warDocBase = new File(expandedDocBase.getAbsolutePath() + ".war");
                        if (warDocBase.exists()) {
                            deployedApp.redeployResources.put(warDocBase.getAbsolutePath(),
                                    Long.valueOf(warDocBase.lastModified()));
                        } else {
                            // Trigger a redeploy if a WAR is added 如果这个war后面被添加进来了,那么就触发重新加载
                            deployedApp.redeployResources.put(
                                    warDocBase.getAbsolutePath(),
                                    Long.valueOf(0));
                        }
                    }
    //这段代码和上面的war时的代码是一样的
                    if (unpackWAR) {
                        deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
                                Long.valueOf(expandedDocBase.lastModified()));
                        addWatchedResources(deployedApp,
                                expandedDocBase.getAbsolutePath(), context);
                    } else {
                        addWatchedResources(deployedApp, null, context);
                    }
                    if (!isExternal) {
                        // For external docBases, the context.xml will have been
                        // added above.
                        deployedApp.redeployResources.put(
                                contextXml.getAbsolutePath(),
                                Long.valueOf(contextXml.lastModified()));
                    }
                }
                // Add the global redeploy resources (which are never deleted) at
                // the end so they don't interfere with the deletion process
    //添加全局重新部署资源,如conf/engine名称+host名/context.xml.default和conf/context.xml
                addGlobalRedeployResources(deployedApp);
            }
    //如果这个web应用已经成功添加到host中,那么记录这个应用已经被发布
            if (host.findChild(context.getName()) != null) {
                deployed.put(context.getName(), deployedApp);
            }
    
            if (log.isInfoEnabled()) {
                log.info(sm.getString("hostConfig.deployDescriptor.finished",
                    contextXml.getAbsolutePath(), Long.valueOf(System.currentTimeMillis() - startTime)));
            }
        }
    
    
    
    host.addChild最终调用了StandardHost.addChildInternal方法
    
    private void addChildInternal(Container child) {
    
            if( log.isDebugEnabled() )
                log.debug("Add child " + child + " " + this);
            synchronized(children) {
                if (children.get(child.getName()) != null)
                    throw new IllegalArgumentException("addChild:  Child name '" +
                                                       child.getName() +
                                                       "' is not unique");
                child.setParent(this);  // May throw IAE 给子容器设置父容器,并且触发属性更改事件
                children.put(child.getName(), child);将生成的context以context的名字做key,进行保存到map中
            }
    
            // Start child
            // Don't do this inside sync block - start can be a slow process and
            // locking the children object can cause problems elsewhere
            try {
                if ((getState().isAvailable() ||
                        LifecycleState.STARTING_PREP.equals(getState())) &&
                        startChildren) {
    //启动context容器
                    child.start();
                }
            } catch (LifecycleException e) {
                log.error("ContainerBase.addChild: start: ", e);
                throw new IllegalStateException("ContainerBase.addChild: start: " + e);
            } finally {
                fireContainerEvent(ADD_CHILD_EVENT, child);
            }
        }
    
    child.setParent(this);
    
    public void setParent(Container container) {
    
            Container oldParent = this.parent;
            this.parent = container;
    //调用实现了PropertyChangeListener接口的观察者
            support.firePropertyChange("parent", oldParent, this.parent);
    
        }
    
    
    child.start();
    StandardContext.start();
    
     public final synchronized void start() throws LifecycleException {
            //如果已经正在启动了,那么无需重复启动,直接返回,由于多线程的原因,这个state是volatile修饰的,保证内存可见性
            if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
                    LifecycleState.STARTED.equals(state)) {
    
                if (log.isDebugEnabled()) {
                    Exception e = new LifecycleException();
                    log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
                } else if (log.isInfoEnabled()) {
                    log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
                }
    
                return;
            }
            //如果当前状态还是NEW,也就是连init都么有,那么就需要进行初始化
            if (state.equals(LifecycleState.NEW)) {
                init();
               //如果是失败,那么就停止容器
            } else if (state.equals(LifecycleState.FAILED)) {
                stop();
                //如果没有进行初始化完,就开始启动,那么直接报错
            } else if (!state.equals(LifecycleState.INITIALIZED) &&
                    !state.equals(LifecycleState.STOPPED)) {
                invalidTransition(Lifecycle.BEFORE_START_EVENT);
            }
    
            try {
                //设置声明周期类型,并且触发对应的事件
                setStateInternal(LifecycleState.STARTING_PREP, null, false);
                startInternal();
                if (state.equals(LifecycleState.FAILED)) {
                    // This is a 'controlled' failure. The component put itself into the
                    // FAILED state so call stop() to complete the clean-up.
                    stop();
                } else if (!state.equals(LifecycleState.STARTING)) {
                    // Shouldn't be necessary but acts as a check that sub-classes are
                    // doing what they are supposed to.
                    invalidTransition(Lifecycle.AFTER_START_EVENT);
                } else {
                    setStateInternal(LifecycleState.STARTED, null, false);
                }
            } catch (Throwable t) {
                // This is an 'uncontrolled' failure so put the component into the
                // FAILED state and throw an exception.
                ExceptionUtils.handleThrowable(t);
                setStateInternal(LifecycleState.FAILED, null, false);
                throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
            }
        }
    
    init();
    
    //StandardContext触发after_init事件
    public void lifecycleEvent(LifecycleEvent event) {
    
            // Identify the context we are associated with
            try {
                context = (Context) event.getLifecycle();
            } catch (ClassCastException e) {
                log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
                return;
            }
    
            // Process the event that has occurred
            if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
                configureStart();
            } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
                beforeStart();-----》2
            } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
                // Restore docBase for management tools
                if (originalDocBase != null) {
                    context.setDocBase(originalDocBase);
                }
            } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
                configureStop();
            } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
                init(); -----》1
            } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
                destroy();
            }
    
        }
    
    ContextConfig.init();
    
    protected void init() {
            // Called from StandardContext.init()
    //创建一个Digester用于解析context.xml
            Digester contextDigester = createContextDigester();
            contextDigester.getParser();
    
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("contextConfig.init"));
            }
            context.setConfigured(false);//设置配置状态,默认设置为失败,以免被误任务成功
            ok = true;
    
            contextConfig(contextDigester);
        }
    
    
    ContextConfig.contextConfig()
    
    protected void contextConfig(Digester digester) {
    
            String defaultContextXml = null;
    
            // Open the default context.xml file, if it exists 如果配置了默认的配置,使用它
            if (context instanceof StandardContext) {
                defaultContextXml = ((StandardContext)context).getDefaultContextXml();
            }
            // set the default if we don't have any overrides 如果没有使用tomcat默认的全局配置
            if (defaultContextXml == null) {
                defaultContextXml = Constants.DefaultContextXml;//conf/context.xml
            }
    //如果还没有进行解析,那么就会重新解析,默认的全局配置-》configBase下的context.xml.default-》configBase下的配置
            if (!context.getOverride()) {
                File defaultContextFile = new File(defaultContextXml);
                if (!defaultContextFile.isAbsolute()) {
                    defaultContextFile =
                            new File(context.getCatalinaBase(), defaultContextXml);
                }
                if (defaultContextFile.exists()) {
                    try {
                        URL defaultContextUrl = defaultContextFile.toURI().toURL();
    //处理应用配置,这个配置是默认配置,如果用户指定了默认配置,那么就解析用户指定的配置,如果没有指定,那么就直接使用tomcat提供的全局配置
                        processContextConfig(digester, defaultContextUrl);
                    } catch (MalformedURLException e) {
                        log.error(sm.getString(
                                "contextConfig.badUrl", defaultContextFile), e);
                    }
                }
    
    //Constants.HostContextXml == context.xml.default,这个默认配置文件是host范围的context配置
                File hostContextFile = new File(getHostConfigBase(), Constants.HostContextXml);
                if (hostContextFile.exists()) {
                    try {
                        URL hostContextUrl = hostContextFile.toURI().toURL();
                        processContextConfig(digester, hostContextUrl);
                    } catch (MalformedURLException e) {
                        log.error(sm.getString(
                                "contextConfig.badUrl", hostContextFile), e);
                    }
                }
            }
    //context.getConfigFile() 获取应用的配置的文件,这个配置文件就是config/engine名+host名下扫描出来的配置文件
            if (context.getConfigFile() != null) {
                processContextConfig(digester, context.getConfigFile());
            }
    
        }
    
    
    ContextConfig. processContextConfig()
    
    //解析应用配置文件,从目前tomcat调用这个方法的先后顺序可以看出,tomcat给一个web应用设置属性是从全局默认的配置开始-》host级别的配置-》再到context级别的配置,所以context级别的配置最高。
    protected void processContextConfig(Digester digester, URL contextXml) {
    
            if (log.isDebugEnabled()) {
                log.debug("Processing context [" + context.getName()
                        + "] configuration file [" + contextXml + "]");
            }
    
            InputSource source = null;
            InputStream stream = null;
    
            try {
                source = new InputSource(contextXml.toString());
                URLConnection xmlConn = contextXml.openConnection();
                xmlConn.setUseCaches(false);
                stream = xmlConn.getInputStream();
            } catch (Exception e) {
                log.error(sm.getString("contextConfig.contextMissing",
                          contextXml) , e);
            }
    
            if (source == null) {
                return;
            }
    
            try {
                source.setByteStream(stream);
                digester.setClassLoader(this.getClass().getClassLoader());
    //这里这个digester不会再创建StandardContext对象了,因为在前面已经创建了一个
                digester.setUseContextClassLoader(false);
                digester.push(context.getParent());
                digester.push(context);
                XmlErrorHandler errorHandler = new XmlErrorHandler();
                digester.setErrorHandler(errorHandler);
                digester.parse(source);
                if (errorHandler.getWarnings().size() > 0 ||
                        errorHandler.getErrors().size() > 0) {
                    errorHandler.logFindings(log, contextXml.toString());
                    ok = false;
                }
                if (log.isDebugEnabled()) {
                    log.debug("Successfully processed context [" + context.getName()
                            + "] configuration file [" + contextXml + "]");
                }
            } catch (SAXParseException e) {
                log.error(sm.getString("contextConfig.contextParse",
                        context.getName()), e);
                log.error(sm.getString("contextConfig.defaultPosition",
                                 "" + e.getLineNumber(),
                                 "" + e.getColumnNumber()));
                ok = false;
            } catch (Exception e) {
                log.error(sm.getString("contextConfig.contextParse",
                        context.getName()), e);
                ok = false;
            } finally {
                try {
                    if (stream != null) {
                        stream.close();
                    }
                } catch (IOException e) {
                    log.error(sm.getString("contextConfig.contextClose"), e);
                }
            }
        }
    
    //before_start事件
    ContextConfig.beforeStart();
    
    protected synchronized void beforeStart() {
    
            try {
                fixDocBase();
            } catch (IOException e) {
                log.error(sm.getString(
                        "contextConfig.fixDocBase", context.getName()), e);
            }
    
            antiLocking();
        }
    
    
    ContextConfig.fixDocBase()
    //对docBase做调整
    protected void fixDocBase() throws IOException {
    
            Host host = (Host) context.getParent();
            File appBase = host.getAppBaseFile();
    
            String docBase = context.getDocBase();
    //如果没有设置docBase,那么就根据应用路径重新设置路径
            if (docBase == null) {
                // Trying to guess the docBase according to the path
                String path = context.getPath();
                if (path == null) {
                    return;
                }
    //通过路径和版本去获取docBase
                ContextName cn = new ContextName(path, context.getWebappVersion());
                docBase = cn.getBaseName();
            }
    
            File file = new File(docBase);
            if (!file.isAbsolute()) {
                docBase = (new File(appBase, docBase)).getPath();
            } else {
                docBase = file.getCanonicalPath();
            }
            file = new File(docBase);
            String origDocBase = docBase;
    
            ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
            String pathName = cn.getBaseName();
    
            boolean unpackWARs = true;
            if (host instanceof StandardHost) {
                unpackWARs = ((StandardHost) host).isUnpackWARs();
                if (unpackWARs && context instanceof StandardContext) {
                    unpackWARs =  ((StandardContext) context).getUnpackWAR();
                }
            }
    //判断这个docBase路径是否是webapps下的
            boolean docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
    //如果这个docBase指定的是一个war包,那么就将其解压
            if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) {
                URL war = UriUtil.buildJarUrl(new File(docBase));
                if (unpackWARs) {
    //解压war包,返回解压后的目录路径,war解析源码请看war包解析笔记
                    docBase = ExpandWar.expand(host, war, pathName);
                    file = new File(docBase);
                    docBase = file.getCanonicalPath();
                    if (context instanceof StandardContext) {
    //设置未解压时指定的位置,因为解压时会将解压后的内容放到host指定的appBase目录下
                        ((StandardContext) context).setOriginalDocBase(origDocBase);
                    }
                } else {
    //如果不需要解压,那么就校验对应的docbase是由已经存在了,如果不存在,直接报错
                    ExpandWar.validate(host, war, pathName);
                }
            } else {
                File docDir = new File(docBase);
                File warFile = new File(docBase + ".war");
                URL war = null;
    //如果war包存在,并且是在APPBase中的,那么直接获取其war的url
                if (warFile.exists() && docBaseInAppBase) {
                    war = UriUtil.buildJarUrl(warFile);
                }
    //如果目录存在,并且是war包,允许解压
                if (docDir.exists()) {
                    if (war != null && unpackWARs) {
                        // Check if WAR needs to be re-expanded (e.g. if it has
                        // changed). Note: HostConfig.deployWar() takes care of
                        // ensuring that the correct XML file is used.
                        // This will be a NO-OP if the WAR is unchanged.
                        ExpandWar.expand(host, war, pathName);
                    }
                } else {
                    if (war != null) {
                        if (unpackWARs) {
                            docBase = ExpandWar.expand(host, war, pathName);
                            file = new File(docBase);
                            docBase = file.getCanonicalPath();
                        } else {
                            docBase = warFile.getCanonicalPath();
                            ExpandWar.validate(host, war, pathName);
                        }
                    }
                    if (context instanceof StandardContext) {
                        ((StandardContext) context).setOriginalDocBase(origDocBase);
                    }
                }
            }
    
            // Re-calculate now docBase is a canonical path
            docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
    //如果目录为appBase下的,那么直接截取到appBase后面一截
            if (docBaseInAppBase) {
                docBase = docBase.substring(appBase.getPath().length());
                docBase = docBase.replace(File.separatorChar, '/');
                if (docBase.startsWith("/")) {
                    docBase = docBase.substring(1);
                }
            } else {
                docBase = docBase.replace(File.separatorChar, '/');
            }
    
            context.setDocBase(docBase);
        }
    
    
     protected synchronized void startInternal() throws LifecycleException {
            setConfigured(false);
            boolean ok = true;
    
            // Currently this is effectively a NO-OP but needs to be called to
            // ensure the NamingResources follows the correct lifecycle
            if (namingResources != null) {
                namingResources.start();
            }
    
            // Post work directory 生成工作目录,这个目录用于存放编译后的jsp文件,一般生成的目录格式为tomcat安装目录work+engine名+host名+context的baseName
            postWorkDirectory();
    
            // Add missing components as necessary
            if (getResources() == null) {   // (1) Required by Loader
                if (log.isDebugEnabled())
                    log.debug("Configuring default Resources");
    
                try {
                    setResources(new StandardRoot(this));
                } catch (IllegalArgumentException e) {
                    log.error(sm.getString("standardContext.resourcesInit"), e);
                    ok = false;
                }
            }
            if (ok) {
                resourcesStart();
            }
    //设置web加载器
            if (getLoader() == null) {
                WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
                webappLoader.setDelegate(getDelegate());
                setLoader(webappLoader);
            }
    
           。。。省略代码
           
    
                    // Notify our interested LifecycleListeners
                    fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
    
                    // Start our child containers, if not already started,开始子容器context的子容器是wrapper
                    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();
                    }
    //省略了集群管理代码
                  
                    // Configure default manager if none was specified
                    if (contextManager != null) {
                        if (log.isDebugEnabled()) {
                            log.debug(sm.getString("standardContext.manager",
                                    contextManager.getClass().getName()));
                        }
                        setManager(contextManager);
                    }
    // Set up the context init params 设置初始化参数
                mergeParameters();
    
                // Call ServletContainerInitializers
                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 实例化监听器,并且调用实现了ServletContextListener监听器的初始化方法
                if (ok) {
                    if (!listenerStart()) {
                        log.error(sm.getString("standardContext.listenerFail"));
                        ok = false;
                    }
                }
    
                // Check constraints for uncovered HTTP methods
                // Needs to be after SCIs and listeners as they may programmatically
                // change constraints
                if (ok) {
                    checkConstraintsForUncoveredMethods(findConstraints());
                }
    
                try {
                    // Start manager session管理器
                    Manager manager = getManager();
                    if (manager instanceof Lifecycle) {
                        ((Lifecycle) manager).start();
                    }
                } catch(Exception e) {
                    log.error(sm.getString("standardContext.managerFail"), e);
                    ok = false;
                }
    
                // Configure and call application filters 实例化filter,并且调用其初始化方法
                if (ok) {
                    if (!filterStart()) {
                        log.error(sm.getString("standardContext.filterFail"));
                        ok = false;
                    }
                }
    
                // Load and initialize all "load on startup" servlets 实例化设置了load on startup的Servlet并调用初始化方法
                if (ok) {
                    if (!loadOnStartup(findChildren())){
                        log.error(sm.getString("standardContext.servletFail"));
                        ok = false;
                    }
                }
    
                // Start ContainerBackgroundProcessor thread
                super.threadStart();
            } finally {
                // Unbinding thread
                unbindThread(oldCCL);
            }
    
            // Set available status depending upon startup success
            if (ok) {
                if (log.isDebugEnabled())
                    log.debug("Starting completed");
            } else {
                log.error(sm.getString("standardContext.startFailed", getName()));
            }
    
            startTime=System.currentTimeMillis();
    
            // Send j2ee.state.running notification
            if (ok && (this.getObjectName() != null)) {
                Notification notification =
                    new Notification("j2ee.state.running", this.getObjectName(),
                                     sequenceNumber.getAndIncrement());
                broadcaster.sendNotification(notification);
            }
    
            // The WebResources implementation caches references to JAR files. On
            // some platforms these references may lock the JAR files. Since web
            // application start is likely to have read from lots of JARs, trigger
            // a clean-up now.
            getResources().gc();
    
            // Reinitializing if something went wrong
            if (!ok) {
                setState(LifecycleState.FAILED);
            } else {
                setState(LifecycleState.STARTING);
            }
        }
    
    
    configure_start事件
    ContextConfig.configureStart()
    
    protected synchronized void configureStart() {
            // Called from StandardContext.start()
    //开始web.xml的配置
            webConfig();
    
    //如果未配置忽略应用注解配置,那么对filter,servlet,listener进行Resource注解的搜索
    Resource配置在类,字段,方法上,会根据资源的类型进行划分资源类型,添加到不同资源集合中,比如环境变量,JNDI等等资源
            if (!context.getIgnoreAnnotations()) {
                applicationAnnotationsConfig();
            }
            if (ok) {
    //将context约束的角色和wrapper中注解RunAs或配置中设置的角色添加到context容器中,重复的不会被再次添加
                validateSecurityRoles();
            }
    
            // Configure an authenticator if we need one
    //配置验证器,如果没有Ralm或者实现了 Authenticator的管道阀,那么就会添加一个默认的NonLoginAuthenticator验证器
            if (ok) {
                authenticatorConfig();
            }
            // Make our application available if no problems were encountered
    //如果配置context时没有遇到任何问题,那么就表示配置成功
            if (ok) {
                context.setConfigured(true);
            } else {
                log.error(sm.getString("contextConfig.unavailable"));
                context.setConfigured(false);
            }
    
        }
    
    ContextConfig.webConfig();
    
     protected void webConfig() {
           //new出一个webxml的解析器
            WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
                    context.getXmlValidation(), context.getXmlBlockExternal());
    
            Set<WebXml> defaults = new HashSet<>();
    //获取webxml片段
            defaults.add(getDefaultWebXmlFragment(webXmlParser));
    
            WebXml webXml = createWebXml();
    
            // Parse context level web.xml
            InputSource contextWebXml = getContextWebXmlSource();
            if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
                ok = false;
            }
    
            ServletContext sContext = context.getServletContext();
    
            // Ordering is important here
    
            // Step 1. Identify all the JARs packaged with the application and those
            // provided by the container. If any of the application JARs have a
            // web-fragment.xml it will be parsed at this point. web-fragment.xml
            // files are ignored for container provided JARs.
    //解析应用程序jar包中META-INF/web-fragment.xml
    //key为jar包的全路径名,value是从META-INF/web-fragment.xml解析后的WebXml对象
            Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
    
            // Step 2. Order the fragments. 对片段进行排序,因为在片段中可以设置依赖,在什么什么之前启动,什么什么之后启动。
            Set<WebXml> orderedFragments = null;
            orderedFragments =
                    WebXml.orderWebFragments(webXml, fragments, sContext);
    
            // Step 3. Look for ServletContainerInitializer implementations
            if (ok) {
    //查找META-INF/services/javax.servlet.ServletContainerInitializer配置文件,获取ServletContainerInitializer,这里的ServletContainerInitializer可以使用HandlesTypes注解指定需要处理的类型
                processServletContainerInitializers();
            }
    
            if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
                // Step 4. Process /WEB-INF/classes for annotations and
                // @HandlesTypes matches
                Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();
    
                if (ok) {
    //加载/WEB-INF/classes下的资源
                    WebResource[] webResources =
                            context.getResources().listResources("/WEB-INF/classes");
    
                    for (WebResource webResource : webResources) {
                        // Skip the META-INF directory from any JARs that have been
                        // expanded in to WEB-INF/classes (sometimes IDEs do this).
                        if ("META-INF".equals(webResource.getName())) {
                            continue;
                        }
    //使用自定义的字节码解析器去解析class文件,寻找有@WebServlet,@WebFilter,@WebListener,然后将解析好的ServletDef,FilterDef,Listener注入到WebXml对象当中
                        processAnnotationsWebResource(webResource, webXml,
                                webXml.isMetadataComplete(), javaClassCache);
                    }
                }
    
                // Step 5. Process JARs for annotations and
                // @HandlesTypes matches - only need to process those fragments we
                // are going to use (remember orderedFragments includes any
                // container fragments)
    //解析jar包中的注解Servlet,Listener,FIlter
                if (ok) {
                    processAnnotations(
                            orderedFragments, webXml.isMetadataComplete(), javaClassCache);
                }
    
                // Cache, if used, is no longer required so clear it
                javaClassCache.clear();
            }
    
            if (!webXml.isMetadataComplete()) {
                // Step 6. Merge web-fragment.xml files into the main web.xml
    //合并片段配置文件的内容到主web.xml中
                // file.
                if (ok) {
                    ok = webXml.merge(orderedFragments);
                }
    
                // Step 7. Apply global defaults
                // Have to merge defaults before JSP conversion since defaults
                // provide JSP servlet definition. //应用全局默认配置
                webXml.merge(defaults);
    
                // Step 8. Convert explicitly mentioned jsps to servlets
    //准备数据转换被显示配置的jsp为servlet,指定初始化参数,指定ServletClass为org.apache.jasper.servlet.JspServlet
                if (ok) {
                    convertJsps(webXml);
                }
    
                // Step 9. Apply merged web.xml to Context  将web.xml配置的数据设置到context中
                if (ok) {
                    configureContext(webXml);
                }
            } else {
                webXml.merge(defaults);
                convertJsps(webXml);
                configureContext(webXml);
            }
    
            if (context.getLogEffectiveWebXml()) {
                log.info("web.xml:
    " + webXml.toXml());
            }
    
            // Always need to look for static resources
            // Step 10. Look for static resources packaged in JARs 从jar中中查找静态资源
            if (ok) {
                // Spec does not define an order.
                // Use ordered JARs followed by remaining JARs
                Set<WebXml> resourceJars = new LinkedHashSet<>();
                for (WebXml fragment : orderedFragments) {
                    resourceJars.add(fragment);
                }
                for (WebXml fragment : fragments.values()) {
                    if (!resourceJars.contains(fragment)) {
                        resourceJars.add(fragment);
                    }
                }
                processResourceJARs(resourceJars);
                // See also StandardContext.resourcesStart() for
                // WEB-INF/classes/META-INF/resources configuration
            }
    
            // Step 11. Apply the ServletContainerInitializer config to the
            // context 应用ServletContainerInitializer  Initializer --》要处理的类型(可能是注解标识的,也可能就是指定的类型)
            if (ok) {
                for (Map.Entry<ServletContainerInitializer,
                        Set<Class<?>>> entry :
                            initializerClassMap.entrySet()) {
                    if (entry.getValue().isEmpty()) {
                        context.addServletContainerInitializer(
                                entry.getKey(), null);
                    } else {
                        context.addServletContainerInitializer(
                                entry.getKey(), entry.getValue());
                    }
                }
            }
        }
    
    
    ContextConfig.getDefaultWebXmlFragment
    
    private WebXml getDefaultWebXmlFragment(WebXmlParser webXmlParser) {
    
            // Host should never be null
            Host host = (Host) context.getParent();
    
            DefaultWebXmlCacheEntry entry = hostWebXmlCache.get(host);
    //获取全局webxml配置文件,config/web.xml
            InputSource globalWebXml = getGlobalWebXmlSource();
    //获取host级别的xml配置文件,文件名为web.xml.default
            InputSource hostWebXml = getHostWebXmlSource();
    
            long globalTimeStamp = 0;
            long hostTimeStamp = 0;
    
            if (globalWebXml != null) {
                URLConnection uc = null;
                try {
    //获取systemid对应数据的最后修改时间
                    URL url = new URL(globalWebXml.getSystemId());
                    uc = url.openConnection();
                    globalTimeStamp = uc.getLastModified();
                } catch (IOException e) {
                    globalTimeStamp = -1;
                } finally {
                    if (uc != null) {
                        try {
                            uc.getInputStream().close();
                        } catch (IOException e) {
                            ExceptionUtils.handleThrowable(e);
                            globalTimeStamp = -1;
                        }
                    }
                }
            }
    
            if (hostWebXml != null) {
                URLConnection uc = null;
                try {
    //获取systemid对应数据的最后修改时间
                    URL url = new URL(hostWebXml.getSystemId());
                    uc = url.openConnection();
                    hostTimeStamp = uc.getLastModified();
                } catch (IOException e) {
                    hostTimeStamp = -1;
                } finally {
                    if (uc != null) {
                        try {
                            uc.getInputStream().close();
                        } catch (IOException e) {
                            ExceptionUtils.handleThrowable(e);
                            hostTimeStamp = -1;
                        }
                    }
                }
            }
    //如果已经解析过了,那么关闭资源,直接返回已经解析好的WebXml
            if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
                    entry.getHostTimeStamp() == hostTimeStamp) {
                InputSourceUtil.close(globalWebXml);
                InputSourceUtil.close(hostWebXml);
                return entry.getWebXml();
            }
    
            // Parsing global web.xml is relatively expensive. Use a sync block to
            // make sure it only happens once. Use the pipeline since a lock will
            // already be held on the host by another thread
            synchronized (host.getPipeline()) {
                entry = hostWebXmlCache.get(host);
                if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp &&
                        entry.getHostTimeStamp() == hostTimeStamp) {
                    return entry.getWebXml();
                }
    //直接new出一个WebXml对象
                WebXml webXmlDefaultFragment = createWebXml();
                webXmlDefaultFragment.setOverridable(true);
                // Set to distributable else every app will be prevented from being
                // distributable when the default fragment is merged with the main
                // web.xml
                webXmlDefaultFragment.setDistributable(true);
                // When merging, the default welcome files are only used if the app has
                // not defined any welcomes files.
                webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);
    
                // Parse global web.xml if present
                if (globalWebXml == null) {
                    // This is unusual enough to log
                    log.info(sm.getString("contextConfig.defaultMissing"));
                } else {
    //解析全局web配置
                    if (!webXmlParser.parseWebXml(
                            globalWebXml, webXmlDefaultFragment, false)) {
                        ok = false;
                    }
                }
    
                // Parse host level web.xml if present
                // Additive apart from welcome pages
                webXmlDefaultFragment.setReplaceWelcomeFiles(true);
    //解析host级别的webxml,会覆盖掉全局的相同属性值的内容
                if (!webXmlParser.parseWebXml(
                        hostWebXml, webXmlDefaultFragment, false)) {
                    ok = false;
                }
    
                // Don't update the cache if an error occurs
                if (globalTimeStamp != -1 && hostTimeStamp != -1) {
                    entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
                            globalTimeStamp, hostTimeStamp);
                    hostWebXmlCache.put(host, entry);
                }
    
                return webXmlDefaultFragment;
            }
        }
    
    WebXmlParser.parseWebXml(InputSource source, WebXml dest,
                boolean fragment)
    
    public boolean parseWebXml(InputSource source, WebXml dest,
                boolean fragment) {
    
            boolean ok = true;
    
            if (source == null) {
                return ok;
            }
    
            XmlErrorHandler handler = new XmlErrorHandler();
    
            Digester digester;
            WebRuleSet ruleSet;
            if (fragment) {//如果是web.xml配置片段,那么就使用片段Digester
                digester = webFragmentDigester;
                ruleSet = webFragmentRuleSet;
            } else {
                digester = webDigester;
                ruleSet = webRuleSet;
            }
    
            digester.push(dest);
            digester.setErrorHandler(handler);
            try {
                digester.parse(source);
    //如果解析出了错误,那么设置ok为false,意味着context启动失败
                if (handler.getWarnings().size() > 0 ||
                        handler.getErrors().size() > 0) {
                    ok = false;
                    handler.logFindings(log, source.getSystemId());
                }
            } catch (SAXParseException e) {
                log.error(sm.getString("webXmlParser.applicationParse",
                        source.getSystemId()), e);
                log.error(sm.getString("webXmlParser.applicationPosition",
                                 "" + e.getLineNumber(),
                                 "" + e.getColumnNumber()));
                ok = false;
            } catch (Exception e) {
                log.error(sm.getString("webXmlParser.applicationParse",
                        source.getSystemId()), e);
                ok = false;
            } finally {
                InputSourceUtil.close(source);
                digester.reset();
                ruleSet.recycle();
            }
    
            return ok;
        }
    
    
    Digester
    
    public WebXmlParser(boolean namespaceAware, boolean validation,
                boolean blockExternal) {
            webRuleSet = new WebRuleSet(false);
            webDigester = DigesterFactory.newDigester(validation,
                    namespaceAware, webRuleSet, blockExternal);
            webDigester.getParser();
    
            webFragmentRuleSet = new WebRuleSet(true);
            webFragmentDigester = DigesterFactory.newDigester(validation,
                    namespaceAware, webFragmentRuleSet, blockExternal);
            webFragmentDigester.getParser();
        }
    
    
    public WebRuleSet(String prefix, boolean fragment) {
            super();
            this.prefix = prefix;
            this.fragment = fragment;
    
            if(fragment) {
                fullPrefix = prefix + "web-fragment";
            } else {
                fullPrefix = prefix + "web-app";
            }
    
            absoluteOrdering = new AbsoluteOrderingRule(fragment);
            relativeOrdering = new RelativeOrderingRule(fragment);
        }
    
    
    private void configureContext(WebXml webxml) {
            // As far as possible, process in alphabetical order so it is easy to
            // check everything is present
            // Some validation depends on correct public ID
            context.setPublicId(webxml.getPublicId());
    
            // Everything else in order
            context.setEffectiveMajorVersion(webxml.getMajorVersion());
            context.setEffectiveMinorVersion(webxml.getMinorVersion());
    //将上下文参数添加到context中
            for (Entry<String, String> entry : webxml.getContextParams().entrySet()) {
                context.addParameter(entry.getKey(), entry.getValue());
            }
            context.setDenyUncoveredHttpMethods(
                    webxml.getDenyUncoveredHttpMethods());
            context.setDisplayName(webxml.getDisplayName());
            context.setDistributable(webxml.isDistributable());
    //EJB相关的引用资源,反正我不懂
            for (ContextLocalEjb ejbLocalRef : webxml.getEjbLocalRefs().values()) {
                context.getNamingResources().addLocalEjb(ejbLocalRef);
            }
            for (ContextEjb ejbRef : webxml.getEjbRefs().values()) {
                context.getNamingResources().addEjb(ejbRef);
            }
    //
            for (ContextEnvironment environment : webxml.getEnvEntries().values()) {
                context.getNamingResources().addEnvironment(environment);
            }
    //添加错误页面
            for (ErrorPage errorPage : webxml.getErrorPages().values()) {
                context.addErrorPage(errorPage);
            }
    //添加过滤器,filterName->filterDef
            for (FilterDef filter : webxml.getFilters().values()) {
                if (filter.getAsyncSupported() == null) {
                    filter.setAsyncSupported("false");
                }
                context.addFilterDef(filter);
            }
    //FilterMap urlPattern与Servlet的关联
            for (FilterMap filterMap : webxml.getFilterMappings()) {
                context.addFilterMap(filterMap);
            }
            context.setJspConfigDescriptor(webxml.getJspConfigDescriptor());
    //添加监听器到context的数组中
            for (String listener : webxml.getListeners()) {
                context.addApplicationListener(listener);
            }
    //添加locale-》字符集
            for (Entry<String, String> entry :
                    webxml.getLocaleEncodingMappings().entrySet()) {
                context.addLocaleEncodingMappingParameter(entry.getKey(),
                        entry.getValue());
            }
            // Prevents IAE  设置登录配置
            if (webxml.getLoginConfig() != null) {
                context.setLoginConfig(webxml.getLoginConfig());
            }
    //消息目标引用
            for (MessageDestinationRef mdr :
                    webxml.getMessageDestinationRefs().values()) {
                context.getNamingResources().addMessageDestinationRef(mdr);
            }
    
            // messageDestinations were ignored in Tomcat 6, so ignore here
    
            context.setIgnoreAnnotations(webxml.isMetadataComplete());
    //设置媒体类型
            for (Entry<String, String> entry :
                    webxml.getMimeMappings().entrySet()) {
                context.addMimeMapping(entry.getKey(), entry.getValue());
            }
            // Name is just used for ordering 添加JNDI引用
            for (ContextResourceEnvRef resource :
                    webxml.getResourceEnvRefs().values()) {
                context.getNamingResources().addResourceEnvRef(resource);
            }
    //添加JNDI
            for (ContextResource resource : webxml.getResourceRefs().values()) {
                context.getNamingResources().addResource(resource);
            }
    //设置权限
            boolean allAuthenticatedUsersIsAppRole =
                    webxml.getSecurityRoles().contains(
                            SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS);
            for (SecurityConstraint constraint : webxml.getSecurityConstraints()) {
                if (allAuthenticatedUsersIsAppRole) {
                    constraint.treatAllAuthenticatedUsersAsApplicationRole();
                }
                context.addConstraint(constraint);
            }
    //添加角色
            for (String role : webxml.getSecurityRoles()) {
                context.addSecurityRole(role);
            }
    //添加服务引用
            for (ContextService service : webxml.getServiceRefs().values()) {
                context.getNamingResources().addService(service);
            }
    //添加ServletDef,包装成StandardWrapper容器
            for (ServletDef servlet : webxml.getServlets().values()) {
                Wrapper wrapper = context.createWrapper();
                // Description is ignored
                // Display name is ignored
                // Icons are ignored
    
                // jsp-file gets passed to the JSP Servlet as an init-param
    //设置启动顺序
                if (servlet.getLoadOnStartup() != null) {
                    wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
                }
                if (servlet.getEnabled() != null) {
                    wrapper.setEnabled(servlet.getEnabled().booleanValue());
                }
                wrapper.setName(servlet.getServletName());
                Map<String,String> params = servlet.getParameterMap();
                for (Entry<String, String> entry : params.entrySet()) {
                    wrapper.addInitParameter(entry.getKey(), entry.getValue());
                }
                wrapper.setRunAs(servlet.getRunAs());
                Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
                for (SecurityRoleRef roleRef : roleRefs) {
                    wrapper.addSecurityReference(
                            roleRef.getName(), roleRef.getLink());
                }
                wrapper.setServletClass(servlet.getServletClass());
                MultipartDef multipartdef = servlet.getMultipartDef();
    //设置上传文件的大小
                if (multipartdef != null) {
                    if (multipartdef.getMaxFileSize() != null &&
                            multipartdef.getMaxRequestSize()!= null &&
                            multipartdef.getFileSizeThreshold() != null) {
                        wrapper.setMultipartConfigElement(new MultipartConfigElement(
                                multipartdef.getLocation(),
                                Long.parseLong(multipartdef.getMaxFileSize()),
                                Long.parseLong(multipartdef.getMaxRequestSize()),
                                Integer.parseInt(
                                        multipartdef.getFileSizeThreshold())));
                    } else {
                        wrapper.setMultipartConfigElement(new MultipartConfigElement(
                                multipartdef.getLocation()));
                    }
                }
    //是否异步支持
                if (servlet.getAsyncSupported() != null) {
                    wrapper.setAsyncSupported(
                            servlet.getAsyncSupported().booleanValue());
                }
                wrapper.setOverridable(servlet.isOverridable());
    //添加子容器,初始化和启动子容器
                context.addChild(wrapper);
            }
    //添加ServletMapping urlPattern->servletName
            for (Entry<String, String> entry :
                    webxml.getServletMappings().entrySet()) {
                context.addServletMappingDecoded(entry.getKey(), entry.getValue());
            }
    //获取session配置
            SessionConfig sessionConfig = webxml.getSessionConfig();
            if (sessionConfig != null) {
                if (sessionConfig.getSessionTimeout() != null) {
                    context.setSessionTimeout(
                            sessionConfig.getSessionTimeout().intValue());
                }
                SessionCookieConfig scc =
                    context.getServletContext().getSessionCookieConfig();
                scc.setName(sessionConfig.getCookieName());
                scc.setDomain(sessionConfig.getCookieDomain());
                scc.setPath(sessionConfig.getCookiePath());
                scc.setComment(sessionConfig.getCookieComment());
                if (sessionConfig.getCookieHttpOnly() != null) {
                    scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue());
                }
                if (sessionConfig.getCookieSecure() != null) {
                    scc.setSecure(sessionConfig.getCookieSecure().booleanValue());
                }
                if (sessionConfig.getCookieMaxAge() != null) {
                    scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue());
                }
                if (sessionConfig.getSessionTrackingModes().size() > 0) {
                    context.getServletContext().setSessionTrackingModes(
                            sessionConfig.getSessionTrackingModes());
                }
            }
    
            // Context doesn't use version directly
    //添加欢迎配置
            for (String welcomeFile : webxml.getWelcomeFiles()) {
                /*
                 * The following will result in a welcome file of "" so don't add
                 * that to the context
                 * <welcome-file-list>
                 *   <welcome-file/>
                 * </welcome-file-list>
                 */
                if (welcomeFile != null && welcomeFile.length() > 0) {
                    context.addWelcomeFile(welcomeFile);
                }
            }
    
            // Do this last as it depends on servlets
            for (JspPropertyGroup jspPropertyGroup :
                    webxml.getJspPropertyGroups()) {
                String jspServletName = context.findServletMapping("*.jsp");
                if (jspServletName == null) {
                    jspServletName = "jsp";
                }
                if (context.findChild(jspServletName) != null) {
                    for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
                        context.addServletMappingDecoded(urlPattern, jspServletName, true);
                    }
                } else {
                    if(log.isDebugEnabled()) {
                        for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
                            log.debug("Skipping " + urlPattern + " , no servlet " +
                                    jspServletName);
                        }
                    }
                }
            }
    //添加初始方法
            for (Entry<String, String> entry :
                    webxml.getPostConstructMethods().entrySet()) {
                context.addPostConstructMethod(entry.getKey(), entry.getValue());
            }
    //添加销毁方法
            for (Entry<String, String> entry :
                webxml.getPreDestroyMethods().entrySet()) {
                context.addPreDestroyMethod(entry.getKey(), entry.getValue());
            }
        }
  • 相关阅读:
    [转]utf8编码原理详解
    [转]程序员趣味读物:谈谈Unicode编码
    confluence 安装部署
    统计日志10分钟内出现的次数
    Error occurred during initialization of VM Could not reserve enough space for object heap(可能是内核参数的问题)
    Linux服务器性能查看分析调优
    python break ,continue和 pass 语句(八)
    python 循环基础(六)
    python 循环实例(七)
    python 条件控制(五)
  • 原文地址:https://www.cnblogs.com/honger/p/10362774.html
Copyright © 2020-2023  润新知