tomcat源码:Context容器和Wrapper容器

来源:互联网 发布:集思宝安卓软件 编辑:程序博客网 时间:2024/05/16 19:46

Context容器是一个Web项目的代表,主要管理Servlet实例,在Tomcat中Servlet实例是以Wrapper出现的,现在问题是如何才能通过Context容器找到具体的Servlet呢?在解决这个问题之前,Context容器需要先启动,启动的过程就是加载个类资源文件以及打开子容器以及Pipeline管道的过程。启动Context容器后,就可以处理具体的请求了,具体是通过Request对象,从代码清单4-3的Wrapper wrapper = request.getWrapper()就可以看出来。 
那么Context调用invoke方法后又发生什么了呢?具体执行的是org.apache.catalina.core.StandardContextValve的invoke方法。相当于进入了Context管道中,要开始通过管道中一个个闸门了。 

StandardContext的处理流程可以用下面的图简化:

Context容器处理请求流程简化版

代码清单4-3:

/** * Select the appropriate child Wrapper to process this request, * based on the specified request URI.  If no matching Wrapper can * be found, return an appropriate HTTP error. * * @param request Request to be processed * @param response Response to be produced * * @exception IOException if an input/output error occurred * @exception ServletException if a servlet error occurred */@Overridepublic final void invoke(Request request, Response response)    throws IOException, ServletException {    // 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;    }    // 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;    }    // 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);}
  1. 禁止直接访问WEB-INF或者META-INF目录下的资源
  2. 选择具体的Wrapper处理请求
  3. 返回一个确认响应
  4. 调用Wrapper容器的invoke方法,把处理请求交给StandardWrapperValve处理

Wrapper容器 
Wrapper容器负责管理一个Servlet,包括Servlet的装载、初始化、资源回收。Wrapper是最底层的容器,其不能在添加子容器了。Wrapper是一个接口,其标准实现类是StandardWrapper,下面是这两个类的结构:

Wrapper 
StandardWrapper

上面的图都只是类的一部分,由于Wrapper与Servlet息息相关,其中的loadServlet方法负责装载Servlet,其源码如下:

代码清单4-4:

/** * Load and initialize an instance of this servlet, if there is not already * at least one initialized instance.  This can be used, for example, to * load servlets that are marked in the deployment descriptor to be loaded * at server startup time. */public synchronized Servlet loadServlet() throws ServletException {    if (unloading) {        throw new ServletException(                sm.getString("standardWrapper.unloading", getName()));    }    // Nothing to do if we already have an instance or an instance pool    if (!singleThreadModel && (instance != null))        return instance;    PrintStream out = System.out;    if (swallowOutput) {        SystemLogHandler.startCapture();    }    Servlet servlet;    try {        long t1=System.currentTimeMillis();        // Complain if no servlet class has been specified        if (servletClass == null) {            unavailable(null);            throw new ServletException                (sm.getString("standardWrapper.notClass", getName()));        }        InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();        try {            servlet = (Servlet) instanceManager.newInstance(servletClass);        } catch (ClassCastException e) {            unavailable(null);            // Restore the context ClassLoader            throw new ServletException                (sm.getString("standardWrapper.notServlet", servletClass), e);        } catch (Throwable e) {            e = ExceptionUtils.unwrapInvocationTargetException(e);            ExceptionUtils.handleThrowable(e);            unavailable(null);            // Added extra log statement for Bugzilla 36630:            // http://issues.apache.org/bugzilla/show_bug.cgi?id=36630            if(log.isDebugEnabled()) {                log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);            }            // Restore the context ClassLoader            throw new ServletException                (sm.getString("standardWrapper.instantiate", servletClass), e);        }        if (multipartConfigElement == null) {            MultipartConfig annotation =                    servlet.getClass().getAnnotation(MultipartConfig.class);            if (annotation != null) {                multipartConfigElement =                        new MultipartConfigElement(annotation);            }        }        processServletSecurityAnnotation(servlet.getClass());        // Special handling for ContainerServlet instances        if ((servlet instanceof ContainerServlet) &&                (isContainerProvidedServlet(servletClass) ||                        ((Context) getParent()).getPrivileged() )) {            ((ContainerServlet) servlet).setWrapper(this);        }        classLoadTime=(int) (System.currentTimeMillis() -t1);        if (servlet instanceof SingleThreadModel) {            if (instancePool == null) {                instancePool = new Stack<Servlet>();            }            singleThreadModel = true;        }        //init servlet instance        initServlet(servlet);        fireContainerEvent("load", this);        loadTime=System.currentTimeMillis() -t1;    } finally {        if (swallowOutput) {            String log = SystemLogHandler.stopCapture();            if (log != null && log.length() > 0) {                if (getServletContext() != null) {                    getServletContext().log(log);                } else {                    out.println(log);                }            }        }    }    return servlet;}

该类主要负责初始化一个Servlet实例,并调用该实例的init方法,然后通知感兴趣的事件监听程序。代码清单4-3中调用了Wrapper的invoke方法,这个方法完成什么呢?

代码清单4-5:

/** * Invoke the servlet we are managing, respecting the rules regarding * servlet lifecycle and SingleThreadModel support. * * @param request Request to be processed * @param response Response to be produced * * @exception IOException if an input/output error occurred * @exception ServletException if a servlet error occurred */@Overridepublic final void invoke(Request request, Response response)    throws IOException, ServletException {    // Initialize local variables we may need    boolean unavailable = false;    Throwable throwable = null;    // This should be a Request attribute...    long t1=System.currentTimeMillis();    requestCount++;    StandardWrapper wrapper = (StandardWrapper) getContainer();    Servlet servlet = null;    Context context = (Context) wrapper.getParent();    // Check for the application being marked unavailable    if (!context.getState().isAvailable()) {        response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,                       sm.getString("standardContext.isUnavailable"));        unavailable = true;    }    // Check for the servlet being marked unavailable    if (!unavailable && wrapper.isUnavailable()) {        container.getLogger().info(sm.getString("standardWrapper.isUnavailable",                wrapper.getName()));        long available = wrapper.getAvailable();        if ((available > 0L) && (available < Long.MAX_VALUE)) {            response.setDateHeader("Retry-After", available);            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,                    sm.getString("standardWrapper.isUnavailable",                            wrapper.getName()));        } else if (available == Long.MAX_VALUE) {            response.sendError(HttpServletResponse.SC_NOT_FOUND,                    sm.getString("standardWrapper.notFound",                            wrapper.getName()));        }        unavailable = true;    }    // Allocate a servlet instance to process this request    try {        if (!unavailable) {            servlet = wrapper.allocate();        }    } catch (UnavailableException e) {        container.getLogger().error(                sm.getString("standardWrapper.allocateException",                        wrapper.getName()), e);        long available = wrapper.getAvailable();        if ((available > 0L) && (available < Long.MAX_VALUE)) {            response.setDateHeader("Retry-After", available);            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,                       sm.getString("standardWrapper.isUnavailable",                                    wrapper.getName()));        } else if (available == Long.MAX_VALUE) {            response.sendError(HttpServletResponse.SC_NOT_FOUND,                       sm.getString("standardWrapper.notFound",                                    wrapper.getName()));        }    } catch (ServletException e) {        container.getLogger().error(sm.getString("standardWrapper.allocateException",                         wrapper.getName()), StandardWrapper.getRootCause(e));        throwable = e;        exception(request, response, e);    } catch (Throwable e) {        ExceptionUtils.handleThrowable(e);        container.getLogger().error(sm.getString("standardWrapper.allocateException",                         wrapper.getName()), e);        throwable = e;        exception(request, response, e);        servlet = null;    }    // Identify if the request is Comet related now that the servlet has been allocated    boolean comet = false;    if (servlet instanceof CometProcessor && request.getAttribute(            Globals.COMET_SUPPORTED_ATTR) == Boolean.TRUE) {        comet = true;        request.setComet(true);    }    MessageBytes requestPathMB = request.getRequestPathMB();    DispatcherType dispatcherType = DispatcherType.REQUEST;    if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;     request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);    request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,            requestPathMB);    // Create the filter chain for this request    ApplicationFilterFactory factory =        ApplicationFilterFactory.getInstance();    ApplicationFilterChain filterChain =        factory.createFilterChain(request, wrapper, servlet);    // Reset comet flag value after creating the filter chain    request.setComet(false);    // Call the filter chain for this request    // NOTE: This also calls the servlet's service() method    try {        if ((servlet != null) && (filterChain != null)) {            // Swallow output if needed            if (context.getSwallowOutput()) {                try {                    SystemLogHandler.startCapture();                    if (request.isAsyncDispatching()) {                        //TODO SERVLET3 - async                        ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();                     } else if (comet) {                        filterChain.doFilterEvent(request.getEvent());                        request.setComet(true);                    } else {                        filterChain.doFilter(request.getRequest(),                                 response.getResponse());                    }                } finally {                    String log = SystemLogHandler.stopCapture();                    if (log != null && log.length() > 0) {                        context.getLogger().info(log);                    }                }            } else {                if (request.isAsyncDispatching()) {                    //TODO SERVLET3 - async                    ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();                } else if (comet) {                    request.setComet(true);                    filterChain.doFilterEvent(request.getEvent());                } else {                    filterChain.doFilter                        (request.getRequest(), response.getResponse());                }            }        }    } catch (ClientAbortException e) {        throwable = e;        exception(request, response, e);    } catch (IOException e) {        container.getLogger().error(sm.getString(                "standardWrapper.serviceException", wrapper.getName(),                context.getName()), e);        throwable = e;        exception(request, response, e);    } catch (UnavailableException e) {        container.getLogger().error(sm.getString(                "standardWrapper.serviceException", wrapper.getName(),                context.getName()), e);        //            throwable = e;        //            exception(request, response, e);        wrapper.unavailable(e);        long available = wrapper.getAvailable();        if ((available > 0L) && (available < Long.MAX_VALUE)) {            response.setDateHeader("Retry-After", available);            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,                       sm.getString("standardWrapper.isUnavailable",                                    wrapper.getName()));        } else if (available == Long.MAX_VALUE) {            response.sendError(HttpServletResponse.SC_NOT_FOUND,                        sm.getString("standardWrapper.notFound",                                    wrapper.getName()));        }        // Do not save exception in 'throwable', because we        // do not want to do exception(request, response, e) processing    } catch (ServletException e) {        Throwable rootCause = StandardWrapper.getRootCause(e);        if (!(rootCause instanceof ClientAbortException)) {            container.getLogger().error(sm.getString(                    "standardWrapper.serviceExceptionRoot",                    wrapper.getName(), context.getName(), e.getMessage()),                    rootCause);        }        throwable = e;        exception(request, response, e);    } catch (Throwable e) {        ExceptionUtils.handleThrowable(e);        container.getLogger().error(sm.getString(                "standardWrapper.serviceException", wrapper.getName(),                context.getName()), e);        throwable = e;        exception(request, response, e);    }    // Release the filter chain (if any) for this request    if (filterChain != null) {        if (request.isComet()) {            // If this is a Comet request, then the same chain will be used for the            // processing of all subsequent events.            filterChain.reuse();        } else {            filterChain.release();        }    }    // Deallocate the allocated servlet instance    try {        if (servlet != null) {            wrapper.deallocate(servlet);        }    } catch (Throwable e) {        ExceptionUtils.handleThrowable(e);        container.getLogger().error(sm.getString("standardWrapper.deallocateException",                         wrapper.getName()), e);        if (throwable == null) {            throwable = e;            exception(request, response, e);        }    }    // If this servlet has been marked permanently unavailable,    // unload it and release this instance    try {        if ((servlet != null) &&            (wrapper.getAvailable() == Long.MAX_VALUE)) {            wrapper.unload();        }    } catch (Throwable e) {        ExceptionUtils.handleThrowable(e);        container.getLogger().error(sm.getString("standardWrapper.unloadException",                         wrapper.getName()), e);        if (throwable == null) {            throwable = e;            exception(request, response, e);        }    }}
  1. 初始化一些本地变量
  2. 判断当前应用是否可用,就是判断是否确实有这个项目
  3. 分配一个Servlet实例
  4. 为请求创建一个过滤器链
  5. 过滤器过滤请求
  6. 关闭过滤器
  7. 重新委派原来委派的Servlet实例
  8. 释放资源

这个方法与上面的loadServlet关系如下:

关系

可以看出在调用loadServlet的allocate方法的时候调用了StandardWrapperValve的invoke方法,在Wrapper容器获得请求后,通过allocate方法从实例池栈中弹出一个servlet实例来处理这个请求,servlet实例被封装成filterChain对象,紧接着通过一系列的过滤器过滤到达servlet.service()方法。这个过程可以如下:

Wrapper容器处理请求过程简化版

0 0