001: /*
002: * DefaultFormat.java March 2002
003: *
004: * Copyright (C) 2002, Niall Gallagher <niallg@users.sf.net>
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013: * GNU Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General
016: * Public License along with this library; if not, write to the
017: * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
018: * Boston, MA 02111-1307 USA
019: */
020:
021: package simple.http.serve;
022:
023: import java.io.IOException;
024: import java.io.File;
025:
026: /**
027: * The <code>DefaultFormat</code> object is used to provide pages
028: * for the <code>FileEngine</code> that are consistant and have
029: * a user specified format. This is used as the default system
030: * format when there is no explicit specification of a default
031: * using the system property "simple.http.serve.format". If
032: * this property is specified a class loader will attempt to
033: * load and instantiate a format of the specified type.
034: * <p>
035: * The <code>Format</code> of the <code>FileEngine</code> can
036: * be changed from the <code>DefaultFormat</code> by simply
037: * giving the VM the class name of the desired format like
038: *
039: * <pre>
040: * java -Dsimple.http.serve.format=demo.example.FancyFormat
041: * </pre>
042: *
043: * The <code>FormatFactory</code> produces the system default
044: * <code>Format</code> implementaion. This is used with the
045: * <code>FileEngine.getInstance</code> when the context object
046: * is not explicitly used. This implementation only supports
047: * the contents of directory resources. If the resource is
048: * requested is not a directorys a <code>FormatException</code>
049: * is thrown.
050: *
051: * @author Niall Gallagher
052: */
053: final class DefaultFormat implements Format {
054:
055: /**
056: * This is used to produce the contents of the specified resource.
057: * The <code>Context</code> is used to aquire a <code>File</code>
058: * object that represents the request URI path on the system. This
059: * is then be used to generate a page the provides a view of the
060: * contents of the resource.
061: * <p>
062: * The path given is the request URI path that normally comes with
063: * a HTTP/1.x request. The request URI is the string that identifies
064: * the resource on the host that is required. The context is used
065: * to convert that request URI into the system dependant name. The
066: * request URI is similar to a UNIX path like /usr/bin/README. For
067: * example if the browser requests http://some.host/pub/index.html
068: * then the request URI is /pub/index.html.
069: *
070: * @param context provides the format with a view of the system
071: * @param target the request URI that came with the HTTP request
072: *
073: * @exception FormatException if there is not format possible for
074: * the requested resource
075: */
076: public byte[] getContents(Context context, String target)
077: throws FormatException {
078: if (!context.getFile(target).isDirectory()) {
079: throw new FormatException("No format available");
080: }
081: String fixed = target;
082:
083: if (fixed.indexOf('?') > 0) { /* remove query*/
084: fixed = fixed.substring(0, fixed.indexOf('/') + 1);
085: }
086: if (fixed.lastIndexOf(';') > 0) { /* remove params*/
087: fixed = fixed.substring(0, fixed.indexOf(';') + 1);
088: }
089: return getContents(context, target, !fixed.endsWith("/")
090: && !fixed.endsWith("/."));
091: }
092:
093: /**
094: * This is used to produce the contents of the specified directory.
095: * The <code>Context</code> is used to aquire a <code>File</code>
096: * object that represents the request URI path on the system. This
097: * is then be used to generate a page the provides a listing of the
098: * contents of the directory.
099: * <p>
100: * The path given is the request URI path that normally comes with
101: * a HTTP/1.x request. The request URI is the string that identifies
102: * the resource on the host that is required. The context is used
103: * to convert that request URI into the system dependant name. The
104: * request URI is similar to a UNIX path like /usr/bin/README. For
105: * example if the browser requests http://some.host/pub/index.html
106: * then the request URI is /pub/index.html.
107: * <p>
108: * Often times a web browser will request a directory resource not
109: * knowing that it is a directory. This causes a problem when the
110: * page that is generated contains HTTP hyperlinks to each resource
111: * in that directory, much like Apache does. The problem arises
112: * when the user clicks on the resource hyperlink. Since the client
113: * browser thinks it requested a HTML file it simply requests that
114: * resource with the same directory root. For example say the client
115: * requests the resource http://some.host/usr/bin. The web server
116: * using a <code>FileEngine</code> object will aquire a listing for
117: * the directory /usr/bin. All references to files in this HTML list
118: * must be given the prefix bin/. So that when the browser clicks on
119: * a file, say index.html, its get http://some.host/usr/bin/index.html
120: * and not http://some.host/usr/index.html.
121: *
122: * @param context provides the format with a view of the system
123: * @param target the request URI that came with the HTTP request
124: * @param isRelative provides information as to how the directory
125: * was requested by the client browser
126: */
127: private byte[] getContents(Context context, String target,
128: boolean isRelative) {
129: String path = isRelative ? (context.getName(target) + "/") : "";
130: File directory = context.getFile(target);
131: String index = context.getRequestPath(target);
132: String[] names = directory.list();
133:
134: String text = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
135: + "<HTML><HEAD>"
136: + "<TITLE>Index of "
137: + index
138: + "</TITLE>\n"
139: + "</HEAD><BODY>"
140: + "<H1>Index of "
141: + index
142: + "</H1>\n"
143: + "<HR><TABLE>"
144: + "<TR><TD><B>Name</B></TD>"
145: + "<TD><B>Size</B></TD>"
146: + "<TD><B>Type</B></TD></TR>\n";
147:
148: for (int i = 0; i < names.length; i++) {
149: File file = new File(directory, names[i]);
150: boolean isDirectory = file.isDirectory();
151: String name = names[i] + (isDirectory ? "/" : "");
152: String size = isDirectory ? "-" : "" + file.length();
153: String mime = isDirectory ? "text/html" : context
154: .getContentType("/" + name); /* needs URL */
155:
156: text += "<TR><TD><TT><A HREF=\"" + path + name + "\">"
157: + name + "</A></TT></TD>" + "<TD><TT>" + size
158: + "</TT></TD><TD><TT>" + mime + "</TT></TD></TR>\n";
159: }
160: return getBytes(text + "</TABLE><HR>" + "</BODY></HTML>");
161: }
162:
163: /**
164: * The HTTP protocol defines certain status codes the are to be sent
165: * with descriptive message bodys, this method is used to create the
166: * message body for that status code. This method will generate a
167: * message body that describes the error defined by the status code.
168: * See the HTTP/1.1 specification for a description of the status
169: * codes, RFC 2616.
170: * <p>
171: * The path given is the request URI path that normally comes with
172: * a HTTP/1.x request. The request URI is the string that identifies
173: * the resource on the host that is required. The context is used
174: * to convert that request URI into the system dependant name. The
175: * request URI is similar to a UNIX path like /usr/bin/README. For
176: * example if the browser requests http://some.host/pub/index.html
177: * then the request URI is /pub/index.html.
178: *
179: * @param context provides the format with a view of the system
180: * @param target the request URI that came with the HTTP request
181: * @param report this provides information that can be used by the
182: * specific implementation to present the page
183: *
184: * @return this returns a HTML description of the error message
185: * that caused the
186: */
187: public byte[] getMessage(Context context, String target,
188: Report report) {
189: return getBytes("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
190: + "<HTML><HEAD><TITLE>"
191: + report.getCode()
192: + " "
193: + report.getText()
194: + "</TITLE>\n"
195: + "</HEAD><BODY><H1>"
196: + report.getText()
197: + "</H1>"
198: + "<PRE>The requested URL <i>"
199: + target
200: + "</i></PRE>"
201: + "<HR><PRE>"
202: + report.getCause()
203: + "</PRE></BODY></HTML>");
204: }
205:
206: /**
207: * This will convert the given string object in to UTF-8 format so
208: * that it can be returned as an array of bytes. This will always
209: * work, as the Java Language Specification ensures that UTF-8 is
210: * always supported. See <code>java.nio.charset.Charset</code>
211: * for details on the charset encodings supported.
212: *
213: * @param text the string object that is converted into UTF-8
214: *
215: * @return this will always return the string in the UTF-8 format
216: */
217: private byte[] getBytes(String text) {
218: try {
219: return text.getBytes("utf-8");
220: } catch (IOException never) {
221: return null;
222: }
223: }
224:
225: /**
226: * The contents generated by this object may not be in HTML format,
227: * this is used to retrive the content type. This is nessecary so
228: * that if the contents generated by this <code>Format</code> is
229: * not HTML that the correct MIME type is returned.
230: *
231: * @return this returns the MIME type of the resulting contents
232: */
233: public String getContentType() {
234: return "text/html; charset=utf-8";
235: }
236: }
|