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
021package org.granite.messaging.webapp;
022
023import java.io.ByteArrayInputStream;
024import java.io.ByteArrayOutputStream;
025import java.io.File;
026import java.io.FileOutputStream;
027import java.io.IOException;
028import java.io.InputStream;
029import java.io.OutputStream;
030
031import javax.servlet.Filter;
032import javax.servlet.FilterChain;
033import javax.servlet.FilterConfig;
034import javax.servlet.ServletException;
035import javax.servlet.ServletInputStream;
036import javax.servlet.ServletOutputStream;
037import javax.servlet.ServletRequest;
038import javax.servlet.ServletResponse;
039import javax.servlet.http.HttpServletRequest;
040import javax.servlet.http.HttpServletRequestWrapper;
041import javax.servlet.http.HttpServletResponse;
042import javax.servlet.http.HttpServletResponseWrapper;
043
044import org.granite.logging.Logger;
045import org.granite.util.ServletParams;
046
047/**
048 * @author Franck WOLFF
049 */
050public class DumpFilter implements Filter {
051
052    private static final Logger log = Logger.getLogger(DumpFilter.class);
053    private static final String HEXS = "0123456789ABCDEF";
054    private static final String DUMP_DIR = "dumpDir";
055
056    File dumpDir = null;
057
058    public void init(FilterConfig config) throws ServletException {
059        String dumpDirString = ServletParams.get(config, DUMP_DIR, String.class, null);
060        if (dumpDirString != null) {
061            File dumpDir = new File(dumpDirString);
062            if (!dumpDir.exists() || !dumpDir.isDirectory() || !dumpDir.canWrite())
063                log.warn("Ignoring dump directory (is it a writable directory?): %s", dumpDir);
064            else
065                this.dumpDir = dumpDir;
066        }
067    }
068
069    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
070        throws IOException, ServletException {
071
072        DumpRequestWrapper requestWrapper = new DumpRequestWrapper((HttpServletRequest)request);
073        DumpResponseWrapper responseWrapper= new DumpResponseWrapper((HttpServletResponse)response);
074
075        dumpBytes("request", requestWrapper.getBytes());
076        chain.doFilter(requestWrapper, responseWrapper);
077        dumpBytes("response", responseWrapper.getBytes());
078    }
079
080    public void destroy() {
081        dumpDir = null;
082    }
083
084    private void dumpBytes(String label, byte[] bytes) {
085        StringBuilder hexSb = new StringBuilder();
086        StringBuilder charSb = new StringBuilder();
087
088        for (int i = 0; i < bytes.length; i++) {
089            int b = bytes[i] & 0xFF;
090            if (hexSb.length() > 0) {
091                hexSb.append(' ');
092                charSb.append(' ');
093            }
094            hexSb.append(HEXS.charAt(b >> 4)).append(HEXS.charAt(b & 0x0F));
095            if (b >= 0x20 && b <= 0x7e)
096                charSb.append(' ').append((char)b);
097            else
098                charSb.append("##");
099        }
100
101        log.info("[RAW %s] {\n%s\n%s\n}", label.toUpperCase(), hexSb.toString(), charSb.toString());
102
103        if (dumpDir != null) {
104            File file = new File(dumpDir.getPath() + File.separator + label + "_" + System.currentTimeMillis() + ".amf");
105            for (int i = 1; i < 100 && file.exists(); i++)
106                file = new File(file.getAbsolutePath() + "." + i);
107
108            OutputStream os = null;
109            try {
110                os = new FileOutputStream(file);
111                os.write(bytes);
112            } catch (Exception e) {
113                log.error(e, "Could not write dump file: %s", file);
114            } finally {
115                if (os != null) try {
116                    os.close();
117                } catch (Exception e) {
118                }
119            }
120        }
121    }
122
123    class DumpRequestWrapper extends HttpServletRequestWrapper {
124
125        private byte[] bytes = null;
126
127        public DumpRequestWrapper(HttpServletRequest request) throws IOException {
128            super(request);
129
130            setCharacterEncoding("UTF-8");
131
132            InputStream is = null;
133            try {
134                is = request.getInputStream();
135
136                ByteArrayOutputStream out = new ByteArrayOutputStream(128);
137                for (int b = is.read(); b != -1; b = is.read())
138                    out.write(b);
139
140                this.bytes = out.toByteArray();
141            } catch (Exception e) {
142                throw new RuntimeException(e);
143            }
144        }
145
146        @Override
147        public ServletInputStream getInputStream() throws IOException {
148
149            final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
150
151            return new ServletInputStream() {
152                @Override
153                public int read() throws IOException {
154                    return bais.read();
155                }
156            };
157        }
158
159        public byte[] getBytes() {
160            return bytes;
161        }
162    }
163
164    class DumpResponseWrapper extends HttpServletResponseWrapper {
165
166        private ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
167        private ServletOutputStream out = null;
168
169        public DumpResponseWrapper(HttpServletResponse response) throws IOException {
170            super(response);
171            this.out = response.getOutputStream();
172        }
173
174        @Override
175        public ServletOutputStream getOutputStream() throws IOException {
176
177            return new ServletOutputStream() {
178                @Override
179                public void write(int b) throws IOException {
180                    baos.write(b);
181                    out.write(b);
182                }
183            };
184        }
185
186        public byte[] getBytes() {
187            return baos.toByteArray();
188        }
189    }
190}