KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > winstone > StaticResourceServlet


1 /*
2  * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
3  * Distributed under the terms of either:
4  * - the common development and distribution license (CDDL), v1.0; or
5  * - the GNU Lesser General Public License, v2.1 or later
6  */

7 package winstone;
8
9 import java.io.File JavaDoc;
10 import java.io.FileInputStream JavaDoc;
11 import java.io.IOException JavaDoc;
12 import java.io.InputStream JavaDoc;
13 import java.io.OutputStream JavaDoc;
14 import java.io.StringWriter JavaDoc;
15 import java.io.Writer JavaDoc;
16 import java.text.DateFormat JavaDoc;
17 import java.text.SimpleDateFormat JavaDoc;
18 import java.util.ArrayList JavaDoc;
19 import java.util.Arrays JavaDoc;
20 import java.util.Date JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.StringTokenizer JavaDoc;
24
25 import javax.servlet.ServletConfig JavaDoc;
26 import javax.servlet.ServletException JavaDoc;
27 import javax.servlet.http.HttpServlet JavaDoc;
28 import javax.servlet.http.HttpServletRequest JavaDoc;
29 import javax.servlet.http.HttpServletResponse JavaDoc;
30
31 /**
32  * Servlet to handle static resources. Simply finds and sends them, or
33  * dispatches to the error servlet.
34  *
35  * @author <a HREF="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
36  * @version $Id: StaticResourceServlet.java,v 1.17 2004/12/31 07:21:00
37  * rickknowles Exp $
38  */

