[Tomcatソース系列]構造解析3)要求処理制御構造


一、要求処理制御構造の基礎はライフタイム構造と類似しており、要求処理も二層の構造である.Valve:Valveは最小の処理ユニットです.Valveの定義を見てみましょう.
A Valve is a request processing component associated with a particular Container. A series of Valves are generally associated with each other into a Pipeline.
次はValveのインタフェース定義です
public interface Valve {
public String getInfo();
public Valve getNext();
public void setNext(Valve valve);
public void backgroundProcess();
public void invoke(Request request, Response response)
throws IOException, ServletException;
public void event(Request request, Response response, CometEvent event)
throws IOException, ServletException;
 
その中で私たちが最も注目しなければならないのはinvoke方法で、処理方法を要求して、tomcatのこの方法に対する注釈を見てみましょう(Valveを拡張するならば、よく読む必要があります)
Perform request processing as required by this Valve.An individual Valve MAY perform the following actions, in the specified order:
  • Examine and/or modify the properties of the specified Request and Response.
  • Examine the properties of the specified Request, completely generate the corresponding Response, and return control to the caller.
  • Examine the properties of the specified Request and Response, wrap either or both of these objects to supplement their functionality, and pass them on.
  • If the corresponding Response was not generated (and control was not returned, call the next Valve in the pipeline (if there is one) by executing context.invokeNext(). Examine, but not modify, the properties of the resulting Response (which was created by a subsequently invoked Valve or Container).

  • A Valve MUST NOT do any of the following things:
  • Change request properties that have already been used to direct the flow of processing control for this request (for instance,trying to change the virtual host to which a Request should be sent from a pipeline attached to a Host or Context in the standard implementation).
  • Create a completed Response AND pass this Request and Response on to the next Valve in the pipeline.
  • Consume bytes from the input stream associated with the Request,unless it is completely generating the response, or wrapping the request before passing it on.
  • Modify the HTTP headers included with the Response after the invokeNext() method has returned.
  • Perform any actions on the output stream associated with the specified Response after the invokeNext()method has returned.

  • 2.Pipeline:上記の通り、「A series of Valves are generally associated with each other into a Pipeline」について、TomcatのPipelineインタフェースの説明
    と書く
    Interface describing a collection of Valves that should be executed in sequence when the invoke() method is invoked. It is required that a Valve somewhere in the pipeline (usually the last one) must process the request and create the corresponding response, rather than trying to pass the request on.
    Pipeline(パイプライン)というよりも、ここでは処理チェーンと呼ぶのが適切なようで、中間の各処理ノード(Valve)が一部処理を行い、次の処理ノードが処理を継続しますが、通常、最後の処理ノードは要求を処理し、応答を作成する必要があります.Pipelineインタフェースの定義は次のとおりです.
    public interface Pipeline {
        public Valve getBasic();
        public void setBasic(Valve valve);
        public void addValve(Valve valve);
        public Valve[] getValves();
        public void removeValve(Valve valve);
        public Valve getFirst();
    }

     
    二、要求処理過程の解析1.処理プロセスプレビューTomcatの主な処理コンポーネントEngine、Host、Context、Wrapperの実装では、Pipelineインタフェースが実装されます(実際にはContainerBaseが実装されています).第1編では,実際に要求に対する処理はAdpaterであり,TomcatにおけるAdapterの実装はCoyoteAdapterであるため,要求処理の入口はCoyoteAdapterのservice法であり,我々の要求処理過程はCoyoteAdapterから始まることを知った.
    1. CoyoteAdapter.service
    --要求処理チェーンを組み立てる
       --StandardEngine. getPipeline().getFirst().invoke(request, response);
           --XxxValve.invoke
           --StandardEngineValve.invoke
    2. StandardEngineValve.invoke
       --Host. getPipeline().getFirst().invoke(request, response);
          --YyyValve.invoke
          --StandardHostValve.invoke
    3. StandardHostValve.invoke
      --Context. getPipeline().getFirst().invoke(request, response);
         --ZzzValve.invoke
         --StandardContextValve.invoke
    4. StandardContextValve.invoke
        --ServletRequestListener. requestInitialized
        --Wrapper. getPipeline().getFirst().invoke(request, response);
              --StandardWrapperValve.invoke
        -- ServletRequestListener. requestDestroyed
    5. StandardWrapperValve.invoke
    --Filter+サーブレットの組み立て
    --要求の処理
     
    2.処理解析1)CoyoteAdapter.サービスは入口で、Tomcatの要求処理プロセス全体を見てみましょう.service
        public void service(org.apache.coyote.Request req, 
        	                org.apache.coyote.Response res)
            throws Exception {
            Request request = (Request) req.getNote(ADAPTER_NOTES);
            Response response = (Response) res.getNote(ADAPTER_NOTES);
    
            if (request == null) {
                 //  request、response  
                 ... 
            }
           
            try {
                // Parse and set Catalina and configuration specific
                // request parameters
                req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
                //             ,      
                if (postParseRequest(req, request, res, response)) {
                    // Calling the container
                    connector.getContainer().getPipeline().getFirst().invoke(request, response); //   Container StandardEngine  
    
                    ... 
    
            } catch (IOException e) {
                ;
            } catch (Throwable t) {
                log.error(sm.getString("coyoteAdapter.service"), t);
            } finally {
                ... 
            }
        }

     
    2)デフォルトのStandardEngineというPipelineにはStandardEngineValveという処理ユニットがあります.他の処理ユニットを処理チェーンに配置することができ、例えばTomcatは以下の処理ユニットを定義する.
    と書く


