From 7401b163e1dce7d9d8e65b1d1cd0d65e15750b8e Mon Sep 17 00:00:00 2001 From: fhanik Date: Wed, 15 Jul 2009 00:33:14 +0000 Subject: [PATCH] Start working on async, fairly similar to comet but much more convulated. I'm gonna do checkins in fairly small chunks so folks can tag along and help out. Instead of doing one giant checkin git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@794122 13f79535-47bb-0310-9956-ffa450edef68 --- .../catalina/connector/AsyncContextImpl.java | 161 +++++++++++++++++++++ .../catalina/connector/AsyncListenerWrapper.java | 72 +++++++++ .../apache/catalina/connector/CoyoteAdapter.java | 5 +- java/org/apache/catalina/connector/Request.java | 93 ++++++++---- java/org/apache/catalina/deploy/FilterDef.java | 10 ++ java/org/apache/catalina/startup/WebRuleSet.java | 5 + java/org/apache/coyote/ActionCode.java | 15 ++ .../apache/coyote/http11/Http11AprProcessor.java | 7 + .../apache/coyote/http11/Http11NioProcessor.java | 6 + java/org/apache/coyote/http11/Http11Processor.java | 6 + 10 files changed, 353 insertions(+), 27 deletions(-) create mode 100644 java/org/apache/catalina/connector/AsyncContextImpl.java create mode 100644 java/org/apache/catalina/connector/AsyncListenerWrapper.java diff --git a/java/org/apache/catalina/connector/AsyncContextImpl.java b/java/org/apache/catalina/connector/AsyncContextImpl.java new file mode 100644 index 000000000..06f843a0a --- /dev/null +++ b/java/org/apache/catalina/connector/AsyncContextImpl.java @@ -0,0 +1,161 @@ +/* + * 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 javax.servlet.AsyncContext; +import javax.servlet.AsyncListener; +import javax.servlet.ServletContext; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +/** + * + * @author fhanik + * + */ +public class AsyncContextImpl implements AsyncContext { + protected static Log log = LogFactory.getLog(AsyncContextImpl.class); + + private boolean started = false; + private ServletRequest servletRequest = null; + private ServletResponse servletResponse = null; + private List listeners = new ArrayList(); + private boolean hasOriginalRequestAndResponse = true; + + public AsyncContextImpl() { + //TODO SERVLET3 - async + } + + @Override + public void complete() { + // TODO SERVLET3 - async + + for (AsyncListenerWrapper wrapper : listeners) { + try { + wrapper.fireOnComplete(); + }catch (IOException x) { + //how does this propagate, or should it? + //TODO SERVLET3 - async + log.error("",x); + } + } + + } + + @Override + public void dispatch() { + // TODO SERVLET3 - async + + } + + @Override + public void dispatch(String path) { + // TODO SERVLET3 - async + + } + + @Override + public void dispatch(ServletContext context, String path) { + // TODO SERVLET3 - async + + } + + @Override + public ServletRequest getRequest() { + return getServletRequest(); + } + + @Override + public ServletResponse getResponse() { + return getServletResponse(); + } + + @Override + public void start(Runnable run) { + // TODO SERVLET3 - async + + } + + 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() { + started = false; + servletRequest = null; + servletResponse = null; + listeners.clear(); + hasOriginalRequestAndResponse = true; + } + + public boolean isStarted() { + return started; + } + + public void setStarted(boolean started) { + this.started = started; + } + + 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; + } + + + + + +} diff --git a/java/org/apache/catalina/connector/AsyncListenerWrapper.java b/java/org/apache/catalina/connector/AsyncListenerWrapper.java new file mode 100644 index 000000000..ffdad5783 --- /dev/null +++ b/java/org/apache/catalina/connector/AsyncListenerWrapper.java @@ -0,0 +1,72 @@ +/* +* 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; + } + + + +} diff --git a/java/org/apache/catalina/connector/CoyoteAdapter.java b/java/org/apache/catalina/connector/CoyoteAdapter.java index 34a74ee73..9258e2fb9 100644 --- a/java/org/apache/catalina/connector/CoyoteAdapter.java +++ b/java/org/apache/catalina/connector/CoyoteAdapter.java @@ -319,7 +319,10 @@ public class CoyoteAdapter } - if (!comet) { + if (request.isAsyncStarted()) { + //TODO SERVLET3 - async + res.action(ActionCode.ACTION_ASYNC_START, request.getAsyncContext()); + } else if (!comet) { response.finishResponse(); req.action(ActionCode.ACTION_POST_REQUEST , null); } diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java index 31ee397eb..f667b649c 100644 --- a/java/org/apache/catalina/connector/Request.java +++ b/java/org/apache/catalina/connector/Request.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.io.IOException; import java.io.BufferedReader; import java.io.UnsupportedEncodingException; +import java.nio.channels.IllegalSelectorException; import java.security.Principal; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -387,10 +388,29 @@ public class Request * Local address */ protected String localName = null; - + + /** + * asyncSupported + */ + protected boolean asyncSupported = true; + + /** + * AsyncContext + */ + protected AsyncContextImpl asyncContext = null; + + /** + * async timeout + */ + protected long asyncTimeout = 0; // --------------------------------------------------------- Public Methods + + + public void setAsyncSupported(boolean asyncSupported) { + this.asyncSupported = asyncSupported; + } /** * Release all object references, and initialize instance variables, in @@ -464,6 +484,9 @@ public class Request reader = null; } } + + asyncSupported = true; + if (asyncContext!=null) asyncContext.recycle(); } @@ -1436,51 +1459,69 @@ public class Request } public AsyncContext startAsync() { - // TODO SERVLET3 - return null; + // TODO SERVLET3 - async + if (!isAsyncSupported()) throw new IllegalStateException("Not supported."); + if (asyncContext==null) asyncContext = new AsyncContextImpl(); + else if (asyncContext.isStarted()) throw new IllegalStateException("Already started."); + asyncContext.setServletRequest(getRequest()); + asyncContext.setServletResponse(response.getResponse()); + asyncContext.setStarted(true); + return asyncContext; } - public AsyncContext startAsync(ServletRequest request, - ServletResponse response) { - // TODO SERVLET3 - return null; + public AsyncContext startAsync(ServletRequest request, ServletResponse response) { + startAsync(); + asyncContext.setServletRequest(request); + asyncContext.setServletResponse(response); + asyncContext.setHasOriginalRequestAndResponse(request==getRequest() && response==getResponse()); + return asyncContext; } public boolean isAsyncStarted() { - // TODO SERVLET3 - return false; + if (asyncContext==null) return false; + else return asyncContext.isStarted(); } public boolean isAsyncSupported() { - // TODO SERVLET3 - return false; + // TODO SERVLET3 - async + return this.asyncSupported; } public AsyncContext getAsyncContext() { - // TODO SERVLET3 - return null; + // TODO SERVLET3 - async + return this.asyncContext; } public void addAsyncListener(AsyncListener listener) { - // TODO SERVLET3 + // TODO SERVLET3 - async + if (isAsyncSupported() && isAsyncStarted()) { + this.asyncContext.addAsyncListener(listener); + } else { + throw new IllegalStateException("Async [Supported:"+isAsyncSupported()+"; Started:"+isAsyncStarted()+"]"); + } } - public void addAsyncListener(AsyncListener listener, - ServletRequest servletRequest, ServletResponse servletResponse) { - // TODO SERVLET3 + public void addAsyncListener(AsyncListener listener, ServletRequest servletRequest, ServletResponse servletResponse) { + // TODO SERVLET3 - async + if (isAsyncSupported() && isAsyncStarted()) { + this.asyncContext.addAsyncListener(listener,servletRequest,servletResponse); + } else { + throw new IllegalStateException("Async [Supported:"+isAsyncSupported()+"; Started:"+isAsyncStarted()+"]"); + } } public void setAsyncTimeout(long timeout) { - // TODO SERVLET3 + // TODO SERVLET3 - async + this.asyncTimeout = timeout; } public long getAsyncTimeout() { - // TODO SERVLET3 - return 0; + // TODO SERVLET3 - async + return asyncTimeout; } public DispatcherType getDispatcherType() { - // TODO SERVLET3 + // TODO SERVLET3 - dispatcher return null; } @@ -2241,26 +2282,26 @@ public class Request } public boolean authenticate(HttpServletResponse response) throws IOException { - // TODO Servlet 3 + // TODO Servlet 3 - authentication return false; } public void login(String username, String password) throws ServletException { - // TODO Servlet 3 + // TODO Servlet 3 - authentication } public void logout() throws ServletException { - // TODO Servlet 3 + // TODO Servlet 3 - authentication } public Iterable getParts() { - // TODO Servlet 3 + // TODO Servlet 3 - authentication return null; } public Part getPart(String name) throws IllegalArgumentException { - // TODO Servlet 3.0 + // TODO Servlet 3.0 - file upload return null; } diff --git a/java/org/apache/catalina/deploy/FilterDef.java b/java/org/apache/catalina/deploy/FilterDef.java index fa66c18ae..f0d1d19c8 100644 --- a/java/org/apache/catalina/deploy/FilterDef.java +++ b/java/org/apache/catalina/deploy/FilterDef.java @@ -134,6 +134,16 @@ public class FilterDef implements Serializable { public void setSmallIcon(String smallIcon) { this.smallIcon = smallIcon; } + + private boolean asyncSupported = false; + + public boolean isAsyncSupported() { + return asyncSupported; + } + + public void setAsyncSupported(boolean asyncSupported) { + this.asyncSupported = asyncSupported; + } // --------------------------------------------------------- Public Methods diff --git a/java/org/apache/catalina/startup/WebRuleSet.java b/java/org/apache/catalina/startup/WebRuleSet.java index 9bc7d55bc..b4b9457e2 100644 --- a/java/org/apache/catalina/startup/WebRuleSet.java +++ b/java/org/apache/catalina/startup/WebRuleSet.java @@ -171,6 +171,11 @@ public class WebRuleSet extends RuleSetBase { "setLargeIcon", 0); digester.addCallMethod(prefix + "web-app/filter/small-icon", "setSmallIcon", 0); + + //spec right now only has an annotation, not XML but we will add it + //TODO SERVLET3 - async + digester.addCallMethod(prefix + "web-app/filter/asyncSupported", + "setAsyncSupported", 0); digester.addCallMethod(prefix + "web-app/filter/init-param", "addInitParameter", 2); diff --git a/java/org/apache/coyote/ActionCode.java b/java/org/apache/coyote/ActionCode.java index ac2e0fcab..7219483e6 100644 --- a/java/org/apache/coyote/ActionCode.java +++ b/java/org/apache/coyote/ActionCode.java @@ -161,6 +161,21 @@ public final class ActionCode { */ public static final ActionCode ACTION_COMET_SETTIMEOUT = new ActionCode(25); + /** + * Callback for an async request + */ + public static final ActionCode ACTION_ASYNC_START = new ActionCode(26); + + /** + * Callback for an async call to {@link javax.servlet.AsyncContext#complete()} + */ + public static final ActionCode ACTION_ASYNC_COMPLETE = new ActionCode(27); + /** + * Callback for an async call to {@link javax.servlet.ServletRequest#setAsyncTimeout(long)} + */ + public static final ActionCode ACTION_ASYNC_SETTIMEOUT = new ActionCode(28); + + // ----------------------------------------------------------- Constructors int code; diff --git a/java/org/apache/coyote/http11/Http11AprProcessor.java b/java/org/apache/coyote/http11/Http11AprProcessor.java index 992045502..9fe459c21 100644 --- a/java/org/apache/coyote/http11/Http11AprProcessor.java +++ b/java/org/apache/coyote/http11/Http11AprProcessor.java @@ -1201,7 +1201,14 @@ public class Http11AprProcessor implements ActionHook { //no op } else if (actionCode == ActionCode.ACTION_COMET_SETTIMEOUT) { //no op + } else if (actionCode == ActionCode.ACTION_ASYNC_START) { + //TODO SERVLET3 - async + } else if (actionCode == ActionCode.ACTION_ASYNC_COMPLETE) { + //TODO SERVLET3 - async + } else if (actionCode == ActionCode.ACTION_ASYNC_SETTIMEOUT) { + //TODO SERVLET3 - async } + } diff --git a/java/org/apache/coyote/http11/Http11NioProcessor.java b/java/org/apache/coyote/http11/Http11NioProcessor.java index e68d8045f..830540976 100644 --- a/java/org/apache/coyote/http11/Http11NioProcessor.java +++ b/java/org/apache/coyote/http11/Http11NioProcessor.java @@ -1238,6 +1238,12 @@ public class Http11NioProcessor implements ActionHook { RequestInfo rp = request.getRequestProcessor(); if ( rp.getStage() != org.apache.coyote.Constants.STAGE_SERVICE ) //async handling attach.setTimeout(timeout); + } else if (actionCode == ActionCode.ACTION_ASYNC_START) { + //TODO SERVLET3 - async + } else if (actionCode == ActionCode.ACTION_ASYNC_COMPLETE) { + //TODO SERVLET3 - async + } else if (actionCode == ActionCode.ACTION_ASYNC_SETTIMEOUT) { + //TODO SERVLET3 - async } } diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java index 7d54c4804..f70f03f68 100644 --- a/java/org/apache/coyote/http11/Http11Processor.java +++ b/java/org/apache/coyote/http11/Http11Processor.java @@ -1106,6 +1106,12 @@ public class Http11Processor implements ActionHook { InternalInputBuffer internalBuffer = (InternalInputBuffer) request.getInputBuffer(); internalBuffer.addActiveFilter(savedBody); + } else if (actionCode == ActionCode.ACTION_ASYNC_START) { + //TODO SERVLET3 - async + } else if (actionCode == ActionCode.ACTION_ASYNC_COMPLETE) { + //TODO SERVLET3 - async + } else if (actionCode == ActionCode.ACTION_ASYNC_SETTIMEOUT) { + //TODO SERVLET3 - async } } -- 2.11.0