geronimo-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From d...@apache.org
Subject svn commit: r393370 [1/2] - in /geronimo/sandbox/classloader: ./ src/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/geronimo/ src/java/org/apache/geronimo/kernel/ src/java/org/apache/geronimo/kernel/classloader/ src/java/org/apache/g...
Date Wed, 12 Apr 2006 02:48:10 GMT
Author: dain
Date: Tue Apr 11 19:48:07 2006
New Revision: 393370

URL: http://svn.apache.org/viewcvs?rev=393370&view=rev
Log:
Basic implementation of nested jar support.  Need performance tuning before it can be used.

Added:
    geronimo/sandbox/classloader/
    geronimo/sandbox/classloader/NOTICE.txt
    geronimo/sandbox/classloader/src/
    geronimo/sandbox/classloader/src/java/
    geronimo/sandbox/classloader/src/java/org/
    geronimo/sandbox/classloader/src/java/org/apache/
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/GenericClassLoader.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceFinder.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceHandle.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceLoader.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceUtils.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/URIClassLoader.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jar/
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jar/Handler.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/CachedJarFile.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/CachingJarOpener.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/Handler.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarCache.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarOpener.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarUrlConnection.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/JarUrlStreamHandler.java
    geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jarcache/TempJarCache.java
    geronimo/sandbox/classloader/src/test/
    geronimo/sandbox/classloader/src/test/org/
    geronimo/sandbox/classloader/src/test/org/apache/
    geronimo/sandbox/classloader/src/test/org/apache/geronimo/
    geronimo/sandbox/classloader/src/test/org/apache/geronimo/kernel/
    geronimo/sandbox/classloader/src/test/org/apache/geronimo/kernel/classloader/
    geronimo/sandbox/classloader/src/test/org/apache/geronimo/kernel/classloader/jar/
    geronimo/sandbox/classloader/src/test/org/apache/geronimo/kernel/classloader/jar/JarUrlTest.java

