FileDocCategorySizeDatePackage
URLClassLoader.javaAPI DocAndroid 1.5 API56993Wed May 06 22:41:04 BST 2009java.net

URLClassLoader

public class URLClassLoader extends SecureClassLoader
This class loader is responsible for loading classes and resources from a list of URLs which can refer to either directories or JAR files. Classes loaded by this {@code URLClassLoader} are granted permission to access the URLs contained in the URL search list.
since
Android 1.0

Fields Summary
private static URL[]
NO_PATH
URL[]
urls
URL[]
orgUrls
Set
invalidUrls
private Map
resCache
private Object
lock
private URLStreamHandlerFactory
factory
HashMap
extensions
Hashtable[]
indexes
private AccessControlContext
currentContext
Constructors Summary
public URLClassLoader(URL[] searchUrls, ClassLoader parent, URLStreamHandlerFactory factory)
Constructs a new {@code URLClassLoader} instance. The newly created instance will have the specified {@code ClassLoader} as its parent and use the specified factory to create stream handlers. URLs that end with "/" are assumed to be directories, otherwise they are assumed to be JAR files.

param
searchUrls the list of URLs where a specific class or file could be found.
param
parent the {@code ClassLoader} to assign as this loader's parent.
param
factory the factory that will be used to create protocol-specific stream handlers.
throws
SecurityException if a security manager exists and its {@code checkCreateClassLoader()} method doesn't allow creation of new {@code ClassLoader}s.
since
Android 1.0

        super(parent);
        // Required for pre-v1.2 security managers to work
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        this.factory = factory;
        // capture the context of the thread that creates this URLClassLoader
        currentContext = AccessController.getContext();
        int nbUrls = searchUrls.length;
        urls = new URL[nbUrls];
        orgUrls = new URL[nbUrls];
        // Search each jar for CLASS-PATH attribute in manifest
        extensions = new HashMap<URL, URL[]>(nbUrls * 2);
        for (int i = 0; i < nbUrls; i++) {
            try {
                urls[i] = createSearchURL(searchUrls[i]);
                extensions.put(urls[i], null);
            } catch (MalformedURLException e) {
            }
            orgUrls[i] = searchUrls[i];
        }
        // Search each jar for META-INF/INDEX.LIST
        indexes = newHashtableArray(nbUrls);
    
public URLClassLoader(URL[] urls)
Constructs a new {@code URLClassLoader} instance. The newly created instance will have the system ClassLoader as its parent. URLs that end with "/" are assumed to be directories, otherwise they are assumed to be JAR files.

param
urls the list of URLs where a specific class or file could be found.
throws
SecurityException if a security manager exists and its {@code checkCreateClassLoader()} method doesn't allow creation of new ClassLoaders.
since
Android 1.0

        this(urls, ClassLoader.getSystemClassLoader(), null);
    
public URLClassLoader(URL[] urls, ClassLoader parent)
Constructs a new URLClassLoader instance. The newly created instance will have the system ClassLoader as its parent. URLs that end with "/" are assumed to be directories, otherwise they are assumed to be JAR files.

param
urls the list of URLs where a specific class or file could be found.
param
parent the class loader to assign as this loader's parent.
throws
SecurityException if a security manager exists and its {@code checkCreateClassLoader()} method doesn't allow creation of new class loaders.
since
Android 1.0

        this(urls, parent, null);
    
Methods Summary
protected voidaddURL(java.net.URL url)
Adds the specified URL to the search list.

param
url the URL which is to add.
since
Android 1.0

        try {
            URL search = createSearchURL(url);
            urls = addURL(urls, search);
            orgUrls = addURL(orgUrls, url);
            synchronized (extensions) {
                extensions.put(search, null);
            }
        } catch (MalformedURLException e) {
        }
    
java.net.URL[]addURL(java.net.URL[] urlArray, java.net.URL url)
Returns an array with the given URL added to the given array.

param
urlArray {@code java.net.URL[]} the source array
param
url {@code java.net.URL} the URL to be added
return
java.net.URL[] an array made of the given array and the new URL

        URL[] newPath = new URL[urlArray.length + 1];
        System.arraycopy(urlArray, 0, newPath, 0, urlArray.length);
        newPath[urlArray.length] = url;
        Hashtable<String, URL[]>[] newIndexes = newHashtableArray(indexes.length + 1);
        System.arraycopy(indexes, 0, newIndexes, 0, indexes.length);
        indexes = newIndexes;
        return newPath;
    
private java.net.URLcreateSearchURL(java.net.URL url)
Returns an URL that will be checked if it contains the class or resource. If the file component of the URL is not a directory, a Jar URL will be created.

