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

    本文接下来会介绍Host容器和Engine容器,在tomcat的实际部署中,总是会使用一个Host容器;本文介绍Host接口和Engine接口及其相关类

    Host容器是org.apache.catalina.Host接口的实例,Host接口继承自Container接口, 其定义如下

    public interface Host extends Container {
       
        public static final String ADD_ALIAS_EVENT = "addAlias";
        
        public static final String REMOVE_ALIAS_EVENT = "removeAlias";
       
        public String getAppBase();
       
        public void setAppBase(String appBase);
       
        public boolean getAutoDeploy();
       
        public void setAutoDeploy(boolean autoDeploy);
        
        public void addDefaultContext(DefaultContext defaultContext);
       
        public DefaultContext getDefaultContext();
        
        public String getName();
        
        public void setName(String name);
       
        public void importDefaultContext(Context context);
        
        public void addAlias(String alias);
        
        public String[] findAliases();
        
        public Context map(String uri);
       
        public void removeAlias(String alias);
    
    }

    该接口中比较重要的方法是map()方法,该方法返回一个用来处理引入的HTTP请求的Context容器的实例,该方法的具体实现在StandardHost类中

    在tomcat中org.apache.catalina.core.StandardHost类是Host接口的标准实现,该类继承自org.apache.catalina.core.ContainerBase类,实现了Host和Deployer接口

    与StandardContext类和StandardWrapper类相似,StandardHost类的构造函数会将一个基础阀的实例添加到其管道对象中:

    public StandardHost() {
    
            super();
            pipeline.setBasic(new StandardHostValve());
    
        }

    基础阀是org.apache.catalina.core.StandardHostValve类的实例

    当调用其start()方法时,StandardHost实例会添加两个阀,分别为ErrorReportValve类和ErrorDispatcherValve类的实例,这两个阀均位于org.apache.catalina.valves包下

    public synchronized void start() throws LifecycleException {
            // Set error report valve
            if ((errorReportValveClass != null)
                && (!errorReportValveClass.equals(""))) {
                try {
                    Valve valve = (Valve) Class.forName(errorReportValveClass)
                        .newInstance();
                    addValve(valve);
                } catch (Throwable t) {
                    log(sm.getString
                        ("standardHost.invalidErrorReportValveClass",
                         errorReportValveClass));
                }
            }
    
            // Set dispatcher valve
            addValve(new ErrorDispatcherValve());
    
            super.start();
    
        }

    每当引入一个HTTP请求,都会调用Host实例的invoke()方法,这里是StandardHost的父类ContainerBase类的invoke()方法,而ContainerBase类的invoke()方法会调用StandardHost实例的基础阀StandardHostValve实例的invoke()方法;StandardHostValve类的invoke()方法会调用StandardHost类的map()方法来获取相应的Context实例来处理HTTP请求

    public Context map(String uri) {
    
            if (debug > 0)
                log("Mapping request URI '" + uri + "'");
            if (uri == null)
                return (null);
    
            // Match on the longest possible context path prefix
            if (debug > 1)
                log("  Trying the longest context path prefix");
            Context context = null;
            String mapuri = uri;
            while (true) {
                context = (Context) findChild(mapuri);
                if (context != null)
                    break;
                int slash = mapuri.lastIndexOf('/');
                if (slash < 0)
                    break;
                mapuri = mapuri.substring(0, slash);
            }
    
            // If no Context matches, select the default Context
            if (context == null) {
                if (debug > 1)
                    log("  Trying the default context");
                context = (Context) findChild("");
            }
    
            // Complain if no Context has been selected
            if (context == null) {
                log(sm.getString("standardHost.mappingError", uri));
                return (null);
            }
    
            // Return the mapped Context (if any)
            if (debug > 0)
                log(" Mapped to context '" + context.getPath() + "'");
            return (context);
    
        }

    在tomcat4中, StandardHost的父类ContainerBase类会调用其addDefaultMapper()方法创建一个默认映射器,默认映射器的类型由mapperClass属性的值决定

    protected void addDefaultMapper(String mapperClass) {
    
            // Do we need a default Mapper?
            if (mapperClass == null)
                return;
            if (mappers.size() >= 1)
                return;
    
            // Instantiate and add a default Mapper
            try {
                Class clazz = Class.forName(mapperClass);
                Mapper mapper = (Mapper) clazz.newInstance();
                mapper.setProtocol("http");
                addMapper(mapper);
            } catch (Exception e) {
                log(sm.getString("containerBase.addDefaultMapper", mapperClass),
                    e);
            }
    
        }

    变量mapperClass的值定义在StandardHst类中

    private String mapperClass =
            "org.apache.catalina.core.StandardHostMapper";

    在tomcat4中,StandardHost类的start()方法会在方法末尾调用addDefaultMapper()方法,确保默认映射器的创建完成

    当然,StandardHostMapper类中最重要的方法是map()方法,下面是map()方法的实现

    public Container map(Request request, boolean update) {
            // Has this request already been mapped?
            if (update && (request.getContext() != null))
                return (request.getContext());
    
            // Perform mapping on our request URI
            String uri = ((HttpRequest) request).getDecodedRequestURI();
            Context context = host.map(uri);
    
            // Update the request (if requested) and return the selected Context
            if (update) {
                request.setContext(context);
                if (context != null)
                    ((HttpRequest) request).setContextPath(context.getPath());
                else
                    ((HttpRequest) request).setContextPath(null);
            }
            return (context);
    
        }

    注意,这里map()方法仅仅是简单地调用了Host实例的map()方法

    org.apache.catalina.core.StandardHostValve类是StandardHost实例的基础阀,当有引入的HTTP请求时,会调用StandardHostValve类的invoke()方法对其进行处理

    public void invoke(Request request, Response response,
                           ValveContext valveContext)
            throws IOException, ServletException {
    
            // Validate the request and response object types
            if (!(request.getRequest() instanceof HttpServletRequest) ||
                !(response.getResponse() instanceof HttpServletResponse)) {
                return;     // NOTE - Not much else we can do generically
            }
    
            // Select the Context to be used for this Request
            StandardHost host = (StandardHost) getContainer();
            Context context = (Context) host.map(request, true);
            if (context == null) {
                ((HttpServletResponse) response.getResponse()).sendError
                    (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                     sm.getString("standardHost.noContext"));
                return;
            }
    
            // Bind the context CL to the current thread
            Thread.currentThread().setContextClassLoader
                (context.getLoader().getClassLoader());
    
            // Update the session last access time for our session (if any)
            HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
            String sessionId = hreq.getRequestedSessionId();
            if (sessionId != null) {
                Manager manager = context.getManager();
                if (manager != null) {
                    Session session = manager.findSession(sessionId);
                    if ((session != null) && session.isValid())
                        session.access();
                }
            }
    
            // Ask this Context to process this request
            context.invoke(request, response);
    
        }

    在tomcat4中,invoke()方法会调用StandardHost实例的map()方法获取一个相应的Context实例;然后获取与该request对象相关联的session对象,并调用其access()方法,access()方法会修改session对象的最后访问时间;最后调用Context实例的invoke()来处理HTTP请求

    接下来描述Engine容器,Engine容器是org.apache.catalina.Engine接口的实例,Engine容器也就是Tomcat的servlet引擎

    public interface Engine extends Container {
        
        public String getDefaultHost();
       
        public void setDefaultHost(String defaultHost);
        
        public String getJvmRoute();
      
        public void setJvmRoute(String jvmRouteId);
        
        public Service getService();
       
        public void setService(Service service);
       
        public void addDefaultContext(DefaultContext defaultContext);
        
        public DefaultContext getDefaultContext();
        
        public void importDefaultContext(Context context);
    
    }

    在Engine容器中,可以设置一个默认的Host容器或一个默认的Context容器,注意,Engine容器可以与一个服务实例相关联

    org.apache.catalina.core.StandardEngine类是Engine接口的标准实现,在实例化的时候,StandardEngine类会添加一个基础阀

    public StandardEngine() {
    
            super();
            pipeline.setBasic(new StandardEngineValve());
    
        }

    org.apache.catalina.core.StandardEngineValve类是StandardEngine容器的基础阀,下面是它的invoke()方法的实现代码

    public void invoke(Request request, Response response,
                           ValveContext valveContext)
            throws IOException, ServletException {
            // Validate the request and response object types
            if (!(request.getRequest() instanceof HttpServletRequest) ||
                !(response.getResponse() instanceof HttpServletResponse)) {
                return;     // NOTE - Not much else we can do generically
            }
    
            // Validate that any HTTP/1.1 request included a host header
            HttpServletRequest hrequest = (HttpServletRequest) request;
            if ("HTTP/1.1".equals(hrequest.getProtocol()) &&
                (hrequest.getServerName() == null)) {
                ((HttpServletResponse) response.getResponse()).sendError
                    (HttpServletResponse.SC_BAD_REQUEST,
                     sm.getString("standardEngine.noHostHeader",
                                  request.getRequest().getServerName()));
                return;
            }
    
            // Select the Host to be used for this Request
            StandardEngine engine = (StandardEngine) getContainer();
            Host host = (Host) engine.map(request, true);
            if (host == null) {
                ((HttpServletResponse) response.getResponse()).sendError
                    (HttpServletResponse.SC_BAD_REQUEST,
                     sm.getString("standardEngine.noHost",
                                  request.getRequest().getServerName()));
                return;
            }
    
            // Ask this Host to process this request
            host.invoke(request, response);
    
        }

    在验证了request和response对象的类型后,invoke()方法会通过调用Engine实例的map()方法获取Host对象;得到Host对象以后,调用其invoke()方法处理请求

    --------------------------------------------------------------------------- 

    本系列How Tomcat Works系本人原创 

    转载请注明出处 博客园 刺猬的温驯 

    本人邮箱: chenying998179#163.com (#改为@

    本文链接http://www.cnblogs.com/chenying99/p/3249152.html

  • 相关阅读:
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之用户管理(1)
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之创建Viewport(2)
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之用户管理(2)
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之完成登录功能
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之登录窗口调试
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之创建Viewport(1)
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之创建输出验证码图片的控制器
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之调整首页显示
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之登录窗口
    一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之用户管理(3)
  • 原文地址:https://www.cnblogs.com/chenying99/p/3249152.html
Copyright © 2011-2022 走看看