+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
import java.io.IOException;
public interface CometProcessor {
+ /**
+ * Begin will be called by the main service method of the servlet at the beginning
+ * of the processing of the connection. It can be used to initialize any relevant
+ * fields using the request and response objects.
+ *
+ * @param request
+ * @param response
+ * @throws IOException
+ * @throws ServletException
+ */
public void begin(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException;
+
+ /**
+ * End may be called to end the processing of the request. Fields that have
+ * been initialized in the begin method should be reset.
+ *
+ * @param request
+ * @param response
+ * @throws IOException
+ * @throws ServletException
+ */
public void end(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException;
+ /**
+ * 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.
+ *
+ * @param request
+ * @param response
+ * @throws IOException
+ * @throws ServletException
+ */
public void error(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException;
+
+ /**
+ * 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.
+ *
+ * @param request
+ * @param response
+ * @throws IOException
+ * @throws ServletException
+ */
public void read(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException;
\r
// -------------------------------------------------------- Adapter Methods\r
\r
+ \r
+ /**\r
+ * Event method.\r
+ * \r
+ * @return false to indicate an error, expected or not\r
+ */\r
+ public boolean event(org.apache.coyote.Request req, \r
+ org.apache.coyote.Response res, boolean error) {\r
+\r
+ Request request = (Request) req.getNote(ADAPTER_NOTES);\r
+ Response response = (Response) res.getNote(ADAPTER_NOTES);\r
+\r
+ if (request.getWrapper() != null) {\r
+ CometProcessor servlet = null;\r
+ try {\r
+ servlet = (CometProcessor) request.getWrapper().allocate();\r
+ } catch (Throwable t) {\r
+ log.error(sm.getString("coyoteAdapter.service"), t);\r
+ request.removeAttribute("org.apache.tomcat.comet");\r
+ return false;\r
+ }\r
+ try {\r
+ if (error) {\r
+ servlet.error(request.getRequest(), response.getResponse());\r
+ } else {\r
+ servlet.read(request.getRequest(), response.getResponse());\r
+ }\r
+ return (!error);\r
+ } catch (Throwable t) {\r
+ if (!(t instanceof IOException)) {\r
+ log.error(sm.getString("coyoteAdapter.service"), t);\r
+ }\r
+ request.removeAttribute("org.apache.tomcat.comet");\r
+ try {\r
+ servlet.error(request.getRequest(), response.getResponse());\r
+ } catch (Throwable th) {\r
+ log.error(sm.getString("coyoteAdapter.service"), th);\r
+ }\r
+ return false;\r
+ } finally {\r
+ // Recycle the wrapper request and response\r
+ if (request.getAttribute("org.apache.tomcat.comet") == null) {\r
+ request.recycle();\r
+ response.recycle();\r
+ }\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ \r
\r
/**\r
* Service method.\r
\r
}\r
\r
- // Comet processing\r
- if (request.getWrapper() != null \r
- && request.getWrapper() instanceof CometProcessor) {\r
- try {\r
- if (request.getAttribute("org.apache.tomcat.comet.error") != null) {\r
- ((CometProcessor) request.getWrapper()).error(request.getRequest(), response.getResponse());\r
- } else {\r
- ((CometProcessor) request.getWrapper()).read(request.getRequest(), response.getResponse());\r
- }\r
- } catch (IOException e) {\r
- ;\r
- } catch (Throwable t) {\r
- log.error(sm.getString("coyoteAdapter.service"), t);\r
- } finally {\r
- // Recycle the wrapper request and response\r
- if (request.getAttribute("org.apache.tomcat.comet") == null) {\r
- request.recycle();\r
- response.recycle();\r
- }\r
- }\r
- return;\r
- }\r
- \r
if (connector.getXpoweredBy()) {\r
response.addHeader("X-Powered-By", "Servlet/2.5");\r
}\r
connector.getContainer().getPipeline().getFirst().invoke(request, response);\r
}\r
\r
- if (request.getAttribute("org.apache.tomcat.comet.support") == Boolean.TRUE \r
- && request.getWrapper() instanceof CometProcessor) {\r
- request.setAttribute("org.apache.tomcat.comet", Boolean.TRUE);\r
+ if (request.getAttribute("org.apache.tomcat.comet") == Boolean.TRUE\r
+ && request.getWrapper().allocate() instanceof CometProcessor) {\r
comet = true;\r
}\r
\r
import java.io.IOException;
import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public void begin(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
-
+ request.setAttribute("org.apache.tomcat.comet", Boolean.TRUE);
}
public void end(HttpServletRequest request, HttpServletResponse response)
public void service(Request req, Response res)\r
throws Exception;\r
\r
+ public boolean event(Request req, Response res, boolean error)\r
+ throws Exception;\r
\r
}\r
\r
try {\r
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);\r
- if (error) {\r
- request.setAttribute("org.apache.tomcat.comet.error", Boolean.TRUE);\r
- }\r
- // FIXME: It is also possible to add a new "event" method in the adapter\r
- // or something similar\r
- adapter.service(request, response);\r
+ error = !adapter.event(request, response, error);\r
if (request.getAttribute("org.apache.tomcat.comet") == null) {\r
comet = false;\r
- endpoint.getCometPoller().remove(socket);\r
}\r
} catch (InterruptedIOException e) {\r
error = true;\r
endpoint.getPoller().add(socket);\r
return SocketState.OPEN;\r
} else {\r
+ endpoint.getCometPoller().add(socket);\r
return SocketState.LONG;\r
}\r
}\r
boolean keptAlive = false;\r
boolean openSocket = false;\r
\r
- while (!error && keepAlive) {\r
+ while (!error && keepAlive && !comet) {\r
\r
// Parsing the request header\r
try {\r
recycle();\r
return SocketState.CLOSED;\r
} else {\r
- endpoint.getCometPoller().add(socket);\r
return SocketState.LONG;\r
}\r
} else {\r
\r
public SocketState event(long socket, boolean error) {\r
Http11AprProcessor result = connections.get(socket);\r
+ \r
SocketState state = SocketState.CLOSED; \r
if (result != null) {\r
boolean recycle = error;\r
// processor.\r
connections.put(socket, processor);\r
localProcessor.set(null);\r
+ proto.ep.getCometPoller().add(socket);\r
}\r
return state;\r
\r
- } catch(java.net.SocketException e) {\r
+ } catch (java.net.SocketException e) {\r
// SocketExceptions are normal\r
Http11AprProtocol.log.debug\r
(sm.getString\r
pollers[i].destroy();\r
}\r
pollers = null;\r
+ for (int i = 0; i < cometPollers.length; i++) {\r
+ cometPollers[i].destroy();\r
+ }\r
+ cometPollers = null;\r
if (useSendfile) {\r
for (int i = 0; i < sendfiles.length; i++) {\r
sendfiles[i].destroy();\r
\r
protected long[] addS;\r
protected int addCount = 0;\r
- protected long[] removeS;\r
- protected int removeCount = 0;\r
\r
protected boolean comet = true;\r
\r
keepAliveCount = 0;\r
addS = new long[size];\r
addCount = 0;\r
- if (comet) {\r
- removeS = new long[size];\r
- }\r
- removeCount = 0;\r
}\r
\r
/**\r
for (int i = 0; i < addCount; i++) {\r
if (comet) {\r
processSocket(addS[i], true);\r
+ } else {\r
+ Socket.destroy(addS[i]);\r
}\r
- Socket.destroy(addS[i]);\r
- }\r
- // Close all sockets in the remove queue\r
- for (int i = 0; i < removeCount; i++) {\r
- if (comet) {\r
- processSocket(removeS[i], true);\r
- }\r
- Socket.destroy(removeS[i]);\r
}\r
// Close all sockets still in the poller\r
int rv = Poll.pollset(serverPollset, desc);\r
for (int n = 0; n < rv; n++) {\r
if (comet) {\r
processSocket(desc[n*2+1], true);\r
+ } else {\r
+ Socket.destroy(desc[n*2+1]);\r
}\r
- Socket.destroy(desc[n*2+1]);\r
}\r
}\r
Pool.destroy(pool);\r
keepAliveCount = 0;\r
addCount = 0;\r
- removeCount = 0;\r
}\r
\r
/**\r
// Can't do anything: close the socket right away\r
if (comet) {\r
processSocket(socket, true);\r
+ } else {\r
+ Socket.destroy(socket);\r
}\r
- Socket.destroy(socket);\r
return;\r
}\r
addS[addCount] = socket;\r
}\r
\r
/**\r
- * Remove specified socket and associated pool from the poller. The socket will\r
- * be added to a temporary array, and polled first after a maximum amount\r
- * of time equal to pollTime (in most cases, latency will be much lower,\r
- * however). Note that this is automatic, except if the poller is used for\r
- * comet.\r
- *\r
- * @param socket to remove from the poller\r
- */\r
- public void remove(long socket) {\r
- synchronized (this) {\r
- // Add socket to the list. Newly added sockets will wait\r
- // at most for pollTime before being polled\r
- if (removeCount >= removeS.length) {\r
- // Normally, it cannot happen ...\r
- Socket.destroy(socket);\r
- return;\r
- }\r
- removeS[removeCount] = socket;\r
- removeCount++;\r
- this.notify();\r
- }\r
- }\r
-\r
- /**\r
* The background thread that listens for incoming TCP/IP connections and\r
* hands them off to an appropriate processor.\r
*/\r
// Can't do anything: close the socket right away\r
if (comet) {\r
processSocket(addS[i], true);\r
+ } else {\r
+ Socket.destroy(addS[i]);\r
}\r
- Socket.destroy(addS[i]);\r
}\r
}\r
addCount = 0;\r
}\r
}\r
- // Remove sockets which are waiting to the poller\r
- if (removeCount > 0) {\r
- synchronized (this) {\r
- for (int i = 0; i < removeCount; i++) {\r
- int rv = Poll.remove(serverPollset, removeS[i]);\r
- }\r
- removeCount = 0;\r
- }\r
- }\r
\r
maintainTime += pollTime;\r
// Pool for the specified interval\r
- int rv = Poll.poll(serverPollset, pollTime, desc, !comet);\r
+ int rv = Poll.poll(serverPollset, pollTime, desc, true);\r
if (rv > 0) {\r
keepAliveCount -= rv;\r
for (int n = 0; n < rv; n++) {\r
if (((desc[n*2] & Poll.APR_POLLHUP) == Poll.APR_POLLHUP)\r
|| ((desc[n*2] & Poll.APR_POLLERR) == Poll.APR_POLLERR)\r
|| (comet && (!processSocket(desc[n*2+1], false))) \r
- || (!processSocket(desc[n*2+1]))) {\r
+ || (!comet && (!processSocket(desc[n*2+1])))) {\r
// Close socket and clear pool\r
if (comet) {\r
processSocket(desc[n*2+1], true);\r
- Poll.remove(serverPollset, desc[n*2+1]);\r
+ } else {\r
+ Socket.destroy(desc[n*2+1]);\r
}\r
- Socket.destroy(desc[n*2+1]);\r
continue;\r
}\r
}\r
// FIXME: should really close in case of timeout ?\r
// FIXME: maybe comet should use an extended timeout\r
processSocket(desc[n], true);\r
+ } else {\r
+ Socket.destroy(desc[n]);\r
}\r
- Socket.destroy(desc[n]);\r
}\r
}\r
}\r
// Close socket and pool\r
Socket.destroy(socket);\r
socket = 0;\r
- } else if (handler.process(socket) == Handler.SocketState.CLOSED) {\r
+ } else if ((!event) && (handler.process(socket) == Handler.SocketState.CLOSED)) {\r
// Close socket and pool\r
Socket.destroy(socket);\r
socket = 0;\r