/** * Marker interface used to indicate that the instance should only be used * once. Calling {@link #stop()} on an instance that supports this interface * will automatically call {@link #destroy()} after {@link #stop()} * completes. */ publicinterfaceSingleUse { } }
/** * May the public methods other than property getters/setters and lifecycle * methods be called for a component in this state? It returns * <code>true</code> for any component in any of the following states: * <ul> * <li>{@link #STARTING}</li> * <li>{@link #STARTED}</li> * <li>{@link #STOPPING_PREP}</li> * </ul> * * @return <code>true</code> if the component is available for use, * otherwise <code>false</code> */ publicbooleanisAvailable() { return available; }
public String getLifecycleEvent() { return lifecycleEvent; } }
Common interface for component life cycle methods. Catalina components may implement this interface (as well as the appropriate interface(s) for the functionality they support) in order to provide a consistent mechanism to start and stop the component. The valid state transitions for components that support Lifecycle are:
Calling start() while a component is in states STARTING_PREP, STARTING or STARTED has no effect.
Calling start() while a component is in state NEW will cause init() to be called immediately after the start() method is entered.
Calling stop() while a component is in states STOPPING_PREP, STOPPING or STOPPED has no effect.
Calling stop() while a component is in state NEW transitions the component to STOPPED. This is typically encountered when a component fails to start and does not start all its sub-components. When the component is stopped, it will try to stop all sub-components - even those it didn’t start.
Attempting any other transition will throw LifecycleException.
The LifecycleEvents fired during state changes are defined in the methods that trigger the changed. No LifecycleEvents are fired if the attempted transition is not valid. 好的项目就是会把这样的示意图画得很好,把状态流转都画得很明确,而不一定要什么好看的作图工具,用字符就可以 而在 LifecycleBase类中是把更细节的实现了, init方法就是先判断了状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
@Override publicfinalsynchronizedvoidinit()throws LifecycleException { if (!state.equals(LifecycleState.NEW)) { // 只有状态是 new 才可以执行初始化 init invalidTransition(Lifecycle.BEFORE_INIT_EVENT); }
if (log.isDebugEnabled()) { log.debug(sm.getString("lifecycleBase.setState", this, state)); }
if (check) { // Must have been triggered by one of the abstract methods (assume // code in this class is correct) // null is never a valid state if (state == null) { invalidTransition("null"); // Unreachable code - here to stop eclipse complaining about // a possible NPE further down the method return; }
// Any method can transition to failed // startInternal() permits STARTING_PREP to STARTING // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to // STOPPING if (!(state == LifecycleState.FAILED || (this.state == LifecycleState.STARTING_PREP && state == LifecycleState.STARTING) || (this.state == LifecycleState.STOPPING_PREP && state == LifecycleState.STOPPING) || (this.state == LifecycleState.FAILED && state == LifecycleState.STOPPING))) { // No other transition permitted invalidTransition(state.name()); } }
// Make sure parseBodyMethodsSet has a default if (null == parseBodyMethodsSet) { setParseBodyMethods(getParseBodyMethods()); }
if (protocolHandler.isAprRequired() && !AprStatus.isInstanceCreated()) { thrownewLifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener", getProtocolHandlerClassName())); } if (protocolHandler.isAprRequired() && !AprStatus.isAprAvailable()) { thrownewLifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary", getProtocolHandlerClassName())); } if (AprStatus.isAprAvailable() && AprStatus.getUseOpenSSL() && protocolHandler instanceof AbstractHttp11JsseProtocol) { AbstractHttp11JsseProtocol<?> jsseProtocolHandler = (AbstractHttp11JsseProtocol<?>) protocolHandler; if (jsseProtocolHandler.isSSLEnabled() && jsseProtocolHandler.getSslImplementationName() == null) { // OpenSSL is compatible with the JSSE configuration, so use it if APR is available jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName()); } }
try { setStateInternal(LifecycleState.STARTING_PREP, null, false); startInternal(); if (state.equals(LifecycleState.FAILED)) { // This is a 'controlled' failure. The component put itself into the // FAILED state so call stop() to complete the clean-up. stop(); } elseif (!state.equals(LifecycleState.STARTING)) { // Shouldn't be necessary but acts as a check that sub-classes are // doing what they are supposed to. invalidTransition(Lifecycle.AFTER_START_EVENT); } else { setStateInternal(LifecycleState.STARTED, null, false); } } catch (Throwable t) { // This is an 'uncontrolled' failure so put the component into the // FAILED state and throw an exception. handleSubClassException(t, "lifecycleBase.startFail", toString()); } }
// Start our defined Connectors second synchronized (connectorsLock) { for (Connector connector: connectors) { // If it has already failed, don't try and start it if (connector.getState() != LifecycleState.FAILED) { connector.start(); } } } }
// Select the Host to be used for this Request Hosthost= request.getHost(); if (host == null) { // HTTP 0.9 or HTTP 1.0 request without a host when no default host // is defined. // Don't overwrite an existing error if (!response.isError()) { response.sendError(404); } return; } if (request.isAsyncSupported()) { request.setAsyncSupported(host.getPipeline().isAsyncSupported()); }
// Ask this Host to process this request host.getPipeline().getFirst().invoke(request, response); }
比较简单,就是调用 host 中的 pipeline 里的第一个 valve 来处理
第一个是 org.apache.catalina.valves.ErrorReportValve, 这里处理的其实是先调用了 next
// Perform the request getNext().invoke(request, response);
if (response.isCommitted()) { if (response.setErrorReported()) { // Error wasn't previously reported but we can't write an error // page because the response has already been committed.
// See if IO is allowed AtomicBooleanioAllowed=newAtomicBoolean(true); response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, ioAllowed);
if (ioAllowed.get()) { // I/O is currently still allowed. Flush any data that is // still to be written to the client. try { response.flushBuffer(); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); } // Now close immediately to signal to the client that // something went wrong response.getCoyoteResponse().action(ActionCode.CLOSE_NOW, request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)); } } return; }
// If an async request is in progress and is not going to end once this // container thread finishes, do not process any error page here. if (request.isAsync() && !request.isAsyncCompleting()) { return; }
if (throwable != null && !response.isError()) { // Make sure that the necessary methods have been called on the // response. (It is possible a component may just have set the // Throwable. Tomcat won't do that but other components might.) // These are safe to call at this point as we know that the response // has not been committed. response.reset(); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); }
// One way or another, response.sendError() will have been called before // execution reaches this point and suspended the response. Need to // reverse that so this valve can write to the response. response.setSuspended(false);
// Select the Context to be used for this Request Contextcontext= request.getContext(); if (context == null) { // Don't overwrite an existing error if (!response.isError()) { response.sendError(404); } return; }
if (request.isAsyncSupported()) { request.setAsyncSupported(context.getPipeline().isAsyncSupported()); }
if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) { // Don't fire listeners during async processing (the listener // fired for the request that called startAsync()). // If a request init listener throws an exception, the request // is aborted. return; }
// Ask this Context to process this request. Requests that are // already in error must have been routed here to check for // application defined error pages so DO NOT forward them to the the // application for processing. try { if (!response.isErrorReportRequired()) { // 交给 context 去处理请求了 context.getPipeline().getFirst().invoke(request, response); } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); container.getLogger().error("Exception Processing " + request.getRequestURI(), t); // If a new error occurred while trying to report a previous // error allow the original error to be reported. if (!response.isErrorReportRequired()) { request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); throwable(request, response, t); } }
// Now that the request/response pair is back under container // control lift the suspension so that the error handling can // complete and/or the container can flush any remaining data response.setSuspended(false);
// Protect against NPEs if the context was destroyed during a // long running request. if (!context.getState().isAvailable()) { return; }
// Look for (and render if found) an application level error page if (response.isErrorReportRequired()) { // If an error has occurred that prevents further I/O, don't waste time // producing an error report that will never be read AtomicBooleanresult=newAtomicBoolean(false); response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result); if (result.get()) { if (t != null) { throwable(request, response, t); } else { status(request, response); } } }
if (!request.isAsync() && !asyncAtStart) { context.fireRequestDestroyEvent(request.getRequest()); } } finally { // Access a session (if present) to update last accessed time, based // on a strict interpretation of the specification if (ACCESS_SESSION) { request.getSession(false); }
// Disallow any direct access to resources under WEB-INF or META-INF MessageBytesrequestPathMB= request.getRequestPathMB(); if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/META-INF")) || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; }
// Select the Wrapper to be used for this Request Wrapperwrapper= request.getWrapper(); if (wrapper == null || wrapper.isUnavailable()) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; }
// Initialize local variables we may need booleanunavailable=false; Throwablethrowable=null; // This should be a Request attribute... long t1=System.currentTimeMillis(); requestCount.incrementAndGet(); StandardWrapperwrapper= (StandardWrapper) getContainer(); Servletservlet=null; Contextcontext= (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())); longavailable= 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())); } elseif (available == Long.MAX_VALUE) { response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("standardWrapper.notFound", wrapper.getName())); } unavailable = true; }
// 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; } } }