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