0001: /*
0002: * The Apache Software License, Version 1.1
0003: *
0004: * Copyright (c) 1999 The Apache Software Foundation. All rights
0005: * reserved.
0006: *
0007: * Redistribution and use in source and binary forms, with or without
0008: * modification, are permitted provided that the following conditions
0009: * are met:
0010: *
0011: * 1. Redistributions of source code must retain the above copyright
0012: * notice, this list of conditions and the following disclaimer.
0013: *
0014: * 2. Redistributions in binary form must reproduce the above copyright
0015: * notice, this list of conditions and the following disclaimer in
0016: * the documentation and/or other materials provided with the
0017: * distribution.
0018: *
0019: * 3. The end-user documentation included with the redistribution, if
0020: * any, must include the following acknowlegement:
0021: * "This product includes software developed by the
0022: * Apache Software Foundation (http://www.apache.org/)."
0023: * Alternately, this acknowlegement may appear in the software itself,
0024: * if and wherever such third-party acknowlegements normally appear.
0025: *
0026: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
0027: * Foundation" must not be used to endorse or promote products derived
0028: * from this software without prior written permission. For written
0029: * permission, please contact apache@apache.org.
0030: *
0031: * 5. Products derived from this software may not be called "Apache"
0032: * nor may "Apache" appear in their names without prior written
0033: * permission of the Apache Group.
0034: *
0035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0046: * SUCH DAMAGE.
0047: * ====================================================================
0048: *
0049: * This software consists of voluntary contributions made by many
0050: * individuals on behalf of the Apache Software Foundation. For more
0051: * information on the Apache Software Foundation, please see
0052: * <http://www.apache.org/>.
0053: *
0054: * [Additional notices, if required by prior licensing conditions]
0055: *
0056: */
0057:
0058: package com.rimfaxe.webserver.servletapi;
0059:
0060: import java.io.BufferedInputStream;
0061: import java.io.ByteArrayOutputStream;
0062: import java.io.ByteArrayInputStream;
0063: import java.io.File;
0064: import java.io.FileInputStream;
0065: import java.io.InputStream;
0066: import java.io.IOException;
0067: import java.io.PrintWriter;
0068: import java.io.RandomAccessFile;
0069: import java.io.Reader;
0070: import java.io.InputStreamReader;
0071: import java.io.Writer;
0072: import java.io.OutputStreamWriter;
0073: import java.net.MalformedURLException;
0074: import java.net.URL;
0075: import java.sql.Timestamp;
0076: import java.util.Date;
0077: import java.util.Enumeration;
0078: import java.util.Vector;
0079: import java.util.StringTokenizer;
0080: import java.util.Locale;
0081: import java.util.TimeZone;
0082: import java.util.Hashtable;
0083: import java.text.ParseException;
0084: import java.text.SimpleDateFormat;
0085: import java.security.MessageDigest;
0086: import java.security.NoSuchAlgorithmException;
0087: import javax.servlet.RequestDispatcher;
0088: import javax.servlet.ServletException;
0089: import javax.servlet.ServletContext;
0090: import javax.servlet.ServletOutputStream;
0091: import javax.servlet.http.HttpServlet;
0092: import javax.servlet.http.HttpServletRequest;
0093: import javax.servlet.http.HttpServletResponse;
0094: import javax.naming.NamingException;
0095: import javax.naming.InitialContext;
0096: import javax.naming.Context;
0097: import javax.naming.NamingEnumeration;
0098: import javax.naming.NameClassPair;
0099: import javax.naming.directory.DirContext;
0100: import javax.naming.directory.Attribute;
0101: import javax.naming.directory.Attributes;
0102:
0103: import org.apache.naming.resources.Resource;
0104: import org.apache.naming.resources.ResourceAttributes;
0105:
0106: import com.rimfaxe.webserver.servletapi.util.URLEncoder;
0107:
0108: /**
0109: * The default resource-serving servlet for most web applications,
0110: * used to serve static resources such as HTML pages and images.
0111: *
0112: * @author Craig R. McClanahan
0113: * @author Remy Maucherat
0114: * @author Lars Andersen
0115: */
0116:
0117: public class DefaultServlet extends HttpServlet {
0118:
0119: // ----------------------------------------------------- Instance Variables
0120:
0121: /**
0122: * The debugging detail level for this servlet.
0123: */
0124: protected int debug = 0;
0125:
0126: /**
0127: * The input buffer size to use when serving resources.
0128: */
0129: protected int input = 2048;
0130:
0131: /**
0132: * Should we generate directory listings when no welcome file is present?
0133: */
0134: protected boolean listings = true;
0135:
0136: /**
0137: * Read only flag. By default, it's set to true.
0138: */
0139: protected boolean readOnly = true;
0140:
0141: /**
0142: * The output buffer size to use when serving resources.
0143: */
0144: protected int output = 2048;
0145:
0146: /**
0147: * The set of welcome files for this web application
0148: */
0149: protected String welcomes[] = new String[0];
0150:
0151: /**
0152: * MD5 message digest provider.
0153: */
0154: protected static MessageDigest md5Helper;
0155:
0156: /**
0157: * The MD5 helper object for this class.
0158: */
0159: //protected static final MD5Encoder md5Encoder = new MD5Encoder();
0160:
0161: /**
0162: * The set of SimpleDateFormat formats to use in getDateHeader().
0163: */
0164: protected static final SimpleDateFormat formats[] = {
0165: new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz",
0166: Locale.US),
0167: new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz",
0168: Locale.US),
0169: new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) };
0170:
0171: protected final static TimeZone gmtZone = TimeZone
0172: .getTimeZone("GMT");
0173:
0174: /**
0175: * Array containing the safe characters set.
0176: */
0177: protected static URLEncoder urlEncoder;
0178:
0179: /**
0180: * GMT timezone - all HTTP dates are on GMT
0181: */
0182: // ----------------------------------------------------- Static Initializer
0183: static {
0184: formats[0].setTimeZone(gmtZone);
0185: formats[1].setTimeZone(gmtZone);
0186: formats[2].setTimeZone(gmtZone);
0187:
0188: urlEncoder = new URLEncoder();
0189: urlEncoder.addSafeCharacter('-');
0190: urlEncoder.addSafeCharacter('_');
0191: urlEncoder.addSafeCharacter('.');
0192: urlEncoder.addSafeCharacter('*');
0193: urlEncoder.addSafeCharacter('/');
0194: }
0195:
0196: /**
0197: * MIME multipart separation string
0198: */
0199: protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY";
0200:
0201: /**
0202: * JNDI resources name.
0203: */
0204: protected static final String RESOURCES_JNDI_NAME = "java:/comp/Resources";
0205:
0206: /**
0207: * The string manager for this package.
0208: */
0209: //protected static StringManager sm = StringManager.getManager(Constants.Package);
0210:
0211: /**
0212: * Size of file transfer buffer in bytes.
0213: */
0214: private static final int BUFFER_SIZE = 4096;
0215:
0216: // --------------------------------------------------------- Public Methods
0217:
0218: /**
0219: * Finalize this servlet.
0220: */
0221: public void destroy() {
0222:
0223: ; // No actions necessary
0224:
0225: }
0226:
0227: /**
0228: * Initialize this servlet.
0229: */
0230: public void init() throws ServletException {
0231:
0232: //log("DefaultServlet.init: start");
0233: // Set our properties from the initialization parameters
0234: String value = null;
0235: try {
0236: value = getServletConfig().getInitParameter("debug");
0237: debug = Integer.parseInt(value);
0238: } catch (Throwable t) {
0239: ;
0240: }
0241: try {
0242: value = getServletConfig().getInitParameter("input");
0243: input = Integer.parseInt(value);
0244: } catch (Throwable t) {
0245: ;
0246: }
0247: try {
0248: value = getServletConfig().getInitParameter("listings");
0249: listings = (new Boolean(value)).booleanValue();
0250: } catch (Throwable t) {
0251: ;
0252: }
0253: //System.out.println("Listings set to "+listings);
0254: try {
0255: value = getServletConfig().getInitParameter("readonly");
0256: if (value != null)
0257: readOnly = (new Boolean(value)).booleanValue();
0258: } catch (Throwable t) {
0259: ;
0260: }
0261: try {
0262: value = getServletConfig().getInitParameter("output");
0263: output = Integer.parseInt(value);
0264: } catch (Throwable t) {
0265: ;
0266: }
0267:
0268: // Sanity check on the specified buffer sizes
0269: if (input < 256)
0270: input = 256;
0271: if (output < 256)
0272: output = 256;
0273:
0274: // Initialize the set of welcome files for this application
0275: welcomes = (String[]) getServletContext().getAttribute(
0276: "com.rimfaxe.webserver.WELCOME_FILES");
0277:
0278: if (welcomes == null)
0279: welcomes = new String[0];
0280:
0281: if (debug > 0) {
0282: log("DefaultServlet.init: input buffer size=" + input
0283: + ", output buffer size=" + output);
0284: for (int i = 0; i < welcomes.length; i++) {
0285: log("DefaultServlet.init: welcome file=" + welcomes[i]);
0286: }
0287: }
0288:
0289: // Load the MD5 helper used to calculate signatures.
0290: try {
0291: md5Helper = MessageDigest.getInstance("MD5");
0292: //System.out.println("MD5 provider -> "+md5Helper.getProvider().getName() );
0293: } catch (NoSuchAlgorithmException e) {
0294:
0295: throw new IllegalStateException("No such Algoritm : MD5");
0296: }
0297:
0298: //log("DefaultServlet.init: done!");
0299: }
0300:
0301: // ------------------------------------------------------ Protected Methods
0302:
0303: /**
0304: * Get resources. This method will try to retrieve the resources through
0305: * JNDI first, then in the servlet context if JNDI has failed (it could be
0306: * disabled). It will return null.
0307: *
0308: * @return A JNDI DirContext, or null.
0309: */
0310: protected DirContext getResources() {
0311:
0312: DirContext result = null;
0313:
0314: // Try the servlet context
0315: try {
0316: result = (DirContext) getServletContext().getAttribute(
0317: "com.rimfaxe.webserver.resources");
0318: } catch (ClassCastException e) {
0319: // Failed : Not the right type
0320: }
0321:
0322: if (result != null)
0323: return result;
0324:
0325: // Try JNDI
0326: try {
0327: result = (DirContext) new InitialContext()
0328: .lookup(RESOURCES_JNDI_NAME);
0329: } catch (NamingException e) {
0330: // Failed
0331: } catch (ClassCastException e) {
0332: // Failed : Not the right type
0333: }
0334:
0335: return result;
0336:
0337: }
0338:
0339: /**
0340: * Show HTTP header information.
0341: */
0342: protected void showRequestInfo(HttpServletRequest req) {
0343:
0344: System.out.println();
0345: System.out.println("SlideDAV Request Info");
0346: System.out.println();
0347:
0348: // Show generic info
0349: System.out.println("Encoding : " + req.getCharacterEncoding());
0350: System.out.println("Length : " + req.getContentLength());
0351: System.out.println("Type : " + req.getContentType());
0352:
0353: System.out.println();
0354: System.out.println("Parameters");
0355:
0356: Enumeration parameters = req.getParameterNames();
0357:
0358: while (parameters.hasMoreElements()) {
0359: String paramName = (String) parameters.nextElement();
0360: String[] values = req.getParameterValues(paramName);
0361: System.out.print(paramName + " : ");
0362: for (int i = 0; i < values.length; i++) {
0363: System.out.print(values[i] + ", ");
0364: }
0365: System.out.println();
0366: }
0367:
0368: System.out.println();
0369:
0370: System.out.println("Protocol : " + req.getProtocol());
0371: System.out.println("Address : " + req.getRemoteAddr());
0372: System.out.println("Host : " + req.getRemoteHost());
0373: System.out.println("Scheme : " + req.getScheme());
0374: System.out.println("Server Name : " + req.getServerName());
0375: System.out.println("Server Port : " + req.getServerPort());
0376:
0377: System.out.println();
0378: System.out.println("Attributes");
0379:
0380: Enumeration attributes = req.getAttributeNames();
0381:
0382: while (attributes.hasMoreElements()) {
0383: String attributeName = (String) attributes.nextElement();
0384: System.out.print(attributeName + " : ");
0385: System.out.println(req.getAttribute(attributeName)
0386: .toString());
0387: }
0388:
0389: System.out.println();
0390:
0391: // Show HTTP info
0392: System.out.println();
0393: System.out.println("HTTP Header Info");
0394: System.out.println();
0395:
0396: System.out
0397: .println("Authentication Type : " + req.getAuthType());
0398: System.out.println("HTTP Method : " + req.getMethod());
0399: System.out.println("Path Info : " + req.getPathInfo());
0400: System.out.println("Path translated : "
0401: + req.getPathTranslated());
0402: System.out.println("Query string : " + req.getQueryString());
0403: System.out.println("Remote user : " + req.getRemoteUser());
0404: System.out.println("Requested session id : "
0405: + req.getRequestedSessionId());
0406: System.out.println("Request URI : " + req.getRequestURI());
0407: System.out.println("Context path : " + req.getContextPath());
0408: System.out.println("Servlet path : " + req.getServletPath());
0409: System.out
0410: .println("User principal : " + req.getUserPrincipal());
0411:
0412: System.out.println();
0413: System.out.println("Headers : ");
0414:
0415: Enumeration headers = req.getHeaderNames();
0416:
0417: while (headers.hasMoreElements()) {
0418: String headerName = (String) headers.nextElement();
0419: System.out.print(headerName + " : ");
0420: System.out.println(req.getHeader(headerName));
0421: }
0422:
0423: System.out.println();
0424: System.out.println();
0425:
0426: }
0427:
0428: /**
0429: * Return the relative path associated with this servlet.
0430: *
0431: * @param request The servlet request we are processing
0432: */
0433: protected String getRelativePath(HttpServletRequest request) {
0434:
0435: // Are we being processed by a RequestDispatcher.include()?
0436: if (request.getAttribute("javax.servlet.include.request_uri") != null) {
0437: String result = (String) request
0438: .getAttribute("javax.servlet.include.path_info");
0439: if (result == null) {
0440: result = (String) request
0441: .getAttribute("javax.servlet.include.servlet_path");
0442: }
0443: if ((result == null) || (result.equals(""))) {
0444: result = "/";
0445: }
0446: return (result);
0447: }
0448:
0449: // No, extract the desired path directly from the request
0450: String result = request.getPathInfo();
0451: if (result == null) {
0452: result = request.getServletPath();
0453: }
0454: if ((result == null) || (result.equals(""))) {
0455: result = "/";
0456: }
0457: return normalize(result);
0458:
0459: }
0460:
0461: /**
0462: * Process a GET request for the specified resource.
0463: *
0464: * @param request The servlet request we are processing
0465: * @param response The servlet response we are creating
0466: *
0467: * @exception IOException if an input/output error occurs
0468: * @exception ServletException if a servlet-specified error occurs
0469: */
0470: protected void doGet(HttpServletRequest request,
0471: HttpServletResponse response) throws IOException,
0472: ServletException {
0473:
0474: if (debug > 999)
0475: showRequestInfo(request);
0476:
0477: // Serve the requested resource, including the data content
0478:
0479: serveResource(request, response, true);
0480:
0481: }
0482:
0483: /**
0484: * Process a HEAD request for the specified resource.
0485: *
0486: * @param request The servlet request we are processing
0487: * @param response The servlet response we are creating
0488: *
0489: * @exception IOException if an input/output error occurs
0490: * @exception ServletException if a servlet-specified error occurs
0491: */
0492: protected void doHead(HttpServletRequest request,
0493: HttpServletResponse response) throws IOException,
0494: ServletException {
0495:
0496: // Serve the requested resource, without the data content
0497: serveResource(request, response, false);
0498:
0499: }
0500:
0501: /**
0502: * Process a POST request for the specified resource.
0503: *
0504: * @param request The servlet request we are processing
0505: * @param response The servlet response we are creating
0506: *
0507: * @exception IOException if an input/output error occurs
0508: * @exception ServletException if a servlet-specified error occurs
0509: */
0510: protected void doPost(HttpServletRequest request,
0511: HttpServletResponse response) throws IOException,
0512: ServletException {
0513: doGet(request, response);
0514: }
0515:
0516: /**
0517: * Process a POST request for the specified resource.
0518: *
0519: * @param request The servlet request we are processing
0520: * @param response The servlet response we are creating
0521: *
0522: * @exception IOException if an input/output error occurs
0523: * @exception ServletException if a servlet-specified error occurs
0524: */
0525: protected void doPut(HttpServletRequest req,
0526: HttpServletResponse resp) throws ServletException,
0527: IOException {
0528:
0529: if (readOnly) {
0530: resp.sendError(HttpServletResponse.SC_FORBIDDEN);
0531: return;
0532: }
0533:
0534: String path = getRelativePath(req);
0535:
0536: // Retrieve the resources
0537: DirContext resources = getResources();
0538:
0539: if (resources == null) {
0540: resp
0541: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
0542: return;
0543: }
0544:
0545: boolean exists = true;
0546: try {
0547: resources.lookup(path);
0548: } catch (NamingException e) {
0549: exists = false;
0550: }
0551:
0552: boolean result = true;
0553:
0554: // Temp. content file used to support partial PUT
0555: File contentFile = null;
0556:
0557: // Input stream for temp. content file used to support partial PUT
0558: FileInputStream contentFileInStream = null;
0559:
0560: ResourceInfo resourceInfo = new ResourceInfo(path, resources);
0561: Range range = parseContentRange(req, resp);
0562:
0563: InputStream resourceInputStream = null;
0564:
0565: // Append data specified in ranges to existing content for this
0566: // resource - create a temp. file on the local filesystem to
0567: // perform this operation
0568: // Assume just one range is specified for now
0569: if (range != null) {
0570: contentFile = executePartialPut(req, range, path);
0571: resourceInputStream = new FileInputStream(contentFile);
0572: } else {
0573: resourceInputStream = req.getInputStream();
0574: }
0575:
0576: try {
0577: Resource newResource = new Resource(resourceInputStream);
0578: // FIXME: Add attributes
0579: if (exists) {
0580: resources.rebind(path, newResource);
0581: } else {
0582: resources.bind(path, newResource);
0583: }
0584: } catch (NamingException e) {
0585: result = false;
0586: }
0587:
0588: if (result) {
0589: if (exists) {
0590: resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
0591: } else {
0592: resp.setStatus(HttpServletResponse.SC_CREATED);
0593: }
0594: } else {
0595: resp.sendError(HttpServletResponse.SC_CONFLICT);
0596: }
0597:
0598: }
0599:
0600: /**
0601: * Handle a partial PUT. New content specified in request is appended to
0602: * existing content in oldRevisionContent (if present). This code does
0603: * not support simultaneous partial updates to the same resource.
0604: */
0605: protected File executePartialPut(HttpServletRequest req,
0606: Range range, String path) throws IOException {
0607:
0608: // Append data specified in ranges to existing content for this
0609: // resource - create a temp. file on the local filesystem to
0610: // perform this operation
0611: File tempDir = (File) getServletContext().getAttribute(
0612: "javax.servlet.context.tempdir");
0613: // Convert all '/' characters to '.' in resourcePath
0614: String convertedResourcePath = path.replace('/', '.');
0615: File contentFile = new File(tempDir, convertedResourcePath);
0616: if (contentFile.createNewFile()) {
0617: // Clean up contentFile when Tomcat is terminated
0618: contentFile.deleteOnExit();
0619: }
0620:
0621: RandomAccessFile randAccessContentFile = new RandomAccessFile(
0622: contentFile, "rw");
0623:
0624: Resource oldResource = null;
0625: try {
0626: Object obj = getResources().lookup(path);
0627: if (obj instanceof Resource)
0628: oldResource = (Resource) obj;
0629: } catch (NamingException e) {
0630: }
0631:
0632: // Copy data in oldRevisionContent to contentFile
0633: if (oldResource != null) {
0634: BufferedInputStream bufOldRevStream = new BufferedInputStream(
0635: oldResource.streamContent(), BUFFER_SIZE);
0636:
0637: int numBytesRead;
0638: byte[] copyBuffer = new byte[BUFFER_SIZE];
0639: while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) {
0640: randAccessContentFile
0641: .write(copyBuffer, 0, numBytesRead);
0642: }
0643:
0644: bufOldRevStream.close();
0645: }
0646:
0647: randAccessContentFile.setLength(range.length);
0648:
0649: // Append data in request input stream to contentFile
0650: randAccessContentFile.seek(range.start);
0651: int numBytesRead;
0652: byte[] transferBuffer = new byte[BUFFER_SIZE];
0653: BufferedInputStream requestBufInStream = new BufferedInputStream(
0654: req.getInputStream(), BUFFER_SIZE);
0655: while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) {
0656: randAccessContentFile
0657: .write(transferBuffer, 0, numBytesRead);
0658: }
0659: randAccessContentFile.close();
0660: requestBufInStream.close();
0661:
0662: return contentFile;
0663:
0664: }
0665:
0666: /**
0667: * Process a POST request for the specified resource.
0668: *
0669: * @param request The servlet request we are processing
0670: * @param response The servlet response we are creating
0671: *
0672: * @exception IOException if an input/output error occurs
0673: * @exception ServletException if a servlet-specified error occurs
0674: */
0675: protected void doDelete(HttpServletRequest req,
0676: HttpServletResponse resp) throws ServletException,
0677: IOException {
0678:
0679: if (readOnly) {
0680: resp.sendError(HttpServletResponse.SC_FORBIDDEN);
0681: return;
0682: }
0683:
0684: String path = getRelativePath(req);
0685:
0686: // Retrieve the Catalina context
0687: // Retrieve the resources
0688: DirContext resources = getResources();
0689:
0690: if (resources == null) {
0691: resp
0692: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
0693: return;
0694: }
0695:
0696: boolean exists = true;
0697: try {
0698: resources.lookup(path);
0699: } catch (NamingException e) {
0700: exists = false;
0701: }
0702:
0703: if (exists) {
0704: boolean result = true;
0705: try {
0706: resources.unbind(path);
0707: } catch (NamingException e) {
0708: result = false;
0709: }
0710: if (result) {
0711: resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
0712: } else {
0713: resp
0714: .sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
0715: }
0716: } else {
0717: resp.sendError(HttpServletResponse.SC_NOT_FOUND);
0718: }
0719:
0720: }
0721:
0722: /**
0723: * Check if the conditions specified in the optional If headers are
0724: * satisfied.
0725: *
0726: * @param request The servlet request we are processing
0727: * @param response The servlet response we are creating
0728: * @param resourceInfo File object
0729: * @return boolean true if the resource meets all the specified conditions,
0730: * and false if any of the conditions is not satisfied, in which case
0731: * request processing is stopped
0732: */
0733: protected boolean checkIfHeaders(HttpServletRequest request,
0734: HttpServletResponse response, ResourceInfo resourceInfo)
0735: throws IOException {
0736:
0737: return checkIfMatch(request, response, resourceInfo)
0738: && checkIfModifiedSince(request, response, resourceInfo)
0739: && checkIfNoneMatch(request, response, resourceInfo)
0740: && checkIfUnmodifiedSince(request, response,
0741: resourceInfo);
0742:
0743: }
0744:
0745: /**
0746: * Get the ETag associated with a file.
0747: *
0748: * @param resourceInfo File object
0749: * @param strong True if we want a strong ETag, in which case a checksum
0750: * of the file has to be calculated
0751: */
0752: protected String getETag(ResourceInfo resourceInfo) {
0753: if (resourceInfo.strongETag != null) {
0754: return resourceInfo.strongETag;
0755: } else if (resourceInfo.weakETag != null) {
0756: return resourceInfo.weakETag;
0757: } else {
0758: return "W/\"" + resourceInfo.length + "-"
0759: + resourceInfo.date + "\"";
0760: }
0761: }
0762:
0763: /**
0764: * Return a context-relative path, beginning with a "/", that represents
0765: * the canonical version of the specified path after ".." and "." elements
0766: * are resolved out. If the specified path attempts to go outside the
0767: * boundaries of the current context (i.e. too many ".." path elements
0768: * are present), return <code>null</code> instead.
0769: *
0770: * @param path Path to be normalized
0771: */
0772: protected String normalize(String path) {
0773:
0774: if (path == null)
0775: return null;
0776:
0777: // Create a place for the normalized path
0778: String normalized = path;
0779:
0780: /*
0781: * Commented out -- already URL-decoded in StandardContextMapper
0782: * Decoding twice leaves the container vulnerable to %25 --> '%'
0783: * attacks.
0784: *
0785: * if (normalized.indexOf('%') >= 0)
0786: * normalized = RequestUtil.URLDecode(normalized, "UTF8");
0787: */
0788:
0789: if (normalized == null)
0790: return (null);
0791:
0792: if (normalized.equals("/."))
0793: return "/";
0794:
0795: // Normalize the slashes and add leading slash if necessary
0796: if (normalized.indexOf('\\') >= 0)
0797: normalized = normalized.replace('\\', '/');
0798: if (!normalized.startsWith("/"))
0799: normalized = "/" + normalized;
0800:
0801: // Resolve occurrences of "//" in the normalized path
0802: while (true) {
0803: int index = normalized.indexOf("//");
0804: if (index < 0)
0805: break;
0806: normalized = normalized.substring(0, index)
0807: + normalized.substring(index + 1);
0808: }
0809:
0810: // Resolve occurrences of "/./" in the normalized path
0811: while (true) {
0812: int index = normalized.indexOf("/./");
0813: if (index < 0)
0814: break;
0815: normalized = normalized.substring(0, index)
0816: + normalized.substring(index + 2);
0817: }
0818:
0819: // Resolve occurrences of "/../" in the normalized path
0820: while (true) {
0821: int index = normalized.indexOf("/../");
0822: if (index < 0)
0823: break;
0824: if (index == 0)
0825: return (null); // Trying to go outside our context
0826: int index2 = normalized.lastIndexOf('/', index - 1);
0827: normalized = normalized.substring(0, index2)
0828: + normalized.substring(index + 3);
0829: }
0830:
0831: // Return the normalized path that we have completed
0832: return (normalized);
0833:
0834: }
0835:
0836: /**
0837: * URL rewriter.
0838: *
0839: * @param path Path which has to be rewiten
0840: */
0841: protected String rewriteUrl(String path) {
0842: return urlEncoder.encode(path);
0843: }
0844:
0845: /**
0846: * Display the size of a file.
0847: */
0848: protected void displaySize(StringBuffer buf, int filesize) {
0849:
0850: int leftside = filesize / 1024;
0851: int rightside = (filesize % 1024) / 103; // makes 1 digit
0852: // To avoid 0.0 for non-zero file, we bump to 0.1
0853: if (leftside == 0 && rightside == 0 && filesize != 0)
0854: rightside = 1;
0855: buf.append(leftside).append(".").append(rightside);
0856: buf.append(" KB");
0857:
0858: }
0859:
0860: /**
0861: * Serve the specified resource, optionally including the data content.
0862: *
0863: * @param request The servlet request we are processing
0864: * @param response The servlet response we are creating
0865: * @param content Should the content be included?
0866: *
0867: * @exception IOException if an input/output error occurs
0868: * @exception ServletException if a servlet-specified error occurs
0869: */
0870: protected void serveResource(HttpServletRequest request,
0871: HttpServletResponse response, boolean content)
0872: throws IOException, ServletException {
0873:
0874: // Identify the requested resource path
0875: String path = getRelativePath(request);
0876:
0877: //log("1");
0878:
0879: if (debug > 0) {
0880: if (content)
0881: log("DefaultServlet.serveResource: Serving resource '"
0882: + path + "' headers and data");
0883: else
0884: log("DefaultServlet.serveResource: Serving resource '"
0885: + path + "' headers only");
0886:
0887: }
0888:
0889: // Retrieve the Catalina context and Resources implementation
0890: DirContext resources = getResources();
0891: ResourceInfo resourceInfo = new ResourceInfo(path, resources);
0892:
0893: //System.out.println("Is it a collection? -> "+resourceInfo.collection);
0894:
0895: //System.out.println("Does resource info exist? -> "+resourceInfo.exists);
0896: if (!resourceInfo.exists) {
0897: response.sendError(HttpServletResponse.SC_NOT_FOUND,
0898: request.getRequestURI());
0899: return;
0900: }
0901:
0902: // If the resource is not a collection, and the resource path
0903: // ends with "/" or "\", return NOT FOUND
0904: //System.out.println("if the resource is not a collection, and the resource path ends with / return NOT FOUND");
0905: if (!resourceInfo.collection) {
0906: if (path.endsWith("/") || (path.endsWith("\\"))) {
0907: response.sendError(HttpServletResponse.SC_NOT_FOUND,
0908: request.getRequestURI());
0909: return;
0910: }
0911: }
0912:
0913: // If the resource is a collection (aka a directory), we check
0914: // the welcome files list.
0915: if (resourceInfo.collection) {
0916: System.out
0917: .println("It is a collection, lookup in welcome files.\nDefaultServlet Request URI = "
0918: + request.getRequestURI());
0919:
0920: if (!request.getRequestURI().endsWith("/")) {
0921: String redirectPath = path;
0922: String contextPath = request.getContextPath();
0923: if ((contextPath != null) && (!contextPath.equals("/"))) {
0924: redirectPath = contextPath + redirectPath;
0925: }
0926: if (!(redirectPath.endsWith("/")))
0927: redirectPath = redirectPath + "/";
0928: redirectPath = appendParameters(request, redirectPath);
0929: response.sendRedirect(redirectPath);
0930: return;
0931: }
0932:
0933: ResourceInfo welcomeFileInfo = checkWelcomeFiles(path,
0934: resources);
0935: if (welcomeFileInfo != null) {
0936: String redirectPath = welcomeFileInfo.path;
0937: String contextPath = request.getContextPath();
0938:
0939: if ((contextPath != null) && (!contextPath.equals("/"))) {
0940: redirectPath = contextPath + redirectPath;
0941: }
0942: redirectPath = appendParameters(request, redirectPath);
0943: response.sendRedirect(redirectPath);
0944: return;
0945: }
0946:
0947: } else {
0948:
0949: // Checking If headers
0950: //System.out.println("Checking If headers");
0951: boolean included = (request
0952: .getAttribute("javax.servlet.include.context_path") != null);
0953: if (!included
0954: && !checkIfHeaders(request, response, resourceInfo)) {
0955:
0956: return;
0957: }
0958:
0959: }
0960:
0961: // Find content type.
0962: //System.out.println("Find content type.");
0963: String contentType = getServletContext().getMimeType(
0964: resourceInfo.path);
0965:
0966: Vector ranges = null;
0967:
0968: if (resourceInfo.collection) {
0969: // Skip directory listings if we have been configured to
0970: // suppress them
0971: if (!listings) {
0972: //System.out.println("Listings is OFF");
0973: response.sendError(HttpServletResponse.SC_NOT_FOUND,
0974: request.getRequestURI());
0975: return;
0976: }
0977: contentType = "text/html;charset=UTF-8";
0978:
0979: } else {
0980:
0981: // Parse range specifier
0982:
0983: ranges = parseRange(request, response, resourceInfo);
0984:
0985: // ETag header
0986: response.setHeader("ETag", getETag(resourceInfo));
0987:
0988: // Last-Modified header
0989: if (debug > 0)
0990: log("DefaultServlet.serveFile: lastModified='"
0991: + (new Timestamp(resourceInfo.date)).toString()
0992: + "'");
0993: response.setHeader("Last-Modified", resourceInfo.httpDate);
0994:
0995: //response.setHeader("Date", resourceInfo.httpDate);
0996:
0997: }
0998:
0999: ServletOutputStream ostream = null;
1000: PrintWriter writer = null;
1001:
1002: if (content) {
1003:
1004: // Trying to retrieve the servlet output stream
1005:
1006: try {
1007: ostream = response.getOutputStream();
1008: } catch (IllegalStateException e) {
1009: // If it fails, we try to get a Writer instead if we're
1010: // trying to serve a text file
1011: if ((contentType != null)
1012: && (contentType.startsWith("text"))) {
1013: writer = response.getWriter();
1014: } else {
1015: throw e;
1016: }
1017: }
1018:
1019: }
1020:
1021: if ((resourceInfo.collection)
1022: || (((ranges == null) || (ranges.isEmpty())) && (request
1023: .getHeader("Range") == null))) {
1024:
1025: // Set the appropriate output headers
1026: if (contentType != null) {
1027: if (debug > 0)
1028: log("DefaultServlet.serveFile: contentType='"
1029: + contentType + "'");
1030: response.setContentType(contentType);
1031: }
1032: long contentLength = resourceInfo.length;
1033: if ((!resourceInfo.collection) && (contentLength >= 0)) {
1034: if (debug > 0)
1035: log("DefaultServlet.serveFile: contentLength="
1036: + contentLength);
1037: if (request
1038: .getAttribute("javax.servlet.include.context_path") == null) {
1039: //System.out.println("DefaultServlet : set content length");
1040: response.setContentLength((int) contentLength);
1041: } else {
1042: //System.out.println("DefaultServlet : DO NOT set content length! This is an include!");
1043: }
1044: }
1045:
1046: if (resourceInfo.collection) {
1047:
1048: if (content) {
1049: // Serve the directory browser
1050: if (request.getRequestURI().startsWith("/WEB-INF")) {
1051: response.sendError(
1052: HttpServletResponse.SC_NOT_FOUND,
1053: request.getRequestURI());
1054: return;
1055: }
1056:
1057: if (!resourceInfo.exists()) {
1058: response.sendError(
1059: HttpServletResponse.SC_NOT_FOUND,
1060: request.getRequestURI());
1061: return;
1062: }
1063:
1064: resourceInfo.setStream(render(request
1065: .getContextPath(), resourceInfo));
1066: }
1067:
1068: }
1069:
1070: // Copy the input stream to our output stream (if requested)
1071: if (content) {
1072: try {
1073: response.setBufferSize(output);
1074: } catch (IllegalStateException e) {
1075: // Silent catch
1076: }
1077: if (ostream != null) {
1078: copy(resourceInfo, ostream);
1079: } else {
1080: copy(resourceInfo, writer);
1081: }
1082: }
1083:
1084: } else {
1085:
1086: if ((ranges == null) || (ranges.isEmpty()))
1087: return;
1088:
1089: // Partial content response.
1090:
1091: response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
1092:
1093: if (ranges.size() == 1) {
1094:
1095: Range range = (Range) ranges.elementAt(0);
1096: response.addHeader("Content-Range", "bytes "
1097: + range.start + "-" + range.end + "/"
1098: + range.length);
1099:
1100: if (request
1101: .getAttribute("javax.servlet.include.context_path") == null) {
1102: //System.out.println("DefaultServlet : set content length");
1103: response.setContentLength((int) (range.end
1104: - range.start + 1));
1105: } else {
1106: //System.out.println("DefaultServlet : DO NOT set content length! This is an include!");
1107: }
1108:
1109: if (contentType != null) {
1110: if (debug > 0)
1111: log("DefaultServlet.serveFile: contentType='"
1112: + contentType + "'");
1113: response.setContentType(contentType);
1114: }
1115:
1116: if (content) {
1117: try {
1118: response.setBufferSize(output);
1119: } catch (IllegalStateException e) {
1120: // Silent catch
1121: }
1122: if (ostream != null) {
1123: copy(resourceInfo, ostream, range);
1124: } else {
1125: copy(resourceInfo, writer, range);
1126: }
1127: }
1128:
1129: } else {
1130:
1131: response
1132: .setContentType("multipart/byteranges; boundary="
1133: + mimeSeparation);
1134:
1135: if (content) {
1136: try {
1137: response.setBufferSize(output);
1138: } catch (IllegalStateException e) {
1139: // Silent catch
1140: }
1141: if (ostream != null) {
1142: copy(resourceInfo, ostream, ranges.elements(),
1143: contentType);
1144: } else {
1145: copy(resourceInfo, writer, ranges.elements(),
1146: contentType);
1147: }
1148: }
1149:
1150: }
1151:
1152: }
1153:
1154: }
1155:
1156: /**
1157: * Parse the content-range header.
1158: *
1159: * @param request The servlet request we are processing
1160: * @param response The servlet response we are creating
1161: * @return Range
1162: */
1163: protected Range parseContentRange(HttpServletRequest request,
1164: HttpServletResponse response) throws IOException {
1165:
1166: // Retrieving the content-range header (if any is specified
1167: String rangeHeader = request.getHeader("Content-Range");
1168:
1169: if (rangeHeader == null)
1170: return null;
1171:
1172: // bytes is the only range unit supported
1173: if (!rangeHeader.startsWith("bytes")) {
1174: response.sendError(HttpServletResponse.SC_BAD_REQUEST);
1175: return null;
1176: }
1177:
1178: rangeHeader = rangeHeader.substring(6).trim();
1179:
1180: int dashPos = rangeHeader.indexOf('-');
1181: int slashPos = rangeHeader.indexOf('/');
1182:
1183: if (dashPos == -1) {
1184: response.sendError(HttpServletResponse.SC_BAD_REQUEST);
1185: return null;
1186: }
1187:
1188: if (slashPos == -1) {
1189: response.sendError(HttpServletResponse.SC_BAD_REQUEST);
1190: return null;
1191: }
1192:
1193: Range range = new Range();
1194:
1195: try {
1196: range.start = Long.parseLong(rangeHeader.substring(0,
1197: dashPos));
1198: range.end = Long.parseLong(rangeHeader.substring(
1199: dashPos + 1, slashPos));
1200: range.length = Long.parseLong(rangeHeader.substring(
1201: slashPos + 1, rangeHeader.length()));
1202: } catch (NumberFormatException e) {
1203: response.sendError(HttpServletResponse.SC_BAD_REQUEST);
1204: return null;
1205: }
1206:
1207: if (!range.validate()) {
1208: response.sendError(HttpServletResponse.SC_BAD_REQUEST);
1209: return null;
1210: }
1211:
1212: return range;
1213:
1214: }
1215:
1216: /**
1217: * Parse the range header.
1218: *
1219: * @param request The servlet request we are processing
1220: * @param response The servlet response we are creating
1221: * @return Vector of ranges
1222: */
1223: protected Vector parseRange(HttpServletRequest request,
1224: HttpServletResponse response, ResourceInfo resourceInfo)
1225: throws IOException {
1226:
1227: // Checking If-Range
1228: String headerValue = request.getHeader("If-Range");
1229: if (headerValue != null) {
1230:
1231: String eTag = getETag(resourceInfo);
1232: long lastModified = resourceInfo.date;
1233:
1234: Date date = null;
1235:
1236: // Parsing the HTTP Date
1237: for (int i = 0; (date == null) && (i < formats.length); i++) {
1238: try {
1239: date = formats[i].parse(headerValue);
1240: } catch (ParseException e) {
1241: ;
1242: }
1243: }
1244:
1245: if (date == null) {
1246:
1247: // If the ETag the client gave does not match the entity
1248: // etag, then the entire entity is returned.
1249: if (!eTag.equals(headerValue.trim()))
1250: return null;
1251:
1252: } else {
1253:
1254: // If the timestamp of the entity the client got is older than
1255: // the last modification date of the entity, the entire entity
1256: // is returned.
1257: if (lastModified > (date.getTime() + 1000))
1258: return null;
1259:
1260: }
1261:
1262: }
1263:
1264: long fileLength = resourceInfo.length;
1265:
1266: if (fileLength == 0)
1267: return null;
1268:
1269: // Retrieving the range header (if any is specified
1270: String rangeHeader = request.getHeader("Range");
1271:
1272: if (rangeHeader == null)
1273: return null;
1274: // bytes is the only range unit supported (and I don't see the point
1275: // of adding new ones).
1276: if (!rangeHeader.startsWith("bytes")) {
1277: response
1278: .addHeader("Content-Range", "bytes */" + fileLength);
1279: response
1280: .sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
1281: return null;
1282: }
1283:
1284: rangeHeader = rangeHeader.substring(6);
1285:
1286: // Vector which will contain all the ranges which are successfully
1287: // parsed.
1288: Vector result = new Vector();
1289: StringTokenizer commaTokenizer = new StringTokenizer(
1290: rangeHeader, ",");
1291:
1292: // Parsing the range list
1293: while (commaTokenizer.hasMoreTokens()) {
1294: String rangeDefinition = commaTokenizer.nextToken();
1295:
1296: Range currentRange = new Range();
1297: currentRange.length = fileLength;
1298:
1299: int dashPos = rangeDefinition.indexOf('-');
1300:
1301: if (dashPos == -1) {
1302: response.addHeader("Content-Range", "bytes */"
1303: + fileLength);
1304: response
1305: .sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
1306: return null;
1307: }
1308:
1309: if (dashPos == 0) {
1310:
1311: try {
1312: long offset = Long.parseLong(rangeDefinition);
1313: currentRange.start = fileLength + offset;
1314: currentRange.end = fileLength - 1;
1315: } catch (NumberFormatException e) {
1316: response.addHeader("Content-Range", "bytes */"
1317: + fileLength);
1318: response
1319: .sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
1320: return null;
1321: }
1322:
1323: } else {
1324:
1325: try {
1326: currentRange.start = Long.parseLong(rangeDefinition
1327: .substring(0, dashPos));
1328: if (dashPos < rangeDefinition.length() - 1)
1329: currentRange.end = Long
1330: .parseLong(rangeDefinition.substring(
1331: dashPos + 1, rangeDefinition
1332: .length()));
1333: else
1334: currentRange.end = fileLength - 1;
1335: } catch (NumberFormatException e) {
1336: response.addHeader("Content-Range", "bytes */"
1337: + fileLength);
1338: response
1339: .sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
1340: return null;
1341: }
1342:
1343: }
1344:
1345: if (!currentRange.validate()) {
1346: response.addHeader("Content-Range", "bytes */"
1347: + fileLength);
1348: response
1349: .sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
1350: return null;
1351: }
1352:
1353: result.addElement(currentRange);
1354: }
1355:
1356: return result;
1357: }
1358:
1359: /**
1360: * Append the request parameters to the redirection string before calling
1361: * sendRedirect.
1362: */
1363: protected String appendParameters(HttpServletRequest request,
1364: String redirectPath) {
1365:
1366: StringBuffer result = new StringBuffer(rewriteUrl(redirectPath));
1367:
1368: String query = request.getQueryString();
1369: if (query != null)
1370: result.append("?").append(query);
1371:
1372: return result.toString();
1373:
1374: }
1375:
1376: /**
1377: * Return an InputStream to an HTML representation of the contents
1378: * of this directory.
1379: *
1380: * @param contextPath Context path to which our internal paths are
1381: * relative
1382: */
1383: protected InputStream render(String contextPath, ResourceInfo resourceInfo)
1384: {
1385:
1386: String name = resourceInfo.path;
1387:
1388: // Number of characters to trim from the beginnings of filenames
1389: int trim = name.length();
1390: if (!name.endsWith("/"))
1391: trim += 1;
1392: if (name.equals("/"))
1393: trim = 1;
1394:
1395: // Prepare a writer to a buffered area
1396: ByteArrayOutputStream stream = new ByteArrayOutputStream();
1397: OutputStreamWriter osWriter = null;
1398: try {
1399: osWriter = new OutputStreamWriter(stream, "UTF8");
1400: } catch (Exception e) {
1401: // Should never happen
1402: osWriter = new OutputStreamWriter(stream);
1403: }
1404: PrintWriter writer = new PrintWriter(osWriter);
1405:
1406: StringBuffer sb = new StringBuffer();
1407:
1408: // Render the page header
1409: sb.append("<html>");
1410: sb.append("<head>");
1411: sb.append(" <title>");
1412: sb.append(" [ "+name+" ] Directory Listing");
1413: sb.append(" </title>\r\n");
1414: sb.append("</head>");
1415:
1416: sb.append("<body>");
1417:
1418:
1419: sb.append("<p align=\"center\" style=\"color: #676a77; margin-left: 10px; margin-right: 10px; font-weight: bold; font-size: 20pt\">");
1420: sb.append("Directory Listing");
1421: sb.append("</p>");
1422:
1423:
1424: sb.append(" <table align=\"center\" border=\"0\" width=\"92%\">");
1425: sb.append(" <tr>");
1426: sb.append(" <td>");
1427: sb.append(" <p align=\"left\" style=\"font-weight: normal; font-size: 12pt\">");
1428: sb.append(" Browse directory <b>"+name+"</b> <br><br>");
1429: sb.append(" For more information on how to configure the Rimfaxe Web Server, please visit");
1430: sb.append(" <a href=\"http://www.rimfaxe.com\"><b>www.rimfaxe.com</b></a>. <br>");
1431: sb.append(" </p>");
1432: sb.append(" </td>");
1433: sb.append(" </tr>");
1434: sb.append(" <tr> <td> </td> </tr>");
1435:
1436:
1437:
1438: sb.append(" <tr>");
1439: sb.append(" <td>");
1440: sb.append(" <p align=\"left\" style=\"font-weight: normal; font-size: 12pt\">");
1441: sb.append(" <b>[ "+name+" ]</b> ");
1442:
1443: // Render the link to our parent (if required)
1444: String parentDirectory = name;
1445: if (parentDirectory.endsWith("/")) {
1446: parentDirectory =
1447: parentDirectory.substring(0, parentDirectory.length() - 1);
1448: }
1449: int slash = parentDirectory.lastIndexOf('/');
1450: if (slash >= 0)
1451: {
1452: String parent = name.substring(0, slash);
1453: sb.append(" <a href=\"");
1454: sb.append(rewriteUrl(contextPath));
1455: if (parent.equals(""))
1456: parent = "/";
1457: sb.append(rewriteUrl(parent));
1458: if (!parent.endsWith("/"))
1459: sb.append("/");
1460: sb.append("\">");
1461: sb.append("<b>[ ");
1462: sb.append(parent);
1463: sb.append(" ]</b>");
1464: sb.append("</a>");
1465: }
1466:
1467: sb.append(" </p>");
1468: sb.append(" </td>");
1469: sb.append(" </tr>");
1470:
1471: sb.append(" <tr> <td> </td> </tr>");
1472:
1473: sb.append(" <tr>");
1474: sb.append(" <td>");
1475: sb.append(" <table border=\"0\" width=\"100%\" cellspacing=\"2\" cellpadding=\"3\" align=\"center\">");
1476: sb.append(" <tr bgcolor=\"#90a1b3\">");
1477: sb.append(" <td align=\"left\">");
1478: sb.append(" <p align=\"left\" style=\"margin-left: 5px; margin-right: 5px; font-weight: bold; font-size: 12pt\">Filename</p>");
1479: sb.append(" </td>");
1480: sb.append(" <td align=\"right\">");
1481: sb.append(" <p align=\"right\" style=\"margin-left: 5px; margin-right: 5px; font-weight: bold; font-size: 12pt\">Size</p>");
1482: sb.append(" </td>");
1483: sb.append(" <td align=\"right\">");
1484: sb.append(" <p align=\"right\" style=\"margin-left: 5px; margin-right: 5px; font-weight: bold; font-size: 12pt\">Last Modified</p>");
1485: sb.append(" </td>");
1486: sb.append(" </tr>");
1487:
1488:
1489:
1490:
1491: try
1492: {
1493:
1494: // Render the directory entries within this directory
1495: DirContext directory = resourceInfo.directory;
1496: NamingEnumeration enum = resourceInfo.resources.list(resourceInfo.path);
1497: boolean shade = false;
1498: while (enum.hasMoreElements())
1499: {
1500:
1501: NameClassPair ncPair = (NameClassPair) enum.nextElement();
1502: String resourceName = ncPair.getName();
1503: ResourceInfo childResourceInfo = new ResourceInfo(resourceName, directory);
1504:
1505: String trimmed = resourceName/*.substring(trim)*/;
1506: if (trimmed.equalsIgnoreCase("WEB-INF") || trimmed.equalsIgnoreCase("META-INF"))
1507: continue;
1508:
1509: sb.append("<tr");
1510:
1511: if (shade)
1512: sb.append(" bgcolor=\"c7d0d9\"");
1513: else
1514: sb.append(" bgcolor=\"c7d0d9\"");
1515:
1516: sb.append(">\r\n");
1517: shade = !shade;
1518:
1519: sb.append("<td align=\"left\"> \r\n");
1520: sb.append("<a href=\"");
1521: sb.append(rewriteUrl(contextPath));
1522: resourceName = rewriteUrl(name + resourceName);
1523: sb.append(resourceName);
1524: if (childResourceInfo.collection)
1525: sb.append("/");
1526: sb.append("\"><tt>");
1527: sb.append(trimmed);
1528: if (childResourceInfo.collection)
1529: sb.append("/");
1530: sb.append("</tt></a></td>\r\n");
1531:
1532: sb.append("<td align=\"right\"><tt>");
1533: if (childResourceInfo.collection)
1534: sb.append(" ");
1535: else
1536: sb.append(renderSize(childResourceInfo.length));
1537: sb.append("</tt></td>\r\n");
1538:
1539: sb.append("<td align=\"right\"><tt>");
1540: sb.append(childResourceInfo.httpDate);
1541: sb.append("</tt></td>\r\n");
1542:
1543: sb.append("</tr>\r\n");
1544: }
1545:
1546: }
1547: catch (NamingException e)
1548: {
1549: // Appropiate action? TODO
1550: }
1551:
1552: // Render the page footer
1553: sb.append("</table>\r\n");
1554:
1555: sb.append("</td>");
1556: sb.append("</tr>");
1557:
1558: sb.append("<tr> <td> </td> </tr>");
1559:
1560:
1561: sb.append(" <tr>");
1562: sb.append(" <td height=\"15\">");
1563:
1564:
1565: sb.append(" <p style=\"margin-left: 10px; font-size: 10pt\">");
1566: sb.append(" <b>"+com.rimfaxe.webserver.ObjectStore.getConfiguration().getBanner()+"</b> - <a href=\"http://www.rimfaxe.com\">Rimfaxe Software</a>");
1567: sb.append(" </p>");
1568: sb.append(" </td>");
1569: sb.append(" </tr>");
1570:
1571:
1572: sb.append(" </table>");
1573: sb.append("</body>");
1574: sb.append("</html>");
1575:
1576:
1577:
1578: // Return an input stream to the underlying bytes
1579: writer.write(sb.toString());
1580: writer.flush();
1581: return (new ByteArrayInputStream(stream.toByteArray()));
1582:
1583: }
1584:
1585: /**
1586: * Render the specified file size (in bytes).
1587: *
1588: * @param size File size (in bytes)
1589: */
1590: protected String renderSize(long size) {
1591:
1592: long leftSide = size / 1024;
1593: long rightSide = (size % 1024) / 103; // Makes 1 digit
1594: if ((leftSide == 0) && (rightSide == 0) && (size > 0))
1595: rightSide = 1;
1596:
1597: return ("" + leftSide + "." + rightSide + " kb");
1598:
1599: }
1600:
1601: // -------------------------------------------------------- Private Methods
1602:
1603: /**
1604: * Check if the if-match condition is satisfied.
1605: *
1606: * @param request The servlet request we are processing
1607: * @param response The servlet response we are creating
1608: * @param resourceInfo File object
1609: * @return boolean true if the resource meets the specified condition,
1610: * and false if the condition is not satisfied, in which case request
1611: * processing is stopped
1612: */
1613: private boolean checkIfMatch(HttpServletRequest request,
1614: HttpServletResponse response, ResourceInfo resourceInfo)
1615: throws IOException {
1616:
1617: String eTag = getETag(resourceInfo);
1618: String headerValue = request.getHeader("If-Match");
1619: if (headerValue != null) {
1620: if (headerValue.indexOf('*') == -1) {
1621:
1622: StringTokenizer commaTokenizer = new StringTokenizer(
1623: headerValue, ",");
1624: boolean conditionSatisfied = false;
1625:
1626: while (!conditionSatisfied
1627: && commaTokenizer.hasMoreTokens()) {
1628: String currentToken = commaTokenizer.nextToken();
1629: if (currentToken.trim().equals(eTag))
1630: conditionSatisfied = true;
1631: }
1632:
1633: // If none of the given ETags match, 412 Precodition failed is
1634: // sent back
1635: if (!conditionSatisfied) {
1636: response
1637: .sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
1638: return false;
1639: }
1640:
1641: }
1642: }
1643: return true;
1644:
1645: }
1646:
1647: /**
1648: * Check if the if-modified-since condition is satisfied.
1649: *
1650: * @param request The servlet request we are processing
1651: * @param response The servlet response we are creating
1652: * @param resourceInfo File object
1653: * @return boolean true if the resource meets the specified condition,
1654: * and false if the condition is not satisfied, in which case request
1655: * processing is stopped
1656: */
1657: private boolean checkIfModifiedSince(HttpServletRequest request,
1658: HttpServletResponse response, ResourceInfo resourceInfo)
1659: throws IOException {
1660:
1661: try {
1662: long headerValue = request
1663: .getDateHeader("If-Modified-Since");
1664: long lastModified = resourceInfo.date;
1665: if (headerValue != -1) {
1666:
1667: // If an If-None-Match header has been specified, if modified since
1668: // is ignored.
1669: if ((request.getHeader("If-None-Match") == null)
1670: && (lastModified <= headerValue + 1000)) {
1671: // The entity has not been modified since the date
1672: // specified by the client. This is not an error case.
1673: response
1674: .setStatus(HttpServletResponse.SC_NOT_MODIFIED);
1675: return false;
1676: }
1677: }
1678: } catch (IllegalArgumentException illegalArgument) {
1679: return false;
1680: }
1681:
1682: return true;
1683:
1684: }
1685:
1686: /**
1687: * Check if the if-none-match condition is satisfied.
1688: *
1689: * @param request The servlet request we are processing
1690: * @param response The servlet response we are creating
1691: * @param resourceInfo File object
1692: * @return boolean true if the resource meets the specified condition,
1693: * and false if the condition is not satisfied, in which case request
1694: * processing is stopped
1695: */
1696: private boolean checkIfNoneMatch(HttpServletRequest request,
1697: HttpServletResponse response, ResourceInfo resourceInfo)
1698: throws IOException {
1699:
1700: String eTag = getETag(resourceInfo);
1701: String headerValue = request.getHeader("If-None-Match");
1702: if (headerValue != null) {
1703:
1704: boolean conditionSatisfied = false;
1705:
1706: if (!headerValue.equals("*")) {
1707:
1708: StringTokenizer commaTokenizer = new StringTokenizer(
1709: headerValue, ",");
1710:
1711: while (!conditionSatisfied
1712: && commaTokenizer.hasMoreTokens()) {
1713: String currentToken = commaTokenizer.nextToken();
1714: if (currentToken.trim().equals(eTag))
1715: conditionSatisfied = true;
1716: }
1717:
1718: } else {
1719: conditionSatisfied = true;
1720: }
1721:
1722: if (conditionSatisfied) {
1723:
1724: // For GET and HEAD, we should respond with
1725: // 304 Not Modified.
1726: // For every other method, 412 Precondition Failed is sent
1727: // back.
1728: if (("GET".equals(request.getMethod()))
1729: || ("HEAD".equals(request.getMethod()))) {
1730: response
1731: .setStatus(HttpServletResponse.SC_NOT_MODIFIED);
1732: return false;
1733: } else {
1734: response
1735: .sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
1736: return false;
1737: }
1738: }
1739: }
1740: return true;
1741:
1742: }
1743:
1744: /**
1745: * Check if the if-unmodified-since condition is satisfied.
1746: *
1747: * @param request The servlet request we are processing
1748: * @param response The servlet response we are creating
1749: * @param resourceInfo File object
1750: * @return boolean true if the resource meets the specified condition,
1751: * and false if the condition is not satisfied, in which case request
1752: * processing is stopped
1753: */
1754: private boolean checkIfUnmodifiedSince(HttpServletRequest request,
1755: HttpServletResponse response, ResourceInfo resourceInfo)
1756: throws IOException {
1757: try {
1758: long lastModified = resourceInfo.date;
1759: long headerValue = request
1760: .getDateHeader("If-Unmodified-Since");
1761: if (headerValue != -1) {
1762: if (lastModified > headerValue) {
1763: // The entity has not been modified since the date
1764: // specified by the client. This is not an error case.
1765: response
1766: .sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
1767: return false;
1768: }
1769: }
1770: } catch (IllegalArgumentException illegalArgument) {
1771: return false;
1772: }
1773: return true;
1774:
1775: }
1776:
1777: /**
1778: * Copy the contents of the specified input stream to the specified
1779: * output stream, and ensure that both streams are closed before returning
1780: * (even in the face of an exception).
1781: *
1782: * @param istream The input stream to read from
1783: * @param ostream The output stream to write to
1784: *
1785: * @exception IOException if an input/output error occurs
1786: */
1787: private void copy(ResourceInfo resourceInfo,
1788: ServletOutputStream ostream) throws IOException {
1789:
1790: IOException exception = null;
1791:
1792: // FIXME : i18n ?
1793: InputStream resourceInputStream = resourceInfo.getStream();
1794: InputStream istream = new BufferedInputStream(
1795: resourceInputStream, input);
1796:
1797: // Copy the input stream to the output stream
1798: exception = copyRange(istream, ostream);
1799:
1800: // Clean up the input stream
1801: try {
1802: istream.close();
1803: } catch (Throwable t) {
1804: ;
1805: }
1806:
1807: // Rethrow any exception that has occurred
1808: if (exception != null)
1809: throw exception;
1810:
1811: }
1812:
1813: /**
1814: * Copy the contents of the specified input stream to the specified
1815: * output stream, and ensure that both streams are closed before returning
1816: * (even in the face of an exception).
1817: *
1818: * @param istream The input stream to read from
1819: * @param writer The writer to write to
1820: *
1821: * @exception IOException if an input/output error occurs
1822: */
1823: private void copy(ResourceInfo resourceInfo, PrintWriter writer)
1824: throws IOException {
1825:
1826: IOException exception = null;
1827:
1828: InputStream resourceInputStream = resourceInfo.getStream();
1829: // FIXME : i18n ?
1830: Reader reader = new InputStreamReader(resourceInputStream);
1831:
1832: // Copy the input stream to the output stream
1833: exception = copyRange(reader, writer);
1834:
1835: // Clean up the reader
1836: try {
1837: reader.close();
1838: } catch (Throwable t) {
1839: ;
1840: }
1841:
1842: // Rethrow any exception that has occurred
1843: if (exception != null)
1844: throw exception;
1845:
1846: }
1847:
1848: /**
1849: * Copy the contents of the specified input stream to the specified
1850: * output stream, and ensure that both streams are closed before returning
1851: * (even in the face of an exception).
1852: *
1853: * @param resourceInfo The ResourceInfo object
1854: * @param ostream The output stream to write to
1855: * @param range Range the client wanted to retrieve
1856: * @exception IOException if an input/output error occurs
1857: */
1858: private void copy(ResourceInfo resourceInfo,
1859: ServletOutputStream ostream, Range range)
1860: throws IOException {
1861:
1862: IOException exception = null;
1863:
1864: InputStream resourceInputStream = resourceInfo.getStream();
1865: InputStream istream = new BufferedInputStream(
1866: resourceInputStream, input);
1867: exception = copyRange(istream, ostream, range.start, range.end);
1868:
1869: // Clean up the input stream
1870: try {
1871: istream.close();
1872: } catch (Throwable t) {
1873: ;
1874: }
1875:
1876: // Rethrow any exception that has occurred
1877: if (exception != null)
1878: throw exception;
1879:
1880: }
1881:
1882: /**
1883: * Copy the contents of the specified input stream to the specified
1884: * output stream, and ensure that both streams are closed before returning
1885: * (even in the face of an exception).
1886: *
1887: * @param resourceInfo The ResourceInfo object
1888: * @param writer The writer to write to
1889: * @param range Range the client wanted to retrieve
1890: * @exception IOException if an input/output error occurs
1891: */
1892: private void copy(ResourceInfo resourceInfo, PrintWriter writer,
1893: Range range) throws IOException {
1894:
1895: IOException exception = null;
1896:
1897: InputStream resourceInputStream = resourceInfo.getStream();
1898: Reader reader = new InputStreamReader(resourceInputStream);
1899: exception = copyRange(reader, writer, range.start, range.end);
1900:
1901: // Clean up the input stream
1902: try {
1903: reader.close();
1904: } catch (Throwable t) {
1905: ;
1906: }
1907:
1908: // Rethrow any exception that has occurred
1909: if (exception != null)
1910: throw exception;
1911:
1912: }
1913:
1914: /**
1915: * Copy the contents of the specified input stream to the specified
1916: * output stream, and ensure that both streams are closed before returning
1917: * (even in the face of an exception).
1918: *
1919: * @param resourceInfo The ResourceInfo object
1920: * @param ostream The output stream to write to
1921: * @param ranges Enumeration of the ranges the client wanted to retrieve
1922: * @param contentType Content type of the resource
1923: * @exception IOException if an input/output error occurs
1924: */
1925: private void copy(ResourceInfo resourceInfo,
1926: ServletOutputStream ostream, Enumeration ranges,
1927: String contentType) throws IOException {
1928:
1929: IOException exception = null;
1930:
1931: while ((exception == null) && (ranges.hasMoreElements())) {
1932:
1933: InputStream resourceInputStream = resourceInfo.getStream();
1934: InputStream istream = // FIXME: internationalization???????
1935: new BufferedInputStream(resourceInputStream, input);
1936:
1937: Range currentRange = (Range) ranges.nextElement();
1938:
1939: // Writing MIME header.
1940: ostream.println("--" + mimeSeparation);
1941: if (contentType != null)
1942: ostream.println("Content-Type: " + contentType);
1943: ostream.println("Content-Range: bytes "
1944: + currentRange.start + "-" + currentRange.end + "/"
1945: + currentRange.length);
1946: ostream.println();
1947:
1948: // Printing content
1949: exception = copyRange(istream, ostream, currentRange.start,
1950: currentRange.end);
1951:
1952: try {
1953: istream.close();
1954: } catch (Throwable t) {
1955: ;
1956: }
1957:
1958: }
1959:
1960: ostream.print("--" + mimeSeparation + "--");
1961:
1962: // Rethrow any exception that has occurred
1963: if (exception != null)
1964: throw exception;
1965:
1966: }
1967:
1968: /**
1969: * Copy the contents of the specified input stream to the specified
1970: * output stream, and ensure that both streams are closed before returning
1971: * (even in the face of an exception).
1972: *
1973: * @param resourceInfo The ResourceInfo object
1974: * @param writer The writer to write to
1975: * @param ranges Enumeration of the ranges the client wanted to retrieve
1976: * @param contentType Content type of the resource
1977: * @exception IOException if an input/output error occurs
1978: */
1979: private void copy(ResourceInfo resourceInfo, PrintWriter writer,
1980: Enumeration ranges, String contentType) throws IOException {
1981:
1982: IOException exception = null;
1983:
1984: while ((exception == null) && (ranges.hasMoreElements())) {
1985:
1986: InputStream resourceInputStream = resourceInfo.getStream();
1987: Reader reader = new InputStreamReader(resourceInputStream);
1988:
1989: Range currentRange = (Range) ranges.nextElement();
1990:
1991: // Writing MIME header.
1992: writer.println("--" + mimeSeparation);
1993: if (contentType != null)
1994: writer.println("Content-Type: " + contentType);
1995: writer.println("Content-Range: bytes " + currentRange.start
1996: + "-" + currentRange.end + "/"
1997: + currentRange.length);
1998: writer.println();
1999:
2000: // Printing content
2001: exception = copyRange(reader, writer, currentRange.start,
2002: currentRange.end);
2003:
2004: try {
2005: reader.close();
2006: } catch (Throwable t) {
2007: ;
2008: }
2009:
2010: }
2011:
2012: writer.print("--" + mimeSeparation + "--");
2013:
2014: // Rethrow any exception that has occurred
2015: if (exception != null)
2016: throw exception;
2017:
2018: }
2019:
2020: /**
2021: * Copy the contents of the specified input stream to the specified
2022: * output stream, and ensure that both streams are closed before returning
2023: * (even in the face of an exception).
2024: *
2025: * @param istream The input stream to read from
2026: * @param ostream The output stream to write to
2027: * @return Exception which occurred during processing
2028: */
2029: private IOException copyRange(InputStream istream,
2030: ServletOutputStream ostream) {
2031:
2032: // Copy the input stream to the output stream
2033: IOException exception = null;
2034: byte buffer[] = new byte[input];
2035: int len = buffer.length;
2036: while (true) {
2037: try {
2038: len = istream.read(buffer);
2039: if (len == -1)
2040: break;
2041: ostream.write(buffer, 0, len);
2042: } catch (IOException e) {
2043: exception = e;
2044: len = -1;
2045: break;
2046: }
2047: }
2048: return exception;
2049:
2050: }
2051:
2052: /**
2053: * Copy the contents of the specified input stream to the specified
2054: * output stream, and ensure that both streams are closed before returning
2055: * (even in the face of an exception).
2056: *
2057: * @param reader The reader to read from
2058: * @param writer The writer to write to
2059: * @return Exception which occurred during processing
2060: */
2061: private IOException copyRange(Reader reader, PrintWriter writer) {
2062:
2063: // Copy the input stream to the output stream
2064: IOException exception = null;
2065: char buffer[] = new char[input];
2066: int len = buffer.length;
2067: while (true) {
2068: try {
2069: len = reader.read(buffer);
2070: if (len == -1)
2071: break;
2072: writer.write(buffer, 0, len);
2073: } catch (IOException e) {
2074: exception = e;
2075: len = -1;
2076: break;
2077: }
2078: }
2079: return exception;
2080:
2081: }
2082:
2083: /**
2084: * Copy the contents of the specified input stream to the specified
2085: * output stream, and ensure that both streams are closed before returning
2086: * (even in the face of an exception).
2087: *
2088: * @param istream The input stream to read from
2089: * @param ostream The output stream to write to
2090: * @param start Start of the range which will be copied
2091: * @param end End of the range which will be copied
2092: * @return Exception which occurred during processing
2093: */
2094: private IOException copyRange(InputStream istream,
2095: ServletOutputStream ostream, long start, long end) {
2096:
2097: if (debug > 10)
2098: System.out.println("Serving bytes:" + start + "-" + end);
2099:
2100: try {
2101: istream.skip(start);
2102: } catch (IOException e) {
2103: return e;
2104: }
2105:
2106: IOException exception = null;
2107: long bytesToRead = end - start + 1;
2108:
2109: byte buffer[] = new byte[input];
2110: int len = buffer.length;
2111: while ((bytesToRead > 0) && (len >= buffer.length)) {
2112: try {
2113: len = istream.read(buffer);
2114: if (bytesToRead >= len) {
2115: ostream.write(buffer, 0, len);
2116: bytesToRead -= len;
2117: } else {
2118: ostream.write(buffer, 0, (int) bytesToRead);
2119: bytesToRead = 0;
2120: }
2121: } catch (IOException e) {
2122: exception = e;
2123: len = -1;
2124: }
2125: if (len < buffer.length)
2126: break;
2127: }
2128:
2129: return exception;
2130:
2131: }
2132:
2133: /**
2134: * Copy the contents of the specified input stream to the specified
2135: * output stream, and ensure that both streams are closed before returning
2136: * (even in the face of an exception).
2137: *
2138: * @param reader The reader to read from
2139: * @param writer The writer to write to
2140: * @param start Start of the range which will be copied
2141: * @param end End of the range which will be copied
2142: * @return Exception which occurred during processing
2143: */
2144: private IOException copyRange(Reader reader, PrintWriter writer,
2145: long start, long end) {
2146:
2147: try {
2148: reader.skip(start);
2149: } catch (IOException e) {
2150: return e;
2151: }
2152:
2153: IOException exception = null;
2154: long bytesToRead = end - start + 1;
2155:
2156: char buffer[] = new char[input];
2157: int len = buffer.length;
2158: while ((bytesToRead > 0) && (len >= buffer.length)) {
2159: try {
2160: len = reader.read(buffer);
2161: if (bytesToRead >= len) {
2162: writer.write(buffer, 0, len);
2163: bytesToRead -= len;
2164: } else {
2165: writer.write(buffer, 0, (int) bytesToRead);
2166: bytesToRead = 0;
2167: }
2168: } catch (IOException e) {
2169: exception = e;
2170: len = -1;
2171: }
2172: if (len < buffer.length)
2173: break;
2174: }
2175:
2176: return exception;
2177:
2178: }
2179:
2180: /**
2181: * Check to see if a default page exists.
2182: *
2183: * @param pathname Pathname of the file to be served
2184: */
2185: private ResourceInfo checkWelcomeFiles(String pathname,
2186: DirContext resources) {
2187:
2188: String collectionName = pathname;
2189: if (!pathname.endsWith("/")) {
2190: collectionName += "/";
2191: }
2192:
2193: // Refresh our currently defined set of welcome files
2194: synchronized (welcomes) {
2195: welcomes = (String[]) getServletContext().getAttribute(
2196: "com.rimfaxe.webserver.WELCOME_FILES");
2197: if (welcomes == null)
2198: welcomes = new String[0];
2199: }
2200:
2201: // Serve a welcome resource or file if one exists
2202: for (int i = 0; i < welcomes.length; i++) {
2203:
2204: // Does the specified resource exist?
2205: String resourceName = collectionName + welcomes[i];
2206: ResourceInfo resourceInfo = new ResourceInfo(resourceName,
2207: resources);
2208: if (resourceInfo.exists()) {
2209: return resourceInfo;
2210: }
2211:
2212: }
2213:
2214: return null;
2215:
2216: }
2217:
2218: // ------------------------------------------------------ Range Inner Class
2219:
2220: private class Range {
2221:
2222: public long start;
2223: public long end;
2224: public long length;
2225:
2226: /**
2227: * Validate range.
2228: */
2229: public boolean validate() {
2230: if (end >= length)
2231: end = length - 1;
2232: return ((start >= 0) && (end >= 0) && (start <= end) && (length > 0));
2233: }
2234:
2235: public void recycle() {
2236: start = 0;
2237: end = 0;
2238: length = 0;
2239: }
2240:
2241: }
2242:
2243: // ---------------------------------------------- ResourceInfo Inner Class
2244:
2245: protected class ResourceInfo {
2246:
2247: /**
2248: * Constructor.
2249: *
2250: * @param pathname Path name of the file
2251: */
2252: public ResourceInfo(String path, DirContext resources) {
2253: set(path, resources);
2254: }
2255:
2256: public Object object;
2257: public DirContext directory;
2258: public Resource file;
2259: public Attributes attributes;
2260: public String path;
2261: public long creationDate;
2262: public String httpDate;
2263: public long date;
2264: public long length;
2265: public boolean collection;
2266: public String weakETag;
2267: public String strongETag;
2268: public boolean exists;
2269: public DirContext resources;
2270: protected InputStream is;
2271:
2272: public void recycle() {
2273: object = null;
2274: directory = null;
2275: file = null;
2276: attributes = null;
2277: path = null;
2278: creationDate = 0;
2279: httpDate = null;
2280: date = 0;
2281: length = -1;
2282: collection = true;
2283: weakETag = null;
2284: strongETag = null;
2285: exists = false;
2286: resources = null;
2287: is = null;
2288: }
2289:
2290: public void set(String path, DirContext resources) {
2291:
2292: recycle();
2293:
2294: this .path = path;
2295: this .resources = resources;
2296: exists = true;
2297: try {
2298: object = resources.lookup(path);
2299: if (object instanceof Resource) {
2300: file = (Resource) object;
2301: collection = false;
2302: } else if (object instanceof DirContext) {
2303: directory = (DirContext) object;
2304: collection = true;
2305: } else {
2306: // Don't know how to serve another object type
2307: exists = false;
2308: }
2309: } catch (NamingException e) {
2310: exists = false;
2311: }
2312: if (exists) {
2313: try {
2314: attributes = resources.getAttributes(path);
2315: if (attributes instanceof ResourceAttributes) {
2316: ResourceAttributes tempAttrs = (ResourceAttributes) attributes;
2317: Date tempDate = tempAttrs.getCreationDate();
2318: if (tempDate != null)
2319: creationDate = tempDate.getTime();
2320: tempDate = tempAttrs.getLastModifiedDate();
2321:
2322: if (tempDate != null) {
2323: com.rimfaxe.util.Calendar tempCal = new com.rimfaxe.util.Calendar(
2324: tempDate);
2325: httpDate = tempCal.getHttpDate();
2326:
2327: //httpDate = FastHttpDateFormat.getDate(tempDate);
2328: date = tempDate.getTime();
2329: } else {
2330: com.rimfaxe.util.Calendar tempCal = new com.rimfaxe.util.Calendar();
2331: httpDate = tempCal.getHttpDate();
2332:
2333: //httpDate = FastHttpDateFormat.getCurrentDate();
2334: }
2335: weakETag = tempAttrs.getETag();
2336: strongETag = tempAttrs.getETag(true);
2337: length = tempAttrs.getContentLength();
2338: }
2339: } catch (NamingException e) {
2340: // Shouldn't happen, the implementation of the DirContext
2341: // is probably broken
2342: exists = false;
2343: }
2344: }
2345:
2346: }
2347:
2348: /**
2349: * Test if the associated resource exists.
2350: */
2351: public boolean exists() {
2352: return exists;
2353: }
2354:
2355: /**
2356: * String representation.
2357: */
2358: public String toString() {
2359: return path;
2360: }
2361:
2362: /**
2363: * Set IS.
2364: */
2365: public void setStream(InputStream is) {
2366: this .is = is;
2367: }
2368:
2369: /**
2370: * Get IS from resource.
2371: */
2372: public InputStream getStream() throws IOException {
2373: if (is != null)
2374: return is;
2375: if (file != null)
2376: return (file.streamContent());
2377: else
2378: return null;
2379: }
2380:
2381: }
2382:
2383: }
|