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.jbossweb;
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.granite.gravity.AbstractGravityServlet;
031import org.granite.gravity.tomcat.CometIO;
032import org.granite.logging.Logger;
033import org.jboss.servlet.http.HttpEvent;
034import org.jboss.servlet.http.HttpEventServlet;
035
036/**
037 * @author Franck WOLFF
038 */
039public 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}