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    }