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