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.util.ServletParams;
048
049 /**
050 * @author Franck WOLFF
051 */
052 public class AMFMessageFilter implements Filter {
053
054 private static final Logger log = Logger.getLogger(AMFMessageFilter.class);
055
056 private FilterConfig config = null;
057 private GraniteConfig graniteConfig = null;
058 private ServicesConfig servicesConfig = null;
059
060 private Integer inputBufferSize = null;
061 private Integer outputBufferSize = null;
062 private boolean closeStreams = true;
063
064 public void init(FilterConfig config) throws ServletException {
065 this.config = config;
066 this.graniteConfig = ServletGraniteConfig.loadConfig(config.getServletContext());
067 this.servicesConfig = ServletServicesConfig.loadConfig(config.getServletContext());
068
069 closeStreams = ServletParams.get(config, "closeStreams", Boolean.TYPE, true);
070 inputBufferSize = ServletParams.get(config, "inputBufferSize", Integer.TYPE, null);
071 outputBufferSize = ServletParams.get(config, "outputBufferSize", Integer.TYPE, null);
072
073 if (inputBufferSize != null && inputBufferSize <= 0)
074 throw new ServletException("Illegal value for inputBufferSize=" + inputBufferSize + " (should be > 0, fix your web.xml)");
075 if (outputBufferSize != null && outputBufferSize <= 0)
076 throw new ServletException("Illegal value for outputBufferSize=" + outputBufferSize + " (should be > 0, fix your web.xml)");
077
078 log.info("Using configuration: {closeStreams=%s, inputBufferSize=%s, outputBufferSize=%s}", closeStreams, inputBufferSize, outputBufferSize);
079 }
080
081 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
082 throws IOException, ServletException {
083
084 if (!(req instanceof HttpServletRequest) || !(resp instanceof HttpServletResponse))
085 throw new ServletException("Not in HTTP context: " + req + ", " + resp);
086
087 HttpServletRequest request = (HttpServletRequest)req;
088 HttpServletResponse response = (HttpServletResponse)resp;
089
090 log.debug(">> Incoming AMF0 request from: %s", request.getRequestURL());
091
092 InputStream is = null;
093 OutputStream os = null;
094
095 try {
096 if (inputBufferSize != null)
097 is = new BufferedInputStream(request.getInputStream(), inputBufferSize);
098 else
099 is = new BufferedInputStream(request.getInputStream());
100
101 GraniteContext context = HttpGraniteContext.createThreadIntance(
102 graniteConfig, servicesConfig, config.getServletContext(),
103 request, response
104 );
105
106 AMFContextImpl amf = (AMFContextImpl)context.getAMFContext();
107
108 log.debug(">> Deserializing AMF0 request...");
109
110 AMF0Deserializer deserializer = new AMF0Deserializer(is);
111 AMF0Message amf0Request = deserializer.getAMFMessage();
112
113 amf.setAmf0Request(amf0Request);
114
115 log.debug(">> Chaining AMF0 request: %s", amf0Request);
116
117 chain.doFilter(request, response);
118
119 AMF0Message amf0Response = amf.getAmf0Response();
120
121 log.debug("<< Serializing AMF0 response: %s", amf0Response);
122
123 response.setStatus(HttpServletResponse.SC_OK);
124 response.setContentType(AMF0Message.CONTENT_TYPE);
125 response.setDateHeader("Expire", 0L);
126 response.setHeader("Cache-Control", "no-store");
127
128 if (outputBufferSize != null)
129 response.setBufferSize(outputBufferSize);
130
131 os = response.getOutputStream();
132 AMF0Serializer serializer = new AMF0Serializer(os);
133
134 serializer.serializeMessage(amf0Response);
135
136 response.flushBuffer();
137 }
138 catch (IOException e) {
139 if ("org.apache.catalina.connector.ClientAbortException".equals(e.getClass().getName()))
140 log.debug(e, "Connection closed by client");
141 else
142 log.error(e, "AMF message error");
143 throw e;
144 }
145 catch (Exception e) {
146 log.error(e, "AMF message error");
147 throw new ServletException(e);
148 }
149 finally {
150
151 if (closeStreams) {
152 if (is != null) {
153 try {
154 is.close();
155 } catch (IOException e) {
156 log.error(e, "Error while closing request input stream");
157 }
158 }
159
160 if (os != null) {
161 try {
162 os.close();
163 } catch (IOException e) {
164 log.error(e, "Error while closing response output stream");
165 }
166 }
167 }
168
169 GraniteContext.release();
170 }
171 }
172
173 public void destroy() {
174 this.config = null;
175 this.graniteConfig = null;
176 this.servicesConfig = null;
177 }
178 }