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><filter>
030 * <filter-name>jhighlight</filter-name>
031 * <filter-class>com.uwyn.jhighlight.servlet.HighlightFilter</filter-class>
032 *</filter>
033 *
034 *<filter-mapping>
035 * <filter-name>jhighlight</filter-name>
036 * <url-pattern>/*</url-pattern>
037 *</filter-mapping></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 }