Refactor location of AsyncContextImpl to o.a.catalina.core
authorfhanik <fhanik@13f79535-47bb-0310-9956-ffa450edef68>
Fri, 7 Aug 2009 14:49:19 +0000 (14:49 +0000)
committerfhanik <fhanik@13f79535-47bb-0310-9956-ffa450edef68>
Fri, 7 Aug 2009 14:49:19 +0000 (14:49 +0000)
git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@802026 13f79535-47bb-0310-9956-ffa450edef68

12 files changed:
java/javax/servlet/AsyncContext.java
java/javax/servlet/AsyncEvent.java
java/org/apache/catalina/connector/AsyncContextImpl.java [deleted file]
java/org/apache/catalina/connector/AsyncListenerWrapper.java [deleted file]
java/org/apache/catalina/connector/CoyoteAdapter.java
java/org/apache/catalina/connector/Request.java
java/org/apache/catalina/core/AsyncContextImpl.java [new file with mode: 0644]
java/org/apache/catalina/core/AsyncListenerWrapper.java [new file with mode: 0644]
java/org/apache/catalina/core/StandardWrapperValve.java
webapps/examples/WEB-INF/classes/async/Async0.java
webapps/examples/WEB-INF/classes/async/Async1.java
webapps/examples/WEB-INF/classes/async/Async2.java

index b944388..200c764 100644 (file)
@@ -16,7 +16,7 @@
 */
 package javax.servlet;
 
