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