001    /*
002     * Copyright 2004-2006 Geert Bevin <gbevin[remove] at uwyn dot com>
003     * Distributed under the terms of either:
004     * - the common development and distribution license (CDDL), v1.0; or
005     * - the GNU Lesser General Public License, v2.1 or later
006     * $Id: HighlightFilter.java 3108 2006-03-13 18:03:00Z gbevin $
007     */
008    package com.uwyn.jhighlight.servlet;
009    
010    import javax.servlet.*;
011    
012    import com.uwyn.jhighlight.renderer.Renderer;
013    import com.uwyn.jhighlight.renderer.XhtmlRendererFactory;
014    import com.uwyn.jhighlight.tools.FileUtils;
015    import java.io.ByteArrayInputStream;
016    import java.io.ByteArrayOutputStream;
017    import java.io.IOException;
018    import java.io.InputStream;
019    import java.io.OutputStream;
020    import javax.servlet.http.HttpServletRequest;
021    import javax.servlet.http.HttpServletRequestWrapper;
022    import javax.servlet.http.HttpServletResponse;
023    import javax.servlet.http.HttpServletResponseWrapper;
024    
025    /**
026     * A servlet filter that offers on-the-fly syntax highlighting for Java, HTML,
027     * XHTML, XML and LZX files.
028     * <p>The filter should be declared in a similar fashion as this:
029     * <pre>&lt;filter&gt;
030     *    &lt;filter-name&gt;jhighlight&lt;/filter-name&gt;
031     *    &lt;filter-class&gt;com.uwyn.jhighlight.servlet.HighlightFilter&lt;/filter-class&gt;
032     *&lt;/filter&gt;
033     *
034     *&lt;filter-mapping&gt;
035     *    &lt;filter-name&gt;jhighlight&lt;/filter-name&gt;
036     *    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
037     *&lt;/filter-mapping&gt;</pre>
038     * <p>It will respond to files with the following extensions:
039     * <code>.javas</code>, <code>.htmls</code>, <code>.htms</code>,
040     * <code>.xhtmls</code>, <code>.xmls</code> and <code>.lzxs</code>. These will
041     * be automatically mapped to files without the last <code>s</code> in the
042     * filenames. Thus, for example, a request like this:
043     * <pre>http://myhost.com/folder/MySource.javas</pre>
044     * <p>will retrieve this file:
045     * <pre>http://myhost.com/folder/MySource.java</pre>
046     * <p>The contents of this file will be automatically highlighted and the
047     * resulting HTML will be served.
048     *
049     * @author Geert Bevin (gbevin[remove] at uwyn dot com)
050     * @version $Revision: 3108 $
051     * @since 1.0
052     */
053    public final class HighlightFilter implements Filter
054    {
055            public void init(FilterConfig filterConfig)
056            {
057            }
058            
059            public void destroy()
060            {
061            }
062            
063            public void doFilter(ServletRequest request,
064                                                     ServletResponse response, FilterChain chain)
065            throws IOException, ServletException
066            {
067                    if (request instanceof HttpServletRequest &&
068                            response instanceof HttpServletResponse)
069                    {
070                            HttpServletRequest  http_request = (HttpServletRequest)request;
071                            HttpServletResponse http_response = (HttpServletResponse)response;
072                            
073                            Renderer renderer = null;
074                            String uri = http_request.getRequestURI();
075                            String extension = FileUtils.getExtension(uri);
076                            if (extension != null &&
077                                    extension.endsWith("s"))
078                            {
079                                    renderer = XhtmlRendererFactory.getRenderer(extension.substring(0, extension.length()-1));
080                            }
081                            
082                            if (renderer != null)
083                            {
084                                    SourceRequestWrapper    request_wrapper = new SourceRequestWrapper(http_request);
085                                    CharResponseWrapper     response_wrapper = new CharResponseWrapper(http_response);
086                                    
087                                    chain.doFilter(request_wrapper, response_wrapper);
088                                    
089                                    OutputStream out = response.getOutputStream();
090                                    try
091                                    {
092                                            if (HttpServletResponse.SC_OK == response_wrapper.getStatus())
093                                            {
094                                                    InputStream is = new ByteArrayInputStream(response_wrapper.getWrappedOutputStream().toByteArray());
095                                                    ByteArrayOutputStream os = new ByteArrayOutputStream();
096                                                    
097                                                    String encoding = request.getCharacterEncoding();
098                                                    if (null == encoding)
099                                                    {
100                                                            encoding = "UTF-8";
101                                                    }
102                                                    
103                                                    renderer.highlight(http_request.getServletPath().substring(1), is, os, encoding, false);
104                                                    
105                                                    String highlighted = os.toString("ISO-8859-1");
106                                                    
107                                                    response.setContentType("text/html");
108                                                    response.setContentLength(highlighted.length());
109                                                    out.write(highlighted.getBytes("ISO-8859-1"));
110                                            }
111                                            else
112                                            {
113                                                    out.write(response_wrapper.getWrappedOutputStream().toByteArray());
114                                            }
115                                    }
116                                    finally
117                                    {
118                                            out.close();
119                                    }
120                            }
121                            else
122                            {
123                                    chain.doFilter(request, response);
124                            }
125                    }
126                    else
127                    {
128                            chain.doFilter(request, response);
129                    }
130            }
131            
132            private static class SourceRequestWrapper extends HttpServletRequestWrapper
133            {
134                    public SourceRequestWrapper(HttpServletRequest request)
135                    {
136                            super(request);
137                    }
138                    
139                    public String getServletPath()
140                    {
141                            String path = super.getServletPath();
142                            return path.substring(0, path.length() - 1);
143                    }
144                    
145                    public String getPathTranslated()
146                    {
147                            String path = super.getPathTranslated();
148                            return path.substring(0, path.length() - 1);
149                    }
150                    
151                    public String getRequestURI()
152                    {
153                            String uri =  super.getRequestURI();
154                            return uri.substring(0, uri.length() - 1);
155                    }
156                    
157                    public StringBuffer getRequestURL()
158                    {
159                            StringBuffer url =  super.getRequestURL();
160                            url.setLength(url.length() - 1);
161                            return url;
162                    }
163            }
164            
165            private static class CharResponseWrapper extends HttpServletResponseWrapper
166            {
167                    private ServletOutputStreamWrapper mOutput;
168                    private int mStatus = HttpServletResponse.SC_OK;
169                    
170                    public ServletOutputStreamWrapper getWrappedOutputStream()
171                    {
172                            return mOutput;
173                    }
174                    
175                    public CharResponseWrapper(HttpServletResponse response)
176                    {
177                            super(response);
178                            
179                            mOutput = new ServletOutputStreamWrapper();
180                    }
181                    
182                    public ServletOutputStream getOutputStream()
183                    throws IOException
184                    {
185                            return mOutput;
186                    }
187                    
188                    public void setStatus(int status)
189                    {
190                            mStatus = status;
191                            
192                            super.setStatus(status);
193                    }
194                    
195                    public void sendError(int status, String msg)
196                    throws IOException
197                    {
198                            mStatus = status;
199                            
200                            super.sendError(status, msg);
201                    }
202                    
203                    public void sendError(int status)
204                    throws IOException
205                    {
206                            mStatus = status;
207                            
208                            super.sendError(status);
209                    }
210                    
211                    public int getStatus()
212                    {
213                            return mStatus;
214                    }
215            }
216            
217            private static class ServletOutputStreamWrapper extends ServletOutputStream
218            {
219                    protected ByteArrayOutputStream mOutput;
220                    
221                    public ServletOutputStreamWrapper()
222                    {
223                            mOutput = new ByteArrayOutputStream();
224                    }
225                    
226                    public void write(int b) throws IOException
227                    {
228                            mOutput.write(b);
229                    }
230                    
231                    public byte[] toByteArray()
232                    {
233                            return mOutput.toByteArray();
234                    }
235            }
236    }