|
Tomcat example source code file (aio.xml)
The Tomcat aio.xml source code
<?xml version="1.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.
-->
<!DOCTYPE document [
<!ENTITY project SYSTEM "project.xml">
]>
<document url="aio.html">
&project;
<properties>
<title>Advanced IO and Tomcat
<author>Remy Maucherat
</properties>
<body>
<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>
<p>
<b>IMPORTANT NOTE: Usage of these features requires using the APR or NIO
HTTP connectors. The classic java.io HTTP connector and the AJP connectors
do not support them.</b>
</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
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
should read while data is reported available. When encountering a read error,
the servlet should report it by propagating the exception properly. Throwing
an exception will cause the error event to be invoked, and the connection
will be closed.
Alternately, it is also possible to catch any exception, perform clean up
on any data structure the servlet may be using, and using the close method
of the event. It is not allowed to attempt reading data from the request
object outside of the execution of this method.<br/>
On some platforms, like Windows, a client disconnect is indicated by a READ event.
Reading from the stream may result in -1, an IOException or an EOFException.
Make sure you properly handle all these three cases.
If you don't catch the IOException, Tomcat will instantly invoke your event chain with an ERROR as
it catches the error for you, and you will be notified of the error at that time.
</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. End will also be
called when data is available and the end of file is reached on the request input
(this usually indicates the client has pipelined a request).</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>
There are some event subtypes which allow finer processing of events (note: some of these
events require usage of the org.apache.catalina.valves.CometConnectionManagerValve valve):
</p>
<ul>
<li>EventSubType.TIMEOUT: The connection timed out (sub type of ERROR); note that this ERROR
type is not fatal, and the connection will not be closed unless the servlet uses the close
method of the event.
</li>
<li>EventSubType.CLIENT_DISCONNECT: The client connection was closed (sub type of ERROR).
method of the event.
</li>
<li>EventSubType.IOEXCEPTION: An IO exception occurred, such as invalid content, for example,
an invalid chunk block (sub type of ERROR).
</li>
<li>EventSubType.WEBAPP_RELOAD: The web application is being reloaded (sub type of END).
</li>
<li>EventSubType.SESSION_END: The servlet ended the session (sub type of END).
</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); //can throw an IOException
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>
<subsection name="Comet timeouts">
<p>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:
<source>CometEvent event.... event.setTimeout(30*1000); or
<source>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 <code>BEGIN event.
The default value is <code>soTimeout
</p>
<p>If you are using the APR connector, all Comet connections will have the same timeout value. It is
Other Tomcat examples (source code examples)Here is a short list of links related to this Tomcat aio.xml source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.