public static enum AsyncState {
NOT_STARTED, STARTED, DISPATCHING, DISPATCHED, COMPLETING, TIMING_OUT,
- ERROR_DISPATCHING
+ TIMING_OUT_NEED_COMPLETE, ERROR_DISPATCHING
}
private static final Log log = LogFactory.getLog(AsyncContextImpl.class);
}
if (state.get()==AsyncState.COMPLETING) {
//do nothing
- } else if (state.compareAndSet(AsyncState.DISPATCHED, AsyncState.COMPLETING) ||
- state.compareAndSet(AsyncState.STARTED, AsyncState.COMPLETING)) {
+ } else if (state.compareAndSet(AsyncState.DISPATCHED,
+ AsyncState.COMPLETING) ||
+ state.compareAndSet(AsyncState.STARTED,
+ AsyncState.COMPLETING) ||
+ state.compareAndSet(AsyncState.TIMING_OUT_NEED_COMPLETE,
+ AsyncState.COMPLETING)) {
AtomicBoolean dispatched = new AtomicBoolean(false);
- request.getCoyoteRequest().action(ActionCode.ACTION_ASYNC_COMPLETE,dispatched);
+ request.getCoyoteRequest().action(ActionCode.ACTION_ASYNC_COMPLETE,
+ dispatched);
if (!dispatched.get()) doInternalComplete(false);
} else {
- throw new IllegalStateException("Complete not allowed. Invalid state:"+state.get());
+ throw new IllegalStateException(
+ "Complete not allowed. Invalid state:"+state.get());
}
}
}
public void doInternalDispatch() throws ServletException, IOException {
- if (this.state.compareAndSet(AsyncState.TIMING_OUT, AsyncState.COMPLETING)) {
+ if (this.state.compareAndSet(AsyncState.TIMING_OUT,
+ AsyncState.TIMING_OUT_NEED_COMPLETE)) {
log.debug("TIMING OUT!");
boolean listenerInvoked = false;
- for (AsyncListenerWrapper listener : listeners) {
+ List<AsyncListenerWrapper> listenersCopy =
+ new ArrayList<AsyncListenerWrapper>();
+ listenersCopy.addAll(listeners);
+ for (AsyncListenerWrapper listener : listenersCopy) {
listener.fireOnTimeout(event);
listenerInvoked = true;
}
package org.apache.catalina.core;
+import java.io.File;
import java.io.IOException;
import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
}
}
+ public void testTimeout() throws Exception {
+ // Setup Tomcat instance
+ Tomcat tomcat = getTomcatInstance();
+
+ // Must have a real docBase - just use temp
+ File docBase = new File(System.getProperty("java.io.tmpdir"));
+
+ // Create the folder that will trigger the redirect
+ File foo = new File(docBase, "async");
+ if (!foo.exists() && !foo.mkdirs()) {
+ fail("Unable to create async directory in docBase");
+ }
+
+ Context ctx = tomcat.addContext("/", docBase.getAbsolutePath());
+
+ TimeoutServlet timeout = new TimeoutServlet();
+
+ Wrapper wrapper = Tomcat.addServlet(ctx, "time", timeout);
+ wrapper.setAsyncSupported(true);
+ ctx.addServletMapping("/async", "time");
+
+ tomcat.start();
+ ByteChunk res = getUrl("http://localhost:" + getPort() + "/async");
+ assertEquals("OK", res.toString());
+ }
+
+ private static class TimeoutServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ if (req.isAsyncSupported()) {
+ resp.getWriter().print("OK");
+ final AsyncContext ac = req.startAsync();
+ ac.setTimeout(2000);
+
+ ac.addListener(new TimeoutListener());
+ } else
+ resp.getWriter().print("FAIL: Async unsupported");
+ }
+ }
+
+ private static class TimeoutListener implements AsyncListener {
+
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException {
+ // NO-OP
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException {
+ event.getAsyncContext().complete();
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException {
+ // NOOP
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException {
+ // NOOP
+ }
+
+ }
}
refactoring. (markt)
</fix>
<fix>
+ <bug>49698</bug>: Allow a listener to complete an asynchronous request
+ if it times out. (markt)
+ </fix>
+ <fix>
<bug>49714</bug>: The annotation process of Jar doesn't influence
distributable element of web.xml. (kfujino)
</fix>