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 */
022 package org.granite.messaging.webapp;
023
024 import java.io.BufferedInputStream;
025 import java.io.IOException;
026 import java.io.InputStream;
027 import java.io.OutputStream;
028
029 import javax.servlet.Filter;
030 import javax.servlet.FilterChain;
031 import javax.servlet.FilterConfig;
032 import javax.servlet.ServletException;
033 import javax.servlet.ServletRequest;
034 import javax.servlet.ServletResponse;
035 import javax.servlet.http.HttpServletRequest;
036 import javax.servlet.http.HttpServletResponse;
037
038 import org.granite.config.GraniteConfig;
039 import org.granite.config.GraniteConfigListener;
040 import org.granite.config.ServletGraniteConfig;
041 import org.granite.config.flex.ServicesConfig;
042 import org.granite.config.flex.ServletServicesConfig;
043 import org.granite.context.AMFContextImpl;
044 import org.granite.context.GraniteContext;
045 import org.granite.logging.Logger;
046 import org.granite.messaging.amf.AMF0Message;
047 import org.granite.messaging.amf.io.AMF0Deserializer;
048 import org.granite.messaging.amf.io.AMF0Serializer;
049 import org.granite.messaging.jmf.JMFDeserializer;
050 import org.granite.messaging.jmf.JMFSerializer;
051 import org.granite.messaging.jmf.SharedContext;
052 import org.granite.util.ContentType;
053 import org.granite.util.ServletParams;
054
055 /**
056 * @author Franck WOLFF
057 */
058 public 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 }