From 1412ece53407f708d892ffc387bf1bbeea16aea3 Mon Sep 17 00:00:00 2001 From: fhanik Date: Thu, 7 Jun 2007 12:15:22 +0000 Subject: [PATCH] Simplified the API, no need for the IOExceptions Updated documentation, added in some notes about life cycle, more source code examples to come git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@545151 13f79535-47bb-0310-9956-ffa450edef68 --- java/org/apache/catalina/CometEvent.java | 22 ++- java/org/apache/catalina/CometProcessor.java | 12 +- .../apache/catalina/connector/CometEventImpl.java | 21 +-- webapps/docs/aio.xml | 176 ++++++++++++++++++--- 4 files changed, 180 insertions(+), 51 deletions(-) diff --git a/java/org/apache/catalina/CometEvent.java b/java/org/apache/catalina/CometEvent.java index 06ed2faa6..e296be7a8 100644 --- a/java/org/apache/catalina/CometEvent.java +++ b/java/org/apache/catalina/CometEvent.java @@ -26,7 +26,9 @@ import javax.servlet.http.HttpServletResponse; /** * The CometEvent interface. + * A comet event is the contract between the servlet container and the servlet implementation(CometProcessor) for handling comet connections. * + * @see CometProcessor * @author Filip Hanik * @author Remy Maucherat */ @@ -174,14 +176,14 @@ public interface CometEvent { * Tomcat Comet allows you to configure for additional options:
* the COMET_NON_BLOCKING bit signals whether writing and reading from the request * or writing to the response will be non blocking.
- * the COMET_NO_IO bit signals the container that you are not interested in - * receiving any IO events from the container. - * @param cometOptions int - the option bit set, see #COMET_NON_BLOCKING and #COMET_NO_IO - * @throws IOException - + * the COMET_BLOCKING bit signals the container you wish for read and write to be done in a blocking fashion + * @param cometOptions int - the option bit set * @throws IllegalStateException - if this method is invoked outside of the BEGIN event + * @see #CometConfiguration + * @see #isReadable() + * @see #isWriteable() */ - public void configure(CometConfiguration... options) - throws IOException, IllegalStateException; + public void configure(CometConfiguration... options) throws IllegalStateException; /** * Returns the configuration for this Comet connection @@ -202,21 +204,17 @@ public interface CometEvent { * Registers the Comet connection with the container for IO notifications. * These could be notifications * @param operations - * @throws IOException * @throws IllegalStateException - if you are trying to register with a socket that already is registered * or if the operation you are trying to register is invalid. */ - public void register(CometOperation... operations) - throws IOException, IllegalStateException; + public void register(CometOperation... operations) throws IllegalStateException; /** * Unregisters Comet operations for this CometConnection * @param operations CometOperation[] - * @throws IOException * @throws IllegalStateException */ - public void unregister(CometOperation... operations) - throws IOException, IllegalStateException; + public void unregister(CometOperation... operations) throws IllegalStateException; /** * Returns what the current IO notifications that the Comet diff --git a/java/org/apache/catalina/CometProcessor.java b/java/org/apache/catalina/CometProcessor.java index 16b57d994..9a3498d6e 100644 --- a/java/org/apache/catalina/CometProcessor.java +++ b/java/org/apache/catalina/CometProcessor.java @@ -28,7 +28,17 @@ import javax.servlet.Servlet; * asynchronous IO, recieving events when data is available for reading, and * being able to output data without the need for being invoked by the container. * Note: When this interface is implemented, the service method of the servlet will - * never be called, and will be replaced with a begin event. + * never be called, and will be replaced with a begin event. Should the connector you + * have configured not support Comet, the service method will be called, and the + * request/response will not be marked as comet, but instead behave like a regular + * Servlet
+ * + * A Comet request, aka Comet connection, referenced through the #CometEvent and the request/response pair + * and has a lifecycle somewhat different to a regular servlet.
+ * + * Read more about it in the Tomcat documentation about Advanced IO, + * + * */ public interface CometProcessor extends Servlet { diff --git a/java/org/apache/catalina/connector/CometEventImpl.java b/java/org/apache/catalina/connector/CometEventImpl.java index 26832ddb9..a98f65f8f 100644 --- a/java/org/apache/catalina/connector/CometEventImpl.java +++ b/java/org/apache/catalina/connector/CometEventImpl.java @@ -19,6 +19,7 @@ package org.apache.catalina.connector; import java.io.IOException; +import java.util.Arrays; import java.util.HashSet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -28,8 +29,6 @@ import org.apache.catalina.CometEvent; import org.apache.catalina.util.StringManager; import org.apache.coyote.ActionCode; import org.apache.tomcat.util.net.PollerInterest; -import java.util.Arrays; -import org.apache.tomcat.util.MutableBoolean; public class CometEventImpl implements CometEvent { @@ -43,11 +42,8 @@ public class CometEventImpl implements CometEvent { public CometEventImpl(Request request, Response response) { this.request = request; this.response = response; - try { - this.register(CometOperation.OP_READ); - }catch ( IOException x ) { - throw new IllegalStateException(x.getMessage(),x); - } + //default behavior is to only listen for read events + register(CometOperation.OP_READ); } @@ -99,6 +95,8 @@ public class CometEventImpl implements CometEvent { public void clear() { request = null; response = null; + cometConfigurations.clear(); + cometOperations.clear(); } public void setEventType(EventType eventType) { @@ -153,8 +151,7 @@ public class CometEventImpl implements CometEvent { return cometOperations.contains(op); } - public void configure(CometEvent.CometConfiguration... options) - throws IOException, IllegalStateException { + public void configure(CometEvent.CometConfiguration... options) throws IllegalStateException { checkWorkerThread(); cometConfigurations.clear(); for (CometEvent.CometConfiguration cc : options) { @@ -163,15 +160,13 @@ public class CometEventImpl implements CometEvent { request.action(ActionCode.ACTION_COMET_CONFIGURE,options); } - public void register(CometEvent.CometOperation... operations) - throws IOException, IllegalStateException { + public void register(CometEvent.CometOperation... operations) throws IllegalStateException { //add it to the registered set cometOperations.addAll(Arrays.asList(operations)); request.action(ActionCode.ACTION_COMET_REGISTER, translate(cometOperations.toArray(new CometOperation[0]))); } - public void unregister(CometOperation... operations) - throws IOException, IllegalStateException { + public void unregister(CometOperation... operations) throws IllegalStateException { //remove from the registered set cometOperations.removeAll(Arrays.asList(operations)); request.action(ActionCode.ACTION_COMET_REGISTER, translate(cometOperations.toArray(new CometOperation[0]))); diff --git a/webapps/docs/aio.xml b/webapps/docs/aio.xml index 62e0e9bd5..520ea91c2 100644 --- a/webapps/docs/aio.xml +++ b/webapps/docs/aio.xml @@ -9,6 +9,7 @@ Advanced IO and Tomcat Remy Maucherat + Filip Hanik @@ -39,6 +40,118 @@ other source).

+ +

+ Comet Connection - For the readability of this document we will be referring an open ended HTTP request and an open + ended HTTP response that are mapped to a Cometprocessor as a "Comet Connection". +

+

+ Comet Processor - a servlet that implements the org.apache.catalina.CometProcessor interface. + Used to process comet connections and the events on the connection +

+

+ Comet Event - an event triggered by the container related to a Comet Connection +

+

+ Open Ended Request - a HTTP request that is either chunked or has a very large content length, allowing + data to be sent at different times. This is how Tomcat gets around the request/response methodology of + the HTTP protocol allowing Comet Processors to send and receive data on the socket synchronously and asynchronously. +

+

+ Open Ended Response - see Open Ended Request +

+

+ Active Comet Connection, a comet connection that currently is associated with a TCP connection and an open ended request/response. +

+

+ Blocking Comet Connection, an invocation of the read/write methods will block until data was received/sent. +

+

+ Non blocking Comet Connection, an invocation of the read/write methods will not block. +

+

+ Comet operation - comet connections can register themselves for a set of events to be + notified of. The different operations are:
+ OP_READ - results in a READ event when data has been received
+ OP_WRITE - results in a WRITE event when the socket is ready to receive data
+ OP_CALLBACK - results in a CALLBACK event on a Tomcat thread. +

+

+ Connection Centric - Comet connections are based on actual IO events on a TCP connection from the TCP layer. + This is different from servlets that are based on HTTP requests and responses, by the HTTP protocol above the TCP layer. +

+

+ Closing a comet connection - may not actually mean that the underlying TCP connection is closed. Tomcat still + respects the maxKeepAliveRequests attribute of the connector, and may decide to keep the connection + open. This is the case in the event of a connection timeout, the event ERROR/TIMEOUT is signaled and the + TCP connection is reused for the next HTTP request. +

+
+ + +

+ The lifecycle and event cycle of a Comet Connection is slightly different than a regular servlet. + Instead the life/event cycle is very "connection centric" and based on IO events, rater then + a request/response cycle like a normal HTTP request. This is the most common mistake developers make when they + start writing comet servlets, is that they don't realize that it is all based around these different events, and + that the events are some what connection centric. +

+

+ A comet interaction is started by the container invoking the event method on the Comet Processor + and the event will have a BEGIN type. For a deeper explanation of types, keep reading. + Once a BEGIN event has been invoked, the Comet Connection is active. At this type, the Comet Event object + reference can be used to reference the HttpServletRequest/HttpServletResponse objects for asynchronous actions + such as reading or writing from the streams or readers/writers. At this point the comet connection is considered active + or initiated. +

+

+ Once the Comet connection is active, the comet processor will receive events from the container. + These events are defined in the section below.
+ A comet processor may register itself for events, and receive the events when they happen. + In a similar fashion, a comet processor may unregister itself from comet events and + use the isReadable/isWriteable methods on the comet event to detect the state + of the comet connection. The registered events will be in effect until they are unregistered, + the comet connection is closed or terminated. + By default a comet connection gets registered for READ events upon initiation. +
+ The event registration can be compared to poll or select structures of different operating systems, + and also bear some resemblances with the java.nio API. +

+

+ Since Comet Connections deal directly with the IO layer of the container/connection and read and writes can be done + asynchronously, caution and care should be excersized when programming this. the comet connection itself is not thread safe + as such an implementation would suffer performance issues, instead it is up to the comet processor developer to ensure that + thread safety is reached and properly handled. + As an example, registering the comet connection for READ events and at the same time performing asynchronous reads + on the input stream or the reader, can cause data corruption and dead locks. + Same way, using multiple threads to do writes to the output stream or writer can have the same effect. +
To be thread safe, one can synchronize on the event object, but you will also need to coordinate it with + events that are registered. +

+

+ There is an event that is not IO based, that is the CometEvent.EventType.CALLBACK event. + This is an event that is forced by Tomcat when the OP_CALLBACK operation is registered. + Using the OP_CALLBACK operation, Tomcat will spawn a Tomcat worker thread that you can use to piggy back + on for reading/writing data or performing other operations, and not having to have spawn and synchronize + your own background threads.
+ An example scenarion would be where you have one thread pulling content for different comet clients. + When the background thread has content for a client, it can store it in the request object as an attribute, + then register for the OP_CALLBACK event. Once the CALLBACK event, then the application can use Tomcat's worker + thread to write the data to the client.
+ Tomcat guarantees that only one thread will be invoking the CometProcessor event for a particular client, + so by using the Tomcat worker threads to perform your actions, you are thread safe without the expense of + locks or synchronized methods.
+ Another usage scenario for the CALLBACK event, is when you close the comet connection asynchronously and you want + it processed by tomcat without depending on a timeout or another IO event. This would look like + + ... + event.close(); + event.register(CometEvent.CometOperation.OP_CALLBACK); + ... + +

+
+

@@ -55,42 +168,54 @@

@@ -284,11 +409,12 @@ public class ChatServlet

If you are using the NIO connector, you can set individual timeouts for your different comet connections. To set a timeout, simple set a request attribute like the following code shows: - CometEvent event.... event.setTimeout(30*1000); or - event.getHttpServletRequest().setAttribute("org.apache.tomcat.comet.timeout", new Integer(30 * 1000)); - This sets the timeout to 30 seconds. - Important note, in order to set this timeout, it has to be done on the BEGIN event. - The default value is soTimeout + CometEvent event.... event.setTimeout(30*1000); +
You can set the timeout on the comet connection at any point in time, even asynchronously. + Setting a timeout to 1 (one milliseconds) doesn't guarantee that it will timeout at that time. + Setting the timeout gurantees that Tomcat wont timeout the connection before the connection has been idle + for the configured time. The time it actually times out depends on many factors, such as how busy the server is, + when the last timeout scan was run, etc., but generally a timeout will occur fairly close to its configured value.

If you are using the APR connector, all Comet connections will have the same timeout value. It is soTimeout*50

-- 2.11.0