Author: remm
Date: Mon Oct  2 17:17:20 2006
New Revision: 452281

URL: http://svn.apache.org/viewvc?view=rev&rev=452281
Log:
- First pass at docs.

Modified:
    tomcat/tc6.0.x/trunk/webapps/docs/aio.xml

Modified: tomcat/tc6.0.x/trunk/webapps/docs/aio.xml
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/aio.xml?view=diff&rev=452281&r1=452280&r2=452281
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/docs/aio.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/docs/aio.xml Mon Oct  2 17:17:20 2006
@@ -16,7 +16,263 @@
   <section name="Introduction">
 
   <p>
+    With usage of APR or NIO APIs as the basis of its connectors, Tomcat is 
+    able to provide a number of extensions over the regular blocking IO 
+    as provided with support for the Servlet API.
   </p>
+
+  </section>
+
+  <section name="Comet support">
+
+  <p>
+    Comet support allows a servlet to process IO aynchronously, recieving
+    events when data is available for reading on the connection (rather than
+    always using a blocking read), and writing data back on connections
+    asychnonously (most likely responding to some event raised from some
+    other source).
+  </p>
+
+  <subsection name="CometEvent">
+  
+  <p>
+    Servlets which implement the 
<code>org.apache.catalina.CometProcessor</code>
+    interface will have their event method invoked rather than the usual 
service
+    method, according to the event which occurred. The event object gives
+    access to the usual request and response objects, which may be used in the
+    usual way. The main difference is that those objects remain valid and fully
+    functional at any time between processing of the BEGIN event until 
processing
+    an END or ERROR event.
+    The following event types exist:
+  </p>
+  
+  <ul>
+  <li>EventType.BEGIN: will be called at the beginning 
+     of the processing of the connection. It can be used to initialize any 
relevant 
+     fields using the request and response objects. Between the end of the 
processing 
+     of this event, and the beginning of the processing of the end or error 
events,
+     it is possible to use the response object to write data on the open 
connection.
+     Note that the response object and depedent OutputStream and Writer are 
still 
+     not synchronized, so when they are accessed by multiple threads, 
+     synchronization is mandatory. After processing the initial event, the 
request 
+     is considered to be committed.</li>
+  <li>EventType.READ: This indicates that input data is available, and that 
one read can be made
+     without blocking. The available and ready methods of the InputStream or
+     Reader may be used to determine if there is a risk of blocking: the 
servlet
+     should read while data is reported available, and can make one additional 
read
+     without blocking. When encountering a read error or an EOF, the servlet 
MUST
+     report it by either returning false or throwing an exception such as an 
+     IOException. This will cause the error event to be invoked, and the 
connection
+     will be closed. It is not allowed to attempt reading data from the 
request object
+     outside of the execution of this method.</li>
+  <li>EventType.END: End may be called to end the processing of the request. 
Fields that have
+     been initialized in the begin method should be reset. After this event has
+     been processed, the request and response objects, as well as all their 
dependent
+     objects will be recycled and used to process other requests.</li>
+  <li>EventType.ERROR: Error will be called by the container in the case where 
an IO exception
+     or a similar unrecoverable error occurs on the connection. Fields that 
have
+     been initialized in the begin method should be reset. After this event has
+     been processed, the request and response objects, as well as all their 
dependent
+     objects will be recycled and used to process other requests.</li>
+  </ul>
+
+  <p>
+    As described above, the typical lifecycle of a Comet request will consist 
in a series of
+    events such as: BEGIN -> READ -> READ -> READ -> ERROR/TIMEOUT. At any 
time, the servlet 
+    may end processing of the request by using the close method of the event 
object.
+  </p>
+  
+  </subsection>
+
+  <subsection name="CometFilter">
+  
+  <p>
+    Similar to regular filters, a filter chain is invoked when comet events 
are processed.
+    These filters should implement the CometFilter interface (which works in 
the same way as 
+    the regular Filter interface), and should be declared and mapped in the 
deployment
+    descriptor in the same way as a regular filter. The filter chain when 
processing an event
+    will only include filters which match all the usual mapping rules, and 
also implement
+    the CometFiler interface.
+  </p>
+  
+  </subsection>
+
+  <subsection name="Example code">
+  
+  <p>
+    The following pseudo code servlet implments asynchronous chat 
functionality using the API
+    described above:
+  </p>
+  
+  <source>
+public class ChatServlet
+    extends HttpServlet implements CometProcessor {
+
+    protected ArrayList&lt;HttpServletResponse> connections = 
+        new ArrayList&lt;HttpServletResponse>();
+    protected MessageSender messageSender = null;
+    
+    public void init() throws ServletException {
+        messageSender = new MessageSender();
+        Thread messageSenderThread = 
+            new Thread(messageSender, "MessageSender[" + 
getServletContext().getContextPath() + "]");
+        messageSenderThread.setDaemon(true);
+        messageSenderThread.start();
+    }
+
+    public void destroy() {
+        connections.clear();
+        messageSender.stop();
+        messageSender = null;
+    }
+
+    /**
+     * Process the given Comet event.
+     * 
+     * @param event The Comet event that will be processed
+     * @throws IOException
+     * @throws ServletException
+     */
+    public void event(CometEvent event)
+        throws IOException, ServletException {
+        HttpServletRequest request = event.getHttpServletRequest();
+        HttpServletResponse response = event.getHttpServletResponse();
+        if (event.getEventType() == CometEvent.EventType.BEGIN) {
+            log("Begin for session: " + request.getSession(true).getId());
+            PrintWriter writer = response.getWriter();
+            writer.println("&lt;!doctype html public \"-//w3c//dtd html 4.0 
transitional//en\">");
+            writer.println("&lt;head>&lt;title>JSP 
Chat&lt;/title>&lt;/head>&lt;body bgcolor=\"#FFFFFF\">");
+            writer.flush();
+            synchronized(connections) {
+                connections.add(response);
+            }
+        } else if (event.getEventType() == CometEvent.EventType.ERROR) {
+            log("Error for session: " + request.getSession(true).getId());
+            synchronized(connections) {
+                connections.remove(response);
+            }
+            event.close();
+        } else if (event.getEventType() == CometEvent.EventType.END) {
+            log("End for session: " + request.getSession(true).getId());
+            synchronized(connections) {
+                connections.remove(response);
+            }
+            PrintWriter writer = response.getWriter();
+            writer.println("&lt;/body>&lt;/html>");
+            event.close();
+        } else if (event.getEventType() == CometEvent.EventType.READ) {
+            InputStream is = request.getInputStream();
+            byte[] buf = new byte[512];
+            do {
+                int n = is.read(buf);
+                if (n > 0) {
+                    log("Read " + n + " bytes: " + new String(buf, 0, n) 
+                            + " for session: " + 
request.getSession(true).getId());
+                } else if (n &lt; 0) {
+                    error(event, request, response);
+                    return;
+                }
+            } while (is.available() > 0);
+        }
+    }
+
+    public class MessageSender implements Runnable {
+
+        protected boolean running = true;
+        protected ArrayList&lt;String> messages = new ArrayList&lt;String>();
+        
+        public MessageSender() {
+        }
+        
+        public void stop() {
+            running = false;
+        }
+
+        /**
+         * Add message for sending.
+         */
+        public void send(String user, String message) {
+            synchronized (messages) {
+                messages.add("[" + user + "]: " + message);
+                messages.notify();
+            }
+        }
+
+        public void run() {
+
+            while (running) {
+
+                if (messages.size() == 0) {
+                    try {
+                        synchronized (messages) {
+                            messages.wait();
+                        }
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+
+                synchronized (connections) {
+                    String[] pendingMessages = null;
+                    synchronized (messages) {
+                        pendingMessages = messages.toArray(new String[0]);
+                        messages.clear();
+                    }
+                    // Send any pending message on all the open connections
+                    for (int i = 0; i &lt; connections.size(); i++) {
+                        try {
+                            PrintWriter writer = 
connections.get(i).getWriter();
+                            for (int j = 0; j &lt; pendingMessages.length; 
j++) {
+                                writer.println(pendingMessages[j] + "&lt;br>");
+                            }
+                            writer.flush();
+                        } catch (IOException e) {
+                            log("IOExeption sending message", e);
+                        }
+                    }
+                }
+
+            }
+
+        }
+
+    }
+
+}
+  </source>
+  
+  </subsection>
+
+  </section>
+
+  <section name="Asynchronous writes">
+
+  <p>
+    When APR is enabled, Tomcat supports using sendfile to send large static 
files.
+    These writes, as soon as the system load increases, will be performed 
+    asynchronously in the most efficient way. Instead of sending a large 
response using
+    blocking writes, it is possible to write content to a static file, and 
write it
+    using a sendfile code. A caching valve could take advantage of this to 
cache the
+    response data in a file rather than store it in memory. Sendfile support is
+    available if the request attribute 
<code>org.apache.tomcat.sendfile.support</code>
+    is set to <code>Boolean.TRUE</code>.
+  </p>
+  
+  <p>
+    Any servlet can instruct Tomcat to perform a sendfile call by setting the 
appropriate
+    response attributes. When using sendfile, it is best to ensure that 
neither the
+    request or response have been wrapped, since as the response body will be 
sent later
+    by the connector itself, it cannot be filtered. Other than setting the 3 
needed 
+    response attributes, the servlet should not send any response data, but it 
may use
+    any method which will result in modifying the response header (like 
setting cookies).
+  </p>
+  
+  <ul>
+  <li>org.apache.tomcat.sendfile.filename: Canonical filename of the file 
which will be sent as
+      a String</li>
+  <li>org.apache.tomcat.sendfile.start: Start offset as a Long</li>
+  <li>org.apache.tomcat.sendfile.start: End offset as a Long</li>
+  </ul>
 
   </section>
 



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to