Tomcat要求処理(五)--容器間の流れを要求する


要求がTomcatでCoyoteAdapterのサービス()メソッドに転送されると、#サービス()メソッドの文に示すように、Pipelineに入る準備ができます.
connector.getContainer().getPipeline().getFirst().invoke(request, response);
ここのContainerはEngineで、それからそのPipelineオブジェクトを得て、それから彼の最初のValveを得て、もし配置がなければ最初のValveはBasicで、ここはorgです.apache.catalina.core.StandardEngineValveは、最後にその#invoke()メソッドを呼び出し、ソースコードは以下のようになります.
	public final void invoke(Request request, Response response) throws IOException,
			ServletException {

		//   Host  
		Host host = request.getHost();
		if (host == null) {
			response.sendError(HttpServletResponse.SC_BAD_REQUEST, sm.getString(
					"standardEngine.noHost", request.getServerName()));
			return;
		}

		//       Host
		host.getPipeline().getFirst().invoke(request, response);

	}

このValveは実質的なものではなく、要求を伝え続けるだけで、もちろん自分のValveを定義して特殊な行為を実現することができます.
やはりリクエストの処理を継続するには、上のEngineの場合と同様に、リクエストは今回org.apache.catalina.core.StandardHostValveの#invoke()メソッドです.ソースコードは次のとおりです.
	public final void invoke(Request request, Response response) throws IOException,
			ServletException {

		//   Context  
		Context context = request.getContext();
		if (context == null) {
			response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm
					.getString("standardHost.noContext"));
			return;
		}

		//       Loader,     context   ClassLoader
		if (context.getLoader() != null) {
			Thread.currentThread().setContextClassLoader(context.getLoader().getClassLoader());
		}

		//      Pipeline Context
		context.getPipeline().getFirst().invoke(request, response);

		if (Globals.STRICT_SERVLET_COMPLIANCE) {
			request.getSession(false);
		}

		//      
		response.setSuspended(false);

		Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);

		if (t != null) {
			throwable(request, response, t);
		} else {
			status(request, response);
		}

		//   ClassLoader
		Thread.currentThread().setContextClassLoader(StandardHostValve.class.getClassLoader());

	}

意外なことに、お願いして下に着いたorg.apache.catalina.core.StandardContextValve、ソースコードは次のとおりです.
	public final void invoke(Request request, Response response) throws IOException,
			ServletException {

		//        META-INF WEB-INF       。
		MessageBytes requestPathMB = request.getRequestPathMB();
		if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
				|| (requestPathMB.equalsIgnoreCase("/META-INF"))
				|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
				|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
			notFound(response);
			return;
		}

		//            ,      。
		boolean reloaded = false;
		while (context.getPaused()) {
			reloaded = true;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				;
			}
		}

		//       ,    WebappClassLoader       
		if (reloaded && context.getLoader() != null && context.getLoader().getClassLoader() != null) {
			Thread.currentThread().setContextClassLoader(context.getLoader().getClassLoader());
		}

		//   Wrapper  
		Wrapper wrapper = request.getWrapper();
		if (wrapper == null) {
			notFound(response);
			return;
		} else if (wrapper.isUnavailable()) {
			wrapper = (Wrapper) container.findChild(wrapper.getName());
			if (wrapper == null) {
				notFound(response);
				return;
			}
		}

		//   Listener  
		Object instances[] = context.getApplicationEventListeners();

		ServletRequestEvent event = null;

		if ((instances != null) && (instances.length > 0)) {
			event = new ServletRequestEvent(((StandardContext) container).getServletContext(),
					request.getRequest());

			for (int i = 0; i < instances.length; i++) {
				if (instances[i] == null)
					continue;
				if (!(instances[i] instanceof ServletRequestListener))
					continue;
				ServletRequestListener listener = (ServletRequestListener) instances[i];
				try {
					//          
					listener.requestInitialized(event);
				} catch (Throwable t) {
					container.getLogger().error(
							sm.getString("standardContext.requestListener.requestInit",
									instances[i].getClass().getName()), t);
					ServletRequest sreq = request.getRequest();
					sreq.setAttribute(Globals.EXCEPTION_ATTR, t);
					return;
				}
			}
		}

		//   Wrapper Valve
		wrapper.getPipeline().getFirst().invoke(request, response);

		if ((instances != null) && (instances.length > 0)) {
			for (int i = 0; i < instances.length; i++) {
				if (instances[i] == null)
					continue;
				if (!(instances[i] instanceof ServletRequestListener))
					continue;
				ServletRequestListener listener = (ServletRequestListener) instances[i];
				try {
					//         
					listener.requestDestroyed(event);
				} catch (Throwable t) {
					container.getLogger().error(
							sm.getString("standardContext.requestListener.requestDestroy",
									instances[i].getClass().getName()), t);
					ServletRequest sreq = request.getRequest();
					sreq.setAttribute(Globals.EXCEPTION_ATTR, t);
				}
			}
		}

	}

