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}