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