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<HttpServletResponse> connections = + new ArrayList<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("<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">"); + writer.println("<head><title>JSP Chat</title></head><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("</body></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 < 0) { + error(event, request, response); + return; + } + } while (is.available() > 0); + } + } + + public class MessageSender implements Runnable { + + protected boolean running = true; + protected ArrayList<String> messages = new ArrayList<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 < connections.size(); i++) { + try { + PrintWriter writer = connections.get(i).getWriter(); + for (int j = 0; j < pendingMessages.length; j++) { + writer.println(pendingMessages[j] + "<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]