001: // Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
002: // Released under the terms of the GNU General Public License version 2 or later.
003: package fitnesse.responders.files;
004:
005: import fitnesse.http.*;
006: import fitnesse.*;
007: import fitnesse.responders.*;
008: import java.io.*;
009: import java.net.*;
010: import java.util.*;
011: import java.text.ParseException;
012:
013: public class FileResponder implements Responder {
014: private static FileNameMap fileNameMap = URLConnection
015: .getFileNameMap();
016:
017: public String resource;
018:
019: public File requestedFile;
020:
021: public Date lastModifiedDate;
022:
023: public String lastModifiedDateString;
024:
025: public static Responder makeResponder(Request request,
026: String rootPath) throws Exception {
027: String resource = request.getResource();
028:
029: if (fileNameHasSpaces(resource))
030: resource = restoreRealSpacesInFileName(resource);
031:
032: File requestedFile = new File(rootPath + "/" + resource);
033: if (!requestedFile.exists())
034: return new NotFoundResponder();
035:
036: if (requestedFile.isDirectory())
037: return new DirectoryResponder(resource, requestedFile);
038: else
039: return new FileResponder(resource, requestedFile);
040: }
041:
042: public FileResponder(String resource, File requestedFile) {
043: this .resource = resource;
044: this .requestedFile = requestedFile;
045: }
046:
047: public Response makeResponse(FitNesseContext context,
048: Request request) throws Exception {
049: InputStreamResponse response = new InputStreamResponse();
050: determineLastModifiedInfo();
051:
052: if (isNotModified(request))
053: return createNotModifiedResponse();
054: else {
055: response.setBody(requestedFile);
056: setContentType(requestedFile, response);
057: response.setLastModifiedHeader(lastModifiedDateString);
058: }
059: return response;
060: }
061:
062: public static boolean fileNameHasSpaces(String resource) {
063: return resource.indexOf("%20") != 0;
064: }
065:
066: public static String restoreRealSpacesInFileName(String resource)
067: throws Exception {
068: return URLDecoder.decode(resource, "UTF-8");
069: }
070:
071: String getResource() {
072: return resource;
073: }
074:
075: private boolean isNotModified(Request request) {
076: if (request.hasHeader("If-Modified-Since")) {
077: String queryDateString = (String) request
078: .getHeader("If-Modified-Since");
079: try {
080: Date queryDate = Response.makeStandardHttpDateFormat()
081: .parse(queryDateString);
082: if (!queryDate.before(lastModifiedDate))
083: return true;
084: } catch (ParseException e) {
085: e.printStackTrace();
086: }
087: }
088: return false;
089: }
090:
091: private Response createNotModifiedResponse() {
092: Response response = new SimpleResponse();
093: response.setStatus(304);
094: response.addHeader("Date", Response
095: .makeStandardHttpDateFormat().format(new Date()));
096: response.addHeader("Cache-Control", "private");
097: response.setLastModifiedHeader(lastModifiedDateString);
098: return response;
099: }
100:
101: private void determineLastModifiedInfo() {
102: lastModifiedDate = new Date(requestedFile.lastModified());
103: lastModifiedDateString = Response.makeStandardHttpDateFormat()
104: .format(lastModifiedDate);
105:
106: try // remove milliseconds
107: {
108: lastModifiedDate = Response.makeStandardHttpDateFormat()
109: .parse(lastModifiedDateString);
110: } catch (java.text.ParseException jtpe) {
111: jtpe.printStackTrace();
112: }
113: }
114:
115: private void setContentType(File file, Response response) {
116: String contentType = getContentType(file.getName());
117: response.setContentType(contentType);
118: }
119:
120: public static String getContentType(String filename) {
121: String contentType = fileNameMap.getContentTypeFor(filename);
122: if (contentType == null) {
123: if (filename.endsWith(".css")) {
124: contentType = "text/css";
125: } else if (filename.endsWith(".jar")) {
126: contentType = "application/x-java-archive";
127: } else {
128: contentType = "text/plain";
129: }
130: }
131: return contentType;
132: }
133: }
|