diff --git a/apps/jetty/java/src/org/mortbay/util/Resource.java b/apps/jetty/java/src/org/mortbay/util/Resource.java
new file mode 100644
index 0000000000000000000000000000000000000000..4180b8b011ccf33c3e45b5a1da9cf702cc08cc86
--- /dev/null
+++ b/apps/jetty/java/src/org/mortbay/util/Resource.java
@@ -0,0 +1,431 @@
+// ========================================================================
+// $Id: Resource.java,v 1.32 2009/05/16 01:53:36 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.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.DateFormat;
+import java.util.Arrays;
+import java.util.Date;
+
+import org.apache.commons.logging.Log;
+import org.mortbay.log.LogFactory;
+
+
+/* ------------------------------------------------------------ */
+/** Abstract resource class.
+ *
+ * @version $Id: Resource.java,v 1.32 2009/05/16 01:53:36 gregwilkins Exp $
+ * @author Nuno Preguica
+ * @author Greg Wilkins (gregw)
+ */
+public abstract class Resource implements Serializable
+{
+    private static Log log = LogFactory.getLog(Resource.class);
+
+    Object _associate;
+    
+    /* ------------------------------------------------------------ */
+    /** Construct a resource from a url.
+     * @param url A URL.
+     * @return A Resource object.
+     */
+    public static Resource newResource(URL url)
+        throws IOException
+    {
+        if (url==null)
+            return null;
+
+        String urls=url.toExternalForm();
+        if( urls.startsWith( "file:"))
+        {
+            try
+            {
+                FileResource fileResource= new FileResource(url);
+                return fileResource;
+            }
+            catch(Exception e)
+            {
+                log.debug(LogSupport.EXCEPTION,e);
+                return new BadResource(url,e.toString());
+            }
+        }
+        else if( urls.startsWith( "jar:file:"))
+        {
+            return new JarFileResource(url);
+        }
+        else if( urls.startsWith( "jar:"))
+        {
+            return new JarResource(url);
+        }
+
+        return new URLResource(url,null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Construct a resource from a string.
+     * @param resource A URL or filename.
+     * @return A Resource object.
+     */
+    public static Resource newResource(String resource)
+        throws MalformedURLException, IOException
+    {
+        URL url=null;
+        try
+        {
+            // Try to format as a URL?
+            url = new URL(resource);
+        }
+        catch(MalformedURLException e)
+        {
+            if(!resource.startsWith("ftp:") &&
+               !resource.startsWith("file:") &&
+               !resource.startsWith("jar:"))
+            {
+                try
+                {
+                    // It's a file.
+                    if (resource.startsWith("./"))
+                        resource=resource.substring(2);
+                    
+                    File file=new File(resource).getCanonicalFile();
+                    url=file.toURI().toURL();                    
+                    
+                    URLConnection connection=url.openConnection();
+                    FileResource fileResource= new FileResource(url,connection,file);
+                    return fileResource;
+                }
+                catch(Exception e2)
+                {
+                    log.debug(LogSupport.EXCEPTION,e2);
+                    throw e;
+                }
+            }
+            else
+            {
+                log.warn("Bad Resource: "+resource);
+                throw e;
+            }
+        }
+
+        String nurl=url.toString();
+        if (nurl.length()>0 &&
+            nurl.charAt(nurl.length()-1)!=
+            resource.charAt(resource.length()-1))
+        {
+            if ((nurl.charAt(nurl.length()-1)!='/' ||
+                 nurl.charAt(nurl.length()-2)!=resource.charAt(resource.length()-1))
+                &&
+                (resource.charAt(resource.length()-1)!='/' ||
+                 resource.charAt(resource.length()-2)!=nurl.charAt(nurl.length()-1)
+                 ))
+            {
+                return new BadResource(url,"Trailing special characters stripped by URL in "+resource);
+            }
+        }
+        return newResource(url);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Construct a system resource from a string.
+     * The resource is tried as classloader resource before being
+     * treated as a normal resource.
+     */
+    public static Resource newSystemResource(String resource)
+        throws IOException
+    {
+        URL url=null;
+        // Try to format as a URL?
+        ClassLoader
+            loader=Thread.currentThread().getContextClassLoader();
+        if (loader!=null)
+        {
+            url=loader.getResource(resource);
+            if (url==null && resource.startsWith("/"))
+                url=loader.getResource(resource.substring(1));
+        }
+        if (url==null)
+        {
+            loader=Resource.class.getClassLoader();
+            if (loader!=null)
+            {
+                url=loader.getResource(resource);
+                if (url==null && resource.startsWith("/"))
+                    url=loader.getResource(resource.substring(1));
+            }
+        }
+        
+        if (url==null)
+        {
+            url=ClassLoader.getSystemResource(resource);
+            if (url==null && resource.startsWith("/"))
+                url=loader.getResource(resource.substring(1));
+        }
+        
+        if (url==null)
+            return null;
+        
+        return newResource(url);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void finalize()
+    {
+        release();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Release any resources held by the resource.
+     */
+    public abstract void release();
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns true if the respresened resource exists.
+     */
+    public abstract boolean exists();
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns true if the respresenetd resource is a container/directory.
+     * If the resource is not a file, resources ending with "/" are
+     * considered directories.
+     */
+    public abstract boolean isDirectory();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the last modified time
+     */
+    public abstract long lastModified();
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Return the length of the resource
+     */
+    public abstract long length();
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an URL representing the given resource
+     */
+    public abstract URL getURL();
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an File representing the given resource or NULL if this
+     * is not possible.
+     */
+    public abstract File getFile()
+        throws IOException;
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the name of the resource
+     */
+    public abstract String getName();
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an input stream to the resource
+     */
+    public abstract InputStream getInputStream()
+        throws java.io.IOException;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an output stream to the resource
+     */
+    public abstract OutputStream getOutputStream()
+        throws java.io.IOException, SecurityException;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Deletes the given resource
+     */
+    public abstract boolean delete()
+        throws SecurityException;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Rename the given resource
+     */
+    public abstract boolean renameTo( Resource dest)
+        throws SecurityException;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns a list of resource names contained in the given resource
+     * The resource names are not URL encoded.
+     */
+    public abstract String[] list();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the resource contained inside the current resource with the
+     * given name.
+     * @param path The path segment to add, which should be encoded by the
+     * encode method. 
+     */
+    public abstract Resource addPath(String path)
+        throws IOException,MalformedURLException;
+    
+
+    /* ------------------------------------------------------------ */
+    /** Encode according to this resource type.
+     * The default implementation calls URI.encodePath(uri)
+     * @param uri 
+     * @return String encoded for this resource type.
+     */
+    public String encode(String uri)
+    {
+        return URI.encodePath(uri);
+    }
+    
+        
+    /* ------------------------------------------------------------ */
+    public Object getAssociate()
+    {
+        return _associate;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setAssociate(Object o)
+    {
+        _associate=o;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The canonical Alias of this resource or null if none.
+     */
+    public URL getAlias()
+    {
+        return null;
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    public CachedResource cache()
+        throws IOException
+    {
+        return new CachedResource(this);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the resource list as a HTML directory listing.
+     * @param base The base URL
+     * @param parent True if the parent directory should be included
+     * @return String of HTML
+     */
+    public String getListHTML(String base,
+                              boolean parent)
+        throws IOException
+    {
+        if (!isDirectory())
+            return null;
+        
+      
+        String[] ls = list();
+        if (ls==null)
+            return null;
+        Arrays.sort(ls);
+        
+        String title = "Directory: "+URI.decodePath(base);
+        title=StringUtil.replace(StringUtil.replace(title,"<","&lt;"),">","&gt;");
+        StringBuffer buf=new StringBuffer(4096);
+        buf.append("<HTML><HEAD><TITLE>");
+        buf.append(title);
+        buf.append("</TITLE></HEAD><BODY>\n<H1>");
+        buf.append(title);
+        buf.append("</H1><TABLE BORDER=0>");
+        
+        if (parent)
+        {
+            buf.append("<TR><TD><A HREF=");
+            buf.append(URI.encodePath(URI.addPaths(base,"../")));
+            buf.append(">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
+        }
+        
+        DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
+                                                       DateFormat.MEDIUM);
+        for (int i=0 ; i< ls.length ; i++)
+        {   
+            String encoded=URI.encodePath(ls[i]);
+            // bugfix for I2P - Backport from Jetty 6 (zero file lengths and last-modified times)
+            // http://jira.codehaus.org/browse/JETTY-361?page=com.atlassian.jira.plugin.system.issuetabpanels%3Achangehistory-tabpanel#issue-tabs
+            // See resource.diff attachment
+            //Resource item = addPath(encoded);
+            Resource item = addPath(ls[i]);
+            
+            buf.append("<TR><TD><A HREF=\"");
+            
+            String path=URI.addPaths(base,encoded);
+            
+            if (item.isDirectory() && !path.endsWith("/"))
+                path=URI.addPaths(path,"/");
+            buf.append(path);
+            buf.append("\">");
+            buf.append(StringUtil.replace(StringUtil.replace(ls[i],"<","&lt;"),">","&gt;"));
+            buf.append("&nbsp;");
+            buf.append("</TD><TD ALIGN=right>");
+            buf.append(item.length());
+            buf.append(" bytes&nbsp;</TD><TD>");
+            buf.append(dfmt.format(new Date(item.lastModified())));
+            buf.append("</TD></TR>\n");
+        }
+        buf.append("</TABLE>\n");
+	buf.append("</BODY></HTML>\n");
+        
+        return buf.toString();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * @param out 
+     * @param start First byte to write
+     * @param count Bytes to write or -1 for all of them.
+     */
+    public void writeTo(OutputStream out,long start,long count)
+        throws IOException
+    {
+        InputStream in = getInputStream();
+        try
+        {
+            in.skip(start);
+            if (count<0)
+                IO.copy(in,out);
+            else
+                IO.copy(in,out,(int)count);
+        }
+        finally
+        {
+            in.close();
+        }
+    }    
+}