Package org.w3c.jigsaw.frames

Source Code of org.w3c.jigsaw.frames.HTTPFrame

// HTTPFrame.java
// $Id: HTTPFrame.java,v 1.115 2007/03/10 12:35:21 ylafon Exp $
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.jigsaw.frames;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.w3c.tools.codec.Base64Encoder;
import org.w3c.tools.sorter.Sorter;
import org.w3c.tools.resources.Attribute;
import org.w3c.tools.resources.AttributeRegistry;
import org.w3c.tools.resources.BooleanAttribute;
import org.w3c.tools.resources.IntegerAttribute;
import org.w3c.tools.resources.LongAttribute;
import org.w3c.tools.resources.DoubleAttribute;
import org.w3c.tools.resources.StringAttribute;
import org.w3c.tools.resources.StringArrayAttribute;
import org.w3c.tools.resources.ProtocolFrame;
import org.w3c.tools.resources.ProtocolFrame;
import org.w3c.tools.resources.LookupState;
import org.w3c.tools.resources.LookupResult;
import org.w3c.tools.resources.ResourceReference;
import org.w3c.tools.resources.InvalidResourceException;
import org.w3c.tools.resources.MultipleLockException;
import org.w3c.tools.resources.Resource;
import org.w3c.tools.resources.FramedResource;
import org.w3c.tools.resources.ContainerInterface;
import org.w3c.tools.resources.ContainerResource;
import org.w3c.tools.resources.DirectoryResource;
import org.w3c.tools.resources.FileResource;
import org.w3c.tools.resources.RequestInterface;
import org.w3c.tools.resources.ReplyInterface;
import org.w3c.tools.resources.event.AttributeChangedEvent;
import org.w3c.jigsaw.http.Client;
import org.w3c.jigsaw.http.ClientException;
import org.w3c.jigsaw.http.httpd;
import org.w3c.jigsaw.http.Request;
import org.w3c.jigsaw.http.Reply;
import org.w3c.jigsaw.http.HTTPException;

import org.w3c.jigsaw.html.HtmlGenerator;
import org.w3c.jigsaw.html.HtmlLink;
import org.w3c.www.mime.MimeType;
import org.w3c.www.http.ByteRangeOutputStream;
import org.w3c.www.http.HTTP;
import org.w3c.www.http.HttpContentRange;
import org.w3c.www.http.HttpDate;
import org.w3c.www.http.HttpEntityTag;
import org.w3c.www.http.HttpFactory;
import org.w3c.www.http.HttpInteger;
import org.w3c.www.http.HttpMimeType;
import org.w3c.www.http.HttpRange;
import org.w3c.www.http.HttpString;
import org.w3c.www.http.HttpTokenList;

import org.w3c.tools.crypt.Md5;

import org.w3c.tools.resources.ProtocolException;
import org.w3c.tools.resources.ResourceException;

/**
* Default class to handle the HTTP protocol, manage FileResource and
* DirectoryResource.
*/
public class HTTPFrame extends ProtocolFrame {

    public static final
    String STATE_CONTENT_LOCATION = "org.w3c.jigsaw.frames.HTTPFrame.cl";

    private static final boolean debug = false;

    /**
     * Condition check return code - Condition existed but failed.
     */
    public static final int COND_FAILED = 1;
    /**
     * Condition check return code - Condition existed and succeeded.
     */
    public static final int COND_OK = 2;
    /**
     * Condition check return code - Condition existed and succeeded
     *                               but is a weak validation.
     */
    public static final int COND_WEAK = 3;   

    private   static HttpTokenList _accept_ranges  = null;
    static {
  String accept_ranges[] = { "bytes" };
  _accept_ranges = HttpFactory.makeStringList(accept_ranges);
    }
    /**
     * Methods allowed by instances of that class in particular:
     */
    protected        HttpTokenList  allowed = null;

    /**
     * Attributes index - The index for the quality attribute.
     */
    protected static int ATTR_QUALITY = -1 ;
    /**
     * Attribute index - The index for the title attribute.
     */
    protected static int ATTR_TITLE = -1 ;
    /**
     * Attribute index - The index for the content languages attribute.
     */
    protected static int ATTR_CONTENT_LANGUAGE = -1 ;
    /**
     * Attribute index - The index for the content encodings attribute.
     */
    protected static int ATTR_CONTENT_ENCODING = -1 ;
    /**
     * Attribute index - The index for the content type attribute.
     */
    protected static int ATTR_CONTENT_TYPE = -1 ;
    /**
     * Attribute index - The index for the charset attribute.
     */
    protected static int ATTR_CHARSET = -1 ;
    /**
     * Attribute index - The index for the content length attribute.
     */
    protected static int ATTR_CONTENT_LENGTH = -1 ;
    /**
     * Attribute index - The icon (if any) associated to the resource.
     */
    protected static int ATTR_ICON = -1 ;
    /**
     * Attribute index - Max age: the maximum drift allowed from reality.
     */
    protected static int ATTR_MAXAGE = -1 ;
    /**
     * Attribute index - Send MD5 Digest: the md5 digest of the resource sent
     */
    protected static int ATTR_MD5 = -1;
    /**
     * Attribute index - delete allowed for the associated resource ?
     */
    protected static int ATTR_ALLOW_DEL = -1;

    //
    // Attribute relative to FileResource
    //

    /**
     * Attribute index - Do we allow PUT method on this file.
     */
    protected static int ATTR_PUTABLE = -1 ;

    //
    // Attribute relative to DirectoryResource
    //

    /**
     * Attribute index - The index for our relocate attribute.
     */
    protected static int ATTR_RELOCATE = -1 ;
    /**
     * Attribute index - our index resource name.
     */
    protected static int ATTR_INDEX = -1 ;
    /**
     * Attribute index - our indexes resource name.
     */
    protected static int ATTR_INDEXES = -1 ;
    /**
     * Attribute index - The icon directory to use in dir listing.
     */
    protected static int ATTR_ICONDIR = -1 ;
    /**
     * Attribute index - Allow the GNN browse method.
     */
    protected static int ATTR_BROWSABLE = -1 ;
    /**
     * Attribute index - Style sheet for directory listing
     */
    protected static int ATTR_STYLE_LINK = -1 ;

    static {
  Attribute a   = null ;
  Class     cls = null ;

  // Get a pointer to our class:
  try {
      cls = Class.forName("org.w3c.jigsaw.frames.HTTPFrame") ;
  } catch (Exception ex) {
      ex.printStackTrace() ;
      System.exit(1) ;
  }
  // The quality attribute:
  a = new DoubleAttribute("quality"
        , new Double(1.0)
        , Attribute.EDITABLE);
  ATTR_QUALITY = AttributeRegistry.registerAttribute(cls, a) ;
  // The title attribute:
  a = new StringAttribute("title"
        , null
        , Attribute.EDITABLE) ;
  ATTR_TITLE = AttributeRegistry.registerAttribute(cls, a) ;
  // The content language attribute:
  a = new LanguageAttribute("content-language"
          , null
          , Attribute.EDITABLE) ;
  ATTR_CONTENT_LANGUAGE = AttributeRegistry.registerAttribute(cls,a);
  // The content encoding attribute:
  a = new EncodingAttribute("content-encoding"
          , null
          , Attribute.EDITABLE) ;
  ATTR_CONTENT_ENCODING = AttributeRegistry.registerAttribute(cls,a);
  // The content type attribute:
  a = new MimeTypeAttribute("content-type"
          , null
          , Attribute.EDITABLE) ;
  ATTR_CONTENT_TYPE = AttributeRegistry.registerAttribute(cls,a);
  // The Charset attribute:
  a = new StringAttribute("charset"
        , null
        , Attribute.EDITABLE) ;
  ATTR_CHARSET = AttributeRegistry.registerAttribute(cls,a);
        // The content length attribute:
  a = new IntegerAttribute("content-length"
         , null
         , Attribute.COMPUTED);
  ATTR_CONTENT_LENGTH = AttributeRegistry.registerAttribute(cls,a);
  // The icon attribute:
  a = new StringAttribute("icon"
        , null
        , Attribute.EDITABLE) ;
  ATTR_ICON = AttributeRegistry.registerAttribute(cls, a) ;
  // The max age attribute (in ms)
  a = new LongAttribute("maxage"
            , null
            , Attribute.EDITABLE) ;
  ATTR_MAXAGE = AttributeRegistry.registerAttribute(cls, a) ;
  // Should we send MD5 digest?
  a = new BooleanAttribute("send-md5"
         , Boolean.FALSE
         , Attribute.EDITABLE);
  ATTR_MD5 = AttributeRegistry.registerAttribute(cls, a) ;
  // delete allowed for the associated resource ?
  a = new BooleanAttribute("allow-delete"
         , Boolean.FALSE
         , Attribute.EDITABLE);
  ATTR_ALLOW_DEL = AttributeRegistry.registerAttribute(cls, a) ;

  //
  // Attribute relative to a FileResource
  //

  // The putable flag:
  a = new BooleanAttribute("putable"
         , Boolean.FALSE
         , Attribute.EDITABLE) ;
  ATTR_PUTABLE = AttributeRegistry.registerAttribute(cls, a) ;

  //
  // Attribute relative to a DirectoryResource
  //

  //Should we relocate invalid request to this directory ?
  a = new BooleanAttribute("relocate"
         , Boolean.TRUE
         , Attribute.EDITABLE);
  ATTR_RELOCATE = AttributeRegistry.registerAttribute(cls, a) ;
  // Our index resource name (optional).
  a = new StringAttribute("index"
        , null
        , Attribute.EDITABLE) ;
  ATTR_INDEX = AttributeRegistry.registerAttribute(cls, a) ;
  // Our indexes resource name
  a = new StringArrayAttribute("indexes"
             , null
             , Attribute.EDITABLE) ;
  ATTR_INDEXES = AttributeRegistry.registerAttribute(cls, a) ;
  // Our icon directory.
  a = new StringAttribute("icondir"
        , null
        , Attribute.EDITABLE) ;
  ATTR_ICONDIR = AttributeRegistry.registerAttribute(cls,a);
  // The browsable flag:
  a = new BooleanAttribute("browsable"
         , Boolean.FALSE
         , Attribute.EDITABLE) ;
  ATTR_BROWSABLE = AttributeRegistry.registerAttribute(cls, a) ;
  // The style sheet attribute:
  a = new StringAttribute("style-sheet-link"
        , null
        , Attribute.EDITABLE) ;
  ATTR_STYLE_LINK = AttributeRegistry.registerAttribute(cls, a) ;
    }

