*/
package javax.servlet;
+import org.apache.catalina.connector.AsyncContextImpl.AsyncState;
+
/**
* @since Servlet 3.0
* $Id$
void complete();
void start(Runnable run);
+
+ public long getAsyncTimeout();
+
+ public void setAsyncTimeout(long timeout);
}
private boolean hasOriginalRequestAndResponse = true;
private volatile Runnable dispatch = null;
private Context context = null;
- private AtomicReference<AsyncState> state = new AtomicReference<AsyncState>();
+ private AtomicReference<AsyncState> state = new AtomicReference<AsyncState>(AsyncState.NOT_STARTED);
+ private long timeout = -1;
private Request request;
@Override
public void complete() {
- if (state.compareAndSet(AsyncState.STARTED, AsyncState.COMPLETING)) {
+ if (state.compareAndSet(AsyncState.STARTED, AsyncState.COMPLETING) ||
+ state.compareAndSet(AsyncState.DISPATCHED, AsyncState.COMPLETING)) {
// TODO SERVLET3 - async
AtomicBoolean dispatched = new AtomicBoolean(false);
request.coyoteRequest.action(ActionCode.ACTION_ASYNC_COMPLETE,dispatched);
if (!dispatched.get()) doInternalComplete(false);
+ } else {
+ throw new IllegalStateException("Complete not allowed. Invalid state:"+state.get());
}
+
}
@Override
hasOriginalRequestAndResponse = true;
state.set(AsyncState.NOT_STARTED);
context = null;
+ timeout = -1;
}
public boolean isStarted() {
public void doInternalDispatch() throws ServletException, IOException {
if (this.state.compareAndSet(AsyncState.TIMING_OUT, AsyncState.DISPATCHED)) {
+ log.info("TIMING OUT!");
boolean listenerInvoked = false;
for (AsyncListenerWrapper listener : listeners) {
listener.fireOnTimeout();
//this is the same as
//request.startAsync().complete();
recycle();
- } else if (state.compareAndSet(AsyncState.DISPATCHED, AsyncState.NOT_STARTED)) {
+ } else if (state.compareAndSet(AsyncState.DISPATCHED, AsyncState.NOT_STARTED) ||
+ state.compareAndSet(AsyncState.COMPLETING, AsyncState.NOT_STARTED)) {
for (AsyncListenerWrapper wrapper : listeners) {
try {
wrapper.fireOnComplete();
protected void setState(AsyncState st) {
state.set(st);
}
+
+ public long getAsyncTimeout() {
+ return timeout;
+ }
+
+ public void setAsyncTimeout(long timeout) {
+ this.timeout = timeout;
+ request.coyoteRequest.action(ActionCode.ACTION_ASYNC_SETTIMEOUT,new Long(timeout));
+ }
}
--- /dev/null
+/*
+* 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 async;
+
+import java.io.IOException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+public class Async0 extends HttpServlet {
+ protected static Log log = LogFactory.getLog(Async0.class);
+ public Async0() {
+ }
+
+ @Override
+ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ if (req.isAsyncStarted()) {
+ log.info("Received dispatch, completing on the worker thread.");
+ req.getAsyncContext().complete();
+ log.info("After complete called started:"+req.isAsyncStarted());
+ resp.getWriter().write("Async dispatch worked\n");
+ } else {
+ resp.setContentType("text/plain");
+ final AsyncContext actx = req.startAsync();
+ actx.setAsyncTimeout(30*1000);
+ Runnable run = new Runnable() {
+ public void run() {
+ try {
+ Thread.currentThread().setName("Async0-Thread");
+ log.info("Putting AsyncThread to sleep");
+ Thread.sleep(10*1000);
+ log.info("Dispatching");
+ actx.dispatch();
+ }catch (InterruptedException x) {
+ log.error("Async1",x);
+ }catch (IllegalStateException x) {
+ log.error("Async1",x);
+ }
+ }
+ };
+ Thread t = new Thread(run);
+ t.start();
+ }
+ }
+}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
final AsyncContext actx = req.startAsync();
+ actx.setAsyncTimeout(30*1000);
Runnable run = new Runnable() {
public void run() {
try {
<!-- Async examples -->
<servlet>
+ <servlet-name>async0</servlet-name>
+ <servlet-class>async.Async0</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>async0</servlet-name>
+ <url-pattern>/async/async0</url-pattern>
+ </servlet-mapping>
+ <servlet>
<servlet-name>async1</servlet-name>
<servlet-class>async.Async1</servlet-class>
</servlet>
request.getAsyncContext().complete();
}
%>
+Completed async request.
\ No newline at end of file
<%@page session="false"%>
-<a href="<%=response.encodeURL("/examples/async/async1")%>"> Async 1 </a>
+
+
+Use cases:
+
+1. Simple dispatch
+ - servlet does startAsync()
+ - background thread calls ctx.dispatch()
+
+
+2. Simple dispatch
+ - servlet does startAsync()
+ - background thread calls dispatch(/path/to/jsp)
+ <a href="<%=response.encodeURL("/examples/async/async1")%>"> Async 1 </a>
+
+3. Timeout s1
+ - servlet does a startAsync()
+ - servlet does a setAsyncTimeout
+ - returns - waits for timeout to happen should return error page
+
+4. Timeout s2
+ - servlet does a startAsync()
+ - servlet does a setAsyncTimeout
+ - servlet does a addAsyncListener
+ - returns - waits for timeout to happen and listener invoked