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    }