    /**
     * The associated DirectoryResource (if any)
     */
    protected DirectoryResource dresource  = null;

    /**
     * The associated FileResource (if any)
     */
    protected FileResource      fresource  = null;

    /**
     * Register this frame to the given resource.
     * @param resource The resource associated with this frame.
     */
    public void registerResource(FramedResource resource) {
  super.registerResource(resource);
  if (resource instanceof FileResource)
      fresource = (FileResource) resource;
  else if (resource instanceof DirectoryResource)
      dresource = (DirectoryResource) resource;
    }

    /**
     * Get the associated FileResource (if any)
     * @return a FileResource instance or <strong>null</strong>
     * if no FileResource is associated with this frame.
     */
    public FileResource getFileResource() {
  return fresource;
    }

    /**
     * Get the associated DirectoryResource (if any)
     * @return a DirectoryResource instance or <strong>null</strong>
     * if no DirectoryResource is associated with this frame.
     */
    public DirectoryResource getDirectoryResource() {
  return dresource;
    }

    /**
     * use this one instead of registerResource if the resource type
     * doesn't matter or if this is not a file or a directory resource.
     * In subclasses you should have to do that:
     * <pre>
     *  public void registerResource(FramedResource resource) {
     *   super.registerOtherResource(resource);
     *  }
     * </pre>
     * @param the resource to register.
     */
    public void registerOtherResource(FramedResource resource) {
  super.registerResource(resource);
  dresource = null;
  fresource = null;
    }

    // The HTTPResource keeps a cache of ready to use Http values. This
    // allows to save converting to/from wire rep these objects. Not
    // much CPU time, but also memory is spared.
    HttpMimeType  contenttype     = null;
    HttpInteger   contentlength   = null;
    HttpDate      lastmodified    = null;
    HttpTokenList contentencoding = null;
    HttpTokenList contentlanguage = null;

    // The Http entity tag for this resource (for FileResource only)
    HttpEntityTag etag   = null;
    // the MD5 digest for this resource (for FileResource only)
    HttpString md5Digest = null;

    /**
     * Get this resource's help url.
     * @return An URL, encoded as a String, or <strong>null</strong> if not
     * available.
     */

    public String getHelpURL() {
  httpd server = (httpd) getServer();
  if ( server == null )
      return null;
  String docurl = server.getDocumentationURL();
  if ( docurl == null )
      return null;
  return docurl + "/" + getClass().getName() + ".html";
    }

    /**
     * Get the help URL for that resource's attribute.
     * @param topic The topic (can be an attribute name, or a property, etc).
     * @return A String encoded URL, or <strong>null</strong>.
     */

    public String getHelpURL(String topic) {
  httpd server = (httpd) getServer();
  if ( server == null )
      return null;
  String docurl = server.getDocumentationURL();
  if ( docurl == null )
      return null;
  Class defines = AttributeRegistry.getAttributeClass(getClass(), topic);
  if ( defines != null )
      return docurl + "/" + defines.getName() + ".html";
  return null;
    }

    /**
     * give the md5 digest from cache or calculate it
     * @return the HttpString version of the digest
     */

    private HttpString getMd5Digest() {
  if (md5Digest != null)
      return md5Digest;
  // not found, compute it if necessary!
  Resource r = getResource();
  if (r instanceof FileResource) {
      try {
    Md5 md5 = new Md5 (
        new FileInputStream(((FileResource)r).getFile()));
    String s = null;
    try {
        byte b[] = md5.getDigest();
        Base64Encoder b64;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        b64 = new Base64Encoder(new ByteArrayInputStream(b), bos);
        b64.process();
        s = bos.toString();
        md5Digest = HttpFactory.makeString(s);
    } catch (Exception mdex) {
        // error, set it to null
        md5Digest = null;
    }
    return md5Digest;
      } catch (FileNotFoundException ex) {
    // silent fail
    md5Digest = null;
      }
  }
  return null;
    }

    /**
     * Listen its resource.
     */
    public void attributeChanged(AttributeChangedEvent evt) {
  super.attributeChanged(evt);
  String name = evt.getAttribute().getName();
  if (name.equals("file-stamp")) {
      etag = null;
      lastmodified = null;
      md5Digest = null;
  } else if (name.equals("file-length")) {
      setValue(ATTR_CONTENT_LENGTH, evt.getNewValue());
  } else if (name.equals("last-modified")) {
      setValue(ATTR_LAST_MODIFIED, evt.getNewValue());
  } else {
      lastmodified = null;
  }
    }

    /**
     * Catch setValue, to maintain cached header values correctness.
     * @param idx The index of the attribute to be set.
     * @param value The new value for the attribute.
     */

    public synchronized void setValue(int idx, Object value) {
  super.setValue(idx, value);
  if (idx == ATTR_CONTENT_TYPE) {
      contenttype = null;
  } else if (idx == ATTR_CHARSET) {
      contenttype = null;
  } else if (idx == ATTR_CONTENT_LENGTH) {
      contentlength = null;
  } else if ( idx == ATTR_CONTENT_ENCODING ) {
      contentencoding = null;
  } else if ( idx == ATTR_CONTENT_LANGUAGE ) {
      contentlanguage = null;
  } else if ( idx == ATTR_PUTABLE) {
      allowed = null;
  } else if (idx == ATTR_ALLOW_DEL) {
      allowed = null;
  } else if (idx == ATTR_MD5) {
      md5Digest = null; // reset the digest state
  }
  // Any attribute setting modifies the last modified time:
  lastmodified = null;
    }

    /**
     * Get the full URL for that resource.
     * @return An URL instance.
     */
    public URL getURL(Request request) {
  try {
      return new URL(request.getURL(), resource.unsafeGetURLPath());
  } catch (MalformedURLException ex) {
      throw new RuntimeException("unable to build "+
               getURLPath()+
               " full URL, from server "+
               getServer().getURL());
  }
    }

    /**
     * Get this resource quality.
     * @return The resource quality, or some negative value if not defined.
     */

    public double getQuality() {
  return getDouble(ATTR_QUALITY, -1.0) ;
    }

    /**
     * Get this resource quality.
     * @return The resource quality, or some negative value if not defined.
     */

    public double unsafeGetQuality() {
  return unsafeGetDouble(ATTR_QUALITY, -1.0) ;
    }

    /**
     * Get this resource title.
     * @return This resource's title, or <strong>null</strong> if not
     *    defined.
     */

    public String getTitle() {
  return getString(ATTR_TITLE, null) ;
    }

    /**
     * Get this resource content language.
     * Language are stored as a comma separated String of tokens.
     * @return A comma separated string of language tokens, or
     *    <strong>null</strong> if undefined.
     */

    public String getContentLanguage() {
  return (String) getValue(ATTR_CONTENT_LANGUAGE, null) ;
    }

    /**
     * Get this resource content encoding.
     * The content encoding of a resource is stored as a comma separated
     * list of tokens (as decribed in the Content_encoding header of the
     * HTTP specification, and in the order they should appear in the header).
     * @return A string of comma separated encoding tokens, or
     *    <strong>null</strong> if not defined.
     */

    public String getContentEncoding() {
  String def = (String) attributes[ATTR_CONTENT_ENCODING].getDefault();
  String s =  (String) getString (ATTR_CONTENT_ENCODING, def) ;
  return (String) getString (ATTR_CONTENT_ENCODING, def) ;
    }

    /**
     * Get this resource charset.
     * @return A String, or <strong>null</strong> if not
     *    defined.
     */
    public String getCharset() {
  return (String) getValue(ATTR_CHARSET, null);
    }

    /**
     * Get this resource content type.
     * @return An instance of MIMEType, or <strong>null</strong> if not
     *    defined.
     */
    public MimeType getContentType() {
  return (MimeType) getValue(ATTR_CONTENT_TYPE, null);
    }

    /**
     * Compute the ETag string
     * @return a string or null if not applicable
     */
    public String computeETag() {
  String etag_s = null;
  if (fresource != null) {
      long lstamp = fresource.getFileStamp();
      if ( lstamp >= 0L ) {
    StringBuffer sb = new StringBuffer(32);
    sb.append(Integer.toString(getOid(), 32));
    sb.append(':');
    sb.append(Long.toString(lstamp, 32));
    etag_s = sb.toString();
      }
  }
  return etag_s;
    }

    /**
     * Get this resource Etag
     * @return an instance of HttpEntityTag, or <strong>null</strong> if not
     *    defined.
     */

    public HttpEntityTag getETag() {
  if (etag == null) {
      String etag_s = computeETag();
      // no luck, exit
      if (etag_s == null) {
    return null;
      }
      etag = HttpFactory.makeETag(false, etag_s);
  }
  return etag;
    }

