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

  • 相关阅读:
    更改EBSserver域名/IP
    iOS Dev (60) 怎样实现 UITextView 中的 placeHolder
    程序猿的量化交易之路(29)--Cointrader之Tick实体(16)
    美团面试中被问到的问题汇总
    汇报措辞:你懂得如何向领导汇报吗(审阅、审批、审阅、批示、查阅)?
    九月份总结
    Android 编程之第三方开发 MaoZhuaWeiBo微博开发演示样例-1
    OLE操作Excel编译错误处理
    在 VS2008 下操作 Excel 的方法总结
    vs2008 ole excel
  • 原文地址:https://www.cnblogs.com/chenying99/p/3249152.html
Copyright © 2011-2022 走看看