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.ServletException;
027 import javax.servlet.http.HttpServletRequest;
028 import javax.servlet.http.HttpServletResponse;
029
030 import org.apache.catalina.CometEvent;
031 import org.apache.catalina.CometProcessor;
032 import org.granite.gravity.AbstractGravityServlet;
033 import org.granite.logging.Logger;
034
035 /**
036 * @author Franck WOLFF
037 */
038 public abstract class AbstractCometProcessor extends AbstractGravityServlet implements CometProcessor {
039
040 ///////////////////////////////////////////////////////////////////////////
041 // Fields.
042
043 private static final long serialVersionUID = 1L;
044 private static final Logger log = Logger.getLogger(AbstractCometProcessor.class);
045
046 private boolean longPollingTimeoutSupported = true;
047
048 ///////////////////////////////////////////////////////////////////////////
049 // Abstract methods.
050
051 public abstract CometIO createCometIO();
052
053 public abstract boolean handleRequest(CometEvent event, InputStream content)
054 throws IOException, ServletException;
055
056 public abstract boolean handleEnd(CometEvent event)
057 throws IOException, ServletException;
058
059 public abstract boolean handleError(CometEvent event)
060 throws IOException, ServletException;
061
062 ///////////////////////////////////////////////////////////////////////////
063 // CometProcessor implementation.
064
065 public void event(CometEvent event) throws IOException, ServletException {
066
067 // make sure we've got a valid CometEvent (should never happen)
068 if (!EventUtil.isValid(event)) {
069 log.error("Tomcat sent an invalid CometEvent: %s.%s", event.getEventType(), event.getEventSubType());
070 return;
071 }
072
073 if (log.isDebugEnabled()) {
074 log.debug(
075 "%s.%s: %s/%s",
076 event.getEventType(), event.getEventSubType(),
077 event.getHttpServletRequest(), event.getHttpServletResponse()
078 );
079 }
080
081 if (event.getEventType() == CometEvent.EventType.BEGIN)
082 begin(event);
083 else if (event.getEventType() == CometEvent.EventType.READ)
084 read(event);
085 else if (event.getEventType() == CometEvent.EventType.END)
086 end(event);
087 else if (event.getEventType() == CometEvent.EventType.ERROR)
088 error(event);
089 else
090 throw new ServletException("Unknown CometEvent type: " + event.getEventType());
091 }
092
093 ///////////////////////////////////////////////////////////////////////////
094 // Comet events processing.
095
096 protected void begin(CometEvent event) throws IOException, ServletException {
097 boolean close = true;
098 try {
099 // Event timeout isn't supported with APR connectors...
100 if (longPollingTimeoutSupported) {
101 try {
102 event.setTimeout((int)getLongPollingTimeout());
103 }
104 catch (Exception e) {
105 longPollingTimeoutSupported = false;
106 }
107 }
108
109 HttpServletRequest request = event.getHttpServletRequest();
110 CometIO io = createCometIO();
111 io.readFully(request.getInputStream());
112 request.getParameter(""); // Patch GDS-495
113
114 close = handleRequest(event, io.getInputStream());
115 }
116 finally {
117 if (close) {
118 try {
119 event.close();
120 } catch (Exception e) {
121 log.debug(e, "Could not close event: %s", EventUtil.toString(event));
122 }
123 }
124 }
125 }
126
127 protected void read(CometEvent event) {
128 // This implementation doesn't use asynchronous reads.
129 throw new RuntimeException("Unsupported operation");
130 }
131
132 protected void end(CometEvent event) throws IOException, ServletException {
133 boolean close = true;
134 try {
135 close = handleEnd(event);
136 }
137 finally {
138 if (close) {
139 try {
140 event.close();
141 } catch (Exception e) {
142 log.debug(e, "Could not close event: %s", EventUtil.toString(event));
143 }
144 }
145 }
146 }
147
148 protected void error(CometEvent event) throws IOException, ServletException {
149 boolean close = true;
150 try {
151 close = handleError(event);
152 }
153 finally {
154 if (close) {
155 try {
156 event.close();
157 } catch (Exception e) {
158 log.debug(e, "Could not close event: %s", EventUtil.toString(event));
159 }
160 }
161 }
162 }
163
164 ///////////////////////////////////////////////////////////////////////////
165 // Utility.
166
167 @Override
168 protected void service(HttpServletRequest request, HttpServletResponse response)
169 throws IOException, ServletException {
170 throw new ServletException("Not in a valid Comet configuration (use an APR or NIO connector)");
171 }
172 }