Added: geronimo/sandbox/classloader/NOTICE.txt
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/NOTICE.txt?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/NOTICE.txt (added)
+++ geronimo/sandbox/classloader/NOTICE.txt Tue Apr 11 19:48:07 2006
@@ -0,0 +1,4 @@
+Portions of this class loader module were originally developed by 
+Dawid Kurzyniec as part of the Emory University utilities library
+and was released to the public domain, as explained at
+http://creativecommons.org/licenses/publicdomain

Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/GenericClassLoader.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/GenericClassLoader.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/GenericClassLoader.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/GenericClassLoader.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,394 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  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.apache.geronimo.kernel.classloader;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLStreamHandler;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.security.SecureClassLoader;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
+import java.util.jar.Manifest;
+
+/**
+ * This class loader can be used to find class, resource and library
+ * {@link ResourceHandle handles}
+ * as well as load classes, resources and libraries using abstract
+ * {@link ResourceFinder} entity encapsulating the searching approach.
+ * Resource handles allow accessing meta-information (like Attributes,
+ * Certificates etc.) related to classes, resources and libraries prior to
+ * loading them.
+ * <p/>
+ * GenericClassLoader is intended to be used as a base for custom class
+ * loaders. In most applications, GenericClassLoader can be used directly --
+ * the application-specific functionality of resource searching can often be
+ * completely delegated to the resource finder. See {@link URIClassLoader}
+ * for a concrete implementation using a simple resource finder.
+ *
+ * @version $Rev$ $Date$
+ */
+public class GenericClassLoader extends SecureClassLoader {
+
+    protected ResourceFinder finder;
+    private AccessControlContext acc;
+
+    /**
+     * Creates new GenericClassLoader instance using specified
+     * {@link ResourceFinder} to find resources and having specified
+     * parent class loader.
+     */
+    public GenericClassLoader(ResourceFinder finder, ClassLoader parent) {
+        super(parent);
+        SecurityManager security = System.getSecurityManager();
+        if (security != null) {
+            security.checkCreateClassLoader();
+        }
+        this.finder = finder;
+        acc = AccessController.getContext();
+    }
+
+    /**
+     * Creates new GenericClassLoader instance using specified
+     * {@link ResourceFinder} to find resources and with default
+     * parent class loader.
+     */
+    public GenericClassLoader(ResourceFinder finder) {
+        super();
+        // this is to make the stack depth consistent with 1.1
+        SecurityManager security = System.getSecurityManager();
+        if (security != null) {
+            security.checkCreateClassLoader();
+        }
+        this.finder = finder;
+        //acc = AccessController.getContext();
+    }
+
+    /**
+     * Finds and loads the class with the specified name.
+     *
+     * @param name the name of the class
+     * @return the resulting class
+     * @throws ClassNotFoundException if the class could not be found
+     */
+    protected Class findClass(final String name) throws ClassNotFoundException {
+        try {
+            return (Class) AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                public Object run() throws ClassNotFoundException {
+                    String path = name.replace('.', '/').concat(".class");
+                    ResourceHandle h = finder.getResource(path);
+                    if (h != null) {
+                        try {
+                            return defineClass(name, h);
+                        } catch (IOException e) {
+                            throw new ClassNotFoundException(name, e);
+                        }
+                    } else {
+                        throw new ClassNotFoundException(name);
+                    }
+                }
+            }, acc);
+        } catch (java.security.PrivilegedActionException pae) {
+            throw (ClassNotFoundException) pae.getException();
+        }
+    }
+
+    protected Class defineClass(String name, ResourceHandle h) throws IOException {
+        int i = name.lastIndexOf('.');
+        URL url = h.getCodeSourceURL();
+        if (i != -1) { // check package
+            String pkgname = name.substring(0, i);
+            // check if package already loaded
+            Package pkg = getPackage(pkgname);
+            Manifest man = h.getManifest();
+            if (pkg != null) {
+                // package found, so check package sealing
+                boolean ok;
+                if (pkg.isSealed()) {
+                    // verify that code source URLs are the same
+                    ok = pkg.isSealed(url);
+                } else {
+                    // make sure we are not attempting to seal the package
+                    // at this code source URL
+                    ok = (man == null) || !isSealed(pkgname, man);
+                }
+                if (!ok) {
+                    throw new SecurityException("sealing violation: " + name);
+                }
+            } else {
+                // package not yet defined
+                if (man != null) {
+                    definePackage(pkgname, man, url);
+                } else {
+                    definePackage(pkgname, null, null, null, null, null, null, null);
+                }
+            }
+        }
+
+        // now read the class bytes and define the class
+        byte[] b = h.getBytes();
+        java.security.cert.Certificate[] certs = h.getCertificates();
+        CodeSource cs = new CodeSource(url, certs);
+        return defineClass(name, b, 0, b.length, cs);
+    }
+
+    /**
+     * returns true if the specified package name is sealed according to the
+     * given manifest.
+     */
+    private boolean isSealed(String name, Manifest man) {
+        String path = name.replace('.', '/').concat("/");
+        Attributes attr = man.getAttributes(path);
+        String sealed = null;
+        if (attr != null) {
+            sealed = attr.getValue(Name.SEALED);
+        }
+        if (sealed == null) {
+            if ((attr = man.getMainAttributes()) != null) {
+                sealed = attr.getValue(Name.SEALED);
+            }
+        }
+        return "true".equalsIgnoreCase(sealed);
+    }
+
+    /**
+     * Finds the resource with the specified name.
+     *
+     * @param name the name of the resource
+     * @return a <code>URL</code> for the resource, or <code>null</code>
+     *         if the resource could not be found.
+     */
+    protected URL findResource(final String name) {
+        return (URL) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return finder.findResource(name);
+            }
+        }, acc);
+    }
+
+    /**
+     * Returns an Enumeration of URLs representing all of the resources
+     * having the specified name.
+     *
+     * @param name the resource name
+     * @return an <code>Enumeration</code> of <code>URL</code>s
+     * @throws IOException if an I/O exception occurs
+     */
+    protected Enumeration findResources(final String name) throws IOException {
+        return (Enumeration) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return finder.findResources(name);
+            }
+        }, acc);
+    }
+
+    /**
+     * Returns the absolute path name of a native library. The VM
+     * invokes this method to locate the native libraries that belong
+     * to classes loaded with this class loader. If this method returns
+     * <code>null</code>, the VM searches the library along the path
+     * specified as the <code>java.library.path</code> property.
+     * This method invoke {@link #getLibraryHandle} method to find handle
+     * of this library. If the handle is found and its URL protocol is "file",
+     * the system-dependent absolute library file path is returned.
+     * Otherwise this method returns null. <p>
+     * <p/>
+     * Subclasses can override this method to provide specific approaches
+     * in library searching.
+     *
+     * @param libname the library name
+     * @return the absolute path of the native library
+     * @see System#loadLibrary(String)
+     * @see System#mapLibraryName(String)
+     */
+    protected String findLibrary(String libname) {
+        ResourceHandle md = getLibraryHandle(libname);
+        if (md == null) return null;
+        URL url = md.getURL();
+        if (!"file".equals(url.getProtocol())) return null;
+        return new File(URI.create(url.toString())).getPath();
+    }
+//
+//    /**
+//     * Gets the ResourceHandle object for the specified loaded class.
+//     *
+//     * @param clazz the Class
+//     * @return the ResourceHandle of the Class
+//     */
+//    protected ResourceHandle getClassHandle(Class clazz)
+//    {
+//        return null;
+//    }
+
+    /**
+     * Finds the ResourceHandle object for the class with the specified name.
+     * Unlike <code>findClass()</code>, this method does not load the class.
+     *
+     * @param name the name of the class
+     * @return the ResourceHandle of the class
+     */
+    protected ResourceHandle getClassHandle(final String name) {
+        String path = name.replace('.', '/').concat(".class");
+        return getResourceHandle(path);
+    }
+
+    /**
+     * Finds the ResourceHandle object for the resource with the specified name.
+     *
+     * @param name the name of the resource
+     * @return the ResourceHandle of the resource
+     */
+    protected ResourceHandle getResourceHandle(final String name) {
+        return (ResourceHandle) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return finder.getResource(name);
+            }
+        }, acc);
+    }
+
+    /**
+     * Finds the ResourceHandle object for the native library with the specified
+     * name.
+     * The library name must be '/'-separated path. The last part of this
+     * path is substituted by its system-dependent mapping (using
+     * {@link System#mapLibraryName(String)} method). Next, the
+     * <code>ResourceFinder</code> is used to look for the library as it
+     * was ordinary resource. <p>
+     * <p/>
+     * Subclasses can override this method to provide specific approaches
+     * in library searching.
+     *
+     * @param name the name of the library
+     * @return the ResourceHandle of the library
+     */
+    protected ResourceHandle getLibraryHandle(final String name) {
+        int idx = name.lastIndexOf('/');
+        String path;
+        String simplename;
+        if (idx == -1) {
+            path = "";
+            simplename = name;
+        } else if (idx == name.length() - 1) { // name.endsWith('/')
+            throw new IllegalArgumentException(name);
+        } else {
+            path = name.substring(0, idx + 1); // including '/'
+            simplename = name.substring(idx + 1);
+        }
+        return getResourceHandle(path + System.mapLibraryName(simplename));
+    }
+
+    /**
+     * Returns an Enumeration of ResourceHandle objects representing all of the
+     * resources having the specified name.
+     *
+     * @param name the name of the resource
+     * @return the ResourceHandle of the resource
+     */
+    protected Enumeration getResourceHandles(final String name) {
+        return (Enumeration) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return finder.getResources(name);
+            }
+        }, acc);
+    }
+
+    /**
+     * Defines a new package by name in this ClassLoader. The attributes
+     * contained in the specified Manifest will be used to obtain package
+     * version and sealing information. For sealed packages, the additional
+     * URL specifies the code source URL from which the package was loaded.
+     *
+     * @param name the package name
+     * @param man the Manifest containing package version and sealing
+     * information
+     * @param url the code source url for the package, or null if none
+     * @return the newly defined Package object
+     * @throws IllegalArgumentException if the package name duplicates
+     * an existing package either in this class loader or one
+     * of its ancestors
+     */
+    protected Package definePackage(String name, Manifest man, URL url) throws IllegalArgumentException {
+        String path = name.replace('.', '/').concat("/");
+        URL sealBase = null;
+
+        Attributes entryAttr = man.getAttributes(path);
+        Attributes mainAttr = man.getMainAttributes();
+
+        String specTitle = getManifestVal(entryAttr, mainAttr, Name.SPECIFICATION_TITLE);
+        String specVersion = getManifestVal(entryAttr, mainAttr, Name.SPECIFICATION_VERSION);
+        String specVendor = getManifestVal(entryAttr, mainAttr, Name.SPECIFICATION_VENDOR);
+        String implTitle = getManifestVal(entryAttr, mainAttr, Name.IMPLEMENTATION_TITLE);
+        String implVersion = getManifestVal(entryAttr, mainAttr, Name.IMPLEMENTATION_VERSION);
+        String implVendor = getManifestVal(entryAttr, mainAttr, Name.IMPLEMENTATION_VENDOR);
+        String sealed = getManifestVal(entryAttr, mainAttr, Name.SEALED);
+
+        if ("true".equalsIgnoreCase(sealed)) sealBase = url;
+
+        return definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
+    }
+
+    private static String getManifestVal(Attributes entryAttr, Attributes mainAttr, Name key) {
+        String val = null;
+        if (entryAttr != null) {
+            val = entryAttr.getValue(key);
+        }
+        if (val == null && mainAttr != null) {
+            val = mainAttr.getValue(key);
+        }
+        return val;
+    }
+
+
+    public static URLStreamHandler getDefaultURLStreamHandler(String protocol) {
+
+//        String pkgList = (String) java.security.AccessController.doPrivileged(
+//                new GetPropertyAction("java.protocol.handler.pkgs", ""));
+
+        String pkgList = System.getProperty("java.protocol.handler.pkgs", "");
+
+        if (pkgList.length() > 0) pkgList += "|";
+        pkgList += "sun.net.www.protocol";
+
+        StringTokenizer tokenizer = new StringTokenizer(pkgList, "|");
+
+        while (tokenizer.hasMoreTokens()) {
+            String pkg = tokenizer.nextToken().trim();
+            try {
+                String clname = pkg + "." + protocol + ".Handler";
+                Class cls;
+                try {
+                    cls = Class.forName(clname);
+                } catch (ClassNotFoundException e) {
+                    cls = Class.forName(clname, false, null);
+                }
+                return (URLStreamHandler) cls.newInstance();
+            } catch (Exception e) {
+                // ignore and try next one
+            }
+        }
+        return null;
+    }
+
+}

Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceFinder.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceFinder.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceFinder.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceFinder.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,63 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  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.apache.geronimo.kernel.classloader;
+
+import java.net.URL;
+import java.util.Enumeration;
+
+/**
+ * Abstraction of resource searching policy. Given resource name, the resource
+ * finder performs implementation-specific lookup, and, if it is able to locate
+ * the resource, returns the {@link ResourceHandle handle(s)} or URL(s) of it.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface ResourceFinder {
+    /**
+     * Find the resource by name and return URL of it if found.
+     *
+     * @param name the resource name
+     * @return resource URL or null if resource was not found
+     */
+    public URL findResource(String name);
+
+    /**
+     * Find all resources with given name and return enumeration of their URLs.
+     *
+     * @param name the resource name
+     * @return enumeration of resource URLs (possibly empty).
+     */
+    public Enumeration findResources(String name);
+
+    /**
+     * Get the resource by name and, if found, open connection to it and return
+     * the {@link ResourceHandle handle} of it.
+     *
+     * @param name the resource name
+     * @return resource handle or null if resource was not found
+     */
+    public ResourceHandle getResource(String name);
+
+    /**
+     * Get all resources with given name and return enumeration of their
+     * {@link ResourceHandle resource handles}.
+     *
+     * @param name the resource name
+     * @return enumeration of resource handles (possibly empty).
+     */
+    public Enumeration getResources(String name);
+}

Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceHandle.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceHandle.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceHandle.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceHandle.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,154 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  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.apache.geronimo.kernel.classloader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.cert.Certificate;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+/**
+ * This class represents a handle (a connection) to some resource, which may
+ * be a class, native library, text file, image, etc. Handles are returned
+ * by {@link ResourceLoader}'s <i>get</i> methods.
+ * Having the resource handle, in addition to accessing the resource data
+ * (using methods {@link #getInputStream} or {@link #getBytes}) as well as
+ * access resource metadata, such as attributes, certificates, etc.
+ * <p/>
+ * As soon as the handle is no longer in use, it should be explicitly
+ * {@link #close}d, similarly to I/O streams.
+ *
+ * @version $Rev$ $Date$
+ */
+public abstract class ResourceHandle {
+
+    /**
+     * Return the name of the resource. The name is a "/"-separated path
+     * name that identifies the resource.
+     */
+    public abstract String getName();
+
+    /**
+     * Returns the URL of the resource.
+     */
+    public abstract URL getURL();
+
+    /**
+     * Returns the CodeSource URL for the class or resource.
+     */
+    public abstract URL getCodeSourceURL();
+
+    /**
+     * Returns and InputStream for reading this resource data.
+     */
+    public abstract InputStream getInputStream() throws IOException;
+
+    /**
+     * Returns the length of this resource data, or -1 if unknown.
+     */
+    public abstract int getContentLength();
+
+    /**
+     * Returns this resource data as an array of bytes.
+     */
+    public byte[] getBytes() throws IOException {
+        byte[] buf;
+        InputStream in = getInputStream();
+        int len = getContentLength();
+        try {
+            if (len != -1) {
+                // read exactly len bytes
+                buf = new byte[len];
+                while (len > 0) {
+                    int read = in.read(buf, buf.length - len, len);
+                    if (read < 0) {
+                        throw new IOException("unexpected EOF");
+                    }
+                    len -= read;
+                }
+            } else {
+                // read until end of stream is reached
+                buf = new byte[2048];
+                int total = 0;
+                while ((len = in.read(buf, total, buf.length - total)) >= 0) {
+                    total += len;
+                    if (total >= buf.length) {
+                        byte[] aux = new byte[total * 2];
+                        System.arraycopy(buf, 0, aux, 0, total);
+                        buf = aux;
+                    }
+                }
+                // trim if necessary
+                if (total != buf.length) {
+                    byte[] aux = new byte[total];
+                    System.arraycopy(buf, 0, aux, 0, total);
+                    buf = aux;
+                }
+            }
+        } finally {
+            in.close();
+        }
+        return buf;
+    }
+
+    /**
+     * Returns the Manifest of the JAR file from which this resource
+     * was loaded, or null if none.
+     */
+    public Manifest getManifest() throws IOException {
+        return null;
+    }
+
+    /**
+     * Return the Certificates of the resource, or null if none.
+     */
+    public Certificate[] getCertificates() {
+        return null;
+    }
+
+    /**
+     * Return the Attributes of the resource, or null if none.
+     */
+    public Attributes getAttributes() throws IOException {
+        Manifest m = getManifest();
+        if (m == null) return null;
+        String entry = getURL().getFile();
+        return m.getAttributes(entry);
+    }
+
+    /**
+     * Closes a connection to the resource indentified by this handle. Releases
+     * any I/O objects associated with the handle.
+     */
+    public void close() {
+    }
+//
+//    /**
+//     * Ensures that {@link #release()} method is eventually called when this
+//     * object is finalized.
+//     */
+//    protected void finalize() throws Throwable {
+//        super.finalize();
+//        release();
+//    }
+
+    public String toString() {
+        return "[" + getName() + ": " + getURL() + "; code source: " + getCodeSourceURL() + "]";
+    }
+}

Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceLoader.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceLoader.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceLoader.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceLoader.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,854 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  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.apache.geronimo.kernel.classloader;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.security.Permission;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import org.apache.geronimo.kernel.classloader.jarcache.JarUrlStreamHandler;
+
+/**
+ * This class aids in accessing remote resources referred by URLs.
+ * The URLs are resolved into {@link ResourceHandle resource handles} which can
+ * be used to access the resources directly and uniformly, regardless of the
+ * URL type. The resource loader
+ * is particularly useful when dealing with resources fetched from JAR files.
+ * It maintains the cache of opened JAR files (so that so that
+ * subsequent requests for resources coming from the same base Jar file can be
+ * handled efficiently). It fully supports JAR class-path (references from
+ * a JAR file to other JAR files) and JAR index (JAR containing information
+ * about content of other JARs). The caching policy of downloaded JAR files can
+ * be customized via the constructor parameter <code>jarHandler</code>; the
+ * default policy is to use separate cache per each
+ * ResourceLoader instance.
+ * <p/>
+ * This class is particularly useful when implementing custom class loaders.
+ * It provides bottom-level resource fetching functionality. By using one of
+ * the loader methods which accepts an array of URLs, it
+ * is straightforward to implement class-path searching similar to that
+ * of URLClassLoader, with JAR dependencies (Class-Path)
+ * properly resolved and with JAR indexes properly handled.
+ * <p/>
+ * This class provides two set of methods: <i>get</i> methods that return
+ * {@link ResourceHandle}s (or their enumerations) and <i>find</i> methods that
+ * return URLs (or their enumerations). If the resource is not found,
+ * null (or empty enumeration) is returned. Resource handles represent a
+ * connection to the resource and they should be closed when done
+ * processing, just like input streams. In contrast, find methods return
+ * URLs that can be used to open multiple connections to the resource. In
+ * typical class loader applications, when a single retrieval is sufficient,
+ * it is preferable to use <i>get</i> methods since they pose slightly smaller
+ * communication overhead.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ResourceLoader {
+
+    private static final String JAR_INDEX_ENTRY_NAME = "META-INF/INDEX.LIST";
+
+    final URLStreamHandler jarHandler;
+
+    final Map url2jarInfo = new HashMap();
+
+    /**
+     * Constructs new ResourceLoadeer with default JAR caching policy, that is,
+     * to create and use separate cache for this ResourceLoader instance.
+     */
+    public ResourceLoader() {
+        this(new JarUrlStreamHandler());
+    }
+
+    /**
+     * Constructs new ResourceLoader with specified JAR file handler which can
+     * implement custom JAR caching policy.
+     *
+     * @param jarHandler JAR file handler
+     */
+    public ResourceLoader(URLStreamHandler jarHandler) {
+        this.jarHandler = jarHandler;
+    }
+
+    /**
+     * Gets resource with given name at the given source URL. If the URL points
+     * to a directory, the name is the file path relative to this directory.
+     * If the URL points to a JAR file, the name identifies an entry in that
+     * JAR file. If the URL points to a JAR file, the resource is not found
+     * in that JAR file, and the JAR file has Class-Path attribute, the
+     * JAR files identified in the Class-Path are also searched for the
+     * resource.
+     *
+     * @param source the source URL
+     * @param name the resource name
+     * @return handle representing the resource, or null if not found
+     */
+    public ResourceHandle getResource(URL source, String name) {
+        return getResource(source, name, new HashSet(), null);
+    }
+
+    /**
+     * Gets resource with given name at the given search path. The path is
+     * searched iteratively, one URL at a time. If the URL points
+     * to a directory, the name is the file path relative to this directory.
+     * If the URL points to the JAR file, the name identifies an entry in that
+     * JAR file. If the URL points to the JAR file, the resource is not found
+     * in that JAR file, and the JAR file has Class-Path attribute, the
+     * JAR files identified in the Class-Path are also searched for the
+     * resource.
+     *
+     * @param sources the source URL path
+     * @param name the resource name
+     * @return handle representing the resource, or null if not found
+     */
+    public ResourceHandle getResource(URL[] sources, String name) {
+        Set visited = new HashSet();
+        for (int i = 0; i < sources.length; i++) {
+            ResourceHandle h = getResource(sources[i], name, visited, null);
+            if (h != null) return h;
+        }
+        return null;
+    }
+
+    /**
+     * Gets all resources with given name at the given source URL. If the URL
+     * points to a directory, the name is the file path relative to this
+     * directory. If the URL points to a JAR file, the name identifies an entry
+     * in that JAR file. If the URL points to a JAR file, the resource is not
+     * found in that JAR file, and the JAR file has Class-Path attribute, the
+     * JAR files identified in the Class-Path are also searched for the
+     * resource.
+     * <p/>
+     * The search is lazy, that is, "find next resource" operation is triggered
+     * by calling {@link Enumeration#hasMoreElements}.
+     *
+     * @param source the source URL
+     * @param name the resource name
+     * @return enumeration of resource handles representing the resources
+     */
+    public Enumeration getResources(URL source, String name) {
+        return new ResourceEnumeration(new URL[]{source}, name, false);
+    }
+
+    /**
+     * Gets all resources with given name at the given search path. If the URL
+     * points to a directory, the name is the file path relative to this
+     * directory. If the URL points to a JAR file, the name identifies an entry
+     * in that JAR file. If the URL points to a JAR file, the resource is not
+     * found in that JAR file, and the JAR file has Class-Path attribute, the
+     * JAR files identified in the Class-Path are also searched for the
+     * resource.
+     * <p/>
+     * The search is lazy, that is, "find next resource" operation is triggered
+     * by calling {@link Enumeration#hasMoreElements}.
+     *
+     * @param sources the source URL path
+     * @param name the resource name
+     * @return enumeration of resource handles representing the resources
+     */
+    public Enumeration getResources(URL[] sources, String name) {
+        return new ResourceEnumeration((URL[]) sources.clone(), name, false);
+    }
+
+//    public URL[] getResolvedSearchPath(URL[] sources) {
+//        List path = new ArrayList();
+//        for (int i=0; i<sources.length; i++) {
+//            appendToResolvedSearchPath(path, sources[i]);
+//        }
+//        return (URL[])path.toArray(new URL[path.size()]);
+//    }
+//
+//    private void appendToResolvedSearchPath(List path, final URL source) {
+//        if (isDir(source)) {
+//            path.add(source);
+//        }
+//        else {
+//            // URL
+//            try {
+//                getJarInfo(source).appendToResolvedClassPath(path);
+//            }
+//            catch (MalformedURLException e) {}
+//        }
+//    }
+//
+
+    private ResourceHandle getResource(final URL source, String name, Set visitedJars, Set skip) {
+        name = ResourceUtils.canonizePath(name);
+        if (isDir(source)) {
+            // plain resource
+            final URL url;
+            try {
+                // escape spaces etc. to make sure url is well-formed
+                URI relUri = new URI(null, null, null, -1, name, null, null);
+                url = new URL(source, relUri.getRawPath());
+            }
+            catch (URISyntaxException e) {
+                throw new IllegalArgumentException("Illegal resource name: " + name);
+            }
+            catch (MalformedURLException e) {
+                return null;
+            }
+
+            if (skip != null && skip.contains(url)) return null;
+            final URLConnection conn;
+            try {
+                conn = url.openConnection();
+                conn.getInputStream();
+            }
+            catch (IOException e) {
+                return null;
+            }
+            final String finalName = name;
+            return new ResourceHandle() {
+                public String getName() {
+                    return finalName;
+                }
+
+                public URL getURL() {
+                    return url;
+                }
+
+                public URL getCodeSourceURL() {
+                    return source;
+                }
+
+                public InputStream getInputStream() throws IOException {
+                    return conn.getInputStream();
+                }
+
+                public int getContentLength() {
+                    return conn.getContentLength();
+                }
+
+                public void close() {
+                    try {
+                        getInputStream().close();
+                    } catch (IOException e) {
+                    }
+                }
+            };
+        } else {
+            // we deal with a JAR file here
+            try {
+                return getJarInfo(source).getResource(name, visitedJars, skip);
+            }
+            catch (MalformedURLException e) {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Fined resource with given name at the given source URL. If the URL points
+     * to a directory, the name is the file path relative to this directory.
+     * If the URL points to a JAR file, the name identifies an entry in that
+     * JAR file. If the URL points to a JAR file, the resource is not found
+     * in that JAR file, and the JAR file has Class-Path attribute, the
+     * JAR files identified in the Class-Path are also searched for the
+     * resource.
+     *
+     * @param source the source URL
+     * @param name the resource name
+     * @return URL of the resource, or null if not found
+     */
+    public URL findResource(URL source, String name) {
+        return findResource(source, name, new HashSet(), null);
+    }
+
+    /**
+     * Finds resource with given name at the given search path. The path is
+     * searched iteratively, one URL at a time. If the URL points
+     * to a directory, the name is the file path relative to this directory.
+     * If the URL points to the JAR file, the name identifies an entry in that
+     * JAR file. If the URL points to the JAR file, the resource is not found
+     * in that JAR file, and the JAR file has Class-Path attribute, the
+     * JAR files identified in the Class-Path are also searched for the
+     * resource.
+     *
+     * @param sources the source URL path
+     * @param name the resource name
+     * @return URL of the resource, or null if not found
+     */
+    public URL findResource(URL[] sources, String name) {
+        Set visited = new HashSet();
+        for (int i = 0; i < sources.length; i++) {
+            URL url = findResource(sources[i], name, visited, null);
+            if (url != null) return url;
+        }
+        return null;
+    }
+
+    /**
+     * Finds all resources with given name at the given source URL. If the URL
+     * points to a directory, the name is the file path relative to this
+     * directory. If the URL points to a JAR file, the name identifies an entry
+     * in that JAR file. If the URL points to a JAR file, the resource is not
+     * found in that JAR file, and the JAR file has Class-Path attribute, the
+     * JAR files identified in the Class-Path are also searched for the
+     * resource.
+     * <p/>
+     * The search is lazy, that is, "find next resource" operation is triggered
+     * by calling {@link Enumeration#hasMoreElements}.
+     *
+     * @param source the source URL
+     * @param name the resource name
+     * @return enumeration of URLs of the resources
+     */
+    public Enumeration findResources(URL source, String name) {
+        return new ResourceEnumeration(new URL[]{source}, name, true);
+    }
+
+    /**
+     * Finds all resources with given name at the given search path. If the URL
+     * points to a directory, the name is the file path relative to this
+     * directory. If the URL points to a JAR file, the name identifies an entry
+     * in that JAR file. If the URL points to a JAR file, the resource is not
+     * found in that JAR file, and the JAR file has Class-Path attribute, the
+     * JAR files identified in the Class-Path are also searched for the
+     * resource.
+     * <p/>
+     * The search is lazy, that is, "find next resource" operation is triggered
+     * by calling {@link Enumeration#hasMoreElements}.
+     *
+     * @param sources the source URL path
+     * @param name the resource name
+     * @return enumeration of URLs of the resources
+     */
+    public Enumeration findResources(URL[] sources, String name) {
+        return new ResourceEnumeration((URL[]) sources.clone(), name, true);
+    }
+
+
+    private URL findResource(final URL source, String name, Set visitedJars, Set skip) {
+        URL url;
+        name = ResourceUtils.canonizePath(name);
+        if (isDir(source)) {
+            // plain resource
+            try {
+                url = new URL(source, name);
+            }
+            catch (MalformedURLException e) {
+                return null;
+            }
+            if (skip != null && skip.contains(url)) return null;
+            final URLConnection conn;
+            try {
+                conn = url.openConnection();
+                if (conn instanceof HttpURLConnection) {
+                    HttpURLConnection httpConn = (HttpURLConnection) conn;
+                    httpConn.setRequestMethod("HEAD");
+                    if (httpConn.getResponseCode() >= 400) return null;
+                } else {
+                    conn.getInputStream().close();
+                }
+            }
+            catch (IOException e) {
+                return null;
+            }
+            return url;
+        } else {
+            // we deal with a JAR file here
+            try {
+                ResourceHandle rh = getJarInfo(source).getResource(name, visitedJars, skip);
+                return (rh != null) ? rh.getURL() : null;
+            }
+            catch (MalformedURLException e) {
+                return null;
+            }
+        }
+
+    }
+
+    /**
+     * Test whether given URL points to a directory. URL is deemed to point
+     * to a directory if has non-null "file" component ending with "/".
+     *
+     * @param url the URL to test
+     * @return true if the URL points to a directory, false otherwise
+     */
+    protected static boolean isDir(URL url) {
+        String file = url.getFile();
+        return (file != null && file.endsWith("/"));
+    }
+
+    private static class JarInfo {
+        final ResourceLoader loader;
+        final URL source;         // "real" jar file path
+        final URL base;           // "jar:{base}!/"
+        JarFile jar;
+        boolean resolved;
+        Permission perm;
+        URL[] classPath;
+        String[] index;
+        Map package2url;
+
+        JarInfo(ResourceLoader loader, URL source) throws MalformedURLException {
+            this.loader = loader;
+            this.source = source;
+            if (source.getProtocol().equals("jar")) {
+                this.base = new URL("jar", "", -1, source.getPath() + "!/", loader.jarHandler);
+            } else {
+                this.base = new URL("jar", "", -1, source + "!/", loader.jarHandler);
+            }
+        }
+
+        public ResourceHandle getResource(String name) {
+            return getResource(name, new HashSet());
+        }
+
+        ResourceHandle getResource(String name, Set visited) {
+            return getResource(name, visited, null);
+        }
+
+//        void appendToResolvedClassPath(List path) {
+//            path.add(source);
+//            URL[] classPath;
+//            synchronized (this) {
+//                classPath = this.classPath;
+//            }
+//            if (classPath == null) return;
+//            for (int i=0; i<classPath.length; i++) {
+//                URL url = classPath[i];
+//                if (path.contains(url)) continue;
+//                loader.appendToResolvedSearchPath(path, url);
+//            }
+//        }
+
+        ResourceHandle getResource(String name, Set visited, Set skip) {
+            visited.add(source);
+            URL url;
+            try {
+                // escape spaces etc. to make sure url is well-formed
+                URI relUri = new URI(null, null, null, -1, name, null, null);
+                url = new URL(base, relUri.getRawPath());
+            }
+            catch (URISyntaxException e) {
+                throw new IllegalArgumentException("Illegal resource name: " +
+                        name);
+            }
+            catch (MalformedURLException e) {
+                return null;
+            }
+            try {
+                JarFile jfile = getJarFileIfPossiblyContains(name);
+                if (jfile != null) {
+                    JarEntry jentry = jar.getJarEntry(name);
+                    if (jentry != null && (skip == null || !skip.contains(url))) {
+                        return new JarResourceHandle(jfile, jentry, url, source);
+                    }
+                }
+            }
+            catch (IOException e) {
+                return null;
+            }
+
+            // not in here, but check also the dependencies
+            URL[] dependencies;
+            synchronized (this) {
+                if (package2url != null) {
+                    int idx = name.lastIndexOf("/");
+                    String prefix = (idx > 0) ? name.substring(0, idx) : name;
+                    dependencies = (URL[]) package2url.get(prefix);
+                } else {
+                    // classpath might be null only if it was a dependency of
+                    // an indexed JAR with out-of-date index (the index brought
+                    // us here but resource was not found in the JAR). But this
+                    // (out-of-sync index) should be captured by
+                    // getJarFileIfPossiblyContains.
+                    assert classPath != null;
+                    dependencies = classPath;
+                }
+            }
+
+            if (dependencies == null) return null;
+
+            for (int i = 0; i < dependencies.length; i++) {
+                URL cpUrl = dependencies[i];
+                if (visited.contains(cpUrl)) continue;
+                JarInfo depJInfo;
+                try {
+                    depJInfo = loader.getJarInfo(cpUrl);
+                    ResourceHandle rh = depJInfo.getResource(name, visited, skip);
+                    if (rh != null) return rh;
+                }
+                catch (MalformedURLException e) {
+                    // continue with other URLs
+                }
+            }
+
+            // not found
+            return null;
+        }
+
+        synchronized void setIndex(List newIndex) {
+            if (jar != null) {
+                // already loaded; no need for index
+                return;
+            }
+            if (index != null) {
+                // verification - previously declared content must remain there
+                Set violating = new HashSet(Arrays.asList(index));
+                violating.removeAll(newIndex);
+                if (!violating.isEmpty()) {
+                    throw new RuntimeException("Invalid JAR index: " +
+                            "the following entries were previously declared, but " +
+                            "they are not present in the new index: " +
+                            violating.toString());
+                }
+            }
+            this.index = (String[]) newIndex.toArray(new String[newIndex.size()]);
+            Arrays.sort(this.index);
+        }
+
+        public JarFile getJarFileIfPossiblyContains(String name) throws IOException {
+            Map indexes;
+            synchronized (this) {
+                if (jar != null) {
+                    // make sure we would be allowed to load it ourselves
+                    SecurityManager security = System.getSecurityManager();
+                    if (security != null) {
+                        security.checkPermission(perm);
+                    }
+
+                    // other thread may still be updating indexes of dependent
+                    // JAR files
+                    try {
+                        while (!resolved) {
+                            wait();
+                        }
+                    }
+                    catch (InterruptedException e) {
+                        throw new IOException("Interrupted");
+                    }
+                    return jar;
+                }
+
+                if (index != null) {
+                    // we may be able to respond negatively w/o loading the JAR
+                    int pos = name.lastIndexOf('/');
+                    if (pos > 0) name = name.substring(0, pos);
+                    if (Arrays.binarySearch(index, name) < 0) return null;
+                }
+
+                // load the JAR
+                JarURLConnection conn = (JarURLConnection) base.openConnection();
+                this.perm = conn.getPermission();
+                JarFile jar = conn.getJarFile();
+                // conservatively check if index is accurate, that is, does not
+                // contain entries which are not in the JAR file
+                if (index != null) {
+                    Set indices = new HashSet(Arrays.asList(index));
+                    Enumeration entries = jar.entries();
+                    while (entries.hasMoreElements()) {
+                        JarEntry entry = (JarEntry) entries.nextElement();
+                        String indexEntry = entry.getName();
+                        // for non-top, find the package name
+                        int pos = indexEntry.lastIndexOf('/');
+                        if (pos > 0) {
+                            indexEntry = indexEntry.substring(0, pos);
+                        }
+                        indices.remove(indexEntry);
+                    }
+                    if (!indices.isEmpty()) {
+                        throw new RuntimeException("Invalid JAR index: " +
+                                "the following entries not found in JAR: " +
+                                indices);
+                    }
+                }
+                this.jar = jar;
+
+                this.classPath = parseClassPath(jar, source, loader.jarHandler);
+
+                indexes = parseJarIndex(this.source, jar);
+                indexes.remove(this.source.toExternalForm());
+
+                if (!indexes.isEmpty()) {
+                    this.package2url = package2url(indexes);
+                }
+            }
+            // just loaded the JAR - need to resolve the index
+            try {
+                for (Iterator itr = indexes.entrySet().iterator(); itr.hasNext();) {
+                    Map.Entry entry = (Map.Entry) itr.next();
+                    URL url = (URL) entry.getKey();
+                    if (url.toExternalForm().equals(this.source.toExternalForm())) {
+                        continue;
+                    }
+                    List index = (List) entry.getValue();
+                    loader.getJarInfo(url).setIndex(index);
+                }
+            }
+            finally {
+                synchronized (this) {
+                    this.resolved = true;
+                    notifyAll();
+                }
+            }
+            return jar;
+        }
+    }
+
+    private static Map package2url(Map indexes) {
+        Map prefix2url = new HashMap();
+        for (Iterator i = indexes.entrySet().iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry) i.next();
+            URL url = (URL) entry.getKey();
+            List idx = (List) entry.getValue();
+            for (Iterator j = idx.iterator(); j.hasNext();) {
+                String prefix = (String) j.next();
+                List prefixList = (List) prefix2url.get(prefix);
+                if (prefixList == null) {
+                    prefixList = new ArrayList();
+                    prefix2url.put(prefix, prefixList);
+                }
+                prefixList.add(url);
+            }
+        }
+
+        // replace lists with arrays
+        for (Iterator i = prefix2url.entrySet().iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry) i.next();
+            List list = (List) entry.getValue();
+            entry.setValue(list.toArray(new URL[list.size()]));
+        }
+        return prefix2url;
+    }
+
+    private JarInfo getJarInfo(URL url) throws MalformedURLException {
+        JarInfo jinfo;
+        synchronized (url2jarInfo) {
+            // fix: no longer use url.equals, since it distinguishes between
+            // "" and null in the host part of file URLs. The ""-type urls are
+            // correct but "null"-type ones come from file.toURI().toURL()
+            // on 1.4.1. (It is fixed in 1.4.2)
+            jinfo = (JarInfo) url2jarInfo.get(url.toExternalForm());
+            if (jinfo == null) {
+                jinfo = new JarInfo(this, url);
+                url2jarInfo.put(url.toExternalForm(), jinfo);
+            }
+        }
+        return jinfo;
+    }
+
+    private static class JarResourceHandle extends ResourceHandle {
+        final JarFile jar;
+        final JarEntry jentry;
+        final URL url;
+        final URL codeSource;
+
+        JarResourceHandle(JarFile jar, JarEntry jentry, URL url, URL codeSource) {
+            this.jar = jar;
+            this.jentry = jentry;
+            this.url = url;
+            this.codeSource = codeSource;
+        }
+
+        public String getName() {
+            return jentry.getName();
+        }
+
+        public URL getURL() {
+            return url;
+        }
+
+        public URL getCodeSourceURL() {
+            return codeSource;
+        }
+
+        public InputStream getInputStream() throws IOException {
+            return jar.getInputStream(jentry);
+        }
+
+        public int getContentLength() {
+            return (int) jentry.getSize();
+        }
+
+        public Manifest getManifest() throws IOException {
+            return jar.getManifest();
+        }
+
+        public Attributes getAttributes() throws IOException {
+            return jentry.getAttributes();
+        }
+
+        public Certificate[] getCertificates() {
+            return jentry.getCertificates();
+        }
+
+        public void close() {
+        }
+    }
+
+    private static Map parseJarIndex(URL cxt, JarFile jar) throws IOException {
+        JarEntry entry = jar.getJarEntry(JAR_INDEX_ENTRY_NAME);
+        if (entry == null) return Collections.EMPTY_MAP;
+        InputStream is = jar.getInputStream(entry);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+
+        Map result = new LinkedHashMap();
+
+        String line;
+
+        // skip version-info
+        do {
+            line = reader.readLine();
+        }
+        while (line != null && line.trim().length() > 0);
+
+        URL currentURL;
+        List currentList = null;
+        while (true) {
+            // skip the blank line
+            line = reader.readLine();
+            if (line == null) {
+                return result;
+            }
+
+            currentURL = new URL(cxt, line);
+            currentList = new ArrayList();
+            result.put(currentURL, currentList);
+
+            while (true) {
+                line = reader.readLine();
+                if (line == null || line.trim().length() == 0) break;
+                currentList.add(line);
+            }
+        }
+    }
+
+    private static URL[] parseClassPath(JarFile jar, URL source, URLStreamHandler jarHandler) throws IOException {
+        Manifest man = jar.getManifest();
+        if (man == null) return new URL[0];
+        Attributes attr = man.getMainAttributes();
+        if (attr == null) return new URL[0];
+        String cp = attr.getValue(Attributes.Name.CLASS_PATH);
+        if (cp == null) return new URL[0];
+        StringTokenizer tokenizer = new StringTokenizer(cp);
+        List cpList = new ArrayList();
+        URI sourceURI;
+        if ("jar".equals(source.getProtocol())) {
+            sourceURI = URI.create(source.getPath());
+        } else {
+            sourceURI = URI.create(source.toString());
+        }
+        while (tokenizer.hasMoreTokens()) {
+            String token = tokenizer.nextToken();
+            try {
+                try {
+                    URI uri = new URI(token);
+                    if (!uri.isAbsolute()) {
+                        uri = sourceURI.resolve(uri);
+                    }
+                    if ("jar".equals(source.getProtocol())) {
+                        cpList.add(new URL("jar", null, -1, uri.toString(), jarHandler));
+                    } else {
+                        cpList.add(uri.toURL());
+                    }
+                }
+                catch (URISyntaxException e) {
+                    // tolerate malformed URIs for backward-compatibility
+                    URL url = new URL(source, token);
+                    cpList.add(url);
+                }
+            }
+            catch (MalformedURLException e) {
+                throw new IOException(e.getMessage());
+            }
+        }
+        return (URL[]) cpList.toArray(new URL[cpList.size()]);
+    }
+
+    private class ResourceEnumeration implements Enumeration {
+        final URL[] urls;
+        final String name;
+        final boolean findOnly;
+        int idx;
+        Object next;
+        Set previousURLs = new HashSet();
+
+        ResourceEnumeration(URL[] urls, String name, boolean findOnly) {
+            this.urls = urls;
+            this.name = name;
+            this.findOnly = findOnly;
+            this.idx = 0;
+        }
+
+        public boolean hasMoreElements() {
+            fetchNext();
+            return (next != null);
+        }
+
+        public Object nextElement() {
+            fetchNext();
+            Object next = this.next;
+            this.next = null;
+            if (next == null) throw new NoSuchElementException();
+            return next;
+        }
+
+        private void fetchNext() {
+            if (next != null) return;
+            while (idx < urls.length) {
+                Object found;
+                if (findOnly) {
+                    URL url = findResource(urls[idx], name, new HashSet(), previousURLs);
+                    if (url != null) {
+                        previousURLs.add(url);
+                        next = url;
+                        return;
+                    }
+                } else {
+                    ResourceHandle h = getResource(urls[idx], name, new HashSet(),
+                            previousURLs);
+                    if (h != null) {
+                        previousURLs.add(h.getURL());
+                        next = h;
+                        return;
+                    }
+                }
+                idx++;
+            }
+        }
+    }
+}

Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceUtils.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceUtils.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceUtils.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/ResourceUtils.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,139 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  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.apache.geronimo.kernel.classloader;
+
+import java.net.URI;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Utility methods related to remote resource access.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ResourceUtils {
+
+    private static final Pattern dotInMiddlePattern = Pattern.compile("/\\./");
+    private static final Pattern dotAtBegPattern = Pattern.compile("^\\./");
+    private static final Pattern dotAtEndPattern = Pattern.compile("/\\.$");
+    private static final Pattern multipleSlashPattern = Pattern.compile("//+");
+    private static final Pattern initialParentPattern = Pattern.compile("/?(\\.\\./)*");
+    private static final Pattern embeddedParentPattern = Pattern.compile("[^/]+/\\.\\./");
+    private static final Pattern trailingParentPattern = Pattern.compile("[^/]+/\\.\\.$");
+    private static final Pattern initialAbsParentPattern = Pattern.compile("^/(\\.\\./)+");
+    private static final Pattern initialAbsSingleParentPattern = Pattern.compile("^/\\.\\.$");
+
+    private ResourceUtils() {
+    }
+
+    /**
+     * Checks if the URI points to the local file.
+     *
+     * @param uri the uri to check
+     * @return true if the URI points to a local file
+     */
+    public static boolean isLocalFile(URI uri) {
+        if (!uri.isAbsolute()) return false;
+        if (uri.isOpaque()) return false;
+        if (!"file".equalsIgnoreCase(uri.getScheme())) return false;
+        if (uri.getAuthority() != null) return false;
+        if (uri.getFragment() != null) return false;
+        if (uri.getQuery() != null) return false;
+        if ("".equals(uri.getPath())) return false;
+        return true;
+    }
+
+    /**
+     * Returns the path converted to the canonic form. Examples:
+     * <pre>
+     *  "/aaa/b/"                                      ->  "/aaa/b/"
+     *  "/aaa/b/c/../.."                               ->  "/aaa/"
+     *  "/aaa/../bbb/cc/./.././../dd/eee/fff/."        ->  "/dd/eee/fff/"
+     *  "../aaa/../././bbb/./../ccc/"                  ->  "../ccc/"
+     *  "aa/ddfdd/./sadfd/.././sdafa/../../.././././"  ->  ""
+     *  "./aaa/."                                      ->  "aaa/"
+     *  ".///aa//bb/"                                  ->  "aa/bb/"
+     *  "../../aaa"                                    ->  "../../aaa"
+     *  "/../../aaa"                                   ->  "/aaa"
+     * </pre>
+     */
+    public static String canonizePath(String path) {
+        StringBuffer buf = new StringBuffer(path);
+        StringBuffer aux = new StringBuffer();
+        while (replaceAll(buf, aux, dotInMiddlePattern, "/", 0)) {
+            ;
+        }
+        replaceAll(buf, aux, multipleSlashPattern, "/", 0);
+        replaceFirst(buf, aux, dotAtBegPattern, "", 0);
+        replaceFirst(buf, aux, dotAtEndPattern, "/", 0);
+
+        int pos = 0;
+        while (pos < buf.length()) {
+            pos += skipPrefix(buf, aux, initialParentPattern, pos);
+            if (!replaceFirst(buf, aux, embeddedParentPattern, "", pos)) {
+                break;
+            }
+        }
+
+        replaceFirst(buf, aux, trailingParentPattern, "", pos);
+
+        replaceFirst(buf, aux, initialAbsParentPattern, "/", 0);
+        replaceFirst(buf, aux, initialAbsSingleParentPattern, "/", 0);
+        return buf.toString();
+    }
+
+    public static boolean isAbsolute(String path) {
+        return (path.length() > 0 && path.charAt(0) == '/');
+    }
+
+    private static boolean replaceFirst(StringBuffer buf, StringBuffer aux, Pattern pattern, String replacement, int pos) {
+        boolean chg = false;
+        aux.setLength(0);
+        Matcher matcher = pattern.matcher(buf);
+        if (matcher.find(pos)) {
+            matcher.appendReplacement(aux, replacement);
+            chg = true;
+        }
+        matcher.appendTail(aux);
+        buf.setLength(0);
+        buf.append(aux);
+        return chg;
+    }
+
+    private static boolean replaceAll(StringBuffer buf, StringBuffer aux, Pattern pattern, String replacement, int pos) {
+        aux.setLength(0);
+        Matcher matcher = pattern.matcher(buf);
+        boolean found = matcher.find(pos);
+        boolean chg = found;
+        while (found) {
+            matcher.appendReplacement(aux, replacement);
+            found = matcher.find();
+        }
+        matcher.appendTail(aux);
+        buf.setLength(0);
+        buf.append(aux);
+        return chg;
+    }
+
+    private static int skipPrefix(StringBuffer buf, StringBuffer aux, Pattern pattern, int pos) {
+        Matcher matcher = initialParentPattern.matcher(buf);
+        if (matcher.find(pos)) {
+            return matcher.end() - pos;
+        }
+        return 0;
+    }
+}

Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/URIClassLoader.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/URIClassLoader.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/URIClassLoader.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/URIClassLoader.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,430 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  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.apache.geronimo.kernel.classloader;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLStreamHandler;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.util.Enumeration;
+import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
+import java.util.jar.Manifest;
+
+/**
+ * Equivalent of java.net.URLClassloader but without bugs related to ill-formed
+ * URLs and with customizable JAR caching policy. The standard URLClassLoader
+ * accepts URLs containing spaces and other characters which are forbidden in
+ * the URI syntax, according to the RFC 2396.
+ * As a workaround to this problem, Java escapes and un-escapes URLs in various
+ * arbitrary places; however, this is inconsistent and leads to numerous
+ * problems with URLs referring to local files with spaces in the path. SUN
+ * acknowledges the problem, but refuses to modify the behavior for
+ * compatibility reasons; see Java Bug Parade 4273532, 4466485.
+ * <p/>
+ * Additionally, the JAR caching policy used by
+ * URLClassLoader is system-wide and inflexible: once downloaded JAR files are
+ * never re-downloaded, even if one creates a fresh instance of the class
+ * loader that happens to have the same URL in its search path. In fact, that
+ * policy is a security vulnerability: it is possible to crash any URL class
+ * loader, thus affecting potentially separate part of the system, by creating
+ * URL connection to one of the URLs of that class loader search path and
+ * closing the associated JAR file.
+ * See Java Bug Parade 4405789, 4388666, 4639900.
+ * <p/>
+ * This class avoids these problems by 1) using URIs instead of URLs for the
+ * search path (thus enforcing strict syntax conformance and defining precise
+ * escaping semantics), and 2) using custom URLStreamHandler which ensures
+ * per-classloader JAR caching policy.
+ *
+ * @version $Rev$ $Date$
+ */
+public class URIClassLoader extends URLClassLoader {
+
+    final URIResourceFinder finder;
+    final AccessControlContext acc;
+
+    /**
+     * Creates URIClassLoader with the specified search path.
+     *
+     * @param uris the search path
+     */
+    public URIClassLoader(URI[] uris) {
+        this(uris, (URLStreamHandler) null);
+    }
+
+    /**
+     * Creates URIClassLoader with the specified search path.
+     *
+     * @param uris the search path
+     * @param jarHandler stream handler for JAR files; implements caching policy
+     */
+    public URIClassLoader(URI[] uris, URLStreamHandler jarHandler) {
+        super(new URL[0]);
+        this.finder = new URIResourceFinder(uris, jarHandler);
+        this.acc = AccessController.getContext();
+    }
+
+    /**
+     * Creates URIClassLoader with the specified search path and parent class
+     * loader.
+     *
+     * @param uris the search path
+     * @param parent the parent class loader.
+     */
+    public URIClassLoader(URI[] uris, ClassLoader parent) {
+        this(uris, parent, null);
+    }
+
+    /**
+     * Creates URIClassLoader with the specified search path and parent class
+     * loader.
+     *
+     * @param uris the search path
+     * @param parent the parent class loader.
+     * @param jarHandler stream handler for JAR files; implements caching policy
+     */
+    public URIClassLoader(URI[] uris, ClassLoader parent, URLStreamHandler jarHandler) {
+        super(new URL[0], parent);
+        this.finder = new URIResourceFinder(uris, jarHandler);
+        this.acc = AccessController.getContext();
+    }
+
+    /**
+     * Add specified URI at the end of the search path.
+     *
+     * @param uri the URI to add
+     */
+    protected void addURI(URI uri) {
+        finder.addURI(uri);
+    }
+
+    /**
+     * Add specified URL at the end of the search path.
+     *
+     * @param url the URL to add
+     * @deprecated use addURI
+     */
+    protected void addURL(URL url) {
+        finder.addURI(URI.create(url.toExternalForm()));
+    }
+
+    public URL[] getURLs() {
+        return (URL[]) finder.getUrls().clone();
+    }
+
+//    public URL[] getAllResolvedURLs() {
+//        return finder.getResolvedSearchPath();
+//    }
+
+    /**
+     * Finds and loads the class with the specified name.
+     *
+     * @param name the name of the class
+     * @return the resulting class
+     * @throws ClassNotFoundException if the class could not be found
+     */
+    protected Class findClass(final String name) throws ClassNotFoundException {
+        try {
+            return (Class) AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                public Object run() throws ClassNotFoundException {
+                    String path = name.replace('.', '/').concat(".class");
+                    ResourceHandle h = finder.getResource(path);
+                    if (h != null) {
+                        try {
+                            return defineClass(name, h);
+                        } catch (IOException e) {
+                            throw new ClassNotFoundException(name, e);
+                        }
+                    } else {
+                        throw new ClassNotFoundException(name);
+                    }
+                }
+            }, acc);
+        } catch (java.security.PrivilegedActionException pae) {
+            throw (ClassNotFoundException) pae.getException();
+        }
+    }
+
+    protected Class defineClass(String name, ResourceHandle h) throws IOException {
+        int i = name.lastIndexOf('.');
+        URL url = h.getCodeSourceURL();
+        if (i != -1) { // check package
+            String pkgname = name.substring(0, i);
+            // check if package already loaded
+            Package pkg = getPackage(pkgname);
+            Manifest man = h.getManifest();
+            if (pkg != null) {
+                // package found, so check package sealing
+                boolean ok;
+                if (pkg.isSealed()) {
+                    // verify that code source URLs are the same
+                    ok = pkg.isSealed(url);
+                } else {
+                    // make sure we are not attempting to seal the package
+                    // at this code source URL
+                    ok = (man == null) || !isSealed(pkgname, man);
+                }
+                if (!ok) {
+                    throw new SecurityException("sealing violation: " + name);
+                }
+            } else { // package not yet defined
+                if (man != null) {
+                    definePackage(pkgname, man, url);
+                } else {
+                    definePackage(pkgname, null, null, null, null, null, null, null);
+                }
+            }
+        }
+
+        // now read the class bytes and define the class
+        byte[] b = h.getBytes();
+        java.security.cert.Certificate[] certs = h.getCertificates();
+        CodeSource cs = new CodeSource(url, certs);
+        return defineClass(name, b, 0, b.length, cs);
+    }
+
+    /**
+     * returns true if the specified package name is sealed according to the
+     * given manifest.
+     */
+    private boolean isSealed(String name, Manifest man) {
+        String path = name.replace('.', '/').concat("/");
+        Attributes attr = man.getAttributes(path);
+        String sealed = null;
+        if (attr != null) {
+            sealed = attr.getValue(Name.SEALED);
+        }
+        if (sealed == null) {
+            if ((attr = man.getMainAttributes()) != null) {
+                sealed = attr.getValue(Name.SEALED);
+            }
+        }
+        return "true".equalsIgnoreCase(sealed);
+    }
+
+    /**
+     * Finds the resource with the specified name.
+     *
+     * @param name the name of the resource
+     * @return a <code>URL</code> for the resource, or <code>null</code>
+     *         if the resource could not be found.
+     */
+    public URL findResource(final String name) {
+        return (URL) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return finder.findResource(name);
+            }
+        }, acc);
+    }
+
+    /**
+     * Returns an Enumeration of URLs representing all of the resources
+     * having the specified name.
+     *
+     * @param name the resource name
+     * @return an <code>Enumeration</code> of <code>URL</code>s
+     * @throws IOException if an I/O exception occurs
+     */
+    public Enumeration findResources(final String name) throws IOException {
+        return (Enumeration) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return finder.findResources(name);
+            }
+        }, acc);
+    }
+
+    /**
+     * Returns the absolute path name of a native library. The VM
+     * invokes this method to locate the native libraries that belong
+     * to classes loaded with this class loader. If this method returns
+     * <code>null</code>, the VM searches the library along the path
+     * specified as the <code>java.library.path</code> property.
+     * This method invoke {@link #getLibraryHandle} method to find handle
+     * of this library. If the handle is found and its URL protocol is "file",
+     * the system-dependent absolute library file path is returned.
+     * Otherwise this method returns null. <p>
+     * <p/>
+     * Subclasses can override this method to provide specific approaches
+     * in library searching.
+     *
+     * @param libname the library name
+     * @return the absolute path of the native library
+     * @see System#loadLibrary(String)
+     * @see System#mapLibraryName(String)
+     */
+    protected String findLibrary(String libname) {
+        ResourceHandle md = getLibraryHandle(libname);
+        if (md == null) return null;
+        URL url = md.getURL();
+        if (!"file".equals(url.getProtocol())) return null;
+        return new File(URI.create(url.toString())).getPath();
+    }
+//
+//    /**
+//     * Gets the ResourceHandle object for the specified loaded class.
+//     *
+//     * @param clazz the Class
+//     * @return the ResourceHandle of the Class
+//     */
+//    protected ResourceHandle getClassHandle(Class clazz)
+//    {
+//        return null;
+//    }
+
+    /**
+     * Finds the ResourceHandle object for the class with the specified name.
+     * Unlike <code>findClass()</code>, this method does not load the class.
+     *
+     * @param name the name of the class
+     * @return the ResourceHandle of the class
+     */
+    protected ResourceHandle getClassHandle(final String name) {
+        String path = name.replace('.', '/').concat(".class");
+        return getResourceHandle(path);
+    }
+
+    /**
+     * Finds the ResourceHandle object for the resource with the specified name.
+     *
+     * @param name the name of the resource
+     * @return the ResourceHandle of the resource
+     */
+    protected ResourceHandle getResourceHandle(final String name) {
+        return (ResourceHandle) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return finder.getResource(name);
+            }
+        }, acc);
+    }
+
+    /**
+     * Finds the ResourceHandle object for the native library with the specified
+     * name.
+     * The library name must be '/'-separated path. The last part of this
+     * path is substituted by its system-dependent mapping (using
+     * {@link System#mapLibraryName(String)} method). Next, the
+     * <code>ResourceFinder</code> is used to look for the library as it
+     * was ordinary resource. <p>
+     * <p/>
+     * Subclasses can override this method to provide specific approaches
+     * in library searching.
+     *
+     * @param name the name of the library
+     * @return the ResourceHandle of the library
+     */
+    protected ResourceHandle getLibraryHandle(final String name) {
+        int idx = name.lastIndexOf('/');
+        String path;
+        String simplename;
+        if (idx == -1) {
+            path = "";
+            simplename = name;
+        } else if (idx == name.length() - 1) { // name.endsWith('/')
+            throw new IllegalArgumentException(name);
+        } else {
+            path = name.substring(0, idx + 1); // including '/'
+            simplename = name.substring(idx + 1);
+        }
+        return getResourceHandle(path + System.mapLibraryName(simplename));
+    }
+
+    /**
+     * Returns an Enumeration of ResourceHandle objects representing all of the
+     * resources having the specified name.
+     *
+     * @param name the name of the resource
+     * @return the ResourceHandle of the resource
+     */
+    protected Enumeration getResourceHandles(final String name) {
+        return (Enumeration) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return finder.getResources(name);
+            }
+        }, acc);
+    }
+
+    protected URLStreamHandler getJarHandler() {
+        return finder.loader.jarHandler;
+    }
+
+    private static class URIResourceFinder implements ResourceFinder {
+        URL[] urls;
+        final ResourceLoader loader;
+
+        public URIResourceFinder(URI[] uris, URLStreamHandler jarHandler) {
+            try {
+                this.loader = (jarHandler != null)
+                        ? new ResourceLoader(jarHandler)
+                        : new ResourceLoader();
+                URL[] urls = new URL[uris.length];
+                for (int i = 0; i < uris.length; i++) {
+                    urls[i] = uris[i].toURL();
+                }
+                this.urls = urls;
+            }
+            catch (MalformedURLException e) {
+                throw new IllegalArgumentException(e.getMessage());
+            }
+        }
+
+        public synchronized void addURI(URI uri) {
+            try {
+                URL url = uri.toURL();
+                int len = this.urls.length;
+                URL[] urls = new URL[len + 1];
+                System.arraycopy(this.urls, 0, urls, 0, len);
+                urls[len] = url;
+                this.urls = urls;
+            }
+            catch (MalformedURLException e) {
+                throw new IllegalArgumentException(e.getMessage());
+            }
+        }
+
+        private synchronized final URL[] getUrls() {
+            return urls;
+        }
+
+        public ResourceHandle getResource(String name) {
+            return loader.getResource(getUrls(), name);
+        }
+
+        public Enumeration getResources(String name) {
+            return loader.getResources(getUrls(), name);
+        }
+
+        public URL findResource(String name) {
+            return loader.findResource(getUrls(), name);
+        }
+
+        public Enumeration findResources(String name) {
+            return loader.findResources(getUrls(), name);
+        }
+//        public URL[] getResolvedSearchPath() {
+//            return loader.getResolvedSearchPath(getUrls());
+//        }
+    }
+}

Added: geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jar/Handler.java
URL: http://svn.apache.org/viewcvs/geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jar/Handler.java?rev=393370&view=auto
==============================================================================
--- geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jar/Handler.java (added)
+++ geronimo/sandbox/classloader/src/java/org/apache/geronimo/kernel/classloader/jar/Handler.java Tue Apr 11 19:48:07 2006
@@ -0,0 +1,25 @@
+/**
+ *
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  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.apache.geronimo.kernel.classloader.jar;
+
+import org.apache.geronimo.kernel.classloader.jarcache.JarUrlStreamHandler;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class Handler extends JarUrlStreamHandler {
+}



Mime
View raw message