39 public class StaticResourceServlet extends HttpServlet JavaDoc {
40     // final String JSP_FILE = "org.apache.catalina.jsp_file";
41
final static String JavaDoc FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
42     final static String JavaDoc INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
43     final static String JavaDoc CACHED_RESOURCE_DATE_HEADER = "If-Modified-Since";
44     final static String JavaDoc LAST_MODIFIED_DATE_HEADER = "Last-Modified";
45     final static String JavaDoc RANGE_HEADER = "Range";
46     final static String JavaDoc ACCEPT_RANGES_HEADER = "Accept-Ranges";
47     final static String JavaDoc CONTENT_RANGE_HEADER = "Content-Range";
48     final static String JavaDoc RESOURCE_FILE = "winstone.LocalStrings";
49     private DateFormat JavaDoc sdfFileDate = new SimpleDateFormat JavaDoc("dd-MM-yyyy HH:mm");
50     private File JavaDoc webRoot;
51     private String JavaDoc prefix;
52     private boolean directoryList;
53
54     public void init(ServletConfig JavaDoc config) throws ServletException JavaDoc {
55         super.init(config);
56         this.webRoot = new File JavaDoc(config.getInitParameter("webRoot"));
57         this.prefix = config.getInitParameter("prefix");
58         String JavaDoc dirList = config.getInitParameter("directoryList");
59         this.directoryList = (dirList == null)
60                 || dirList.equalsIgnoreCase("true")
61                 || dirList.equalsIgnoreCase("yes");
62     }
63
64     public void doPost(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
65             throws ServletException JavaDoc, IOException JavaDoc {
66         doGet(request, response);
67     }
68
69     public void doGet(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
70             throws ServletException JavaDoc, IOException JavaDoc {
71         boolean isInclude = (request.getAttribute(INCLUDE_SERVLET_PATH) != null);
72         boolean isForward = (request.getAttribute(FORWARD_SERVLET_PATH) != null);
73         String JavaDoc path = null;
74
75         if (isInclude)
76             path = (String JavaDoc) request.getAttribute(INCLUDE_SERVLET_PATH);
77         else {
78             path = request.getServletPath();
79         }
80
81         // URL decode path
82
path = WinstoneRequest.decodeURLToken(path);
83
84         long cachedResDate = request.getDateHeader(CACHED_RESOURCE_DATE_HEADER);
85         Logger.log(Logger.DEBUG, Launcher.RESOURCES,
86                 "StaticResourceServlet.PathRequested", new String JavaDoc[] {
87                         getServletConfig().getServletName(), path });
88
89         // Check for the resource
90
File JavaDoc res = path.equals("") ? this.webRoot : new File JavaDoc(
91                 this.webRoot, path);
92
93         // Send a 404 if not found
94
if (!res.exists())
95             response.sendError(HttpServletResponse.SC_NOT_FOUND, Launcher.RESOURCES
96                     .getString("StaticResourceServlet.PathNotFound", path));
97
98         // Check we are below the webroot
99
else if (!isDescendant(this.webRoot, res, this.webRoot)) {
100             Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES, "StaticResourceServlet.OutsideWebroot",
101                     new String JavaDoc[] {res.getCanonicalPath(), this.webRoot.toString()});
102             response.sendError(HttpServletResponse.SC_FORBIDDEN, Launcher.RESOURCES
103                     .getString("StaticResourceServlet.PathInvalid", path));
104         }
105
106         // Check we are not below the web-inf
107
else if (!isInclude && !isForward && isDescendant(new File JavaDoc(this.webRoot, "WEB-INF"), res, this.webRoot))
108             response.sendError(HttpServletResponse.SC_NOT_FOUND, Launcher.RESOURCES
109                     .getString("StaticResourceServlet.PathInvalid", path));
110
111         // Check we are not below the meta-inf
112
else if (!isInclude && !isForward && isDescendant(new File JavaDoc(this.webRoot, "META-INF"), res, this.webRoot))
113             response.sendError(HttpServletResponse.SC_NOT_FOUND, Launcher.RESOURCES
114                     .getString("StaticResourceServlet.PathInvalid", path));
115
116         // check for the directory case
117
else if (res.isDirectory()) {
118             if (path.endsWith("/")) {
119                 // Try to match each of the welcome files
120
// String matchedWelcome = matchWelcomeFiles(path, res);
121
// if (matchedWelcome != null)
122
// response.sendRedirect(this.prefix + path + matchedWelcome);
123
// else
124
if (this.directoryList)
125                     generateDirectoryList(request, response, path);
126                 else
127                     response.sendError(HttpServletResponse.SC_FORBIDDEN,
128                             Launcher.RESOURCES.getString("StaticResourceServlet.AccessDenied"));
129             } else
130                 response.sendRedirect(this.prefix + path + "/");
131         }
132
133         // Send a 304 if not modified
134
else if (!isInclude && (cachedResDate != -1)
135                 && (cachedResDate < (System.currentTimeMillis() / 1000L * 1000L))
136                 && (cachedResDate >= (res.lastModified() / 1000L * 1000L))) {
137             String JavaDoc mimeType = getServletContext().getMimeType(
138                     res.getName().toLowerCase());
139             if (mimeType != null)
140                 response.setContentType(mimeType);
141             response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
142             response.setContentLength(0);
143             response.flushBuffer();
144         }
145
146         // Write out the resource if not range or is included
147
else if ((request.getHeader(RANGE_HEADER) == null) || isInclude) {
148             String JavaDoc mimeType = getServletContext().getMimeType(
149                     res.getName().toLowerCase());
150             if (mimeType != null)
151                 response.setContentType(mimeType);
152             InputStream JavaDoc resStream = new FileInputStream JavaDoc(res);
153
154             response.setStatus(HttpServletResponse.SC_OK);
155             response.setContentLength((int) res.length());
156 // response.addHeader(ACCEPT_RANGES_HEADER, "bytes");
157
response.addDateHeader(LAST_MODIFIED_DATE_HEADER, res.lastModified());
158             OutputStream JavaDoc out = null;
159             Writer JavaDoc outWriter = null;
160             try {
161                 out = response.getOutputStream();
162             } catch (IllegalStateException JavaDoc err) {
163                 outWriter = response.getWriter();
164             } catch (IllegalArgumentException JavaDoc err) {
165                 outWriter = response.getWriter();
166             }
167             byte buffer[] = new byte[4096];
168             int read = resStream.read(buffer);
169             while (read > 0) {
170                 if (out != null) {
171                     out.write(buffer, 0, read);
172                 } else {
173                     outWriter.write(new String JavaDoc(buffer, 0, read,
174                             response.getCharacterEncoding()));
175                 }
176                 read = resStream.read(buffer);
177             }
178             resStream.close();
179         } else if (request.getHeader(RANGE_HEADER).startsWith("bytes=")) {
180             String JavaDoc mimeType = getServletContext().getMimeType(
181                     res.getName().toLowerCase());
182             if (mimeType != null)
183                 response.setContentType(mimeType);
184             InputStream JavaDoc resStream = new FileInputStream JavaDoc(res);
185
186             List JavaDoc ranges = new ArrayList JavaDoc();
187             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(request.getHeader(
188                     RANGE_HEADER).substring(6).trim(), ",", false);
189             int totalSent = 0;
190             String JavaDoc rangeText = "";
191             while (st.hasMoreTokens()) {
192                 String JavaDoc rangeBlock = st.nextToken();
193                 int start = 0;
194                 int end = (int) res.length();
195                 int delim = rangeBlock.indexOf('-');
196                 if (delim != 0)
197                     start = Integer.parseInt(rangeBlock.substring(0, delim)
198                             .trim());
199                 if (delim != rangeBlock.length() - 1)
200                     end = Integer.parseInt(rangeBlock.substring(delim + 1)
201                             .trim());
202                 totalSent += (end - start);
203                 rangeText += "," + start + "-" + end;
204                 ranges.add(start + "-" + end);
205             }
206             response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
207             response.addHeader(CONTENT_RANGE_HEADER, "bytes "
208                     + rangeText.substring(1) + "/" + res.length());
209             response.setContentLength(totalSent);
210
211             response.addHeader(ACCEPT_RANGES_HEADER, "bytes");
212             response.addDateHeader(LAST_MODIFIED_DATE_HEADER, res
213                     .lastModified());
214             OutputStream JavaDoc out = response.getOutputStream();
215             int bytesRead = 0;
216             for (Iterator JavaDoc i = ranges.iterator(); i.hasNext();) {
217                 String JavaDoc rangeBlock = (String JavaDoc) i.next();
218                 int delim = rangeBlock.indexOf('-');
219                 int start = Integer.parseInt(rangeBlock.substring(0, delim));
220                 int end = Integer.parseInt(rangeBlock.substring(delim + 1));
221                 int read = 0;
222                 while ((read != -1) && (bytesRead <= res.length())) {
223                     read = resStream.read();
224                     if ((bytesRead >= start) && (bytesRead < end))
225                         out.write(read);
226                     bytesRead++;
227                 }
228             }
229             resStream.close();
230         } else
231             response
232                     .sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
233     }
234
235     /**
236      * Generate a list of the files in this directory
237      */

238     private void generateDirectoryList(HttpServletRequest JavaDoc request,
239             HttpServletResponse JavaDoc response, String JavaDoc path) throws ServletException JavaDoc,
240             IOException JavaDoc {
241         // Get the file list
242
File JavaDoc dir = path.equals("") ? this.webRoot : new File JavaDoc(
243                 this.webRoot, path);
244         File JavaDoc children[] = dir.listFiles();
245         Arrays.sort(children);
246
247         // Build row content
248
StringWriter JavaDoc rowString = new StringWriter JavaDoc();
249         String JavaDoc oddColour = Launcher.RESOURCES
250                 .getString("StaticResourceServlet.DirectoryList.OddColour");
251         String JavaDoc evenColour = Launcher.RESOURCES
252                 .getString("StaticResourceServlet.DirectoryList.EvenColour");
253         String JavaDoc rowTextColour = Launcher.RESOURCES
254                 .getString("StaticResourceServlet.DirectoryList.RowTextColour");
255
256         String JavaDoc directoryLabel = Launcher.RESOURCES
257                 .getString("StaticResourceServlet.DirectoryList.DirectoryLabel");
258         String JavaDoc parentDirLabel = Launcher.RESOURCES
259                 .getString("StaticResourceServlet.DirectoryList.ParentDirectoryLabel");
260         String JavaDoc noDateLabel = Launcher.RESOURCES
261                 .getString("StaticResourceServlet.DirectoryList.NoDateLabel");
262
263         int rowCount = 0;
264
265         // Write the parent dir row
266
if (!path.equals("") && !path.equals("/")) {
267             rowString.write(Launcher.RESOURCES.getString(
268                     "StaticResourceServlet.DirectoryList.Row", new String JavaDoc[] {
269                             rowTextColour, evenColour, parentDirLabel, "..",
270                             noDateLabel, directoryLabel }));
271             rowCount++;
272         }
273
274         // Write the rows for each file
275
for (int n = 0; n < children.length; n++) {
276             if (!children[n].getName().equalsIgnoreCase("web-inf") &&
277                     !children[n].getName().equalsIgnoreCase("meta-inf")) {
278                 File JavaDoc file = children[n];
279                 String JavaDoc date = noDateLabel;
280                 String JavaDoc size = directoryLabel;
281                 if (!file.isDirectory()) {
282                     size = "" + file.length();
283                     synchronized (sdfFileDate) {
284                         date = sdfFileDate.format(new Date JavaDoc(file.lastModified()));
285                     }
286                 }
287                 rowString.write(Launcher.RESOURCES.getString(
288                         "StaticResourceServlet.DirectoryList.Row",
289                         new String JavaDoc[] {
290                                 rowTextColour,
291                                 rowCount % 2 == 0 ? evenColour : oddColour,
292                                 file.getName() + (file.isDirectory() ? "/" : ""),
293                                 "./" + file.getName() + (file.isDirectory() ? "/" : ""),
294                                 date, size}));
295                 rowCount++;
296             }
297         }
298         
299         // Build wrapper body
300
String JavaDoc out = Launcher.RESOURCES.getString("StaticResourceServlet.DirectoryList.Body",
301                 new String JavaDoc[] {
302                         Launcher.RESOURCES.getString("StaticResourceServlet.DirectoryList.HeaderColour"),
303                         Launcher.RESOURCES.getString("StaticResourceServlet.DirectoryList.HeaderTextColour"),
304                         Launcher.RESOURCES.getString("StaticResourceServlet.DirectoryList.LabelColour"),
305                         Launcher.RESOURCES.getString("StaticResourceServlet.DirectoryList.LabelTextColour"),
306                         new Date JavaDoc() + "",
307                         Launcher.RESOURCES.getString("ServerVersion"),
308                         path.equals("") ? "/" : path,
309                         rowString.toString() });
310
311         response.setContentLength(out.getBytes().length);
312         response.setContentType("text/html");
313         Writer JavaDoc w = response.getWriter();
314         w.write(out);
315         w.close();
316     }
317     
318     public static boolean isDescendant(File JavaDoc parent, File JavaDoc child, File JavaDoc commonBase) throws IOException JavaDoc {
319         if (child.equals(parent)) {
320             return true;
321         } else {
322             // Start by checking canonicals
323
String JavaDoc canonicalParent = parent.getAbsoluteFile().getCanonicalPath();
324             String JavaDoc canonicalChild = child.getAbsoluteFile().getCanonicalPath();
325             if (canonicalChild.startsWith(canonicalParent)) {
326                 return true;
327             }
328             
329             // If canonicals don't match, we're dealing with symlinked files, so if we can
330
// build a path from the parent to the child,
331
String JavaDoc childOCValue = constructOurCanonicalVersion(child, commonBase);
332             String JavaDoc parentOCValue = constructOurCanonicalVersion(parent, commonBase);
333             return childOCValue.startsWith(parentOCValue);
334         }
335     }
336     
337     public static String JavaDoc constructOurCanonicalVersion(File JavaDoc current, File JavaDoc stopPoint) {
338         int backOnes = 0;
339         StringBuffer JavaDoc ourCanonicalVersion = new StringBuffer JavaDoc();
340         while ((current != null) && !current.equals(stopPoint)) {
341             if (current.getName().equals("..")) {
342                 backOnes++;
343             } else if (current.getName().equals(".")) {
344                 // skip - do nothing
345
} else if (backOnes > 0) {
346                 backOnes--;
347             } else {
348                 ourCanonicalVersion.insert(0, "/" + current.getName());
349             }
350             current = current.getParentFile();
351         }
352         return ourCanonicalVersion.toString();
353     }
354 }
355
Popular Tags