/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.apache.catalina.core;


import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

import jakarta.servlet.DispatcherType;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.UnavailableException;
import jakarta.servlet.http.HttpServletResponse;

import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import org.apache.coyote.CloseNowException;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.log.SystemLogHandler;
import org.apache.tomcat.util.res.StringManager;

Valve that implements the default basic behavior for the StandardWrapper container implementation.
Author:Craig R. McClanahan
/** * Valve that implements the default basic behavior for the * <code>StandardWrapper</code> container implementation. * * @author Craig R. McClanahan */
final class StandardWrapperValve extends ValveBase { //------------------------------------------------------ Constructor public StandardWrapperValve() { super(true); } // ----------------------------------------------------- Instance Variables // Some JMX statistics. This valve is associated with a StandardWrapper. // We expose the StandardWrapper as JMX ( j2eeType=Servlet ). The fields // are here for performance. private volatile long processingTime; private volatile long maxTime; private volatile long minTime = Long.MAX_VALUE; private final AtomicInteger requestCount = new AtomicInteger(0); private final AtomicInteger errorCount = new AtomicInteger(0);
The string manager for this package.
/** * The string manager for this package. */
private static final StringManager sm = StringManager.getManager(Constants.Package); // --------------------------------------------------------- Public Methods
Invoke the servlet we are managing, respecting the rules regarding servlet lifecycle and SingleThreadModel support.
Params:
  • request – Request to be processed
  • response – Response to be produced
Throws:
/** * 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 */
@Override public 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.incrementAndGet(); 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; } 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 ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); // Call the filter chain for this request // NOTE: This also calls the servlet's service() method Container container = this.container; try { if ((servlet != null) && (filterChain != null)) { // Swallow output if needed if (context.getSwallowOutput()) { try { SystemLogHandler.startCapture(); if (request.isAsyncDispatching()) { request.getAsyncContextInternal().doInternalDispatch(); } 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()) { request.getAsyncContextInternal().doInternalDispatch(); } else { filterChain.doFilter (request.getRequest(), response.getResponse()); } } } } catch (ClientAbortException | CloseNowException e) { if (container.getLogger().isDebugEnabled()) { container.getLogger().debug(sm.getString( "standardWrapper.serviceException", wrapper.getName(), context.getName()), 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); } finally { // Release the filter chain (if any) for this request if (filterChain != null) { 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) { exception(request, response, e); } } long t2=System.currentTimeMillis(); long time=t2-t1; processingTime += time; if( time > maxTime) maxTime=time; if( time < minTime) minTime=time; } } // -------------------------------------------------------- Private Methods
Handle the specified ServletException encountered while processing the specified Request to produce the specified Response. Any exceptions that occur during generation of the exception report are logged and swallowed.
Params:
  • request – The request being processed
  • response – The response being generated
  • exception – The exception that occurred (which possibly wraps a root cause exception
/** * Handle the specified ServletException encountered while processing * the specified Request to produce the specified Response. Any * exceptions that occur during generation of the exception report are * logged and swallowed. * * @param request The request being processed * @param response The response being generated * @param exception The exception that occurred (which possibly wraps * a root cause exception */
private void exception(Request request, Response response, Throwable exception) { request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, exception); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.setError(); } public long getProcessingTime() { return processingTime; } public long getMaxTime() { return maxTime; } public long getMinTime() { return minTime; } public int getRequestCount() { return requestCount.get(); } public int getErrorCount() { return errorCount.get(); } public void incrementErrorCount() { errorCount.incrementAndGet(); } @Override protected void initInternal() throws LifecycleException { // NOOP - Don't register this Valve in JMX } }