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.messaging.webapp; 023 024import java.io.BufferedInputStream; 025import java.io.IOException; 026import java.io.InputStream; 027import java.io.OutputStream; 028 029import javax.servlet.Filter; 030import javax.servlet.FilterChain; 031import javax.servlet.FilterConfig; 032import javax.servlet.ServletException; 033import javax.servlet.ServletRequest; 034import javax.servlet.ServletResponse; 035import javax.servlet.http.HttpServletRequest; 036import javax.servlet.http.HttpServletResponse; 037 038import org.granite.config.GraniteConfig; 039import org.granite.config.GraniteConfigListener; 040import org.granite.config.ServletGraniteConfig; 041import org.granite.config.flex.ServicesConfig; 042import org.granite.config.flex.ServletServicesConfig; 043import org.granite.context.AMFContextImpl; 044import org.granite.context.GraniteContext; 045import org.granite.logging.Logger; 046import org.granite.messaging.amf.AMF0Message; 047import org.granite.messaging.amf.io.AMF0Deserializer; 048import org.granite.messaging.amf.io.AMF0Serializer; 049import org.granite.messaging.jmf.JMFDeserializer; 050import org.granite.messaging.jmf.JMFSerializer; 051import org.granite.messaging.jmf.SharedContext; 052import org.granite.util.ContentType; 053import org.granite.util.ServletParams; 054 055/** 056 * @author Franck WOLFF 057 */ 058public class AMFMessageFilter implements Filter { 059 060 private static final Logger log = Logger.getLogger(AMFMessageFilter.class); 061 062 protected FilterConfig config = null; 063 protected GraniteConfig graniteConfig = null; 064 protected ServicesConfig servicesConfig = null; 065 066 protected Integer inputBufferSize = null; 067 protected Integer outputBufferSize = null; 068 protected boolean closeStreams = true; 069 070 protected SharedContext jmfSharedContext = null; 071 072 public void init(FilterConfig config) throws ServletException { 073 this.config = config; 074 this.graniteConfig = ServletGraniteConfig.loadConfig(config.getServletContext()); 075 this.servicesConfig = ServletServicesConfig.loadConfig(config.getServletContext()); 076 077 closeStreams = ServletParams.get(config, "closeStreams", Boolean.TYPE, true); 078 inputBufferSize = ServletParams.get(config, "inputBufferSize", Integer.TYPE, null); 079 outputBufferSize = ServletParams.get(config, "outputBufferSize", Integer.TYPE, null); 080 081 if (inputBufferSize != null && inputBufferSize <= 0) 082 throw new ServletException("Illegal value for inputBufferSize=" + inputBufferSize + " (should be > 0, fix your web.xml)"); 083 if (outputBufferSize != null && outputBufferSize <= 0) 084 throw new ServletException("Illegal value for outputBufferSize=" + outputBufferSize + " (should be > 0, fix your web.xml)"); 085 086 log.info("Using configuration: {closeStreams=%s, inputBufferSize=%s, outputBufferSize=%s}", closeStreams, inputBufferSize, outputBufferSize); 087 088 jmfSharedContext = GraniteConfigListener.getSharedContext(config.getServletContext()); 089 } 090 091 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 092 throws IOException, ServletException { 093 094 if (!(req instanceof HttpServletRequest) || !(resp instanceof HttpServletResponse)) 095 throw new ServletException("Not in HTTP context: " + req + ", " + resp); 096 097 HttpServletRequest request = (HttpServletRequest)req; 098 HttpServletResponse response = (HttpServletResponse)resp; 099 100 if (ContentType.JMF_AMF.mimeType().equals(request.getContentType())) 101 doJMFAMFFilter(request, response, chain); 102 else 103 doAMFFilter(request, response, chain); 104 } 105 106 protected void doAMFFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) 107 throws IOException, ServletException { 108 109 log.debug(">> Incoming AMF0 request from: %s", request.getRequestURL()); 110 111 InputStream is = null; 112 OutputStream os = null; 113 114 try { 115 if (inputBufferSize != null) 116 is = new BufferedInputStream(request.getInputStream(), inputBufferSize); 117 else 118 is = request.getInputStream(); 119 120 GraniteContext context = HttpGraniteContext.createThreadIntance( 121 graniteConfig, servicesConfig, config.getServletContext(), 122 request, response 123 ); 124 125 AMFContextImpl amf = (AMFContextImpl)context.getAMFContext(); 126 127 log.debug(">> Deserializing AMF0 request..."); 128 129 AMF0Deserializer deserializer = new AMF0Deserializer(is); 130 AMF0Message amf0Request = deserializer.getAMFMessage(); 131 132 amf.setAmf0Request(amf0Request); 133 134 log.debug(">> Chaining AMF0 request: %s", amf0Request); 135 136 chain.doFilter(request, response); 137 138 AMF0Message amf0Response = amf.getAmf0Response(); 139 140 log.debug("<< Serializing AMF0 response: %s", amf0Response); 141 142 response.setStatus(HttpServletResponse.SC_OK); 143 response.setContentType(ContentType.AMF.mimeType()); 144 response.setDateHeader("Expire", 0L); 145 response.setHeader("Cache-Control", "no-store"); 146 147 if (outputBufferSize != null) 148 response.setBufferSize(outputBufferSize); 149 150 os = response.getOutputStream(); 151 AMF0Serializer serializer = new AMF0Serializer(os); 152 153 serializer.serializeMessage(amf0Response); 154 155 response.flushBuffer(); 156 } 157 catch (IOException e) { 158 if ("org.apache.catalina.connector.ClientAbortException".equals(e.getClass().getName())) 159 log.debug(e, "Connection closed by client"); 160 else 161 log.error(e, "AMF message error"); 162 throw e; 163 } 164 catch (Exception e) { 165 log.error(e, "AMF message error"); 166 throw new ServletException(e); 167 } 168 finally { 169 170 if (closeStreams) { 171 if (is != null) { 172 try { 173 is.close(); 174 } catch (IOException e) { 175 log.error(e, "Error while closing request input stream"); 176 } 177 } 178 179 if (os != null) { 180 try { 181 os.close(); 182 } catch (IOException e) { 183 log.error(e, "Error while closing response output stream"); 184 } 185 } 186 } 187 188 GraniteContext.release(); 189 } 190 } 191 192 protected void doJMFAMFFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) 193 throws IOException, ServletException { 194 195 log.debug(">> Incoming JMF+AMF request from: %s", request.getRequestURL()); 196 197 if (jmfSharedContext == null) 198 throw GraniteConfigListener.newSharedContextNotInitializedException(); 199 200 InputStream is = null; 201 OutputStream os = null; 202 203 try { 204 is = request.getInputStream(); 205 206 GraniteContext context = HttpGraniteContext.createThreadIntance( 207 graniteConfig, servicesConfig, config.getServletContext(), 208 request, response 209 ); 210 211 AMFContextImpl amf = (AMFContextImpl)context.getAMFContext(); 212 213 log.debug(">> Deserializing JMF+AMF request..."); 214 215 @SuppressWarnings("all") // JDK7 warning (Resource leak: 'deserializer' is never closed)... 216 JMFDeserializer deserializer = new JMFDeserializer(is, jmfSharedContext); 217 AMF0Message amf0Request = (AMF0Message)deserializer.readObject(); 218 219 amf.setAmf0Request(amf0Request); 220 221 log.debug(">> Chaining AMF0 request: %s", amf0Request); 222 223 chain.doFilter(request, response); 224 225 AMF0Message amf0Response = amf.getAmf0Response(); 226 227 log.debug("<< Serializing JMF+AMF response: %s", amf0Response); 228 229 response.setStatus(HttpServletResponse.SC_OK); 230 response.setContentType(ContentType.JMF_AMF.mimeType()); 231 response.setDateHeader("Expire", 0L); 232 response.setHeader("Cache-Control", "no-store"); 233 234 os = response.getOutputStream(); 235 236 @SuppressWarnings("all") // JDK7 warning (Resource leak: 'serializer' is never closed)... 237 JMFSerializer serializer = new JMFSerializer(os, jmfSharedContext); 238 serializer.writeObject(amf0Response); 239 240 response.flushBuffer(); 241 } 242 catch (IOException e) { 243 if ("org.apache.catalina.connector.ClientAbortException".equals(e.getClass().getName())) 244 log.debug(e, "Connection closed by client"); 245 else 246 log.error(e, "JMF+AMF message error"); 247 throw e; 248 } 249 catch (Exception e) { 250 log.error(e, "JMF+AMF message error"); 251 throw new ServletException(e); 252 } 253 finally { 254 if (is != null) { 255 try { 256 is.close(); 257 } catch (IOException e) { 258 log.error(e, "Error while closing request input stream"); 259 } 260 } 261 262 if (os != null) { 263 try { 264 os.close(); 265 } catch (IOException e) { 266 log.error(e, "Error while closing response output stream"); 267 } 268 } 269 270 GraniteContext.release(); 271 } 272 } 273 274 public void destroy() { 275 this.config = null; 276 this.graniteConfig = null; 277 this.servicesConfig = null; 278 279 this.jmfSharedContext = null; 280 } 281}