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    }