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