001 /**
002 * GRANITE DATA SERVICES
003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 * This file is part of the Granite Data Services Platform.
006 *
007 * Granite Data Services is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * Granite Data Services is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 * General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 * USA, or see <http://www.gnu.org/licenses/>.
021 */
022 package org.granite.gravity.tomcat;
023
024 import java.io.IOException;
025 import java.io.InputStream;
026
027 import javax.servlet.ServletException;
028 import javax.servlet.http.HttpServletRequest;
029 import javax.servlet.http.HttpServletResponse;
030
031 import org.apache.catalina.CometEvent;
032 import org.apache.catalina.CometProcessor;
033 import org.granite.gravity.AbstractGravityServlet;
034 import org.granite.logging.Logger;
035
036 /**
037 * @author Franck WOLFF
038 */
039 public abstract class AbstractCometProcessor extends AbstractGravityServlet implements CometProcessor {
040
041 ///////////////////////////////////////////////////////////////////////////
042 // Fields.
043
044 private static final long serialVersionUID = 1L;
045 private static final Logger log = Logger.getLogger(AbstractCometProcessor.class);
046
047 private boolean longPollingTimeoutSupported = true;
048
049 ///////////////////////////////////////////////////////////////////////////
050 // Abstract methods.
051
052 public abstract CometIO createCometIO();
053
054 public abstract boolean handleRequest(CometEvent event, InputStream content)
055 throws IOException, ServletException;
056
057 public abstract boolean handleEnd(CometEvent event)
058 throws IOException, ServletException;
059
060 public abstract boolean handleError(CometEvent event)
061 throws IOException, ServletException;
062
063 ///////////////////////////////////////////////////////////////////////////
064 // CometProcessor implementation.
065
066 public void event(CometEvent event) throws IOException, ServletException {
067
068 // make sure we've got a valid CometEvent (should never happen)
069 if (!EventUtil.isValid(event)) {
070 log.error("Tomcat sent an invalid CometEvent: %s.%s", event.getEventType(), event.getEventSubType());
071 return;
072 }
073
074 if (log.isDebugEnabled()) {
075 log.debug(
076 "%s.%s: %s/%s",
077 event.getEventType(), event.getEventSubType(),
078 event.getHttpServletRequest(), event.getHttpServletResponse()
079 );
080 }
081
082 if (event.getEventType() == CometEvent.EventType.BEGIN)
083 begin(event);
084 else if (event.getEventType() == CometEvent.EventType.READ)
085 read(event);
086 else if (event.getEventType() == CometEvent.EventType.END)
087 end(event);
088 else if (event.getEventType() == CometEvent.EventType.ERROR)
089 error(event);
090 else
091 throw new ServletException("Unknown CometEvent type: " + event.getEventType());
092 }
093
094 ///////////////////////////////////////////////////////////////////////////
095 // Comet events processing.
096
097 protected void begin(CometEvent event) throws IOException, ServletException {
098 boolean close = true;
099 try {
100 // Event timeout isn't supported with APR connectors...
101 if (longPollingTimeoutSupported) {
102 try {
103 event.setTimeout((int)getLongPollingTimeout());
104 }
105 catch (Exception e) {
106 longPollingTimeoutSupported = false;
107 }
108 }
109
110 HttpServletRequest request = event.getHttpServletRequest();
111 CometIO io = createCometIO();
112 io.readFully(request.getInputStream());
113 request.getParameter(""); // Patch GDS-495
114
115 close = handleRequest(event, io.getInputStream());
116 }
117 finally {
118 if (close) {
119 try {
120 event.close();
121 } catch (Exception e) {
122 log.debug(e, "Could not close event: %s", EventUtil.toString(event));
123 }
124 }
125 }
126 }
127
128 protected void read(CometEvent event) {
129 // This implementation doesn't use asynchronous reads.
130 throw new RuntimeException("Unsupported operation");
131 }
132
133 protected void end(CometEvent event) throws IOException, ServletException {
134 boolean close = true;
135 try {
136 close = handleEnd(event);
137 }
138 finally {
139 if (close) {
140 try {
141 event.close();
142 } catch (Exception e) {
143 log.debug(e, "Could not close event: %s", EventUtil.toString(event));
144 }
145 }
146 }
147 }
148
149 protected void error(CometEvent event) throws IOException, ServletException {
150 boolean close = true;
151 try {
152 close = handleError(event);
153 }
154 finally {
155 if (close) {
156 try {
157 event.close();
158 } catch (Exception e) {
159 log.debug(e, "Could not close event: %s", EventUtil.toString(event));
160 }
161 }
162 }
163 }
164
165 ///////////////////////////////////////////////////////////////////////////
166 // Utility.
167
168 @Override
169 protected void service(HttpServletRequest request, HttpServletResponse response)
170 throws IOException, ServletException {
171 throw new ServletException("Not in a valid Comet configuration (use an APR or NIO connector)");
172 }
173 }