-import org.apache.catalina.connector.AsyncContextImpl.AsyncState;
+import org.apache.catalina.core.AsyncContextImpl.AsyncState;
 
 /**
  * @since Servlet 3.0
index 3012de9..6ba2f0c 100644 (file)
@@ -25,7 +25,7 @@ public class AsyncEvent {
     private ServletRequest request;
     private ServletResponse response;
     
-    AsyncEvent(ServletRequest request, ServletResponse response) {
+    public AsyncEvent(ServletRequest request, ServletResponse response) {
         this.request = request;
         this.response = response;
     }
diff --git a/java/org/apache/catalina/connector/AsyncContextImpl.java b/java/org/apache/catalina/connector/AsyncContextImpl.java
deleted file mode 100644 (file)
index 95909b8..0000000
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * 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.connector;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.servlet.AsyncContext;
-import javax.servlet.AsyncListener;
-import javax.servlet.DispatcherType;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.catalina.Context;
-import org.apache.catalina.Globals;
-import org.apache.coyote.ActionCode;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-/**
- * 
- * @author fhanik
- *
- */
-public class AsyncContextImpl implements AsyncContext {
-    
-    public static enum AsyncState {
-        NOT_STARTED, STARTED, DISPATCHING, DISPATCHED, COMPLETING, TIMING_OUT
-    }
-    
-    protected static Log log = LogFactory.getLog(AsyncContextImpl.class);
-    
-    private ServletRequest servletRequest = null;
-    private ServletResponse servletResponse = null;
-    private List<AsyncListenerWrapper> listeners = new ArrayList<AsyncListenerWrapper>();
-    private boolean hasOriginalRequestAndResponse = true;
-    private volatile Runnable dispatch = null;
-    private Context context = null;
-    private AtomicReference<AsyncState> state = new AtomicReference<AsyncState>(AsyncState.NOT_STARTED);
-    private long timeout = -1;
-    
-    private Request request;
-    
-    public AsyncContextImpl(Request request) {
-        //TODO SERVLET3 - async
-        this.request = request;
-    }
-
-    @Override
-    public void complete() {
-        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
-    public void dispatch() {
-        HttpServletRequest sr = (HttpServletRequest)getServletRequest();
-        String path = sr.getRequestURI();
-        String cpath = sr.getContextPath();
-        if (cpath.length()>1) path = path.substring(cpath.length());
-        dispatch(path);
-    }
-
-    @Override
-    public void dispatch(String path) {
-        dispatch(request.getServletContext(),path);
-    }
-
-    @Override
-    public void dispatch(ServletContext context, String path) {
-        // TODO SERVLET3 - async
-        if (state.compareAndSet(AsyncState.STARTED, AsyncState.DISPATCHING) ||
-            state.compareAndSet(AsyncState.DISPATCHED, AsyncState.DISPATCHING)) {
-
-            if (request.getAttribute(ASYNC_REQUEST_URI)==null) {
-                request.setAttribute(ASYNC_REQUEST_URI, request.getRequestURI());
-                request.setAttribute(ASYNC_CONTEXT_PATH, request.getContextPath());
-                request.setAttribute(ASYNC_SERVLET_PATH, request.getServletPath());
-                request.setAttribute(ASYNC_QUERY_STRING, request.getQueryString());
-            }
-            final RequestDispatcher requestDispatcher = context.getRequestDispatcher(path);
-            final HttpServletRequest servletRequest = (HttpServletRequest)getRequest();
-            final HttpServletResponse servletResponse = (HttpServletResponse)getResponse();
-            Runnable run = new Runnable() {
-                public void run() {
-                    DispatcherType type = (DispatcherType)request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
-                    try {
-                        //piggy back on the request dispatcher to ensure that filters etc get called.
-                        //TODO SERVLET3 - async should this be include/forward or a new dispatch type
-                        //javadoc suggests include with the type of DispatcherType.ASYNC
-                        request.setAttribute(Globals.DISPATCHER_TYPE_ATTR, DispatcherType.ASYNC);
-                        requestDispatcher.include(servletRequest, servletResponse);
-                    }catch (Exception x) {
-                        //log.error("Async.dispatch",x);
-                        throw new RuntimeException(x);
-                    }finally {
-                        request.setAttribute(Globals.DISPATCHER_TYPE_ATTR, type);
-                    }
-                }
-            };
-            this.dispatch = run;
-            AtomicBoolean dispatched = new AtomicBoolean(false);
-            request.coyoteRequest.action(ActionCode.ACTION_ASYNC_DISPATCH, dispatched );
-            if (!dispatched.get()) {
-                try {
-                    doInternalDispatch();
-                }catch (ServletException sx) {
-                    throw new RuntimeException(sx);
-                }catch (IOException ix) {
-                    throw new RuntimeException(ix);
-                }
-            }
-
-        } else {
-            throw new IllegalStateException("Dispatch not allowed. Invalid state:"+state.get());
-        }
-    }
-
-    @Override
-    public ServletRequest getRequest() {
-        return getServletRequest();
-    }
-
-    @Override
-    public ServletResponse getResponse() {
-        return getServletResponse();
-    }
-
-    @Override
-    public void start(final Runnable run) {
-        if (state.compareAndSet(AsyncState.STARTED, AsyncState.DISPATCHING) ||
-            state.compareAndSet(AsyncState.DISPATCHED, AsyncState.DISPATCHING)) {
-            // TODO SERVLET3 - async
-            final ServletContext sctx = getServletRequest().getServletContext();
-            Runnable r = new Runnable() {
-                public void run() {
-                    //TODO SERVLET3 - async - set context class loader when running the task.
-                    try {
-                        
-                        run.run();
-                    }catch (Exception x) {
-                        log.error("Unable to run async task.",x);
-                    }
-                }
-            };
-            this.dispatch = r;
-            AtomicBoolean dispatched = new AtomicBoolean(false);
-            request.coyoteRequest.action(ActionCode.ACTION_ASYNC_DISPATCH, dispatched );
-            if (!dispatched.get()) {
-                try {
-                    doInternalDispatch();
-                }catch (ServletException sx) {
-                    throw new RuntimeException(sx);
-                }catch (IOException ix) {
-                    throw new RuntimeException(ix);
-                }
-            }
-        } else {
-            throw new IllegalStateException("Dispatch not allowed. Invalid state:"+state.get());
-        }
-    }
-    
-    public void addAsyncListener(AsyncListener listener) {
-        AsyncListenerWrapper wrapper = new AsyncListenerWrapper();
-        wrapper.setListener(listener);
-        wrapper.setServletRequest(getServletRequest());
-        wrapper.setServletResponse(getServletResponse());
-        listeners.add(wrapper);
-    }
-
-    public void addAsyncListener(AsyncListener listener, ServletRequest servletRequest, ServletResponse servletResponse) {
-        AsyncListenerWrapper wrapper = new AsyncListenerWrapper();
-        wrapper.setListener(listener);
-        wrapper.setServletRequest(servletRequest);
-        wrapper.setServletResponse(servletResponse);
-        listeners.add(wrapper);
-    }
-    
-    
-    protected void recycle() {
-        servletRequest = null;
-        servletResponse = null;
-        listeners.clear();
-        hasOriginalRequestAndResponse = true;
-        state.set(AsyncState.NOT_STARTED);
-        context = null;
-        timeout = -1;
-    }
-
-    public boolean isStarted() {
-        return (state.get()!=AsyncState.NOT_STARTED);
-    }
-
-    public void setStarted(Context context) {
-        if (state.compareAndSet(AsyncState.NOT_STARTED, AsyncState.STARTED)) {
-            this.context = context;
-        } else {
-            throw new IllegalStateException("Start illegal. Invalid state: "+state.get());
-        }
-    }
-
-    public ServletRequest getServletRequest() {
-        return servletRequest;
-    }
-
-    public void setServletRequest(ServletRequest servletRequest) {
-        this.servletRequest = servletRequest;
-    }
-
-    public ServletResponse getServletResponse() {
-        return servletResponse;
-    }
-
-    public void setServletResponse(ServletResponse servletResponse) {
-        this.servletResponse = servletResponse;
-    }
-
-    @Override
-    public boolean hasOriginalRequestAndResponse() {
-        return hasOriginalRequestAndResponse;
-    }
-
-    public void setHasOriginalRequestAndResponse(boolean hasOriginalRequestAndResponse) {
-        this.hasOriginalRequestAndResponse = hasOriginalRequestAndResponse;
-    }
-
-    public boolean isCompleted() {
-        return (state.get()==AsyncState.NOT_STARTED);
-    }
-
-    public void setCompleted() {
-        this.state.set(AsyncState.NOT_STARTED);
-    }
-    
-    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();
-                listenerInvoked = true;
-            }
-            if (!listenerInvoked) {
-                ((HttpServletResponse)servletResponse).setStatus(500);
-            }
-            doInternalComplete(true);
-        } else if (this.state.compareAndSet(AsyncState.DISPATCHING, AsyncState.DISPATCHED)) {
-            if (this.dispatch!=null) {
-                try {
-                    dispatch.run();
-                } catch (RuntimeException x) {
-                    doInternalComplete(true);
-                    if (x.getCause() instanceof ServletException) throw (ServletException)x.getCause();
-                    if (x.getCause() instanceof IOException) throw (IOException)x.getCause();
-                    else throw new ServletException(x);
-                } finally {
-                    dispatch = null;
-                }
-            }
-        } else if (this.state.get()==AsyncState.COMPLETING) {
-            doInternalComplete(false);
-        } else {
-            throw new IllegalStateException("Dispatch illegal. Invalid state: "+state.get());
-        }
-    }
-    
-    public void doInternalComplete(boolean error) {
-        if (isCompleted()) return;
-        if (state.compareAndSet(AsyncState.STARTED, AsyncState.NOT_STARTED)) {
-            //this is the same as
-            //request.startAsync().complete();
-            recycle();
-        } else if (state.compareAndSet(AsyncState.COMPLETING, AsyncState.NOT_STARTED)) {
-            for (AsyncListenerWrapper wrapper : listeners) {
-                try {
-                    wrapper.fireOnComplete();
-                }catch (IOException x) {
-                    //how does this propagate, or should it?
-                    //TODO SERVLET3 - async 
-                    log.error("",x);
-                }
-            }
-            try {
-                if (!error) getResponse().flushBuffer();
-
-            }catch (Exception x) {
-                log.error("",x);
-            }
-            recycle();
-            
-        } else { 
-            throw new IllegalStateException("Complete illegal. Invalid state:"+state.get());
-        }
-    }
-    
-    public AsyncState getState() {
-        return state.get();
-    }
-    
-    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));
-    }
-
-}
diff --git a/java/org/apache/catalina/connector/AsyncListenerWrapper.java b/java/org/apache/catalina/connector/AsyncListenerWrapper.java
deleted file mode 100644 (file)
index ffdad57..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
-* 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.connector;
-
-import java.io.IOException;
-
-import javax.servlet.AsyncListener;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-/**
- * TODO SERVLET 3 - async 
- * @author fhanik
- *
- */
-public class AsyncListenerWrapper {
-
-    private AsyncListener listener = null;
-    private ServletRequest servletRequest = null;
-    private ServletResponse servletResponse = null;
-    
-    public void fireOnComplete() throws IOException {
-        // TODO SERVLET 3 - async 
-
-    }
-
-
-    public void fireOnTimeout() throws IOException {
-        // TODO SERVLET 3 - async 
-
-    }
-
-    public ServletRequest getServletRequest() {
-        return servletRequest;
-    }
-
-    public void setServletRequest(ServletRequest servletRequest) {
-        this.servletRequest = servletRequest;
-    }
-
-    public ServletResponse getServletResponse() {
-        return servletResponse;
-    }
-
-    public void setServletResponse(ServletResponse servletResponse) {
-        this.servletResponse = servletResponse;
-    }
-
-    public AsyncListener getListener() {
-        return listener;
-    }
-
-    public void setListener(AsyncListener listener) {
-        this.listener = listener;
-    }
-    
-    
-
-}
index 9571b80..2771dca 100644 (file)
@@ -29,6 +29,7 @@ import org.apache.catalina.Context;
 import org.apache.catalina.Globals;
 import org.apache.catalina.Wrapper;
 import org.apache.tomcat.util.res.StringManager;
