Author: markt Date: Wed Jan 23 16:33:47 2019 New Revision: 1851947 URL: http://svn.apache.org/viewvc?rev=1851947&view=rev Log: Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63003 Extend the unloadDelay attribute on a Context to include in-flight asynchronous requests.
Modified: tomcat/trunk/java/org/apache/catalina/Context.java tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties tomcat/trunk/java/org/apache/catalina/core/StandardContext.java tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java tomcat/trunk/java/org/apache/coyote/AsyncContextCallback.java tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java tomcat/trunk/test/org/apache/tomcat/unittest/TesterContext.java tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/catalina/Context.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Context.java?rev=1851947&r1=1851946&r2=1851947&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/Context.java (original) +++ tomcat/trunk/java/org/apache/catalina/Context.java Wed Jan 23 16:33:47 2019 @@ -1865,4 +1865,10 @@ public interface Context extends Contain * otherwise <code>false</code> */ public boolean getAllowMultipleLeadingForwardSlashInPath(); + + + public void incrementInProgressAsyncCount(); + + + public void decrementInProgressAsyncCount(); } Modified: tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java?rev=1851947&r1=1851946&r2=1851947&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java Wed Jan 23 16:33:47 2019 @@ -111,6 +111,7 @@ public class AsyncContextImpl implements } finally { context.fireRequestDestroyEvent(request.getRequest()); clearServletRequestResponse(); + this.context.decrementInProgressAsyncCount(); context.unbind(Globals.IS_SECURITY_ENABLED, oldCL); } } @@ -205,6 +206,7 @@ public class AsyncContextImpl implements request, applicationDispatcher, servletRequest, servletResponse); this.request.getCoyoteRequest().action(ActionCode.ASYNC_DISPATCH, null); clearServletRequestResponse(); + this.context.decrementInProgressAsyncCount(); } } @@ -311,6 +313,7 @@ public class AsyncContextImpl implements ActionCode.ASYNC_START, this); this.context = context; + context.incrementInProgressAsyncCount(); this.servletRequest = request; this.servletResponse = response; this.hasOriginalRequestAndResponse = originalRequestResponse; @@ -377,6 +380,17 @@ public class AsyncContextImpl implements } + + @Override + public boolean isAvailable() { + Context context = this.context; + if (context == null) { + return false; + } + return context.getState().isAvailable(); + } + + public void setErrorState(Throwable t, boolean fireOnError) { if (t!=null) request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); request.getCoyoteRequest().action(ActionCode.ASYNC_ERROR, null); Modified: tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties?rev=1851947&r1=1851946&r2=1851947&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties [UTF-8] (original) +++ tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties [UTF-8] Wed Jan 23 16:33:47 2019 @@ -222,6 +222,7 @@ standardContext.setManager.start=Error s standardContext.setManager.stop=Error stopping old manager standardContext.startFailed=Context [{0}] startup failed due to previous errors standardContext.startingContext=Exception starting Context with name [{0}] +standardContext.stop.asyncWaitInterrupted=Interrupt received while waiting unloadDelay milliseconds for in-flight asynchronous requests to complete. Context stop will continue without further delay. standardContext.stoppingContext=Exception stopping Context with name [{0}] standardContext.suspiciousUrl=Suspicious URL pattern: [{0}] in context [{1}], see sections 12.1 and 12.2 of the Servlet specification standardContext.threadBindingListenerError=An error occurred in the thread binding listener configured for Context [{0}] Modified: tomcat/trunk/java/org/apache/catalina/core/StandardContext.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardContext.java?rev=1851947&r1=1851946&r2=1851947&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/StandardContext.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/StandardContext.java Wed Jan 23 16:33:47 2019 @@ -820,10 +820,24 @@ public class StandardContext extends Con private boolean allowMultipleLeadingForwardSlashInPath = false; + private final AtomicLong inProgressAsyncCount = new AtomicLong(0); + // ----------------------------------------------------- Context Properties @Override + public void incrementInProgressAsyncCount() { + inProgressAsyncCount.incrementAndGet(); + } + + + @Override + public void decrementInProgressAsyncCount() { + inProgressAsyncCount.decrementAndGet(); + } + + + @Override public void setAllowMultipleLeadingForwardSlashInPath( boolean allowMultipleLeadingForwardSlashInPath) { this.allowMultipleLeadingForwardSlashInPath = allowMultipleLeadingForwardSlashInPath; @@ -5316,6 +5330,22 @@ public class StandardContext extends Con broadcaster.sendNotification(notification); } + // Context has been removed from Mapper at this point (so no new + // requests will be mapped) but is still available. + + // Give the in progress async requests a chance to complete + long limit = System.currentTimeMillis() + unloadDelay; + while (inProgressAsyncCount.get() > 0 && System.currentTimeMillis() < limit) { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + log.info(sm.getString("standardContext.stop.asyncWaitInterrupted"), e); + break; + } + } + + // Once the state is set to STOPPING, the Context will report itself as + // not available and any in progress async requests will timeout setState(LifecycleState.STOPPING); // Binding thread Modified: tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java?rev=1851947&r1=1851946&r2=1851947&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java (original) +++ tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java Wed Jan 23 16:33:47 2019 @@ -809,4 +809,9 @@ public class FailedContext extends Lifec } @Override public boolean getAllowMultipleLeadingForwardSlashInPath() { return false; } + + @Override + public void incrementInProgressAsyncCount() { /* NO-OP */ } + @Override + public void decrementInProgressAsyncCount() { /* NO-OP */ } } \ No newline at end of file Modified: tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java?rev=1851947&r1=1851946&r2=1851947&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java (original) +++ tomcat/trunk/java/org/apache/coyote/AbstractProcessor.java Wed Jan 23 16:33:47 2019 @@ -639,6 +639,10 @@ public abstract class AbstractProcessor if ((now - asyncStart) > asyncTimeout) { doTimeoutAsync(); } + } else if (!asyncStateMachine.isAvailable()) { + // Timeout the async process if the associated web application + // is no longer running. + doTimeoutAsync(); } } } Modified: tomcat/trunk/java/org/apache/coyote/AsyncContextCallback.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/AsyncContextCallback.java?rev=1851947&r1=1851946&r2=1851947&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/AsyncContextCallback.java (original) +++ tomcat/trunk/java/org/apache/coyote/AsyncContextCallback.java Wed Jan 23 16:33:47 2019 @@ -17,12 +17,20 @@ package org.apache.coyote; /** - * Provides a mechanism for the Coyote connectors to signal to a - * {@link javax.servlet.AsyncContext} implementation that an action, such as - * firing event listeners needs to be taken. It is implemented in this manner - * so that the org.apache.coyote package does not have a dependency on the + * Provides a mechanism for the Coyote connectors to communicate with the + * {@link javax.servlet.AsyncContext}. It is implemented in this manner so that + * the org.apache.coyote package does not have a dependency on the * org.apache.catalina package. */ public interface AsyncContextCallback { public void fireOnComplete(); + + /** + * Reports if the web application associated with this async request is + * available. + * + * @return {@code true} if the associated web application is available, + * otherwise {@code false} + */ + public boolean isAvailable(); } Modified: tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java?rev=1851947&r1=1851946&r2=1851947&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java [UTF-8] (original) +++ tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java [UTF-8] Wed Jan 23 16:33:47 2019 @@ -483,6 +483,16 @@ class AsyncStateMachine { } + synchronized boolean isAvailable() { + if (asyncCtxt == null) { + // Async processing has probably been completed in another thread. + // Trigger a timeout to make sure the Processor is cleaned up. + return false; + } + return asyncCtxt.isAvailable(); + } + + synchronized void recycle() { // Use lastAsyncStart to determine if this instance has been used since // it was last recycled. If it hasn't there is no need to recycle again Modified: tomcat/trunk/test/org/apache/tomcat/unittest/TesterContext.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/unittest/TesterContext.java?rev=1851947&r1=1851946&r2=1851947&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/tomcat/unittest/TesterContext.java (original) +++ tomcat/trunk/test/org/apache/tomcat/unittest/TesterContext.java Wed Jan 23 16:33:47 2019 @@ -1274,4 +1274,9 @@ public class TesterContext implements Co } @Override public boolean getAllowMultipleLeadingForwardSlashInPath() { return false; } + + @Override + public void incrementInProgressAsyncCount() { /* NO-OP */ } + @Override + public void decrementInProgressAsyncCount() { /* NO-OP */ } } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1851947&r1=1851946&r2=1851947&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Wed Jan 23 16:33:47 2019 @@ -113,6 +113,10 @@ <code>trimCredentials</code> on the <code>BasicAuthenticator</code>. (markt) </add> + <fix> + <bug>63003</bug>: Extend the <code>unloadDelay</code> attribute on a + <code>Context</code> to include in-flight asynchronous requests. (markt) + </fix> <add> <bug>63026</bug>: Add a new attribute, <code>forceDnHexEscape</code>, to the <code>JNDIRealm</code> that forces escaping in the String --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org