001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2014 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 */
022package org.granite.gravity.jbossweb;
023
024import org.granite.gravity.AbstractGravityServlet;
025import org.granite.logging.Logger;
026import org.jboss.servlet.http.HttpEvent;
027import org.jboss.servlet.http.HttpEventServlet;
028
029import javax.servlet.ServletException;
030import javax.servlet.http.HttpServletRequest;
031import javax.servlet.http.HttpServletResponse;
032import java.io.IOException;
033import java.io.InputStream;
034
035/**
036 * @author Franck WOLFF
037 */
038public abstract class AbstractHttpEventServlet extends AbstractGravityServlet implements HttpEventServlet {
039
040    ///////////////////////////////////////////////////////////////////////////
041    // Fields.
042
043    private static final long serialVersionUID = 1L;
044    private static final Logger log = Logger.getLogger(AbstractHttpEventServlet.class);
045    
046    private boolean longPollingTimeoutSupported = true;
047
048    ///////////////////////////////////////////////////////////////////////////
049    // Abstract methods.
050
051        public abstract CometIO createCometIO();
052        
053    public abstract boolean handleRequest(HttpEvent event, InputStream content)
054        throws IOException, ServletException;
055
056    public abstract boolean handleEnd(HttpEvent event)
057        throws IOException, ServletException;
058
059    public abstract boolean handleError(HttpEvent event)
060        throws IOException, ServletException;
061
062    ///////////////////////////////////////////////////////////////////////////
063    // CometProcessor implementation.
064
065    public void event(HttpEvent 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("JBossWeb sent an invalid HttpEvent: %s", event.getType());
070                return;
071        }
072
073        if (log.isDebugEnabled()) {
074                log.debug(
075                    "%s: %s/%s",
076                    event.getType(),
077                    event.getHttpServletRequest(), event.getHttpServletResponse()
078                );
079        }
080
081        if (event.getType() == HttpEvent.EventType.BEGIN)
082            begin(event);
083        else if (event.getType() == HttpEvent.EventType.READ)
084            read(event);
085        else if (event.getType() == HttpEvent.EventType.END)
086            end(event);
087        else if (event.getType() == HttpEvent.EventType.ERROR 
088                || event.getType() == HttpEvent.EventType.EOF 
089                || event.getType() == HttpEvent.EventType.TIMEOUT)
090            error(event);
091        else
092            throw new ServletException("Unknown HttpEvent type: " + event.getType());
093    }
094
095    ///////////////////////////////////////////////////////////////////////////
096    // Comet events processing.
097
098    protected void begin(HttpEvent event) throws IOException, ServletException {
099        boolean close = true;
100        try {
101                // Event timeout isn't supported with APR connectors...
102                if (longPollingTimeoutSupported) {
103                        try {
104                                event.setTimeout((int)getLongPollingTimeout());
105                        }
106                        catch (Exception e) {
107                                longPollingTimeoutSupported = false;
108                        }
109                }
110
111                HttpServletRequest request = event.getHttpServletRequest();
112            CometIO io = createCometIO();
113                io.readFully(request.getInputStream());
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(HttpEvent event) {
129                // This implementation doesn't use asynchronous reads.
130                throw new RuntimeException("Unsupported operation");
131    }
132    
133        protected void end(HttpEvent 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(HttpEvent 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}