+import org.apache.catalina.core.AsyncContextImpl;
 import org.apache.catalina.util.URLEncoder;
 import org.apache.coyote.ActionCode;
 import org.apache.coyote.Adapter;
@@ -269,14 +270,8 @@ public class CoyoteAdapter
                    AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();
                    //TODO SERVLET3 - async
                    //configure settings for timed out
-                   asyncConImpl.setState(AsyncContextImpl.AsyncState.TIMING_OUT);
+                   asyncConImpl.setTimeoutState();
                 }
-                if (status==SocketStatus.TIMEOUT) {
-                    AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();
-                    //TODO SERVLET3 - async
-                    //configure settings for timed out
-                    asyncConImpl.setState(AsyncContextImpl.AsyncState.TIMING_OUT);
-                 }
                 connector.getContainer().getPipeline().getFirst().invoke(request, response);
             }catch (RuntimeException x) {
                 success = false;
index 847d6f2..9a5c0c3 100644 (file)
@@ -36,6 +36,7 @@ import java.util.TreeMap;
 
 import javax.security.auth.Subject;
 import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
 import javax.servlet.AsyncListener;
 import javax.servlet.DispatcherType;
 import javax.servlet.FilterChain;
@@ -62,6 +63,7 @@ import org.apache.catalina.Manager;
 import org.apache.catalina.Realm;
 import org.apache.catalina.Session;
 import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.AsyncContextImpl;
 import org.apache.catalina.realm.GenericPrincipal;
 import org.apache.catalina.util.Enumerator;
 import org.apache.catalina.util.ParameterMap;
@@ -1465,19 +1467,17 @@ public class Request
 
     public AsyncContext startAsync() {
         // TODO SERVLET3 - async
+        return startAsync(getRequest(),response.getResponse());
+    }
+
+    public AsyncContext startAsync(ServletRequest request, ServletResponse response) {
         if (!isAsyncSupported()) throw new IllegalStateException("Not supported.");
         if (asyncContext==null) asyncContext = new AsyncContextImpl(this);
         else if (asyncContext.isStarted()) throw new IllegalStateException("Already started.");
         asyncContext.setStarted(getContext());
-        asyncContext.setServletRequest(getRequest());
-        asyncContext.setServletResponse(response.getResponse());
-        return asyncContext;
-    }
-
-    public AsyncContext startAsync(ServletRequest request, ServletResponse response) {
-        startAsync();
         asyncContext.setServletRequest(request);
         asyncContext.setServletResponse(response);
+        asyncContext.initEvent();
         //TODO SERVLET3 - async - need to retrieve the ServletContext here
         //or just the webapp classloader associated with to do 
         //run with start(Runnable)
diff --git a/java/org/apache/catalina/core/AsyncContextImpl.java b/java/org/apache/catalina/core/AsyncContextImpl.java
new file mode 100644 (file)
index 0000000..be4d6f1
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.connector.Request;
+import org.apache.coyote.ActionCode;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+/**
+ * 
+ * @author fhanik
+ *
+ */
+public class AsyncContextImpl implements AsyncContext {
+    
+    public static enum AsyncState {
+        NOT_STARTED, STARTED, DISPATCHING, DISPATCHED, COMPLETING, TIMING_OUT
+    }
+    
+    protected static Log log = LogFactory.getLog(AsyncContextImpl.class);
+    
+    private ServletRequest servletRequest = null;
+    private ServletResponse servletResponse = null;
+    private List<AsyncListenerWrapper> listeners = new ArrayList<AsyncListenerWrapper>();
+    private boolean hasOriginalRequestAndResponse = true;
+    private volatile Runnable dispatch = null;
+    private Context context = null;
+    private AtomicReference<AsyncState> state = new AtomicReference<AsyncState>(AsyncState.NOT_STARTED);
+    private long timeout = -1;
+    private AsyncEvent event = null;
+    
+    private Request request;
+    
+    public AsyncContextImpl(Request request) {
+        //TODO SERVLET3 - async
+        this.request = request;
+    }
+
+    @Override
+    public void complete() {
+        if (state.compareAndSet(AsyncState.STARTED, AsyncState.COMPLETING) ||
+            state.compareAndSet(AsyncState.DISPATCHED, AsyncState.COMPLETING)) {
+            // TODO SERVLET3 - async
+            AtomicBoolean dispatched = new AtomicBoolean(false);
+            request.getCoyoteRequest().action(ActionCode.ACTION_ASYNC_COMPLETE,dispatched);
+            if (!dispatched.get()) doInternalComplete(false);
+        } else {
+            throw new IllegalStateException("Complete not allowed. Invalid state:"+state.get());
+        }
+       
+    }
+
+    @Override
+    public void dispatch() {
+        HttpServletRequest sr = (HttpServletRequest)getServletRequest();
+        String path = sr.getRequestURI();
+        String cpath = sr.getContextPath();
+        if (cpath.length()>1) path = path.substring(cpath.length());
+        dispatch(path);
+    }
+
+    @Override
+    public void dispatch(String path) {
+        dispatch(request.getServletContext(),path);
+    }
+
+    @Override
+    public void dispatch(ServletContext context, String path) {
+        // TODO SERVLET3 - async
+        if (state.compareAndSet(AsyncState.STARTED, AsyncState.DISPATCHING) ||
+            state.compareAndSet(AsyncState.DISPATCHED, AsyncState.DISPATCHING)) {
+
+            if (request.getAttribute(ASYNC_REQUEST_URI)==null) {
+                request.setAttribute(ASYNC_REQUEST_URI, request.getRequestURI());
+                request.setAttribute(ASYNC_CONTEXT_PATH, request.getContextPath());
+                request.setAttribute(ASYNC_SERVLET_PATH, request.getServletPath());
+                request.setAttribute(ASYNC_QUERY_STRING, request.getQueryString());
+            }
+            final RequestDispatcher requestDispatcher = context.getRequestDispatcher(path);
+            final HttpServletRequest servletRequest = (HttpServletRequest)getRequest();
+            final HttpServletResponse servletResponse = (HttpServletResponse)getResponse();
+            Runnable run = new Runnable() {
+                public void run() {
+                    DispatcherType type = (DispatcherType)request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
+                    try {
+                        //piggy back on the request dispatcher to ensure that filters etc get called.
+                        //TODO SERVLET3 - async should this be include/forward or a new dispatch type
+                        //javadoc suggests include with the type of DispatcherType.ASYNC
+                        request.setAttribute(Globals.DISPATCHER_TYPE_ATTR, DispatcherType.ASYNC);
+                        requestDispatcher.include(servletRequest, servletResponse);
+                    }catch (Exception x) {
+                        //log.error("Async.dispatch",x);
+                        throw new RuntimeException(x);
+                    }finally {
+                        request.setAttribute(Globals.DISPATCHER_TYPE_ATTR, type);
+                    }
+                }
+            };
+            this.dispatch = run;
+            AtomicBoolean dispatched = new AtomicBoolean(false);
+            request.getCoyoteRequest().action(ActionCode.ACTION_ASYNC_DISPATCH, dispatched );
+            if (!dispatched.get()) {
+                try {
+                    doInternalDispatch();
+                }catch (ServletException sx) {
+                    throw new RuntimeException(sx);
+                }catch (IOException ix) {
+                    throw new RuntimeException(ix);
+                }
+            }
+
+        } else {
+            throw new IllegalStateException("Dispatch not allowed. Invalid state:"+state.get());
+        }
+    }
+
+    @Override
+    public ServletRequest getRequest() {
+        return getServletRequest();
+    }
+
+    @Override
+    public ServletResponse getResponse() {
+        return getServletResponse();
+    }
+
+    @Override
+    public void start(final Runnable run) {
+        if (state.compareAndSet(AsyncState.STARTED, AsyncState.DISPATCHING) ||
+            state.compareAndSet(AsyncState.DISPATCHED, AsyncState.DISPATCHING)) {
+            // TODO SERVLET3 - async
+            final ServletContext sctx = getServletRequest().getServletContext();
+            Runnable r = new Runnable() {
+                public void run() {
+                    //TODO SERVLET3 - async - set context class loader when running the task.
+                    try {
+                        
+                        run.run();
+                    }catch (Exception x) {
+                        log.error("Unable to run async task.",x);
+                    }
+                }
+            };
+            this.dispatch = r;
+            AtomicBoolean dispatched = new AtomicBoolean(false);
+            request.getCoyoteRequest().action(ActionCode.ACTION_ASYNC_DISPATCH, dispatched );
+            if (!dispatched.get()) {
+                try {
+                    doInternalDispatch();
+                }catch (ServletException sx) {
+                    throw new RuntimeException(sx);
+                }catch (IOException ix) {
+                    throw new RuntimeException(ix);
+                }
+            }
+        } else {
+            throw new IllegalStateException("Dispatch not allowed. Invalid state:"+state.get());
+        }
+    }
+    
+    public void addAsyncListener(AsyncListener listener) {
+        AsyncListenerWrapper wrapper = new AsyncListenerWrapper();
+        wrapper.setListener(listener);
+        listeners.add(wrapper);
+    }
+
+    public void addAsyncListener(AsyncListener listener, ServletRequest servletRequest, ServletResponse servletResponse) {
+        AsyncListenerWrapper wrapper = new AsyncListenerWrapper();
+        wrapper.setListener(listener);
+        listeners.add(wrapper);
+    }
+    
+    
+    public void recycle() {
+        servletRequest = null;
+        servletResponse = null;
+        listeners.clear();
+        hasOriginalRequestAndResponse = true;
+        state.set(AsyncState.NOT_STARTED);
+        context = null;
+        timeout = -1;
+        event = null;
+    }
+
+    public boolean isStarted() {
+        return (state.get()!=AsyncState.NOT_STARTED);
+    }
+
+    public void setStarted(Context context) {
+        if (state.compareAndSet(AsyncState.NOT_STARTED, AsyncState.STARTED)) {
+            this.context = context;
+        } else {
+            throw new IllegalStateException("Start illegal. Invalid state: "+state.get());
+        }
+    }
+
+    public ServletRequest getServletRequest() {
+        return servletRequest;
+    }
+
+    public void setServletRequest(ServletRequest servletRequest) {
+        this.servletRequest = servletRequest;
+    }
+
+    public ServletResponse getServletResponse() {
+        return servletResponse;
+    }
+
+    public void setServletResponse(ServletResponse servletResponse) {
+        this.servletResponse = servletResponse;
+    }
+
+    @Override
+    public boolean hasOriginalRequestAndResponse() {
+        return hasOriginalRequestAndResponse;
+    }
+
+    public void setHasOriginalRequestAndResponse(boolean hasOriginalRequestAndResponse) {
+        this.hasOriginalRequestAndResponse = hasOriginalRequestAndResponse;
+    }
+
+    public boolean isCompleted() {
+        return (state.get()==AsyncState.NOT_STARTED);
+    }
+
+    public void setCompleted() {
+        this.state.set(AsyncState.NOT_STARTED);
+    }
+    
+    public void doInternalDispatch() throws ServletException, IOException {
+        if (this.state.compareAndSet(AsyncState.TIMING_OUT, AsyncState.DISPATCHED)) {
+            log.debug("TIMING OUT!");
+            boolean listenerInvoked = false;
+            for (AsyncListenerWrapper listener : listeners) {
+                listener.fireOnTimeout(event);
+                listenerInvoked = true;
+            }
+            if (!listenerInvoked) {
+                ((HttpServletResponse)servletResponse).setStatus(500);
+            }
+            doInternalComplete(true);
+        } else if (this.state.compareAndSet(AsyncState.DISPATCHING, AsyncState.DISPATCHED)) {
+            if (this.dispatch!=null) {
+                try {
+                    dispatch.run();
+                } catch (RuntimeException x) {
+                    doInternalComplete(true);
+                    if (x.getCause() instanceof ServletException) throw (ServletException)x.getCause();
+                    if (x.getCause() instanceof IOException) throw (IOException)x.getCause();
+                    else throw new ServletException(x);
+                } finally {
+                    dispatch = null;
+                }
+            }
+        } else if (this.state.get()==AsyncState.COMPLETING) {
+            doInternalComplete(false);
+        } else {
+            throw new IllegalStateException("Dispatch illegal. Invalid state: "+state.get());
+        }
+    }
+    
+    public void doInternalComplete(boolean error) {
+        if (isCompleted()) return;
+        if (state.compareAndSet(AsyncState.STARTED, AsyncState.NOT_STARTED)) {
+            //this is the same as
+            //request.startAsync().complete();
+            recycle();
+        } else if (state.compareAndSet(AsyncState.COMPLETING, AsyncState.NOT_STARTED)) {
+            for (AsyncListenerWrapper wrapper : listeners) {
+                try {
+                    wrapper.fireOnComplete(event);
+                }catch (IOException x) {
+                    //how does this propagate, or should it?
+                    //TODO SERVLET3 - async 
+                    log.error("",x);
+                }
+            }
+            try {
+                if (!error) getResponse().flushBuffer();
+
+            }catch (Exception x) {
+                log.error("",x);
+            }
+            recycle();
+            
+        } else { 
+            throw new IllegalStateException("Complete illegal. Invalid state:"+state.get());
+        }
+    }
+    
+    public AsyncState getState() {
+        return state.get();
+    }
+    
+    protected void setState(AsyncState st) {
+        state.set(st);
+    }
+    
+    public long getAsyncTimeout() {
+        return timeout;
+    }
+    
+    public void setAsyncTimeout(long timeout) {
+        this.timeout = timeout;
+        request.getCoyoteRequest().action(ActionCode.ACTION_ASYNC_SETTIMEOUT,new Long(timeout));
+    }
+    
+    public void setTimeoutState() {
+        state.set(AsyncState.TIMING_OUT);
+    }
+    
+    public void initEvent() {
+        event = new AsyncEvent(getRequest(),getResponse()); 
+    }
+
+}
diff --git a/java/org/apache/catalina/core/AsyncListenerWrapper.java b/java/org/apache/catalina/core/AsyncListenerWrapper.java
new file mode 100644 (file)
index 0000000..0f4085a
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+* 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 javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+/**
+ * TODO SERVLET 3 - async 
+ * @author fhanik
+ *
+ */
+public class AsyncListenerWrapper {
+
+    private AsyncListener listener = null;
+    
+    public void fireOnComplete(AsyncEvent event) throws IOException {
+        // TODO SERVLET 3 - async
+        listener.onComplete(event);
+    }
+
+
+    public void fireOnTimeout(AsyncEvent event) throws IOException {
+        // TODO SERVLET 3 - async 
+        listener.onTimeout(event);
+    }
+
+    public AsyncListener getListener() {
+        return listener;
+    }
+
+    public void setListener(AsyncListener listener) {
+        this.listener = listener;
+    }
+    
+    
+
+}
index 2f0b266..74522f0 100644 (file)
@@ -33,7 +33,6 @@ import org.apache.catalina.CometEvent;
 import org.apache.catalina.CometProcessor;
 import org.apache.catalina.Context;
 import org.apache.catalina.Globals;
-import org.apache.catalina.connector.AsyncContextImpl;
 import org.apache.catalina.connector.ClientAbortException;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
index 777f077..6929141 100644 (file)
@@ -48,7 +48,7 @@ public class Async0 extends HttpServlet {
                     try {
                         Thread.currentThread().setName("Async0-Thread");
                         log.info("Putting AsyncThread to sleep");
-                        Thread.sleep(10*1000);
+                        Thread.sleep(2*1000);
                         log.info("Dispatching");
                         actx.dispatch();
                     }catch (InterruptedException x) {
index 98708e5..497bfb2 100644 (file)
@@ -42,7 +42,7 @@ public class Async1 extends HttpServlet {
                     String path = "/jsp/async/async1.jsp";
                     Thread.currentThread().setName("Async1-Thread");
                     log.info("Putting AsyncThread to sleep");
-                    Thread.sleep(10*1000);
+                    Thread.sleep(2*1000);
                     log.info("Dispatching to "+path);
                     actx.dispatch(path);
                 }catch (InterruptedException x) {
index 5cda1ec..0ef07ad 100644 (file)
@@ -41,7 +41,7 @@ public class Async2 extends HttpServlet {
                 try {
                     Thread.currentThread().setName("Async2-Thread");
                     log.info("Putting AsyncThread to sleep");
-                    Thread.sleep(10*1000);
+                    Thread.sleep(2*1000);
                     log.info("Writing data.");
                     actx.getResponse().getWriter().write("Output from background thread. Time:"+System.currentTimeMillis()+"\n");
                     actx.complete();