    /**
     * Get this resource content length.
     * @return The resource content length, or <strong>-1</strong> if not
     *    defined.
     */

    public int getContentLength() {
  return getInt(ATTR_CONTENT_LENGTH, -1) ;
    }

    /**
     * Get this resource's icon.
     */

    public String getIcon() {
  return getString(ATTR_ICON, null) ;
    }

    /**
     * Get this resource's max age.
     * The max age of a resource indicates how much drift is allowed between
     * the physicall version of the resource, and any in-memory cached version
     * of it.
     * <p>The max age attribute is a long number giving the number of
     * milliseconds of allowed drift.
     */

    public long getMaxAge() {
  return getLong(ATTR_MAXAGE, (long) -1) ;
    }

    //
    // Relative to FileResource ...
    //

    /**
     * Does this resource support byte ranges.
     */
    protected boolean acceptRanges = false;

    /**
     * Get the PUT'able flag (are we allow to PUT to the resource ?)
     */
    public boolean getPutableFlag() {
  return getBoolean(ATTR_PUTABLE, false) ;
    }

    /**
     * Do we send the MD5 digest?
     */
    public boolean getMD5Flag() {
  return getBoolean(ATTR_MD5, false) ;
    }

    /**
     * delete allowed for the associated resource ?
     */
    public boolean getAllowDeleteFlag() {
  return getBoolean(ATTR_ALLOW_DEL, false);
    }

    /**
     * get the Allowed methods for this resource
     * @return an HttpTokenList
     */
    protected HttpTokenList getAllow() {
  if (allowed != null) {
      return allowed;
  }
  int size = 4; // the default HEAD GET OPTIONS TRACE
  if (getPutableFlag()) {
      size++;
  }
  if (getAllowDeleteFlag()) {
      size++;
  }
  String allow_str[] = new String[size];
  int i = 0;
  if (getAllowDeleteFlag()) {
      allow_str[i++] = "DELETE";
  }
  allow_str[i++] = "HEAD";
  allow_str[i++] = "GET";
  allow_str[i++] = "OPTIONS";
  if (getPutableFlag()) {
      allow_str[i++] = "PUT";
  }
  allow_str[i] = "TRACE";
  allowed = HttpFactory.makeStringList(allow_str);
  return allowed;
    }

    /**
     * handles a Range Request
     * @param request, the request
     * @param r, the HttpRange
     * @return a Reply if range is valid, or null if there is a change in the
     * resource, or if the HttpRange is not valid ( 4-2, for example).
     * @exception ProtocolException If processsing the request failed.
     */