return
java.net.URL a test URL

        if (url == null) {
            return url;
        }

        String protocol = url.getProtocol();

        if (isDirectory(url) || protocol.equals("jar")) { //$NON-NLS-1$
            return url;
        }
        if (factory == null) {
            return new URL("jar", "", //$NON-NLS-1$ //$NON-NLS-2$
                    -1, url.toString() + "!/"); //$NON-NLS-1$
        }
        return new URL("jar", "", //$NON-NLS-1$ //$NON-NLS-2$
                -1, url.toString() + "!/", //$NON-NLS-1$
                factory.createURLStreamHandler(protocol));
    
protected java.lang.PackagedefinePackage(java.lang.String packageName, java.util.jar.Manifest manifest, java.net.URL url)
Defines a new package using the information extracted from the specified manifest.

param
packageName the name of the new package.
param
manifest the manifest containing additional information for the new package.
param
url the URL to the code source for the new package.
return
the created package.
throws
IllegalArgumentException if a package with the given name already exists.
since
Android 1.0

        Attributes mainAttributes = manifest.getMainAttributes();
        String dirName = packageName.replace('.", '/") + "/"; //$NON-NLS-1$
        Attributes packageAttributes = manifest.getAttributes(dirName);
        boolean noEntry = false;
        if (packageAttributes == null) {
            noEntry = true;
            packageAttributes = mainAttributes;
        }
        String specificationTitle = packageAttributes
                .getValue(Attributes.Name.SPECIFICATION_TITLE);
        if (specificationTitle == null && !noEntry) {
            specificationTitle = mainAttributes
                    .getValue(Attributes.Name.SPECIFICATION_TITLE);
        }
        String specificationVersion = packageAttributes
                .getValue(Attributes.Name.SPECIFICATION_VERSION);
        if (specificationVersion == null && !noEntry) {
            specificationVersion = mainAttributes
                    .getValue(Attributes.Name.SPECIFICATION_VERSION);
        }
        String specificationVendor = packageAttributes
                .getValue(Attributes.Name.SPECIFICATION_VENDOR);
        if (specificationVendor == null && !noEntry) {
            specificationVendor = mainAttributes
                    .getValue(Attributes.Name.SPECIFICATION_VENDOR);
        }
        String implementationTitle = packageAttributes
                .getValue(Attributes.Name.IMPLEMENTATION_TITLE);
        if (implementationTitle == null && !noEntry) {
            implementationTitle = mainAttributes
                    .getValue(Attributes.Name.IMPLEMENTATION_TITLE);
        }
        String implementationVersion = packageAttributes
                .getValue(Attributes.Name.IMPLEMENTATION_VERSION);
        if (implementationVersion == null && !noEntry) {
            implementationVersion = mainAttributes
                    .getValue(Attributes.Name.IMPLEMENTATION_VERSION);
        }
        String implementationVendor = packageAttributes
                .getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
        if (implementationVendor == null && !noEntry) {
            implementationVendor = mainAttributes
                    .getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
        }

        return definePackage(packageName, specificationTitle,
                specificationVersion, specificationVendor, implementationTitle,
                implementationVersion, implementationVendor, isSealed(manifest,
                        dirName) ? url : null);
    
java.net.URL[]explore(java.net.URL url, int indexNumber)

param
url URL the URL to explore
param
indexNumber int the index in extensions to consider
return
URL[] the URLs of bundled extensions that have been found (i.e. the URL of jar files in the class-path attribute), or null if none. if an INDEX.LIST has been found, an empty array is returned

        URL[] internal;
        synchronized (extensions) {
            internal = extensions.get(url);
        }
        if (internal != null) {
            return internal;
        }
        if (indexes[indexNumber] != null) {
            return null;
        }

        if (!url.getProtocol().equals("jar")) { //$NON-NLS-1$
            return null;
        }

        JarFile jf = resCache.get(url);
        // Add mappings from INDEX.LIST
        ZipEntry ze = jf.getEntry("META-INF/INDEX.LIST"); //$NON-NLS-1$
        if (ze != null) {
            if (url.equals(urls[indexNumber])) {
                try {
                    Hashtable<String, URL[]> index = new Hashtable<String, URL[]>(
                            15);
                    InputStream indexIS = jf.getInputStream(ze);
                    List<String> lines = readLines(indexIS);
                    indexIS.close();
                    ListIterator<String> iterator = lines.listIterator();
                    // Ignore the 2 first lines (index version)
                    iterator.next();
                    iterator.next();
                    // Add mappings from resource to jar file
                    URL fileURL = ((JarURLConnection) url.openConnection())
                            .getJarFileURL();
                    String file = fileURL.getFile();
                    String parentFile = new File(file).getParent();
                    parentFile = parentFile.replace(File.separatorChar, '/");
                    if (parentFile.charAt(0) != '/") {
                        parentFile = "/" + parentFile; //$NON-NLS-1$
                    }
                    URL parentURL = new URL(fileURL.getProtocol(), fileURL
                            .getHost(), fileURL.getPort(), parentFile);
                    while (iterator.hasNext()) {
                        URL jar = new URL("jar:" //$NON-NLS-1$
                                + parentURL.toExternalForm() + "/" //$NON-NLS-1$
                                + iterator.next() + "!/"); //$NON-NLS-1$
                        String resource = null;
                        while (iterator.hasNext()
                                && !(resource = iterator.next()).equals("")) { //$NON-NLS-1$
                            if (index.containsKey(resource)) {
                                URL[] jars = index.get(resource);
                                URL[] newJars = new URL[jars.length + 1];
                                System.arraycopy(jars, 0, newJars, 0,
                                        jars.length);
                                newJars[jars.length] = jar;
                                index.put(resource, newJars);
                            } else {
                                URL[] jars = { jar };
                                index.put(resource, jars);
                            }
                        }
                    }
                    indexes[indexNumber] = index;
                } catch (MalformedURLException e) {
                    // Ignore this jar's index
                } catch (IOException e) {
                    // Ignore this jar's index
                }
            }
            return null;
        }

        // Returns URLs referenced by the class-path attribute.
        Manifest manifest = null;
        try {
            manifest = jf.getManifest();
        } catch (IOException e) {
        }
        String classpath = null;
        if (manifest != null) {
            classpath = manifest.getMainAttributes().getValue(
                    Attributes.Name.CLASS_PATH);
        }
        synchronized (extensions) {
            internal = extensions.get(url);
            if (internal == null) {
                internal = classpath != null ? getInternalURLs(url, classpath)
                        : NO_PATH;
                extensions.put(url, internal);
            }
        }
        return internal;
    
protected java.lang.ClassfindClass(java.lang.String clsName)
Tries to locate and load the specified class using the known URLs. If the class could be found, a class object representing the loaded class will be returned.

return
the class that has been loaded.
param
clsName the name of the class which has to be found.
throws
ClassNotFoundException if the specified class cannot be loaded.
since
Android 1.0

        Class<?> cls = AccessController.doPrivileged(
                new PrivilegedAction<Class<?>>() {
                    public Class<?> run() {
                        return findClassImpl(urls, clsName);
                    }
                }, currentContext);
        if (cls != null) {
            return cls;
        }
        throw new ClassNotFoundException(clsName);
    
java.lang.ClassfindClassImpl(java.net.URL[] searchURLs, java.lang.String clsName)

param
searchURLs java.net.URL[] the URLs to search in
param
clsName java.lang.String the class name to be found
return
Class the class found or null if not found

        boolean readAvailable = false;
        boolean findInExtensions = searchURLs == urls;
        final String name = new StringBuffer(clsName.replace('.", '/")).append(
                ".class").toString(); //$NON-NLS-1$
        for (int i = 0; i < searchURLs.length; i++) {
            if (searchURLs[i] == null) {
                // KA024=One of urls is null
                throw new NullPointerException(Msg.getString("KA024")); //$NON-NLS-1$
            } else if (!invalidUrls.contains(searchURLs[i])) {
                Manifest manifest = null;
                InputStream is = null;
                JarEntry entry = null;
                JarFile jf = null;
                byte[] clBuf = null;
                try {
                    URL thisURL = searchURLs[i];
                    String protocol = thisURL.getProtocol();
                    if (protocol.equals("jar")) { //$NON-NLS-1$
                        jf = resCache.get(thisURL);
                        if ((jf == null) && (!invalidUrls.contains(thisURL))) {
                            synchronized (lock) {
                                // Check the cache again in case another thread updated it 
                                // updated it while we're waiting on lock
                                jf = resCache.get(thisURL);
                                if (jf == null) {
                                    if (invalidUrls.contains(thisURL)) {
                                        continue;
                                    }
                                    // If the connection for testURL or thisURL is used,
                                    // getJarFile() will throw an exception if the entry
                                    // doesn't exist.
                                    URL jarURL = ((JarURLConnection) thisURL
                                              .openConnection()).getJarFileURL();
                                    try {
                                        JarURLConnection juc = (JarURLConnection) new URL(
                                                "jar", "", jarURL.toExternalForm() + "!/") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                                                .openConnection();
                                        jf = juc.getJarFile();
                                        resCache.put(thisURL, jf);
                                    } catch (IOException e) {
                                        // Don't look for this jar file again
                                        invalidUrls.add(searchURLs[i]);
                                        throw e;
                                    }
                                }
                            }
                        }
                        if (thisURL.getFile().endsWith("!/")) { //$NON-NLS-1$
                            entry = jf.getJarEntry(name);
                        } else {
                            String file = thisURL.getFile();
                            int sepIdx = file.lastIndexOf("!/"); //$NON-NLS-1$
                            if (sepIdx == -1) {
                                // Invalid URL, don't look here again
                                invalidUrls.add(searchURLs[i]);
                                continue;
                            }
                            sepIdx += 2;
                            String entryName = new StringBuffer(file.length()
                                    - sepIdx + name.length()).append(
                                    file.substring(sepIdx)).append(name)
                                    .toString();
                            entry = jf.getJarEntry(entryName);
                        }
                        if (entry != null) {
                            readAvailable = true;
                            is = jf.getInputStream(entry);
                            /**
                             * Avoid recursive load class, especially the class
                             * is an implementation class of security provider
                             * and the jar is signed.
                             */
                            Class loadedClass = findLoadedClass(clsName);
                            if (null != loadedClass) {
                                is.close();
                                return loadedClass;
                            }
                            manifest = jf.getManifest();
                        }
                    } else if (protocol.equals("file")) { //$NON-NLS-1$
                        String filename = thisURL.getFile();
                        String host = thisURL.getHost();
                        if (host != null && host.length() > 0) {
                            filename = new StringBuffer(host.length()
                                    + filename.length() + name.length() + 2)
                                    .append("//").append(host).append(filename) //$NON-NLS-1$
                                    .append(name).toString();
                        } else {
                            filename = new StringBuffer(filename.length()
                                    + name.length()).append(filename).append(
                                    name).toString();
                        }

                        // Just return null for caller to throw
                        // ClassNotFoundException.
                        try {
                            filename = URLDecoder.decode(filename, "UTF-8"); //$NON-NLS-1$
                        } catch (IllegalArgumentException e) {
                            return null;
                        }

                        File file = new File(filename);
                        // Don't throw exceptions for speed
                        if (file.exists()) {
                            is = new FileInputStream(file);
                            readAvailable = true;
                        } else {
                            continue;
                        }
                    } else {
                        is = targetURL(thisURL, name).openStream();
                    }
                } catch (MalformedURLException e) {
                    // Keep iterating through the URL list
                } catch (IOException e) {
                }
                if (is != null) {
                    URL codeSourceURL = null;
                    Certificate[] certificates = null;
                    CodeSource codeS = null;
                    try {
                        codeSourceURL = findInExtensions ? orgUrls[i]
                                : ((JarURLConnection) searchURLs[i]
                                        .openConnection()).getJarFileURL();
                    } catch (IOException e) {
                        codeSourceURL = searchURLs[i];
                    }
                    if (is != null) {
                        try {
                            clBuf = getBytes(is, readAvailable);
                            is.close();
                        } catch (IOException e) {
                            return null;
                        }
                    }
                    if (entry != null) {
                        certificates = entry.getCertificates();
                    }
                    // Use the original URL, not the possible jar URL
                    codeS = new CodeSource(codeSourceURL, certificates);
                    int dotIndex = clsName.lastIndexOf("."); //$NON-NLS-1$
                    if (dotIndex != -1) {
                        String packageName = clsName.substring(0, dotIndex);
                        synchronized (this) {
                            Package packageObj = getPackage(packageName);
                            if (packageObj == null) {
                                if (manifest != null) {
                                    definePackage(packageName, manifest,
                                            codeSourceURL);
                                } else {
                                    definePackage(packageName, null, null,
                                            null, null, null, null, null);
                                }
                            } else {
                                boolean exception = false;
                                if (manifest != null) {
                                    String dirName = packageName.replace('.",
                                            '/")
                                            + "/"; //$NON-NLS-1$
                                    if (isSealed(manifest, dirName)) {
                                        exception = !packageObj
                                                .isSealed(codeSourceURL);
                                    }
                                } else {
                                    exception = packageObj.isSealed();
                                }
                                if (exception) {
                                    throw new SecurityException(Msg
                                            .getString("K004c")); //$NON-NLS-1$
                                }
                            }
                        }
                    }
                    return defineClass(clsName, clBuf, 0, clBuf.length, codeS);
                }
                if ((jf != null) && findInExtensions) {
                    if (indexes[i] != null) {
                        try {
                            Class<?> c = (Class<?>) findInIndex(i, clsName,
                                    null, false);
                            if (c != null) {
                                return c;
                            }
                        } catch (InvalidJarIndexException ex) {
                            // Ignore misleading/wrong jar index
                        }
                    } else {
                        Class<?> c = (Class<?>) findInExtensions(explore(
                                searchURLs[i], i), clsName, i, null, false);
                        if (c != null) {
                            return c;
                        }
                    }
                }
            }
        }
        return null;
    
java.lang.ObjectfindInExtensions(java.net.URL[] newExtensions, java.lang.String name, int i, java.util.Vector resources, boolean url)
Returns an {@code Object[]} containing a Class, a URL, and a Vector of URLs, two of which are {@code null}, according to the caller, which is identified by the {@code int} type.

return
Object[] a 3-element array : {Class, URL, Vector}. The non-null element contains the resource(s) found, which are searched in newExtensions.
param
newExtensions URL[] the URLs to look in for.
param
name the resource to look for : either a resource or a class.
param
i the index of 'indexes' array to use.
param
resources indicates that a Vector of URL should be returned as the non-null element in {@code Object[]}.
param
url if {@code true} a URL should be returned as the non-null element, if {@code false} a class should be returned.

        if (newExtensions != null) {
            for (int k = 0; k < newExtensions.length; k++) {
                if (newExtensions[k] != null) {
                    URL[] search = new URL[] { newExtensions[k] };
                    if (resources != null) {
                        URL res = findResourceImpl(search, name);
                        if (!invalidUrls.contains(search[0])) { // the URL does
                            // not exist
                            if (res != null && !resources.contains(res)) {
                                resources.addElement(res);
                            }
                            findInExtensions(explore(newExtensions[k], i),
                                    name, i, resources, url);
                        }
                    } else {
                        Object result;
                        if (url) {
                            result = findResourceImpl(search, name);
                        } else {
                            result = findClassImpl(search, name);
                        }
                        if (result != null) {
                            return result;
                        }
                        if (!invalidUrls.contains(search[0])) { // the URL
                            // exists
                            result = findInExtensions(explore(newExtensions[k],
                                    i), name, i, null, url);
                            if (result != null) {
                                return result;
                            }
                        }
                    }
                }
            }
        } else {
            try {
                return findInIndex(i, name, resources, url);
            } catch (InvalidJarIndexException ex) {
                // Ignore misleading/wrong jar index
                return null;
            }
        }
        return null;
    
java.lang.ObjectfindInIndex(int i, java.lang.String name, java.util.Vector resources, boolean url)
Returns an {@code Object[]} containing a class, a URL, and a vector of URLs, two of which are {@code null}, according to the caller, which is identified by the {@code int} type.

return
Object[] a 3-element array : {Class, URL, Vector}. The non-null element contains the resource(s) found, which are searched in in {@code indexes[i]}.
param
i the index of 'indexes' array to use.
param
name the resource to look for : either a resource or a class.
param
resources {@code boolean} indicates that a vector of URL should be returned as the non {@code null} element in {@code Object[]}.
param
url if {@code true} a URL should be returned as the non-null element, if {@code false} a class should be returned.

        Hashtable<String, URL[]> index = indexes[i];
        if (index != null) {
            int pos = name.lastIndexOf("/"); //$NON-NLS-1$
            // only keep the directory part of the resource
            // as index.list only keeps track of directories and root files
            String indexedName = (pos > 0) ? name.substring(0, pos) : name;
            URL[] jarURLs;
            if (resources != null) {
                jarURLs = index.get(indexedName);
                if (jarURLs != null) {
                    findResources(jarURLs, name, resources);
                }
            } else if (url) {
                jarURLs = index.get(indexedName);
                if (jarURLs != null) {
                    return findResourceImpl(jarURLs, name);
                }
            } else {
                String clsName = name;
                String partialName = clsName.replace('.", '/");
                int position;
                if ((position = partialName.lastIndexOf('/")) != -1) {
                    String packageName = partialName.substring(0, position);
                    jarURLs = index.get(packageName);
                } else {
                    String className = partialName.substring(0, partialName
                            .length())
                            + ".class"; //$NON-NLS-1$
                    jarURLs = index.get(className);
                }
                if (jarURLs != null) {
                    Class<?> c = findClassImpl(jarURLs, clsName);
                    // InvalidJarException is thrown when a mapping for a class
                    // is not valid, i.e. we can't find the class by following
                    // the mapping.
                    if (c == null) {
                        throw new InvalidJarIndexException();
                    }
                    return c;
                }
            }
        }
        return null;
    
public java.net.URLfindResource(java.lang.String name)
Returns an URL referencing the specified resource or {@code null} if the resource could not be found.

param
name the name of the requested resource.
return
the URL which points to the given resource.
since
Android 1.0

        if (name == null) {
            return null;
        }
        URL result = AccessController.doPrivileged(new PrivilegedAction<URL>() {
            public URL run() {
                return findResourceImpl(urls, name);
            }
        }, currentContext);
        SecurityManager sm;
        if (result != null && (sm = System.getSecurityManager()) != null) {
            try {
                sm.checkPermission(result.openConnection().getPermission());
            } catch (IOException e) {
                return null;
            } catch (SecurityException e) {
                return null;
            }
        }
        return result;
    
java.net.URLfindResourceImpl(java.net.URL[] searchList, java.lang.String resName)
Returns a URL among the given ones referencing the specified resource or null if no resource could be found.

return
URL URL for the resource.
param
searchList java.net.URL[] the array to be searched
param
resName java.lang.String the name of the requested resource

        boolean findInExtensions = searchList == urls;
        int i = 0;
        while (i < searchList.length) {
            if (searchList[i] == null) {
                // KA024=One of urls is null
                throw new NullPointerException(Msg.getString("KA024")); //$NON-NLS-1$
            } else if (!invalidUrls.contains(searchList[i])) {
                JarFile jf = null;
                try {
                    URL currentUrl = searchList[i];
                    String protocol = currentUrl.getProtocol();
                    
                    if (protocol.equals("jar")) { //$NON-NLS-1$
                        jf = resCache.get(currentUrl);
                        if (jf == null) {
                            if (invalidUrls.contains(currentUrl)) {
                                continue;
                            }
                            // each jf should be found only once 
                            // so we do this job in the synchronized block
                            synchronized (lock) {
                                // Check the cache again in case another thread 
                                // updated it while we're waiting on lock
                                jf = resCache.get(currentUrl);
                                if (jf == null) {
                                    if (invalidUrls.contains(currentUrl)) {
                                        continue;
                                    }
                                    /*
                                     * If the connection for currentUrl or resURL is
                                     * used, getJarFile() will throw an exception if the
                                     * entry doesn't exist.
                                     */
                                    URL jarURL = ((JarURLConnection) currentUrl
                                              .openConnection()).getJarFileURL();
                                    try {
                                        JarURLConnection juc = (JarURLConnection) new URL(
                                                "jar", "", //$NON-NLS-1$ //$NON-NLS-2$
                                                jarURL.toExternalForm() + "!/").openConnection(); //$NON-NLS-1$
                                        jf = juc.getJarFile();
                                        resCache.put(currentUrl, jf);
                                    } catch (IOException e) {
                                        // Don't look for this jar file again
                                        invalidUrls.add(searchList[i]);
                                        throw e;
                                    }
                                }
                            }
                        }
                        String entryName;
                        if (currentUrl.getFile().endsWith("!/")) { //$NON-NLS-1$
                            entryName = resName;
                        } else {
                            String file = currentUrl.getFile();
                            int sepIdx = file.lastIndexOf("!/"); //$NON-NLS-1$
                            if (sepIdx == -1) {
                                // Invalid URL, don't look here again
                                invalidUrls.add(searchList[i]);
                                continue;
                            }
                            sepIdx += 2;
                            entryName = new StringBuffer(file.length() - sepIdx
                                    + resName.length()).append(
                                    file.substring(sepIdx)).append(resName)
                                    .toString();
                        }
                        if (jf.getEntry(entryName) != null) {
                            return targetURL(currentUrl, resName);
                        }
                    } else if (protocol.equals("file")) { //$NON-NLS-1$
                        String baseFile = currentUrl.getFile();
                        String host = currentUrl.getHost();
                        int hostLength = 0;
                        if (host != null) {
                            hostLength = host.length();
                        }
                        StringBuffer buf = new StringBuffer(2 + hostLength
                                + baseFile.length() + resName.length());
                        if (hostLength > 0) {
                            buf.append("//").append(host); //$NON-NLS-1$
                        }
                        // baseFile always ends with '/'
                        buf.append(baseFile);
                        String fixedResName = resName;
                        // Do not create a UNC path, i.e. \\host
                        while (fixedResName.startsWith("/") //$NON-NLS-1$
                                || fixedResName.startsWith("\\")) { //$NON-NLS-1$
                            fixedResName = fixedResName.substring(1);
                        }
                        buf.append(fixedResName);

                        String filename = buf.toString();
                        
                        try {
                            filename = URLDecoder.decode(filename, "UTF-8"); //$NON-NLS-1$
                        } catch (IllegalArgumentException e) {
                            return null;
                        }

                        if (new File(filename).exists()) {
                            return targetURL(currentUrl, fixedResName);
                        }
                    } else {
                        URL resURL = targetURL(currentUrl, resName);
                        URLConnection uc = resURL.openConnection();
                        try {
                            uc.getInputStream().close();
                        } catch (SecurityException e) {
                            return null;
                        }
                        // HTTP can return a stream on a non-existent file
                        // So check for the return code;
                        if (!resURL.getProtocol().equals("http")) { //$NON-NLS-1$
                            return resURL;
                        }
                        int code;
                        if ((code = ((HttpURLConnection) uc).getResponseCode()) >= 200
                                && code < 300) {
                            return resURL;
                        }
                    }
                } catch (MalformedURLException e) {
                    // Keep iterating through the URL list
                } catch (IOException e) {
                } catch (SecurityException e) {
                }
                if ((jf != null) && findInExtensions) {
                    if (indexes[i] != null) {
                        try {
                            URL result = (URL) findInIndex(i, resName, null,
                                    true);
                            if (result != null) {
                                return result;
                            }
                        } catch (InvalidJarIndexException ex) {
                            // Ignore invalid/misleading JAR index file
                        }
                    } else {
                        URL result = (URL) findInExtensions(explore(
                                searchList[i], i), resName, i, null, true);
                        if (result != null) {
                            return result;
                        }
                    }
                }
            }
            ++i;
        }
        return null;
    
public java.util.EnumerationfindResources(java.lang.String name)
Returns all known URLs which point to the specified resource.

param
name the name of the requested resource.
return
the enumeration of URLs which point to the specified resource.
throws
IOException if an I/O error occurs while attempting to connect.
since
Android 1.0

        if (name == null) {
            return null;
        }
        Vector<URL> result = AccessController.doPrivileged(
                new PrivilegedAction<Vector<URL>>() {
                    public Vector<URL> run() {
                        return findResources(urls, name, new Vector<URL>());
                    }
                }, currentContext);
        SecurityManager sm;
        int length = result.size();
        if (length > 0 && (sm = System.getSecurityManager()) != null) {
            Vector<URL> reduced = new Vector<URL>(length);
            for (int i = 0; i < length; i++) {
                URL url = result.elementAt(i);
                try {
                    sm.checkPermission(url.openConnection().getPermission());
                    reduced.addElement(url);
                } catch (IOException e) {
                } catch (SecurityException e) {
                }
            }
            result = reduced;
        }
        return result.elements();
    
java.util.VectorfindResources(java.net.URL[] searchURLs, java.lang.String name, java.util.Vector result)
Returns a Vector of URLs among the given ones that contain the specified resource.

return
Vector the enumeration of URLs that contain the specified resource.
param
searchURLs {@code java.net.URL[]} the array to be searched.
param
name {@code java.lang.String} the name of the requested resource.

        boolean findInExtensions = searchURLs == urls;
        for (int i = 0; i < searchURLs.length; i++) {
            if (!invalidUrls.contains(searchURLs[i])) {
                URL[] search = new URL[] { searchURLs[i] };
                URL res = findResourceImpl(search, name);
                if (!invalidUrls.contains(search[0])) {
                    if (res != null && !result.contains(res)) {
                        result.addElement(res);
                    }
                    if (findInExtensions) {
                        findInExtensions(explore(searchURLs[i], i), name, i,
                                result, false);
                    }
                }
            }
        }
        return result;
    
private static byte[]getBytes(java.io.InputStream is, boolean readAvailable)
Converts an input stream into a byte array.

return
byte[] the byte array
param
is the input stream

        if (readAvailable) {
            byte[] buf = new byte[is.available()];
            is.read(buf, 0, buf.length);
            is.close();
            return buf;
        }
        byte[] buf = new byte[4096];
        int size = is.available();
        if (size < 1024) {
            size = 1024;
        }
        ByteArrayOutputStream bos = new ByteArrayOutputStream(size);
        int count;
        while ((count = is.read(buf)) > 0) {
            bos.write(buf, 0, count);
        }
        return bos.toByteArray();
    
private java.net.URL[]getInternalURLs(java.net.URL root, java.lang.String classpath)
returns URLs referenced in the string classpath.

param
root the jar URL that classpath is related to
param
classpath the relative URLs separated by spaces
return
URL[] the URLs contained in the string classpath.

        // Class-path attribute is composed of space-separated values.
        StringTokenizer tokenizer = new java.util.StringTokenizer(classpath);
        Vector<URL> addedURLs = new Vector<URL>();
        String file = root.getFile();        
        int jarIndex = file.lastIndexOf("!/") - 1; //$NON-NLS-1$
        int index = file.lastIndexOf("/", jarIndex) + 1; //$NON-NLS-1$
        if (index == 0) {
            index = file.lastIndexOf(
                    System.getProperty("file.separator"), jarIndex) + 1; //$NON-NLS-1$
        }
        file = file.substring(0, index);
        String protocol = root.getProtocol();
        String host = root.getHost();
        int port = root.getPort();
        while (tokenizer.hasMoreElements()) {
            String element = tokenizer.nextToken();
            if (!element.equals("")) { //$NON-NLS-1$
                try {
                    URL newURL = new URL(protocol, host, port, file + element
                            + "!/"); //$NON-NLS-1$
                    synchronized (extensions) {
                        if (!extensions.containsKey(newURL)) {
                            extensions.put(newURL, null);
                            addedURLs.add(newURL);
                        }
                    }
                } catch (MalformedURLException e) {
                    // Nothing is added
                }
            }
        }
        URL[] newURLs = addedURLs.toArray(new URL[] {});
        return newURLs;
    
protected java.security.PermissionCollectiongetPermissions(java.security.CodeSource codesource)
Gets all permissions for the specified {@code codesource}. First, this method retrieves the permissions from the system policy. If the protocol is "file:/" then a new permission, {@code FilePermission}, granting the read permission to the file is added to the permission collection. Otherwise, connecting to and accepting connections from the URL is granted.

param
codesource the code source object whose permissions have to be known.
return
the list of permissions according to the code source object.
since
Android 1.0

        PermissionCollection pc = super.getPermissions(codesource);
        URL u = codesource.getLocation();
        if (u.getProtocol().equals("jar")) { //$NON-NLS-1$
            try {
                // Create a URL for the resource the jar refers to
                u = ((JarURLConnection) u.openConnection()).getJarFileURL();
            } catch (IOException e) {
                // This should never occur. If it does continue using the jar
                // URL
            }
        }
        if (u.getProtocol().equals("file")) { //$NON-NLS-1$
            String path = u.getFile();
            String host = u.getHost();
            if (host != null && host.length() > 0) {
                path = "//" + host + path; //$NON-NLS-1$
            }

            if (File.separatorChar != '/") {
                path = path.replace('/", File.separatorChar);
            }
            if (isDirectory(u)) {
                pc.add(new FilePermission(path + "-", "read")); //$NON-NLS-1$ //$NON-NLS-2$
            } else {
                pc.add(new FilePermission(path, "read")); //$NON-NLS-1$
            }
        } else {
            String host = u.getHost();
            if (host.length() == 0) {
                host = "localhost"; //$NON-NLS-1$
            }
            pc.add(new SocketPermission(host, "connect, accept")); //$NON-NLS-1$
        }
        return pc;
    
public java.net.URL[]getURLs()
Returns the search list of this {@code URLClassLoader}.

return
the list of all known URLs of this instance.
since
Android 1.0

        return orgUrls.clone();
    
private static booleanisDirectory(java.net.URL url)
Determines if the URL is pointing to a directory.

        String file = url.getFile();
        return (file.length() > 0 && file.charAt(file.length() - 1) == '/");
    
private booleanisSealed(java.util.jar.Manifest manifest, java.lang.String dirName)

        Attributes mainAttributes = manifest.getMainAttributes();
        String value = mainAttributes.getValue(Attributes.Name.SEALED);
        boolean sealed = value != null && value.toLowerCase().equals("true"); //$NON-NLS-1$
        Attributes attributes = manifest.getAttributes(dirName);
        if (attributes != null) {
            value = attributes.getValue(Attributes.Name.SEALED);
            if (value != null) {
                sealed = value.toLowerCase().equals("true"); //$NON-NLS-1$
            }
        }
        return sealed;
    
private static java.util.Hashtable[]newHashtableArray(int size)

        return new Hashtable[size];
    
public static java.net.URLClassLoadernewInstance(java.net.URL[] urls)
Returns a new {@code URLClassLoader} instance for the given URLs and the system {@code ClassLoader} as its parent. The method {@code loadClass()} of the new instance will call {@code SecurityManager.checkPackageAccess()} before loading a class.

param
urls the list of URLs that is passed to the new {@code URLClassloader}.
return
the created {@code URLClassLoader} instance.
since
Android 1.0

        URLClassLoader sub = AccessController
                .doPrivileged(new PrivilegedAction<URLClassLoader>() {
                    public URLClassLoader run() {
                        return new SubURLClassLoader(urls);
                    }
                });
        sub.currentContext = AccessController.getContext();
        return sub;
    
public static java.net.URLClassLoadernewInstance(java.net.URL[] urls, java.lang.ClassLoader parentCl)
Returns a new {@code URLClassLoader} instance for the given URLs and the specified {@code ClassLoader} as its parent. The method {@code loadClass()} of the new instance will call the SecurityManager's {@code checkPackageAccess()} before loading a class.

param
urls the list of URLs that is passed to the new URLClassloader.
param
parentCl the parent class loader that is passed to the new URLClassloader.
return
the created {@code URLClassLoader} instance.
since
Android 1.0

        URLClassLoader sub = AccessController
                .doPrivileged(new PrivilegedAction<URLClassLoader>() {
                    public URLClassLoader run() {
                        return new SubURLClassLoader(urls, parentCl);
                    }
                });
        sub.currentContext = AccessController.getContext();
        return sub;
    
private java.util.ListreadLines(java.io.InputStream in)

param
in InputStream the stream to read lines from
return
List a list of String lines

        byte[] buff = new byte[144];
        List<String> lines = new ArrayList<String>();
        int pos = 0;
        int next;
        while ((next = in.read()) != -1) {
            if (next == '\n") {
                lines.add(new String(buff, 0, pos, "UTF8")); //$NON-NLS-1$
                pos = 0;
                continue;
            }
            if (next == '\r") {
                lines.add(new String(buff, 0, pos, "UTF8")); //$NON-NLS-1$
                pos = 0;
                if ((next = in.read()) == '\n") {
                    continue;
                }
            }
            if (pos == buff.length) {
                byte[] newBuf = new byte[buff.length * 2];
                System.arraycopy(buff, 0, newBuf, 0, buff.length);
                buff = newBuf;
            }
            buff[pos++] = (byte) next;
        }
        if (pos > 0) {
            lines.add(new String(buff, 0, pos, "UTF8")); //$NON-NLS-1$
        }
        return lines;
    
private java.net.URLtargetURL(java.net.URL base, java.lang.String name)

        try {
            String file = base.getFile() + URIEncoderDecoder.quoteIllegal(name,
                    "/@" + URI.someLegal);

            return new URL(base.getProtocol(), base.getHost(), base.getPort(),
                    file, null);
        } catch (UnsupportedEncodingException e) {
            MalformedURLException e2 = new MalformedURLException(e.toString());
            
            e2.initCause(e);
            throw e2;
        }