このValveにはListenerの呼び出しが含まれていることが重要で、最後にWrapperのValve呼び出し(org.apache.catalina.core.StandardWrapperValve)を見てみましょう.
public final void invoke(Request request, Response response) throws IOException,
			ServletException {

		boolean unavailable = false;
		Throwable throwable = null;

		long t1 = System.currentTimeMillis();
		requestCount++;
		//   Wrapper      
		StandardWrapper wrapper = (StandardWrapper) getContainer();
		// Servlet  
		Servlet servlet = null;
		//   Context  
		Context context = (Context) wrapper.getParent();

		if (!context.getAvailable()) {
			response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm
					.getString("standardContext.isUnavailable"));
			unavailable = true;
		}

		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;
		}

		try {
			if (!unavailable) {
				//     Servelt  
				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);
			servlet = null;
		} catch (Throwable e) {
			container.getLogger().error(
					sm.getString("standardWrapper.allocateException", wrapper.getName()), e);
			throwable = e;
			exception(request, response, e);
			servlet = null;
		}

		boolean comet = false;
		if (servlet instanceof CometProcessor
				&& request.getAttribute("org.apache.tomcat.comet.support") == Boolean.TRUE) {
			comet = true;
			request.setComet(true);
		}

		//   Request    
		try {
			response.sendAcknowledgement();
		} catch (IOException e) {
			request.removeAttribute(Globals.JSP_FILE_ATTR);
			container.getLogger().warn(
					sm.getString("standardWrapper.acknowledgeException", wrapper.getName()), e);
			throwable = e;
			exception(request, response, e);
		} catch (Throwable e) {
			container.getLogger().error(
					sm.getString("standardWrapper.acknowledgeException", wrapper.getName()), e);
			throwable = e;
			exception(request, response, e);
			servlet = null;
		}
		MessageBytes requestPathMB = null;
		if (request != null) {
			requestPathMB = request.getRequestPathMB();
		}
		
		request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
				ApplicationFilterFactory.REQUEST_INTEGER);
		request.setAttribute(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR, requestPathMB);
		//   FilterChain
		ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
		ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);

		request.setComet(false);

		try {
			// web.xml  <jsp-file>  
			String jspFile = wrapper.getJspFile();
			if (jspFile != null)
				request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
			else
				request.removeAttribute(Globals.JSP_FILE_ATTR);
			
			//   Filter
			if ((servlet != null) && (filterChain != null)) {
				if (context.getSwallowOutput()) {
					try {
						SystemLogHandler.startCapture();
						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 (comet) {
						request.setComet(true);
						filterChain.doFilterEvent(request.getEvent());
					} else {
						filterChain.doFilter(request.getRequest(), response.getResponse());
					}
				}

			}
			request.removeAttribute(Globals.JSP_FILE_ATTR);
		} catch (ClientAbortException e) {
			request.removeAttribute(Globals.JSP_FILE_ATTR);
			throwable = e;
			exception(request, response, e);
		} catch (IOException e) {
			request.removeAttribute(Globals.JSP_FILE_ATTR);
			container.getLogger().error(
					sm.getString("standardWrapper.serviceException", wrapper.getName()), e);
			throwable = e;
			exception(request, response, e);
		} catch (UnavailableException e) {
			request.removeAttribute(Globals.JSP_FILE_ATTR);
			container.getLogger().error(
					sm.getString("standardWrapper.serviceException", wrapper.getName()), 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()));
			}

		} catch (ServletException e) {
			request.removeAttribute(Globals.JSP_FILE_ATTR);
			Throwable rootCause = StandardWrapper.getRootCause(e);
			if (!(rootCause instanceof ClientAbortException)) {
				container.getLogger().error(
						sm.getString("standardWrapper.serviceException", wrapper.getName()),
						rootCause);
			}
			throwable = e;
			exception(request, response, e);
		} catch (Throwable e) {
			request.removeAttribute(Globals.JSP_FILE_ATTR);
			container.getLogger().error(
					sm.getString("standardWrapper.serviceException", wrapper.getName()), e);
			throwable = e;
			exception(request, response, e);
		}

		if (filterChain != null) {
			if (request.isComet()) {
				filterChain.reuse();
			} else {
				filterChain.release();
			}
		}

		//   Servlet  
		try {
			if (servlet != null) {
				wrapper.deallocate(servlet);
			}
		} catch (Throwable e) {
			container.getLogger().error(
					sm.getString("standardWrapper.deallocateException", wrapper.getName()), e);
			if (throwable == null) {
				throwable = e;
				exception(request, response, e);
			}
		}

		try {
			if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) {
				wrapper.unload();
			}
		} catch (Throwable e) {
			container.getLogger().error(
					sm.getString("standardWrapper.unloadException", wrapper.getName()), e);
			if (throwable == null) {
				throwable = e;
				exception(request, response, e);
			}
		}
		long t2 = System.currentTimeMillis();

		long time = t2 - t1;
		processingTime += time;
		if (time > maxTime)
			maxTime = time;
		if (time < minTime)
			minTime = time;

	}

このコードのキーは、サーブレットインスタンスの構築(servlet=wrapper.allocate();Filter Chainの呼び出し(filterChain.doFilter(request.getRequest()、response.getResponse());),具体的には方法の内部に入ってよく見てみましょう.comet方式は後で詳しく見ます.