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
021package org.granite.gravity.tomcat;
022
023import java.io.IOException;
024import java.io.InputStream;
025
026import javax.servlet.ServletException;
027import javax.servlet.http.HttpServletRequest;
028import javax.servlet.http.HttpServletResponse;
029
030import org.apache.catalina.CometEvent;
031import org.apache.catalina.CometProcessor;
032import org.granite.gravity.AbstractGravityServlet;
033import org.granite.logging.Logger;
034
035/**
036 * @author Franck WOLFF
037 */
038public 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}