Tomcat源码分析(九) -- Host

319 阅读2分钟

Tomcat 中的 Host

Host 定义

public interface Host extends Container {

    public static final String ADD_ALIAS_EVENT = "addAlias";

    public static final String REMOVE_ALIAS_EVENT = "removeAlias";

    public String getXmlBase();

    public void setXmlBase(String xmlBase);

    public File getConfigBaseFile();

    public String getAppBase();

    public File getAppBaseFile();

    public void setAppBase(String appBase);

    public boolean getAutoDeploy();

    public void setAutoDeploy(boolean autoDeploy);

    public String getConfigClass();

    public void setConfigClass(String configClass);

    public boolean getDeployOnStartup();

    public void setDeployOnStartup(boolean deployOnStartup);

    public String getDeployIgnore();

    public Pattern getDeployIgnorePattern();

    public void setDeployIgnore(String deployIgnore);

    public ExecutorService getStartStopExecutor();

    public boolean getCreateDirs();

    public void setCreateDirs(boolean createDirs);

    public boolean getUndeployOldVersions();

    public void setUndeployOldVersions(boolean undeployOldVersions);

    public void addAlias(String alias);

    public String[] findAliases();

    public void removeAlias(String alias);
}

从定义中可以看出

  • Host 继承了 Container 接口,而 Container 接口继承了 Lifecycle 接口
  • Host 有部署服务功能
  • Host 的实现类是 StandardHost

StandardHost 的构造方法

public StandardHost() {
    super();
    //设置基础阀 StandardHostValve
    pipeline.setBasic(new StandardHostValve());
}

StandardHost 的 startInternal 方法

protected synchronized void startInternal() throws LifecycleException {

    // Set error report valve
    //在 pipeline 中添加 org.apache.catalina.valves.ErrorReportValve
    String errorValve = getErrorReportValveClass();
    if ((errorValve != null) && (!errorValve.equals(""))) {
        try {
            boolean found = false;
            Valve[] valves = getPipeline().getValves();
            for (Valve valve : valves) {
                if (errorValve.equals(valve.getClass().getName())) {
                    found = true;
                    break;
                }
            }
            if(!found) {
                Valve valve =
                    (Valve) Class.forName(errorValve).getConstructor().newInstance();
                getPipeline().addValve(valve);
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error(sm.getString(
                "standardHost.invalidErrorReportValveClass",
                errorValve), t);
        }
    }
    //直接调用父类的方法,这里需要注意在父类的 startInternal 方法中会加载Host的子容器 Context
    super.startInternal();
}

StandardHost 的基础阀 -- StandardHostValve

public final void invoke(Request request, Response response)
        throws IOException, ServletException {

    // Select the Context to be used for this Request
    //从 request 中获取 Context 对象
    Context context = request.getContext();
    if (context == null) {
        return;
    }

    if (request.isAsyncSupported()) {
        request.setAsyncSupported(context.getPipeline().isAsyncSupported());
    }

    boolean asyncAtStart = request.isAsync();

    try {
        context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);

        if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) {
            // Don't fire listeners during async processing (the listener
            // fired for the request that called startAsync()).
            // If a request init listener throws an exception, the request
            // is aborted.
            return;
        }

        // Ask this Context to process this request. Requests that are
        // already in error must have been routed here to check for
        // application defined error pages so DO NOT forward them to the the
        // application for processing.
        try {
            if (!response.isErrorReportRequired()) {
                //调用 Context 的pipeline
                context.getPipeline().getFirst().invoke(request, response);
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            container.getLogger().error("Exception Processing " + request.getRequestURI(), t);
            // If a new error occurred while trying to report a previous
            // error allow the original error to be reported.
            if (!response.isErrorReportRequired()) {
                request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
                throwable(request, response, t);
            }
        }

        // Now that the request/response pair is back under container
        // control lift the suspension so that the error handling can
        // complete and/or the container can flush any remaining data
        response.setSuspended(false);

        Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);

        // Protect against NPEs if the context was destroyed during a
        // long running request.
        if (!context.getState().isAvailable()) {
            return;
        }

        // Look for (and render if found) an application level error page
        if (response.isErrorReportRequired()) {
            // If an error has occurred that prevents further I/O, don't waste time
            // producing an error report that will never be read
            AtomicBoolean result = new AtomicBoolean(false);
            response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result);
            if (result.get()) {
                if (t != null) {
                    throwable(request, response, t);
                } else {
                    status(request, response);
                }
            }
        }

        if (!request.isAsync() && !asyncAtStart) {
            context.fireRequestDestroyEvent(request.getRequest());
        }
    } finally {
        // Access a session (if present) to update last accessed time, based
        // on a strict interpretation of the specification
        if (ACCESS_SESSION) {
            request.getSession(false);
        }

        context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
    }
}
  • StandardHostValve 中只需要关心调用了 Context 的pipeline

小结

  • Host 中有部署的功能,部署先跳过,这里关心对子容器 Context 的操作