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}