diff --git a/apps/jetty/java/src/org/mortbay/util/FileResource.java b/apps/jetty/java/src/org/mortbay/util/FileResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..2738d679b8a0f1b67d5440789261b078924e878f
--- /dev/null
+++ b/apps/jetty/java/src/org/mortbay/util/FileResource.java
@@ -0,0 +1,350 @@
+// ========================================================================
+// $Id: FileResource.java,v 1.31 2006/01/04 13:55:31 gregwilkins Exp $
+// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at 
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+package org.mortbay.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.Permission;
+
+import org.apache.commons.logging.Log;
+import org.mortbay.log.LogFactory;
+
+
+/* ------------------------------------------------------------ */
+/** File Resource.
+ *
+ * Handle resources of implied or explicit file type.
+ * This class can check for aliasing in the filesystem (eg case
+ * insensitivity).  By default this is turned on if the platform does
+ * not have the "/" path separator, or it can be controlled with the
+ * "org.mortbay.util.FileResource.checkAliases" system parameter.
+ *
+ * If alias checking is turned on, then aliased resources are
+ * treated as if they do not exist, nor can they be created.
+ *
+ * @version $Revision: 1.31 $
+ * @author Greg Wilkins (gregw)
+ */
+public class FileResource extends URLResource
+{
+	private static Log log = LogFactory.getLog(Credential.class);
+    private static boolean __checkAliases;
+    static
+    {
+        __checkAliases=
+            "true".equalsIgnoreCase
+            (System.getProperty("org.mortbay.util.FileResource.checkAliases","true"));
+ 
+       if (__checkAliases)
+            log.info("Checking Resource aliases");
+    }
+    
+    /* ------------------------------------------------------------ */
+    private File _file;
+    private transient URL _alias=null;
+    private transient boolean _aliasChecked=false;
+
+    /* ------------------------------------------------------------------------------- */
+    /** setCheckAliases.
+     * @param checkAliases True of resource aliases are to be checked for (eg case insensitivity or 8.3 short names) and treated as not found.
+     */
+    public static void setCheckAliases(boolean checkAliases)
+    {
+        __checkAliases=checkAliases;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /** getCheckAliases.
+     * @return True of resource aliases are to be checked for (eg case insensitivity or 8.3 short names) and treated as not found.
+     */
+    public static boolean getCheckAliases()
+    {
+        return __checkAliases;
+    }
+    
+    /* -------------------------------------------------------- */
+    FileResource(URL url)
+        throws IOException, URISyntaxException
+    {
+        super(url,null);
+
+        try
+        {
+            // Try standard API to convert URL to file.
+            _file =new File(new URI(url.toString()));
+        }
+        catch (Exception e)
+        {
+            LogSupport.ignore(log,e);
+            try
+            {
+                // Assume that File.toURL produced unencoded chars. So try
+                // encoding them.
+                String urls=
+                    "file:"+org.mortbay.util.URI.encodePath(url.toString().substring(5));
+                _file =new File(new URI(urls));
+            }
+            catch (Exception e2)
+            {
+                LogSupport.ignore(log,e2);
+
+                // Still can't get the file.  Doh! try good old hack!
+                checkConnection();
+                Permission perm = _connection.getPermission();
+                _file = new File(perm==null?url.getFile():perm.getName());
+            }
+        }
+        
+        if (_file.isDirectory() && !_urlString.endsWith("/"))
+            _urlString=_urlString+"/";
+    }
+    
+    /* -------------------------------------------------------- */
+    FileResource(URL url, URLConnection connection, File file)
+    {
+        super(url,connection);
+        _file=file;
+        if (_file.isDirectory() && !_urlString.endsWith("/"))
+            _urlString=_urlString+"/";
+    }
+    
+    /* -------------------------------------------------------- */
+    public Resource addPath(String path)
+        throws IOException,MalformedURLException
+    {
+        FileResource r=null;
+
+        if (!isDirectory())
+        {
+            r=(FileResource)super.addPath(path);
+        }
+        else
+        {
+            path = org.mortbay.util.URI.canonicalPath(path);
+            
+            // treat all paths being added as relative
+            String rel=path;
+            if (path.startsWith("/"))
+                rel = path.substring(1);
+            
+            File newFile = new File(_file,rel.replace('/', File.separatorChar));
+            r=new FileResource(newFile.toURI().toURL(),null,newFile);
+        }
+        
+        String encoded=org.mortbay.util.URI.encodePath(path);
+        int expected=r._urlString.length()-encoded.length();
+        int index = r._urlString.lastIndexOf(encoded, expected);
+        
+        if (expected!=index && ((expected-1)!=index || path.endsWith("/") || !r.isDirectory()))
+        {
+            r._alias=r._url;
+            r._aliasChecked=true;
+        }                   
+        return r;
+    }
+   
+    
+    /* ------------------------------------------------------------ */
+    public URL getAlias()
+    {
+        if (__checkAliases && !_aliasChecked)
+        {
+            try
+            {    
+                String abs=_file.getAbsolutePath();
+                String can=_file.getCanonicalPath();
+                
+                if (abs.length()!=can.length() || !abs.equals(can))
+                    _alias=new File(can).toURI().toURL();
+                
+                _aliasChecked=true;
+                
+                if (_alias!=null && log.isDebugEnabled())
+                {
+                    log.debug("ALIAS abs="+abs);
+                    log.debug("ALIAS can="+can);
+                }
+            }
+            catch(Exception e)
+            {
+                log.warn(LogSupport.EXCEPTION,e);
+                return getURL();
+            }                
+        }
+        return _alias;
+    }
+    
+    /* -------------------------------------------------------- */
+    /**
+     * Returns true if the resource exists.
+     */
+    public boolean exists()
+    {
+        return _file.exists();
+    }
+        
+    /* -------------------------------------------------------- */
+    /**
+     * Returns the last modified time
+     */
+    public long lastModified()
+    {
+        return _file.lastModified();
+    }
+
+    /* -------------------------------------------------------- */
+    /**
+     * Returns true if the respresenetd resource is a container/directory.
+     */
+    public boolean isDirectory()
+    {
+        return _file.isDirectory();
+    }
+
+    /* --------------------------------------------------------- */
+    /**
+     * Return the length of the resource
+     */
+    public long length()
+    {
+        return _file.length();
+    }
+        
+
+    /* --------------------------------------------------------- */
+    /**
+     * Returns the name of the resource
+     */
+    public String getName()
+    {
+        return _file.getAbsolutePath();
+    }
+        
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an File representing the given resource or NULL if this
+     * is not possible.
+     */
+    public File getFile()
+    {
+        return _file;
+    }
+        
+    /* --------------------------------------------------------- */
+    /**
+     * Returns an input stream to the resource
+     */
+    public InputStream getInputStream() throws IOException
+    {
+        return new FileInputStream(_file);
+    }
+        
+    /* --------------------------------------------------------- */
+    /**
+     * Returns an output stream to the resource
+     */
+    public OutputStream getOutputStream()
+        throws java.io.IOException, SecurityException
+    {
+        return new FileOutputStream(_file);
+    }
+        
+    /* --------------------------------------------------------- */
+    /**
+     * Deletes the given resource
+     */
+    public boolean delete()
+        throws SecurityException
+    {
+        return _file.delete();
+    }
+
+    /* --------------------------------------------------------- */
+    /**
+     * Rename the given resource
+     */
+    public boolean renameTo( Resource dest)
+        throws SecurityException
+    {
+        if( dest instanceof FileResource)
+            return _file.renameTo( ((FileResource)dest)._file);
+        else
+            return false;
+    }
+
+    /* --------------------------------------------------------- */
+    /**
+     * Returns a list of resources contained in the given resource
+     */
+    public String[] list()
+    {
+        String[] list =_file.list();
+        if (list==null)
+            return null;
+        for (int i=list.length;i-->0;)
+        {
+            if (new File(_file,list[i]).isDirectory() &&
+                !list[i].endsWith("/"))
+                list[i]+="/";
+        }
+        return list;
+    }
+         
+    /* ------------------------------------------------------------ */
+    /** Encode according to this resource type.
+     * File URIs are encoded.
+     * @param uri URI to encode.
+     * @return The uri unchanged.
+     */
+    public String encode(String uri)
+    {
+        return uri;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * @param o
+     * @return 
+     */
+    public boolean equals( Object o)
+    {
+        if (this == o)
+            return true;
+
+        if (null == o || ! (o instanceof FileResource))
+            return false;
+
+        FileResource f=(FileResource)o;
+        return f._file == _file || (null != _file && _file.equals(f._file));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the hashcode.
+     */
+    public int hashCode()
+    {
+       return null == _file ? super.hashCode() : _file.hashCode();
+    }
+}
diff --git a/apps/jetty/java/src/org/mortbay/util/URI.java b/apps/jetty/java/src/org/mortbay/util/URI.java
index 073951a57cce54141e092f392db4f690aa952d24..d5543832b77d67ff3e8c73a351e05b6f061e656f 100644
--- a/apps/jetty/java/src/org/mortbay/util/URI.java
+++ b/apps/jetty/java/src/org/mortbay/util/URI.java
@@ -53,6 +53,9 @@ public class URI
     private String _query;
     private UrlEncoded _parameters;
     private boolean _dirty;
+    private static String unreserved = "/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~";
+    private static String reserved = "!*'();:@&=+$,?%#[]";
+    private static String hexchars = "0123456789ABCDEF";
     
     /* ------------------------------------------------------------ */
     /** Copy Constructor .
@@ -237,6 +240,7 @@ public class URI
                 else
                     _parameters.clear();
                 _parameters.decode(_query,__CHARSET);
+                
             }
             else
                 _query=null;           
@@ -566,52 +570,48 @@ public class URI
      */
     public static StringBuffer encodePath(StringBuffer buf, String path)
     {
-        if (buf==null)
-        {
-        loop:
-            for (int i=0;i<path.length();i++)
-            {
-                char c=path.charAt(i);
-                switch(c)
-                {
-                  case '%':
-                  case '?':
-                  case ';':
-                  case '#':
-                  case ' ':
-                      buf=new StringBuffer(path.length()<<1);
-                      break loop;
+        // Convert path to native first.
+        byte[] b = null;
+        /*
+        try {
+            b = path.getBytes(__CHARSET);
+        } catch(UnsupportedEncodingException ex) {
+            return null; // Shouldn't be possible.
+        }
+        */
+        b = path.getBytes();
+        StringBuffer x = new StringBuffer(b.length);
+        for(int i=0; i<b.length; i++) {
+            x.append((char)(b[i]&0xff));
+        }
+        String _path = new String(x);
+        if(buf == null) {
+            loop:
+            for(int i = 0; i < _path.length(); i++) {
+                char c = _path.charAt(i);
+                String cs = "" + c;
+                if(reserved.contains(cs) || !unreserved.contains(cs)) {
+                    buf = new StringBuffer(_path.length() << 1);
+                    break loop;
                 }
             }
-            if (buf==null)
+            if(buf == null) {
                 return null;
+            }
         }
-        
-        synchronized(buf)
-        {
-            for (int i=0;i<path.length();i++)
-            {
-                char c=path.charAt(i);       
-                switch(c)
-                {
-                  case '%':
-                      buf.append("%25");
-                      continue;
-                  case '?':
-                      buf.append("%3F");
-                      continue;
-                  case ';':
-                      buf.append("%3B");
-                      continue;
-                  case '#':
-                      buf.append("%23");
-                      continue;
-                  case ' ':
-                      buf.append("%20");
-                      continue;
-                  default:
-                      buf.append(c);
-                      continue;
+        synchronized(buf) {
+            for(int i = 0; i < _path.length(); i++) {
+                char c = _path.charAt(i);
+                String cs = "" + c;
+                if(reserved.contains(cs) || !unreserved.contains(cs)) {
+                    if((c & 0xff) == c) {
+                        buf.append(gethex(c & 0xff));
+                    } else {
+                        buf.append(gethex((c >> 8) & 0xff));
+                        buf.append(gethex(c & 0xff));
+                    }
+                } else {
+                    buf.append(c);
                 }
             }
         }
@@ -619,6 +619,14 @@ public class URI
         return buf;
     }
     
+    /**
+     *
+     * @param decimal value not greater than 255.
+     * @return a percent sign followed by two hexadecimal digits.
+     */
+    private static String gethex(int decimal) {
+        return new String("%" + hexchars.charAt(decimal >> 4) + hexchars.charAt(decimal & 0xF));
+    }
     /* ------------------------------------------------------------ */
     /** Encode a URI path.
      * @param path The path the encode
@@ -708,6 +716,7 @@ public class URI
         if (noDecode)
             return path;
 
+        /*
         try
         {    
             return new String(bytes,0,n,__CHARSET);
@@ -717,6 +726,10 @@ public class URI
             log.warn(LogSupport.EXCEPTION,e);
             return new String(bytes,0,n);
         }
+        */
+        
+        return new String(bytes,0,n);
+        
     }
 
     /* ------------------------------------------------------------ */