    StandardEngineValveを見てみましょうinvoke
       public final void invoke(Request request, Response response)
            throws IOException, ServletException {
    
            // Select the Host to be used for this Request
            //       Host  CoyoteAdapter.postParseRequest     ,           
            Host host = request.getHost();
            if (host == null) {
                response.sendError
                    (HttpServletResponse.SC_BAD_REQUEST,
                     sm.getString("standardEngine.noHost",
                                  request.getServerName()));
                return;
            }
    
            // Ask this Host to process this request
            host.getPipeline().getFirst().invoke(request, response);
    
        }
     
    3)同様に,StandardHostというPipelineにはStandardHostValveという処理ユニットがある.他の処理ユニットを処理チェーンに配置することができ、例えばTomcatは以下の処理ユニットを定義する.

    prefix="localhost_access_log."suffix=".txt"pattern="common"resolveHosts="false"/>
    StandardHostValveがどのように要求を処理するかはStandardEngineValveと類似しており、次にStandardContextValveに要求する.invoke 4)同様に,StandardContextというPipelineにはStandardContextValveという処理ユニットがある.他の処理ユニットを処理チェーンに配置することができ、例えばTomcatは以下の処理ユニットを定義する.
    と書く
    prefix="localhost_access_log."suffix=".txt"
    pattern="common"/>
    StandardContextValveがリクエストをどのように処理しているかを見てみましょう
        public 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"))) {
                String requestURI = request.getDecodedRequestURI();
                notFound(requestURI, response);
                return;
            }
    
            // Wait if we are reloading
            while (context.getPaused()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    ;
                }
            }
    
            // Select the Wrapper to be used for this Request
            Wrapper wrapper = request.getWrapper();
            if (wrapper == null) {
                String requestURI = request.getDecodedRequestURI();
                notFound(requestURI, response);
                return;
            }
    
    //ServletRequestListener. requestInitialized
    ... 
    
            wrapper.getPipeline().getFirst().invoke(request, response);
    
    //ServletRequestListener.requestDestroyed
    ... 
           }

    5)同様に、StandardWrapperというPipelineにはStandardWrapperValveという処理ユニットがあり、詳細な処理手順はorgを参照することができる.apache.catalina.core.StandardWrapperValve.invokeコード3.要求処理チェーンは上記のコードに組み込まれており,実際の要求がStandardEngineValveに入ったとき,どのHost,どのContext,どのWrapperが処理プロセスに関与するかが実際に決定されており,このプロセスがどのように処理されているかを見る.Tomcatはorgを使用する.apache.tomcat.util.http.mapper.この要求が特定のHost、Context、Wrapperにどのようにマッピングされるかを管理するために、Mapperが使用される.このプロセスは2つのフェーズに分けられる1)初期化フェーズはEngine,Host,Contextの初期化フェーズでは,サブコンポーネントがaddChildメソッドにより親コンポーネントに追加され,StandardContext.addChildを例に挙げてどう処理するか見てみましょう
       public void addChild(Container child) {
    
            // Global JspServlet
            Wrapper oldJspServlet = null;
    
            if (!(child instanceof Wrapper)) {
                throw new IllegalArgumentException
                    (sm.getString("standardContext.notWrapper"));
            }
    
            Wrapper wrapper = (Wrapper) child;
            boolean isJspServlet = "jsp".equals(child.getName());
    
            // Allow webapp to override JspServlet inherited from global web.xml.
            if (isJspServlet) {
                oldJspServlet = (Wrapper) findChild("jsp");
                if (oldJspServlet != null) {
                    removeChild(oldJspServlet);
                }
            }
    
            String jspFile = wrapper.getJspFile();
            if ((jspFile != null) && !jspFile.startsWith("/")) {
                if (isServlet22()) {
                    if(log.isDebugEnabled())
                        log.debug(sm.getString("standardContext.wrapper.warning",
                                           jspFile));
                    wrapper.setJspFile("/" + jspFile);
                } else {
                    throw new IllegalArgumentException
                        (sm.getString("standardContext.wrapper.error", jspFile));
                }
            }
    
            super.addChild(child);
    
            if (isJspServlet && oldJspServlet != null) {
                /*
                 * The webapp-specific JspServlet inherits all the mappings
                 * specified in the global web.xml, and may add additional ones.
                 */
                String[] jspMappings = oldJspServlet.findMappings();
                for (int i=0; jspMappings!=null && i<jspMappings.length; i++) {
                    addServletMapping(jspMappings[i], child.getName());
                }
            }
        }

    上記の処理では,addServertMappingに焦点を当て,さらにaddServertMappingに入ると,最終的にWrapperオブジェクトをMapperオブジェクトに伝えるコードが以下のようになる.
    public void addServletMapping(String pattern, String name,
                                      boolean jspWildCard) {
            // Validate the proposed mapping
            if (findChild(name) == null)
                throw new IllegalArgumentException
                    (sm.getString("standardContext.servletMap.name", name));
            pattern = adjustURLPattern(RequestUtil.URLDecode(pattern));
            if (!validateURLPattern(pattern))
                throw new IllegalArgumentException
                    (sm.getString("standardContext.servletMap.pattern", pattern));
    
            // Add this mapping to our registered set
            synchronized (servletMappings) {
                String name2 = (String) servletMappings.get(pattern);
                if (name2 != null) {
                    // Don't allow more than one servlet on the same pattern
                    Wrapper wrapper = (Wrapper) findChild(name2);
                    wrapper.removeMapping(pattern);
                    mapper.removeWrapper(pattern);
                }
                servletMappings.put(pattern, name);
            }
            Wrapper wrapper = (Wrapper) findChild(name);
            wrapper.addMapping(pattern);
    
            // Update context mapper
            mapper.addWrapper(pattern, wrapper, jspWildCard);
    
            fireContainerEvent("addServletMapping", pattern);
    
        }

    StandardEngineとStandardHostにも同様の処理があるが、ここではこれ以上繰り返されず、これら2つのクラスのaddChild実装2)要求処理チェーン組立段階は上記のようにCoyoteAdpaterを分析する.サービスの過程でStandardEngineValveに入ることを知っています.invokeの前に,要求処理チェーンを先に用意しておき,実際にはMapperというオブジェクトと上記のような基礎があり,この処理過程はあまり複雑ではない.コード処理はCoyoteAdpaterである.service-->CoyoteAdapter. postParseRequest-->Mapper.mapper、興味があればorgを直接見ることができます.apache.tomcat.util.http.mapper.Mapper.mapperメソッド3は、上記のように処理することによって、最終的に私たちの実際の処理サーブレットに要求される.