    public Reply handleRangeRequest(Request request, HttpRange r)
  throws ProtocolException
    {
  // Should we check against a IfRange header ?
  HttpEntityTag t = request.getIfRange();

  if ( t != null ) {
      if (t.isWeak() || ! t.getTag().equals(etag.getTag()))
    return null;
  }
  // Check the range:
  int cl = getContentLength();
  int fb = r.getFirstPosition();
  int lb = r.getLastPosition();
  int sz;

  if (fb > cl-1) { // first byte already out of range
      HttpContentRange cr = HttpFactory.makeContentRange("bytes", 0,
                     cl - 1, cl);
      Reply rr;
      rr = createDefaultReply(request,
            HTTP.REQUESTED_RANGE_NOT_SATISFIABLE);
      rr.setContentLength(-1);
      rr.setHeaderValue(rr.H_CONTENT_RANGE, cr);
      if (getMD5Flag())
    rr.setContentMD5(null);
      return rr;
  }

  if ((fb < 0) && (lb >= 0)) { // ex: bytes=-20 final 20 bytes
      if (lb >= cl)   // cut the end
    lb = cl;
      sz = lb;
      fb = cl - lb;
      lb = cl - 1;
  } else if (lb < 0) {  // ex: bytes=10- the last size - 10
      lb = cl-1;
      sz = lb-fb+1;
  } else {              // ex: bytes=10-20
      if (lb >= cl// cut the end
    lb = cl-1;
      sz = lb-fb+1;
  }
  if ((fb < 0) || (lb < 0) || (fb <= lb)) {
      HttpContentRange cr = null;
      fb = (fb < 0) ? 0 : fb;
      lb = ((lb > cl) || (lb < 0)) ? cl : lb;
      cr = HttpFactory.makeContentRange("bytes", fb, lb, cl);
      // Emit reply:
      Reply rr = createDefaultReply(request, HTTP.PARTIAL_CONTENT);
      // FIXME check for MD5 of only the subpart
      try { // create the MD5 for the subpart
    if (getMD5Flag()) {
        String s = null;
        try {
      ByteRangeOutputStream br;
      br = new ByteRangeOutputStream(fresource.getFile(),
                   fb, lb+1);
      Md5 md5 = new Md5 (br);
      byte b[] = md5.getDigest();
      Base64Encoder b64;
      ByteArrayOutputStream bs = new ByteArrayOutputStream();
      b64 = new Base64Encoder(new ByteArrayInputStream(b),
            bs);
      b64.process();
      s = bs.toString();
        } catch (Exception md_ex) {
      // default to null, no action here then
        }
        if (s == null)
      rr.setContentMD5(null);
        else
      rr.setContentMD5(s);
    }
          rr.setContentLength(sz);
    rr.setHeaderValue(rr.H_CONTENT_RANGE, cr);
    rr.setStream(new ByteRangeOutputStream(fresource.getFile(),
                   fb,
                   lb+1));
    return rr;
      } catch (IOException ex) {
      }
  }
  return null;
    }

    //
    // Relative to DirectoryResource ...
    //

    /**
     * Get this class browsable flag.
     */
    public boolean getBrowsableFlag() {
  return getBoolean (ATTR_BROWSABLE, false) ;
    }

    /**
     * Get this frame style sheet link
     */
    public String getStyleSheetURL() {
  return getString (ATTR_STYLE_LINK, null);
    }

    /**
     * Our current (cached) directory listing.
     */
    protected HtmlGenerator listing = null ;
    /**
     * The time at which we generated the directory index.
     */
    protected long listing_stamp = -1 ;

    private String getUnextendedName(String name) {
  int strlen = name.length() ;
  for (int i = 0 ; i < strlen ; i++) {
      // FIXME: Should use the system props to get the right sep
      if ( name.charAt(i) == '.' ) {
    if ( i == 0 )
        return null ;
    return name.substring(0, i) ;
      }
  }
  return null ;
    }

    /**
     * Get the optional icon directory.
     */
    public String getIconDirectory() {
  return getString(ATTR_ICONDIR, "/icons") ;
    }

    /**
     * Should we relocate invalid requests to this directory.
     * @return A boolean <strong>true</strong> if we should relocate.
     */
    public boolean getRelocateFlag() {
  return getBoolean(ATTR_RELOCATE, true) ;
    }

    /**
     * Get the optional main index name for this directory listing.
     * @return The name of the resource responsible to list that container.
     */
    public String getIndex() {
  return (String) getValue(ATTR_INDEX, null) ;
    }

    /**
     * Get the optional index name array for this directory listing.
     * @return The index name array (including the main index)
     * @see #getIndex
     */
    public String[] getIndexes() {
  String mainIndex = getIndex();
  if (mainIndex != null) {
      String indexes[] = (String[]) getValue(ATTR_INDEXES, null);
      if (indexes != null) {
    int    len          = indexes.length + 1;
    String mergeIndex[] = new String[len];
    mergeIndex[0] = mainIndex;
    System.arraycopy(indexes, 0, mergeIndex, 1, len-1);
    return mergeIndex;
      } else {
    indexes = new String[1];
    indexes[0] = mainIndex;
    return indexes;
      }
  } else {
      return (String[]) getValue(ATTR_INDEXES, null) ;
  }
    }

    /**
     * Add our own Style Sheet to the HtmlGenerator.
     * @param g The HtmlGenerator.
     */
    public void addStyleSheet(HtmlGenerator g) {
  // Add style link
  String css_url = getStyleSheetURL();
  if (css_url != null) {
      g.addLink( new HtmlLink("STYLESHEET", css_url));
  }
    }

    /**
     * Get ContainerResource listing
     * @param refresh should we refresh the listing?
     * @return a boolean (true if refreshed)
     */
    public boolean computeContainerListing(boolean refresh) {
  ContainerResource cresource = (ContainerResource)resource;
  synchronized (cresource) {
      if ((refresh) ||
          (listing == null) ||
    (cresource.getLastModified() > listing_stamp) ||
    (getLastModified() > listing_stamp)) {
     
    Class http_class = null;
    try {
        http_class =
          Class.forName("org.w3c.jigsaw.frames.HTTPFrame");
    } catch (ClassNotFoundException ex) {
      http_class = null;
    }

    Enumeration   e     = cresource.enumerateResourceIdentifiers();
    Vector    resources = Sorter.sortStringEnumeration(e) ;
    HtmlGenerator g     =
          new HtmlGenerator("Index of "+cresource.getIdentifier());
    // Add style link
    addStyleSheet(g);
    g.append("<h1>"+cresource.getIdentifier()+"</h1>");
    // Link to the parent, when possible:
    if ( cresource.getParent() != null ) {
        g.append("<p><a href=\"..\">Parent</a><br>");
    }
      // List the children:
    for (int i = 0 ; i < resources.size() ; i++) {
        String            name = (String) resources.elementAt(i);
        ResourceReference rr   = null;
        long              size = -1;
        rr = cresource.lookup(name);
        FramedResource resource = null;
        if (rr != null) {
      try {
          resource = (FramedResource) rr.unsafeLock();
          // remove manually deleted FileResources
          if( resource instanceof FileResource ) {
              FileResource fr = (FileResource)resource;
        if( !fr.getFile().exists() ) {
            try {
          fr.delete();
            } catch (MultipleLockException ex) {};
            continue;
        } else {
          size = fr.getFile().length();
        }
          }
          HTTPFrame itsframe = null;
          if (http_class != null)
        itsframe =
            (HTTPFrame) resource.getFrame(http_class);
          if (itsframe != null) {
        // Icon first, if available
        String icon = itsframe.getIcon() ;
        if ( icon != null ) {
            g.append("<img src=\""+
               getIconDirectory() +"/"+ icon+
               "\" alt=\"" + icon + "\">");
        }
        // Resource's name with link:
        if (resource instanceof ContainerInterface) {
            g.append("<a href=\""
               , URLEncoder.encode(name)
               , "/\">"+name+"</a>");
        } else {
            g.append("<a href=\""
               , URLEncoder.encode(name)
               , "\">"+name+"</a>");
        }
        // resource's title, if any:
        String title = itsframe.getTitle();
        if ( title != null ) {
            g.append(" "+title);
        }
        //size (if any)
        if (size != -1) {
            String s = null;
            if (size > 1023) {
          s = " ["+(size/1024)+" KB]";
            } else {
          s = " ["+size+" bytes]";
            }
            g.append(s);
        }
        g.append("<br>\n");
          } else {
        // Resource's name with link:
        g.append(name+
           " (<i>Not available via HTTP.</i>)");
        g.append("<br>\n");
          }
      } catch (InvalidResourceException ex) {
          g.append(name+
          " cannot be loaded (server misconfigured)");
          g.append("<br>\n");
          continue;
      } finally {
          rr.unlock();
      }
        }
    }
    g.close() ;
    listing_stamp = getLastModified() ;
    listing       = g ;
    return true;
      }
  }
  return false;
    }

    /**
     * Reply with an HTML doc listing the resources of this container.
     * This function takes special care not to regenerate a listing
     * when one is available. It also caches the date of the
     * listing, so that it can win big with NOT_MODIFIED.
     * <p>Using a modem, I know that each place I can reply with an
     * NOT_MODIFIED, <strong>is</strong> a big win.
     * @param request The request to handle.
     * @exception ProtocolException If processsing the request failed.
     * @exception ResourceException If the resource got a fatal error.
     */

    public Reply getDirectoryListing(Request request)
  throws ProtocolException, ResourceException
    {
  if (! (resource instanceof ContainerResource)) {
      throw new ResourceException("this frame is not attached to a "+
          "ContainerResource. ("+
          resource.getIdentifier()+")");
  }
  // delete us if the directory was deleted
  boolean refresh = false;
  if (dresource != null) {
      synchronized (dresource) {
          if (! dresource.getDirectory().exists()) {
           //delete us and emit an error
        String msg = dresource.getIdentifier()+
            ": deleted, removing the DirectoryResource";
        getServer().errlog(dresource, msg);
        try {
            dresource.delete();
        } catch (MultipleLockException ex) {
        }
        // Emit an error back:
        Reply error = request.makeReply(HTTP.NOT_FOUND) ;
        error.setContent ("<h1>Document not found</h1>"+
                 "<p>The document "+
              request.getURL()+
              " is indexed but not available."+
              "<p>The server is misconfigured.") ;
        throw new HTTPException (error) ;
    }
    refresh =
      (dresource.getDirectory().lastModified() >
                                             listing_stamp);
      }
  }
  if ((! computeContainerListing(refresh)) &&
      ( checkIfModifiedSince(request) == COND_FAILED )) {
      // Is it an IMS request ?
      Reply reply = createDefaultReply(request, HTTP.NOT_MODIFIED) ;
      return reply;
  }
  // New content or need update:
  Reply reply = createDefaultReply(request, HTTP.OK) ;
  reply.setLastModified(listing_stamp) ;
  reply.setStream(listing) ;
  // check MD5
  return reply ;
    }

    //
    // Commom part.
    //

    /**
     * Update the cached headers value.
     * Each resource maintains a set of cached values for headers, this
     * allows for a nice sped-up in headers marshalling, which - as the
     * complexity of the protocol increases - becomes a bottleneck.
     */

    protected void updateCachedHeaders() {
  // Precompute a set of header values to keep by:
  if ( contenttype == null ) {
      String charset = getCharset();
      if (charset == null)
    contenttype = HttpFactory.makeMimeType(getContentType());
      else {
    MimeType ctype = getContentType().getClone();
    ctype.addParameter("charset", charset);
    contenttype = HttpFactory.makeMimeType(ctype);
      }
  }
  if (contentlength == null) {
      int cl = -1;
      if (fresource != null)
    cl = fresource.getFileLength();
      if ( cl >= 0 ) {
    if (cl != getInt(ATTR_CONTENT_LENGTH, -1)) {
        setValue(ATTR_CONTENT_LENGTH, new Integer(cl));
    }
    contentlength = HttpFactory.makeInteger(cl);
      }
  }
  if ( lastmodified == null ) {
      long lm = getLastModified();
      if ( lm > 0 )
    lastmodified = HttpFactory.makeDate(lm);
  }
  if (definesAttribute(ATTR_CONTENT_ENCODING) &&(contentencoding==null))
      contentencoding = HttpFactory.makeStringList(getContentEncoding());
  if (definesAttribute(ATTR_CONTENT_LANGUAGE) &&(contentlanguage==null))
      contentlanguage = HttpFactory.makeStringList(getContentLanguage());

  if (fresource != null) {
      // We only take care of etag here:
      if ( etag == null ) {
    getETag();
      }
      if (getMD5Flag() && (md5Digest == null)) {
    getMd5Digest();
      }
  }
    }

    /**
     * Create a reply to answer to request on this file.
     * This method will create a suitable reply (matching the given request)
     * and will set all its default header values to the appropriate
     * values.
     * @param request The request to make a reply for.
     * @return An instance of Reply, suited to answer this request.
     */

    public Reply createDefaultReply(Request request, int status) {
  Reply reply = request.makeReply(status);
  updateCachedHeaders();
  if ( status != HTTP.NOT_MODIFIED ) {
      if ( contentlength != null )
    reply.setHeaderValue(Reply.H_CONTENT_LENGTH, contentlength);
      if ( contenttype != null )
    reply.setHeaderValue(Reply.H_CONTENT_TYPE, contenttype);
      if ( lastmodified != null )
    reply.setHeaderValue(Reply.H_LAST_MODIFIED, lastmodified);
      if ( contentencoding != null ) {
    reply.setHeaderValue(Reply.H_CONTENT_ENCODING,contentencoding);
      }
      if ( contentlanguage != null )
    reply.setHeaderValue(Reply.H_CONTENT_LANGUAGE,contentlanguage);

  }
  long maxage = getMaxAge();
  if ( maxage >= 0 ) {
      if (reply.getMajorVersion() >= 1 ) {
    if (reply.getMinorVersion() >= 1) {
        reply.setMaxAge((int) (maxage / 1000));
    }
    // If max-age is zero, say what you mean:
    long expires = (System.currentTimeMillis()
        + ((maxage == 0) ? -1000 : maxage));
    reply.setExpires(expires);
      }
  }
  // Set the date of the reply (round it to secs):
  reply.setDate((System.currentTimeMillis() / 1000L) * 1000L);

  if (fresource != null) {
      // Set the entity tag:
      if ( getETag() != null ) {
    reply.setETag(etag);
      }
      if ( status != HTTP.NOT_MODIFIED ) {
    if ( acceptRanges ) {
        reply.setHeaderValue(reply.H_ACCEPT_RANGES,_accept_ranges);
    }
    if ( getMD5Flag()) {
        reply.setHeaderValue(reply.H_CONTENT_MD5, getMd5Digest());
    }
      }
  }
  return reply;
    }

    /**
     * Check the <code>If-Match</code> condition of that request.
     * @param request The request to check.
     * @return An integer, either <code>COND_FAILED</cond> if condition
     * was checked, but failed, <code>COND_OK</code> if condition was checked
     * and succeeded, or <strong>0</strong> if the condition was not checked
     * at all (eg because the resource or the request didn't support it).
     */

    public int checkIfMatch(Request request) {
  if (fresource != null) {
      HttpEntityTag tags[] = request.getIfMatch();
      if ( tags != null ) {
    // Good, real validators in use:
    if ( etag != null ) {
        // Note: if etag is null this means that the resource has
        // changed and has not been even emited since then...
        for (int i = 0 ; i < tags.length ; i++) {
      HttpEntityTag t = tags[i];
      if (t.getTag().equals(etag.getTag())) {
          if (t.isWeak() || etag.isWeak()) {
        return COND_WEAK;
          } else {
        return COND_OK;
          }
      }
        }
    }
    return COND_FAILED;
      }
  }
  return 0;
    }

    /**
     * Check the <code>If-None-Match</code> condition of that request.
     * @param request The request to check.
     * @return An integer, either <code>COND_FAILED</cond> if condition
     * was checked, but failed, <code>COND_OK</code> if condition was checked
     * and succeeded, or <strong>0</strong> if the condition was not checked
     * at all (eg because the resource or the request didn't support it).
     */

    public int checkIfNoneMatch(Request request) {
  if (fresource != null) {
      // Check for an If-None-Match conditional:
      HttpEntityTag tags[] = request.getIfNoneMatch();
      if ( tags != null ) {
    if ( etag == null ) {
        return COND_OK;
    }
    int status = COND_OK;
    for (int i = 0 ; i < tags.length ; i++) {
        HttpEntityTag t = tags[i];
        if (t.getTag().equals(etag.getTag())) {
      if (t.isWeak() || etag.isWeak()) {
          status = COND_WEAK;
      } else {
          return COND_FAILED;
      }
        }
        if (t.getTag().equals("*")) {
      if (fresource != null) {
          File f = fresource.getFile();
          if (f.exists()) {
        return COND_FAILED;
          }
      } else {
          return COND_FAILED;
      }
        }
    }
    return status;
      }
  }
  return 0;
    }

    /**
     * Check the <code>If-Modified-Since</code> condition of that request.
     * @param request The request to check.
     * @return An integer, either <code>COND_FAILED</cond> if condition
     * was checked, but failed, <code>COND_OK</code> if condition was checked
     * and succeeded, or <strong>0</strong> if the condition was not checked
     * at all (eg because the resource or the request didn't support it).
     */

    public int checkIfModifiedSince(Request request) {
  // Check for an If-Modified-Since conditional:
  long ims = request.getIfModifiedSince();
  if (dresource != null) {
      if (ims >= 0) {
    if (listing_stamp > 0) {
        long s_listing_stamp = listing_stamp / 1000;
        long s_ims = ims / 1000;
        if (s_listing_stamp < s_ims) {
      return COND_FAILED;
        } else if (s_listing_stamp == s_ims) {
      return COND_WEAK;
        }
        return COND_OK;
    }
      }
  } else if (fresource != null) {
      long cmt = getLastModified();
      if ( ims >= 0 ) {
    if (cmt > 0) {
        long s_cmt = cmt / 1000;
        long s_ims = ims / 1000;       
        if (s_cmt < s_ims) {
      return COND_FAILED;
        } else if (s_cmt == s_ims) {
      return COND_WEAK;
        }
        return COND_OK;
    }
      }
  }
  return 0;
    }

    /**
     * Check the <code>If-Unmodified-Since</code> condition of that request.
     * @param request The request to check.
     * @return An integer, either <code>COND_FAILED</cond> if condition
     * was checked, but failed, <code>COND_OK</code> if condition was checked
     * and succeeded, or <strong>0</strong> if the condition was not checked
     * at all (eg because the resource or the request didn't support it).
     */

    public int checkIfUnmodifiedSince(Request request) {
  if (fresource != null) {
      // Check for an If-Unmodified-Since conditional:
      long iums = request.getIfUnmodifiedSince();
      long cmt = getLastModified();
      if ( iums >= 0 )
    return ((cmt > 0) && (cmt - 1000) >= iums)
        ? COND_FAILED : COND_OK;
  }
  return 0;
    }

    /**
     * Check the <code>Expect</code> condition of that request
     * @param request The request to check.
     * @return A boolean <code>true</code> if the requirement is known
     */

    public boolean checkExpect(Request request) {
  // crude for now as we only support 100-continue
  // so FIXME for a more evolved version of this.
  String exp = request.getExpect();
  if (exp != null) {
      if (!exp.equalsIgnoreCase(HTTP.HTTP_100_CONTINUE)) {
    return false;
      }
  }
  return true;
    }

    /**
     * check the validators namely LMT/Etags according to rfc2616 rules
     * @return An integer, either <code>COND_FAILED</cond> if condition
     * was checked, but failed, <code>COND_OK</code> if condition was checked
     * and succeeded, or <strong>0</strong> if the condition was not checked
     * at all (eg because the resource or the request didn't support it).
     */
    public int checkValidators(Request request) {
  int v_inm = checkIfNoneMatch(request);
  int v_ims = checkIfModifiedSince(request);

  if ((v_inm == COND_OK) || (v_ims == COND_OK)) {
      return COND_OK;
  }
  if ((v_inm == COND_FAILED) || (v_ims == COND_FAILED)) {
      return COND_FAILED;
  }
  if ((v_inm == COND_WEAK) || (v_ims == COND_WEAK)) {
      return COND_FAILED;
  }
  return 0;
    }

     /**
     * Lookup the target resource. Lookup filters and then resource.
     * @param ls The current lookup state
     * @param lr The result
     * @return true if lookup is done.
     * @exception ProtocolException If an error relative to the protocol occurs
     * @see org.w3c.tools.resources.ResourceFrame#lookupFilters
     * @see #lookupResource
     */
    public boolean lookup(LookupState ls, LookupResult lr)
  throws ProtocolException
    {
  RequestInterface req = ls.getRequest();
  if (! checkRequest(req))
      return false;
  if (lookupFilters(ls,lr))
      return true;
  return lookupResource(ls,lr);
    }

     /**
     * Lookup the target resource (dispath to more specific lookup methods).
     * @param ls The current lookup state
     * @param lr The result
     * @return true if lookup is done.
     * @exception ProtocolException If an error relative to the protocol occurs
     * @see #lookupDirectory
     * @see #lookupFile
     * @see #lookupOther
     */
    protected boolean lookupResource(LookupState ls, LookupResult lr)
  throws ProtocolException
    {
  if (fresource != null) {
      return lookupFile(ls,lr);
  } else if (dresource != null) {
      return lookupDirectory(ls,lr);
  } else {
      return lookupOther(ls,lr);
  }
    }

    /**
     * Lookup the target resource when associated with a DirectoryResource.
     * @param ls The current lookup state
     * @param lr The result
     * @return true if lookup is done.
     * @exception ProtocolException If an error relative to the protocol
     * occurs
     */
    protected boolean lookupDirectory(LookupState ls, LookupResult lr)
  throws ProtocolException
    {
  // Give a chance to our super-class to run its own lookup scheme:
  // do we have to create a resource (PUT) ?
  if ((ls.hasMoreComponents()) && getPutableFlag()) {
      Request request = (Request) ls.getRequest() ;
      if ((request == null) || request.getMethod().equals("PUT")) {
    // We might well want to create a resource:
    String            name = ls.peekNextComponent() ;
    ResourceReference rr   = dresource.lookup(name);
    if ((rr == null) && (dresource.getExtensibleFlag())) {
        if (ls.countRemainingComponents() == 1)
      rr = dresource.createResource(name, request);
        else
      rr = dresource.createDirectoryResource(name);
        if (rr == null) {
      Reply error =
          request.makeReply(HTTP.UNSUPPORTED_MEDIA_TYPE);
      error.setContent(
          "Failed to create resource "+
          name +" : "+
          "Unable to create the appropriate file:"+
          request.getURLPath()+
          " this media type is not supported");
      throw new HTTPException (error);
        }
    } else if (rr == null) {
        Reply error = request.makeReply(HTTP.FORBIDDEN) ;
        error.setContent("You are not allowed to create resource "+
             name +" : "+
             dresource.getIdentifier()+
             " is not extensible.");
        throw new HTTPException (error);
    }
      }
  }
  if ( super.lookup(ls, lr) ) {
      if ( ! ls.isDirectory() && ! ls.isInternal() ) {
    // The directory lookup URL doesn't end with a slash:
    Request request = (Request)ls.getRequest() ;
    if ( request == null ) {
        lr.setTarget(null);
        return true;
    }
    URL url = null;
    try {
        if ((request != null ) &&
      request.hasState(Request.ORIG_URL_STATE)) {
      URL oldurl;
      oldurl = (URL)request.getState(Request.ORIG_URL_STATE);
      url = new URL(oldurl, oldurl.getFile() + "/");
        } else {
      url = (ls.hasRequest()
             ? getURL(request)
             : new URL(getServer().getURL(),
           resource.getURLPath()));
        }
    } catch (MalformedURLException ex) {
        getServer().errlog(this, "unable to build full URL.");
        throw new HTTPException("Internal server error");
    }
    String msg = "Invalid requested URL: the directory resource "+
        " you are trying to reach is available only through "+
        " its full URL: <a href=\""+
        url + "\">" + url + "</a>.";
    if ( getRelocateFlag() ) {
        // Emit an error (with reloc if allowed)
        Reply reloc = request.makeReply(HTTP.FOUND);
        reloc.setContent(msg) ;
        reloc.setLocation(url);
        lr.setTarget(null);
        lr.setReply(reloc);
        return true;
    } else {
        Reply error = request.makeReply(HTTP.NOT_FOUND) ;
        error.setContent(msg) ;
        lr.setTarget(null);
        lr.setReply(error);
        return true;
    }
      } else if ( ! ls.isInternal() ) {
    Request request = (Request)ls.getRequest() ;
    request.setState(STATE_CONTENT_LOCATION, "true");
    // return the index file.
    String indexes[] = getIndexes();
    if (indexes != null) {
        for (int i = 0 ; i < indexes.length ; i++) {
      String index = indexes[i];
      if ( index != null && index.length() > 0) {
          DirectoryResource dir =
        (DirectoryResource) resource;
          ResourceReference rr = dir.lookup(index);
          if (rr != null) {
        try {
            FramedResource rindex =
          (FramedResource) rr.lock();
            return rindex.lookup(ls,lr);
        } catch (InvalidResourceException ex) {
        } finally {
            rr.unlock();
        }
          }
      }
        } 
    }
      }
      return true;
  }
  return false;
    }

    /**
     * Lookup the target resource when associated with a FileResource.
     * @param ls The current lookup state
     * @param lr The result
     * @return true if lookup is done.
     * @exception ProtocolException If an error relative to the protocol occurs
     */
    protected boolean lookupFile(LookupState ls, LookupResult lr)
  throws ProtocolException
    {
  return super.lookup(ls,lr);
    }

    /**
     * Lookup the target resource when associated with an unknown resource.
     * @param ls The current lookup state
     * @param lr The result
     * @return true if lookup is done.
     * @exception ProtocolException If an error relative to the protocol occurs
     */
    protected boolean lookupOther(LookupState ls, LookupResult lr)
  throws ProtocolException
    {
  return super.lookup(ls,lr);
    }

    /**
     * Check the request.
     * @param request the incomming request.
     * @return true if the request is an HTTP Request.
     */
    public boolean checkRequest(RequestInterface request) {
  return ((request == null)
    ? true
    :(request instanceof org.w3c.jigsaw.http.Request));
    }

    /**
     * Perform the request on all the frames of that resource. The
     * Reply returned is the first non-null reply.
     * @param request A RequestInterface instance.
     * @return A ReplyInterface instance.
     * @exception ProtocolException If an error relative to the protocol occurs
     * @exception ResourceException If an error not relative to the
     * protocol occurs
     */
    protected ReplyInterface performFrames(RequestInterface request)
  throws ProtocolException, ResourceException
    {
  return super.performFrames(request);
    }

    /**
     * Perform the request
     * @param req The request to handle.
     * @exception ProtocolException If processsing the request failed.
     * @exception ResourceException If the resource got a fatal error.
     */

    public ReplyInterface perform(RequestInterface req)
  throws ProtocolException, ResourceException
    {
  ReplyInterface repi = super.perform(req);
  if (repi != null)
      return repi;

  if (! checkRequest(req))
      return null;

  Reply  reply  = null;
  Request request = (Request) req;
  String method = request.getMethod () ;
  // Perform the request:
  if ( method.equals("GET") ) {
      reply = get(request) ;
  } else if ( method.equals("HEAD") ) {
      reply = head(request) ;
  } else if ( method.equals("POST") ) {
      reply = post(request) ;
  } else if ( method.equals("PUT") ) {
      reply = put(request) ;
  } else if ( method.equals("OPTIONS") ) {
      reply = options(request);
  } else if ( method.equals("DELETE") ) {
      reply = delete(request) ;
  } else if ( method.equals("LINK") ) {
      reply = link(request) ;
  } else if ( method.equals("UNLINK") ) {
      reply = unlink(request) ;
  } else if ( method.equals("TRACE") ) {
      reply = trace(request) ;
  } else {
      reply = extended(request) ;
  }
  return reply;
    }

    /**
     * The default GET method.
     * @param request The request to handle.
     * @exception ProtocolException If processsing the request failed.
     * @exception ResourceException If the resource got a fatal error.
     */

    public Reply get(Request request)
  throws ProtocolException, ResourceException
    {
  if (dresource != null) {
      // we manage a DirectoryResource
      return getDirectoryResource(request) ;
  } else if (fresource != null) {
      // we manage a FileResource
      return getFileResource(request);
  } else {
      return getOtherResource(request);
  }
    }

    /**
     * The default GET method for other king of associated resource
     * @param request The request to handle.
     * @exception ProtocolException If processsing the request failed.
     * @exception ResourceException If the resource got a fatal error.
     */
    protected Reply getOtherResource(Request request)
  throws ProtocolException, ResourceException
    {
  if (resource instanceof ContainerResource) {
      return getDirectoryResource(request);
  } else {
      // we don't manage this kind of resource
      Reply error = request.makeReply(HTTP.NOT_IMPLEMENTED) ;
      error.setContent("Method GET not implemented.") ;
      throw new HTTPException (error) ;
  }
    }

    /**
     * Create the reply relative to the given file.
     * @param request the incomming request.
     * @return A Reply instance
     * @exception ProtocolException If processsing the request failed.
     * @exception ResourceException If the resource got a fatal error.
     */
    protected Reply createFileReply(Request request)
  throws ProtocolException, ResourceException
    {
  File file = fresource.getFile() ;
  Reply reply = null;
  // Check for a range request:
  HttpRange ranges[] = request.getRange();
  if ((ranges != null) && (ranges.length == 1)) {
      Reply rangereply = handleRangeRequest(request, ranges[0]);
      if ( rangereply != null )
    return rangereply;
  }
  // Default to full reply:
  reply = createDefaultReply(request, HTTP.OK) ;
  try {
      reply.setStream(new FileInputStream(file));
  } catch (IOException ex) {
      Reply error = request.makeReply(HTTP.SERVICE_UNAVAILABLE);
      error.setContent("Error while accessing filesystem");
      return error;
  }
  return reply ;
    }

    /**
     * Alway throws an HTTPException
     */
    protected Reply deleteMe(Request request)
  throws HTTPException
    {
  // Delete the resource if parent is extensible:
  boolean shrinkable = false;
  ResourceReference rr = fresource.getParent();
  ResourceReference rrtemp = null;
  Resource p = null;
  while ( true ) {
      try {
    if (rr == null)
        break;
    p = rr.lock();
    if (p instanceof DirectoryResource) {
        shrinkable =
      ((DirectoryResource)p).getShrinkableFlag();
        break;
    }
    rrtemp = p.getParent();
      } catch (InvalidResourceException ex) {
    break;
      } finally {
    if (rr != null)
        rr.unlock();
      }
      rr = rrtemp;
  }
  if (shrinkable) {
      // The resource is indexed but has no file, emit an error
      String msg = fresource.getFile()+
    ": deleted, removing the FileResource.";
      getServer().errlog(fresource, msg);
      try {
    fresource.delete();
      } catch (MultipleLockException ex) {
    Reply error = request.makeReply(HTTP.GONE) ;
    error.setContentMD5(null); // FIXME must compute it!
    error.setContent ("<h1>Document Gone</h1>"+
          "<p>The document "+
          request.getURL()+
          " is indexed but no longer available.</p>"+
          "<p>"+ex.getMessage()+"</p>");
    throw new HTTPException (error) ;
      }
  }
  // Emit an error back:
  Reply error = request.makeReply(HTTP.GONE) ;
  error.setContentMD5(null);
  error.setContent ("<h1>Document Gone</h1>"+
        "<p>The document "+
        request.getURL()+
        " is indexed but no longer available.</p>");
  return error;
    }

    /**
     * Get for FileResource
     * @param request the incomming request.
     * @return A Reply instance
     * @exception ProtocolException If processsing the request failed.
     * @exception ResourceException If the resource got a fatal error.
     */
    protected Reply getFileResource(Request request)
  throws ProtocolException, ResourceException
    {
  if (fresource == null)
      throw new ResourceException("this frame is not attached to a "+
          "FileResource. ("+
          resource.getIdentifier()+")");
  Reply reply = null;
  if (!checkExpect(request)) {
      reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
  File file = fresource.getFile() ;
  fresource.checkContent();
  updateCachedHeaders();
  // Check validators:
  int cim = checkIfMatch(request);
  if ((cim == COND_FAILED) || (cim == COND_WEAK)) {
      reply = request.makeReply(HTTP.PRECONDITION_FAILED);
      reply.setContent("Pre-conditions failed.");
      reply.setContentMD5(null);
      return reply;
  }
  if ( checkIfUnmodifiedSince(request) == COND_FAILED ) {
      reply = request.makeReply(HTTP.PRECONDITION_FAILED);
      reply.setContent("Pre-conditions failed.");
      reply.setContentMD5(null);
      return reply;
  }
  if (checkValidators(request) == COND_FAILED) {
      reply = createDefaultReply(request, HTTP.NOT_MODIFIED);
      return reply;
  }
  // Does this file really exists, if so send it back
  if ( file.exists() ) {
      reply = createFileReply(request);
      if (request.hasState(STATE_CONTENT_LOCATION))
    reply.setContentLocation(getURL(request).toExternalForm());
      return reply;
  } else {
      return deleteMe(request);
  }
    }

    /**
     * Perform a GET for the associated DirectoryResource.
     * @param request the incomming request.
     * @return A Reply instance.
     * @exception ProtocolException if request processing failed.
     * @exception ResourceException If the resource got a fatal error.
     */
    protected Reply getDirectoryResource(Request request)
  throws ProtocolException, ResourceException
    {
  if (!checkExpect(request)) {
      Reply reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
  String index = getIndex();
  if ( index != null && index.length() > 0 ) {
      if (index.equals("*forbid*")) {
    Reply rep = request.makeReply(HTTP.FORBIDDEN);
    rep.setContent("<h1>Forbidden</h1>"+
             "The directory resource "+ request.getURL() +
             " cannot be browsed");
    return rep;
     }
  }
  return getDirectoryListing(request) ;
    }
    /**
     * The default HEAD method replies does a GET and removes entity.
     * @param request The request to handle.
     * @exception ProtocolException Always thrown, to return a NOT_IMPLEMENTED
     * error.
     * @exception ResourceException If the resource got a fatal error.
     */

    public Reply head(Request request)
  throws ProtocolException, ResourceException
    {
  if (dresource != null) {
      return headDirectoryResource(request);
  } else if (fresource != null) {
      return headFileResource(request);
  } else {
      return headOtherResource(request);
  }
    }

    /**
     * Perform a HEAD request for the associated resource.
     * @param request the incomming request.
     * @return A Reply instance
     * @exception ProtocolException If processsing the request failed.
     * @exception ResourceException If the resource got a fatal error.
     */
    protected Reply headOtherResource(Request request)
  throws ProtocolException, ResourceException
    {
  Reply reply = null;
  reply = getOtherResource(request) ;
  reply.setStream((InputStream) null);
  return reply;
    }

    /**
     * Perform a HEAD request for the associated DirectoryResource.
     * @param request the incomming request.
     * @return A Reply instance
     * @exception ProtocolException If processsing the request failed.
     * @exception ResourceException If the resource got a fatal error.
     */
    protected Reply headDirectoryResource(Request request)
  throws ProtocolException, ResourceException
    {
  Reply reply = null;
  reply = getDirectoryResource(request) ;
  reply.setStream((InputStream) null);
  return reply;
    }

    /**
     * Perform a HEAD request for the associated FileResource.
     * @param request the incomming request.
     * @return A Reply instance
     * @exception ProtocolException If processsing the request failed.
     * @exception ResourceException If the resource got a fatal error.
     */
    protected Reply headFileResource(Request request)
  throws ProtocolException, ResourceException
    {
  if (fresource == null)
      throw new ResourceException("this frame is not attached to a "+
              "FileResource. ("+
              resource.getIdentifier()+")");
  Reply reply = null;
  if (!checkExpect(request)) {
      reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
  fresource.checkContent();
  updateCachedHeaders();
  // Conditional check:
  int cim = checkIfMatch(request);
  if ((cim == COND_FAILED) || (cim == COND_WEAK)) {
      Reply r = request.makeReply(HTTP.PRECONDITION_FAILED);
      r.setContent("Pre-conditions failed.");
      return r;
  }
  if ( checkIfUnmodifiedSince(request) == COND_FAILED ) {
      Reply r = request.makeReply(HTTP.PRECONDITION_FAILED);
      r.setContent("Pre-conditions failed.");
      return r;
  }
  if ( checkValidators(request) == COND_FAILED) {
      return createDefaultReply(request, HTTP.NOT_MODIFIED);
  }
  if (! fresource.getFile().exists()) {
      return deleteMe(request);
  }
  reply = createDefaultReply(request, HTTP.OK);
  if (request.hasState(STATE_CONTENT_LOCATION))
      reply.setContentLocation(getURL(request).toExternalForm());
  return reply;
 
    }

    /**
     * The default POST method replies with a not implemented.
     * @param request The request to handle.
     * @exception ProtocolException Always thrown, to return a NOT_IMPLEMENTED
     *    error.
     * @exception ResourceException If the resource got a fatal error.
     */

    public Reply post(Request request)
  throws ProtocolException, ResourceException
    {
  if (!checkExpect(request)) {
      Reply reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
  Reply error = request.makeReply(HTTP.NOT_ALLOWED) ;
  error.setHeaderValue(Reply.H_ALLOW, getAllow());
  error.setContent("Method POST not allowed on this resource.") ;
  throw new HTTPException (error) ;
    }

    /**
     * The default PUT method.
     * @param request The request to handle.
     * @exception ProtocolException If processsing the request failed.
     * @exception ResourceException If the resource got a fatal error.
     */

    public Reply put(Request request)
  throws ProtocolException, ResourceException
    {
  if (fresource != null) {
      return putFileResource(request);
  } else {
      return putOtherResource(request);
  }
    }

    /**
     * Always throw a ProtocolException.
     * @param request The incmming request.
     * @return a Reply instance.
     * @exception ProtocolException (Always thrown).
     */
    protected Reply putOtherResource(Request request)
  throws ProtocolException
    {
  Reply error = request.makeReply(HTTP.NOT_IMPLEMENTED) ;
  error.setContent("Method PUT not implemented.") ;
  throw new HTTPException (error) ;
    }

    /**
     * Change the content of the associated FileResource.
     * @param request The incomming request.
     * @exception org.w3c.tools.resources.ProtocolException if a protocol
     * error occurs
     * @exception ResourceException If the resource got a fatal error.
     */
    protected Reply putFileResource(Request request)
  throws ProtocolException, ResourceException
    {
  Reply reply = null;
  int status = HTTP.OK;

  if (!checkExpect(request)) {
      reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
  fresource.checkContent();
  updateCachedHeaders();
  // Is this resource writable ?
  if ( ! getPutableFlag() ) {
      Reply error = request.makeReply(HTTP.NOT_ALLOWED) ;
      error.setHeaderValue(Reply.H_ALLOW, getAllow());
      error.setContent("Method PUT not allowed.") ;
      throw new HTTPException (error) ;
  }
  // Check validators:
  int cim = checkIfMatch(request)
  if ((cim == COND_FAILED) || (cim == COND_WEAK)
      || (checkIfNoneMatch(request) == COND_FAILED)
      || (checkIfModifiedSince(request) == COND_FAILED)
      || (checkIfUnmodifiedSince(request) == COND_FAILED)) {
      Reply r = request.makeReply(HTTP.PRECONDITION_FAILED);
      r.setContent("Pre-condition failed.");
      return r;
  }
  // Check the request:
  InputStream in = null;
  try {
      in = request.getInputStream();
      if ( in == null ) {
    Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
    error.setContent ("<p>Request doesn't have a valid content.");
    throw new HTTPException (error) ;
      }
  } catch (IOException ex) {
      throw new ClientException(request.getClient(), ex);
  }
  // We do not support (for the time being) put with ranges:
  if ( request.hasContentRange() ) {
      Reply error = request.makeReply(HTTP.NOT_IMPLEMENTED);
      error.setContent("partial PUT not supported.");
      throw new HTTPException(error);
  }
// REMOVED as it is impossile for clients to behave properly.
// Of course it is fare more unsafe now, but it was too impractical IRL.
  // Check that if some type is provided it doesn't conflict:
//  if ( request.hasContentType() ) {
//      MimeType rtype = request.getContentType() ;
//      MimeType type  = getContentType() ;
//      if ( type == null ) {
//    setValue (ATTR_CONTENT_TYPE, rtype) ;
//      } else if ( (rtype.match (type) < 0 ) && !rtype.equiv(type) ) {
//    if (debug) {
//        System.out.println("No match between: ["+
//               rtype.toString()+"] and ["+
//               type.toString()+"]");
//    }
//    Reply error = request.makeReply(HTTP.UNSUPPORTED_MEDIA_TYPE) ;
//    error.setContent ("<p>Invalid content type: "+rtype.toString()
//          + " is not matching resource MIME type: "
//          +type.toString());
//    throw new HTTPException (error) ;
//      }
//  }
  // Write the body back to the file:
  try {
      // We are about to accept the put, notify client before continuing
      Client client = request.getClient();
      if ( client != null  && request.getExpect() != null ) {
    // FIXME we should check for "100-continue" explicitely
    client.sendContinue();
      }
      if ( fresource.newContent(request.getInputStream()) )
    status = HTTP.CREATED;
      else
    status = HTTP.NO_CONTENT;
  } catch (IOException ex) {
      if (debug)
    ex.printStackTrace();
      // so we have a problem replacing or creating the content
      // it is then a configuration problem (access right for the
      // underlying fle resource for example...
      Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;
      error.setReason("File Access Error");
      error.setContent("<p>Unable to save " + request.getURL()
           +" due to IO problems");
      throw new HTTPException (error) ;     
  }
  if ( status == HTTP.CREATED ) {
      reply = request.makeReply(status);
      reply.setContent("<P>Resource succesfully created");
      if (request.hasState(STATE_CONTENT_LOCATION))
    reply.setContentLocation(getURL(request).toExternalForm());
            // Henrik's fix, create the Etag on 201
      if (fresource != null) {
    // We only take car eof etag here:
    if ( etag == null ) {
        reply.setETag(getETag());
    }
      }
      reply.setLocation(getURL(request));
      reply.setContent ("<p>Entity body saved succesfully !") ;
  } else {
      reply = createDefaultReply(request, status);
  }
  return reply ;
    }

    /**
     * The default OPTIONS method replies with a not implemented.
     * @param request The request to handle.
     * @exception ProtocolException In case of errors.
     * @exception ResourceException If the resource got a fatal error.
     */

    public Reply options(Request request)
  throws ProtocolException, ResourceException
    {
  if (!checkExpect(request)) {
      Reply reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
  Reply reply = createDefaultReply(request, HTTP.OK);
  // set size to 0 according to rfc2616 9.2
  reply.setContentLength(0);
  // Removed unused headers:
  reply.setContentType(null);
  // Add the allow header:
  reply.setHeaderValue(Reply.H_ALLOW, getAllow());
  return reply;
    }

    /**
     * The default DELETE method, actually the resource (file, directory)
     * is moved into the trash directory which is not accessible via HTTP.
     * @param request The request to handle.
     * @exception ProtocolException If processsing the request failed.
     * @exception ResourceException If the resource got a fatal error.
     */

    public Reply delete(Request request)
  throws ProtocolException, ResourceException
    {
  if (getAllowDeleteFlag()) {
      if (dresource != null) {
    // we manage a DirectoryResource
    return deleteDirectoryResource(request) ;
      } else if (fresource != null) {
    // we manage a FileResource
    return deleteFileResource(request);
      } else {
    return deleteOtherResource(request);
      }
  } else {
      Reply error = request.makeReply(HTTP.NOT_ALLOWED) ;
      error.setContent("Method DELETE not allowed.") ;
      error.setHeaderValue(Reply.H_ALLOW, getAllow());
      throw new HTTPException (error) ;
  }
    }

    protected File computeTrashFile(File file) {
  File trashdir = new File(getServer().getTrashDirectory());
  if (! trashdir.exists()) {
      trashdir.mkdirs();
  }
  String filename = file.getName();
  File trashfile = new File(trashdir, filename);
  int nb = 1;
  while(trashfile.exists()) {
      trashfile = new File(trashdir, filename+"."+nb);
      nb++;
  }
  return trashfile;
    }

    protected File computeTrashDir(File dir) {
  return computeTrashFile(dir);
    }

    /**
     * Perform a DELETE for the associated DirectoryResource.
     * @param request the incomming request.
     * @return A Reply instance.
     * @exception ProtocolException if request processing failed.
     * @exception ResourceException If the resource got a fatal error.
     */
    protected Reply deleteDirectoryResource(Request request)
  throws ProtocolException, ResourceException
    {
  if (!checkExpect(request)) {
      Reply reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
  File olddir = dresource.getDirectory();
  File newdir = computeTrashDir(olddir);
  try {
      //delete the FileResource
      dresource.delete();
  } catch (MultipleLockException ex) {
      Reply error = request.makeReply(HTTP.FORBIDDEN);
      error.setContent("Can't delete resource: "+
           resource.getIdentifier()+
           " is locked. Try again later.");
      throw new HTTPException(error);
  }
  olddir.renameTo(newdir);
  return request.makeReply(HTTP.NO_CONTENT);
    }

    /**
     * Perform a DELETE for the associated FileResource.
     * @param request the incomming request.
     * @return A Reply instance.
     * @exception ProtocolException if request processing failed.
     * @exception ResourceException If the resource got a fatal error.
     */
    protected Reply deleteFileResource(Request request)
  throws ProtocolException, ResourceException
    {
  if (!checkExpect(request)) {
      Reply reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
  File oldfile = fresource.getFile();
  File newfile = computeTrashFile(oldfile);
  try {
      //delete the FileResource
      fresource.delete();
  } catch (MultipleLockException ex) {
      Reply error = request.makeReply(HTTP.FORBIDDEN);
      error.setContent("Can't delete resource: "+
           resource.getIdentifier()+
           " is locked. Try again later.");
      throw new HTTPException(error);
  }
  oldfile.renameTo(newfile);
  return request.makeReply(HTTP.NO_CONTENT);
    }

    /**
     * Perform a DELETE for the associated resource.
     * @param request the incomming request.
     * @return A Reply instance.
     * @exception ProtocolException if request processing failed.
     * @exception ResourceException If the resource got a fatal error.
     */
    protected Reply deleteOtherResource(Request request)
  throws ProtocolException, ResourceException
    {
  // we don't manage this kind of resource
  if (!checkExpect(request)) {
      Reply reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
  Reply error = request.makeReply(HTTP.NOT_IMPLEMENTED) ;
  error.setContent("Method DELETE not implemented.") ;
  throw new HTTPException (error) ;
    }

    /**
     * The default LINK method replies with a not implemented.
     * @param request The request to handle.
     * @exception ProtocolException Always thrown, to return a NOT_IMPLEMENTED
     * error.
     * @exception ResourceException If the resource got a fatal error.
     */

    public Reply link(Request request)
  throws ProtocolException, ResourceException
    {
  if (!checkExpect(request)) {
      Reply reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
  Reply error = request.makeReply(HTTP.NOT_IMPLEMENTED) ;
  error.setContent("Method LINK not implemented.") ;
  throw new HTTPException (error) ;
    }

    /**
     * The default UNLINK method replies with a not implemented.
     * @param request The request to handle.
     * @exception ProtocolException Always thrown, to return a NOT_IMPLEMENTED
     *    error.
     * @exception ResourceException If the resource got a fatal error.
     */

    public Reply unlink(Request request)
  throws ProtocolException, ResourceException
    {
  if (!checkExpect(request)) {
      Reply reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
  Reply error = request.makeReply(HTTP.NOT_IMPLEMENTED) ;
  error.setContent("Method UNLINK not implemented.") ;
  throw new HTTPException (error) ;
    }

    /**
     * The default TRACE method replies with a not implemented
     * @param request The request to handle.
     * @exception HTTPException In case of errors.
     * @exception ClientException If the client instance controling the
     * request processing got a fatal error.
     */

    public Reply trace(Request request)
        throws HTTPException, ClientException
    {
  Reply reply = null;
  if (!checkExpect(request)) {
      reply = createDefaultReply(request, HTTP.EXPECTATION_FAILED);
      reply.setContent("The requested expectation could not be"+
           " met by the resource");
      return reply;
  }
        reply = createDefaultReply(request, HTTP.OK);
  reply.setNoCache(); // don't cache this
        reply.setMaxAge(-1); //
  reply.setContentMD5(null);
        // Dump the request as the body
        // Removed unused headers:
  // FIXME should be something else for chuncked stream
  ByteArrayOutputStream ba = new ByteArrayOutputStream();
  try {
      reply.setContentType(new MimeType("message/http"));
      request.dump(ba);
      reply.setContentLength(ba.size());
  } catch (Exception ex) {
      ex.printStackTrace();
  }
  reply.setStream(new ByteArrayInputStream(ba.toByteArray()));
  return reply;
    }

    public Reply extended(Request request)
      throws ProtocolException, ResourceException
    {
  String method = request.getMethod() ;
  Reply error   = request.makeReply(HTTP.NOT_IMPLEMENTED) ;
  error.setContent("Method "+method+" not implemented.") ;
  throw new HTTPException (error) ;
    }

    /**
     * The Browse Mime type.
     */
    protected static MimeType  browsetype = null;

    /**
     * Get the Browse Mime type.
     */

    protected synchronized MimeType getBrowseType() {
  if ( browsetype == null ) {
      try {
    browsetype = new MimeType("application/x-navibrowse");
      } catch (Exception ex) {
    ex.printStackTrace();
      }
  }
  return browsetype;
    }


    /**
     * A present to GNNPress users !
     * This method implements the <code>BROWSE</code> method that
     * AOL press (or GNN press, or whatever its last name is) expects.
     * @param request The request to process.
     * @exception ProtocolException If some error occurs.
     * @return A Reply instance.
     */

    public Reply browse (Request request)
  throws ProtocolException
    {
  if (dresource == null) {
      Reply error = request.makeReply(HTTP.NOT_IMPLEMENTED) ;
      error.setContent("Method "+request.getMethod()+
           " not implemented.") ;
      throw new HTTPException (error) ;
  }

  Enumeration  e         = dresource.enumerateResourceIdentifiers() ;
  Vector       resources = Sorter.sortStringEnumeration(e) ;
  int          rsize     = ((resources == null) ? 0 : resources.size()) ;
  StringBuffer sb        = new StringBuffer() ;

  // As we have enumerated all resources, just looking the store is ok
  for (int i = 0 ; i < rsize ; i++) {
      String            rname = (String) resources.elementAt(i) ;
      ResourceReference rr    = null;
      Resource          r     = null;
      try {
    rr = dresource.lookup(rname) ;
    r = rr.lock();
    // may throw InvalidResourceException

    if ( r instanceof DirectoryResource ) {
        sb.append("application/x-navidir "+
            rname+
            "\r\n") ;
    } else {
        HTTPFrame itsframe =
      (HTTPFrame) resource.getFrame(getClass());
        if (itsframe != null) {
      sb.append(itsframe.getContentType().toString()+
          " "+
          rname+
          "\r\n") ;
        }
    }
      } catch (InvalidResourceException ex) {
    continue;
      } finally {
    rr.unlock();
      }
  }
  Reply reply = request.makeReply(HTTP.OK) ;
  reply.addPragma("no-cache");
  reply.setNoCache();
  reply.setContent(sb.toString()) ;
  reply.setContentType(getBrowseType());
  return reply ;
    }

    /**
     * Set the values. (MUST be called before initialize).
     * @param defs The Hashtable containing the values.
     */
    public void pickleValues(Hashtable defs) {
  Object nvalues[] = new Object[attributes.length];
  for (int i = 0 ; i < nvalues.length ; i++) {
      String attrname = attributes[i].getName() ;
      Object def      = defs.get(attrname) ;
      // note that protocol frames have usually names in the
      // frame-XX serie, so it is valid to save (big) on this.
      if ((def != null) && ((i == ATTR_HELP_URL) ||
          (i == ATTR_IDENTIFIER) ||
          (i == ATTR_TITLE) ||
          (i == ATTR_CHARSET) ||
          (i == ATTR_CONTENT_LANGUAGE) ||
          (i == ATTR_CONTENT_ENCODING) ||
          (i == ATTR_ICON) ||
          (i == ATTR_INDEX) ||
          (i == ATTR_ICONDIR) ||
          (i == ATTR_STYLE_LINK))
    && (def instanceof String)) {
    nvalues[i] = ((String) def).intern();
      } else {     
    nvalues[i] = def ;
      }
  }
  this.values = nvalues ;
    }

    /**
     * Initialization method for attribute holders.
     * This method allows to initialize an attribute holder by providing
     * its attributes values through a Hashtable mapping attribute names
     * to attribute values.
     * @param defs The Hashtable containing the default values.
     */

    public void initialize(Hashtable defs) {
  Object values[] = ((this.values == null)
         ? new Object[attributes.length]
         : this.values);
  for (int i = 0 ; i < values.length ; i++) {
      String attrname = attributes[i].getName() ;
      Object def      = defs.get(attrname) ;
      if ( values[i] == null ) {
    // for help_url, we can save lots of space by using
    // String.intern()
    // Those attribute are not, in practise, very variables
    // So it is a valid optimisation to intern them.
    if ((def != null) && ((i == ATTR_HELP_URL) ||
              (i == ATTR_IDENTIFIER) ||
              (i == ATTR_TITLE) ||
              (i == ATTR_CHARSET) ||
              (i == ATTR_CONTENT_LANGUAGE) ||
              (i == ATTR_CONTENT_ENCODING) ||
              (i == ATTR_ICON) ||
              (i == ATTR_INDEX) ||
              (i == ATTR_ICONDIR) ||
              (i == ATTR_STYLE_LINK))
            && (def instanceof String)) {
        values[i] = ((String) def).intern();
    } else {     
        values[i] = def ;
    }
      }
  }
  initialize(values) ;
    }
}
TOP

Related Classes of org.w3c.jigsaw.frames.HTTPFrame

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.