You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2014/05/09 09:03:19 UTC

svn commit: r1593493 [23/24] - in /river/jtsk/skunk/qa_refactor/trunk: qa/ qa/src/com/sun/jini/test/impl/end2end/jssewrapper/ qa/src/com/sun/jini/test/impl/joinmanager/ qa/src/com/sun/jini/test/impl/mahalo/ qa/src/com/sun/jini/test/impl/outrigger/match...

Modified: river/jtsk/skunk/qa_refactor/trunk/src/net/jini/loader/pref/PreferredClassLoader.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/net/jini/loader/pref/PreferredClassLoader.java?rev=1593493&r1=1593492&r2=1593493&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/net/jini/loader/pref/PreferredClassLoader.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/src/net/jini/loader/pref/PreferredClassLoader.java Fri May  9 07:03:18 2014
@@ -1,1363 +1,1385 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 net.jini.loader.pref;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FilePermission;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.JarURLConnection;
-import java.net.MalformedURLException;
-import java.net.SocketPermission;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
-import java.net.URLStreamHandlerFactory;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.CodeSource;
-import java.security.Permission;
-import java.security.PermissionCollection;
-import java.security.Permissions;
-import java.security.Policy;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.security.ProtectionDomain;
-import java.security.cert.Certificate;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import net.jini.loader.ClassAnnotation;
-import net.jini.loader.DownloadPermission;
-import org.apache.river.api.net.RFC3986URLClassLoader;
-
-/**
- * A class loader that supports preferred classes.
- *
- * <p>A preferred class is a class that is to be loaded by a class
- * loader without the loader delegating to its parent class loader
- * first.  Resources may also be preferred.
- *
- * <p>Like {@link java.net.URLClassLoader},
- * <code>PreferredClassLoader</code> loads classes and resources from
- * a search path of URLs.  If a URL in the path ends with a
- * <code>'/'</code>, it is assumed to refer to a directory; otherwise,
- * the URL is assumed to refer to a JAR file.
- *
- * <p>The location of the first URL in the path can contain a
- * <i>preferred list</i> for the entire path.  A preferred list
- * declares names of certain classes and other resources throughout
- * the path as being <i>preferred</i> or not.  When a
- * <code>PreferredClassLoader</code> is asked to load a class or
- * resource that is preferred (according to the preferred list) and
- * the class or resource exists in the loader's path of URLs, the
- * loader will not delegate first to its parent class loader as it
- * otherwise would do; instead, it will attempt to load the class or
- * resource from its own path of URLs only.
- *
- * <p>The preferred list for a path of URLs, if one exists, is located
- * relative to the first URL in the path.  If the first URL refers to
- * a JAR file, then the preferred list is the contents of the file
- * named <code>"META-INF/PREFERRED.LIST"</code> within that JAR file.
- * If the first URL refers to a directory, then the preferred list is
- * the contents of the file at the location
- * <code>"META-INF/PREFERRED.LIST"</code> relative to that directory
- * URL.  If there is no preferred list at the required location, then
- * no classes or resources are preferred for the path of URLs.  A
- * preferred list at any other location (such as relative to one of
- * the other URLs in the path) is ignored.
- *
- * <p>Note that a class or resource is only considered to be preferred
- * if the preferred list declares the name of the class or resource as
- * being preferred and the class or resource actually exists in the
- * path of URLs.
- *
- * <h3>Preferred List Syntax</h3>
- *
- * A preferred list is a UTF-8 encoded text file, with lines separated
- * by CR&nbsp;LF, LF, or CR (not followed by an LF).  Multiple
- * whitespace characters in a line are equivalent to a single
- * whitespace character, and whitespace characters at the beginning or
- * end of a line are ignored.  If the first non-whitespace character
- * of a line is <code>'#'</code>, the line is a comment and is
- * equivalent to a blank line.
- *
- * <p>The first line of a preferred list must contain a version
- * number in the following format:
- *
- * <pre>
- *     PreferredResources-Version: 1.<i>x</i>
- * </pre>
- *
- * This specification defines only version 1.0, but
- * <code>PreferredClassLoader</code> will parse any version
- * 1.<i>x</i>, <i>x</i>>=0 with the format and semantics specified
- * here.
- *
- * <p>After the version number line, a preferred list comprises an
- * optional default preferred entry followed by zero or more named
- * preferred entries.  A preferred list must contain either a default
- * preferred entry or at least one named preferred entry.  Blank lines
- * are allowed before and after preferred entries, as well as between
- * the lines of a named preferred entry.
- *
- * <p>A default preferred entry is a single line in the following
- * format:
- *
- * <pre>
- *     Preferred: <i>preferred-setting</i>
- * </pre>
- *
- * where <i>preferred-setting</i> is a non-empty sequence of
- * characters.  If <i>preferred-setting</i> equals <code>"true"</code>
- * (case insensitive), then resource names not matched by any of the
- * named preferred entries are by default preferred; otherwise,
- * resource names not matched by any of the named preferred entries
- * are by default not preferred.  If there is no default preferred
- * entry, then resource names are by default not preferred.
- *
- * <p>A named preferred entry is two lines in the following format:
- *
- * <pre>
- *     Name: <i>name-expression</i>
- *     Preferred: <i>preferred-setting</i>
- * </pre>
- *
- * where <i>name-expression</i> and <i>preferred-setting</i> are
- * non-empty sequences of characters.  If <i>preferred-setting</i>
- * equals <code>"true"</code> (case insensitive), then resource names
- * that are matched by <i>name-expression</i> (and not any more
- * specific named preferred entries) are preferred; otherwise,
- * resource names that are matched by <i>name-expression</i> (and not
- * any more specific named preferred entries) are not preferred.
- * 
- * <p>If <i>name-expression</i> ends with <code>".class"</code>, it
- * matches a class whose binary name is <i>name-expression</i> without
- * the <code>".class"</code> suffix and with each <code>'/'</code>
- * character replaced with a <code>'.'</code>.  It also matches any
- * class whose binary name starts with that same value followed by a
- * <code>'$'</code>; this rule is intended to match nested classes
- * that have an enclosing class of that name, so that the preferred
- * settings of a class and all of its nested classes are the same by
- * default.  It is possible, but strongly discouraged, to override the
- * preferred setting of a nested class with a named preferred entry
- * that explicitly matches the nested class's binary name.
- *
- * <p><i>name-expression</i> may match arbitrary resource names as
- * well as class names, with path elements separated by
- * <code>'/'</code> characters.
- *
- * <p>If <i>name-expression</i> ends with <code>"/"</code> or
- * <code>"/*"</code>, then the entry is a directory wildcard entry
- * that matches all resources (including classes) in the named
- * directory.  If <i>name-expression</i> ends with <code>"/-"</code>,
- * then the entry is a namespace wildcard entry that matches all
- * resources (including classes) in the named directory and all of its
- * subdirectories.
- *
- * <p>When more than one named preferred entry matches a class or
- * resource name, then the most specific entry takes precedence.  A
- * non-wildcard entry is more specific than a wildcard entry.  A
- * directory wildcard entry is more specific than a namespace wildcard
- * entry.  A namespace wildcard entry with more path elements is more
- * specific than a namespace wildcard entry with fewer path elements.
- * Given two non-wildcard entries, the entry with the longer
- * <i>name-expression</i> is more specific (this rule is only
- * significant when matching a class).  The order of named preferred
- * entries is insignificant.
- *
- * <h3>Example Preferred List</h3>
- *
- * <p>Following is an example preferred list:
- *
- * <pre>
- *     PreferredResources-Version: 1.0
- *     Preferred: false
- *
- *     Name: com/foo/FooBar.class
- *     Preferred: true
- *
- *     Name: com/foo/*
- *     Preferred: false
- *
- *     Name: com/foo/-
- *     Preferred: true
- *
- *     Name: image-files/*
- *     Preferred: mumble
- * </pre>
- *
- * <p>The class <code>com.foo.FooBar</code> is preferred, as well as
- * any nested classes that have it as an enclosing class.  All other
- * classes in the <code>com.foo</code> package are not preferred
- * because of the directory wildcard entry.  Classes in subpackages of
- * <code>com.foo</code> are preferred because of the namespace
- * wildcard entry.  Resources in the directory <code>"com/foo/"</code>
- * are not preferred, and resources in subdirectories of
- * <code>"com/foo/"</code> are preferred.  Resources in the directory
- * <code>"image-files/"</code> are not preferred because preferred
- * settings other than <code>"true"</code> are interpreted as false.
- * Classes that are in a package named <code>com.bar</code> are not
- * preferred because of the default preferred entry.
- *
- * @author Sun Microsystems, Inc.
- * @since 2.0
- **/
-public class PreferredClassLoader extends RFC3986URLClassLoader
-    implements ClassAnnotation
-{
-    /**
-     * well known name of resource that contains the preferred list in
-     * a path of URLs
-     **/
-    private static final String PREF_NAME = "META-INF/PREFERRED.LIST";
-
-    /** first URL in the path, or null if none */
-    private final URL firstURL;
-
-    /** class annotation string for classes defined by this loader */
-    private final String exportAnnotation;
-
-    /** permissions required to access loader through public API */
-    private final PermissionCollection permissions;
-
-    /** security context for loading classes and resources */
-    private final AccessControlContext acc;
-
-    /** permission required to download code? */
-    private final boolean requireDlPerm;
-
-    /** URLStreamHandler to use when creating new "jar:" URLs */
-    private final URLStreamHandler jarHandler;
-
-    /** PreferredResources for this loader (null if no preferred list) */
-    private final PreferredResources preferredResources;
-    
-    private final IOException exceptionWhileLoadingPreferred;
-
-    private static final Permission downloadPermission =
-	new DownloadPermission();
-
-    /**
-     * Creates a new <code>PreferredClassLoader</code> that loads
-     * classes and resources from the specified path of URLs and
-     * delegates to the specified parent class loader.
-     *
-     * <p>If <code>exportAnnotation</code> is not <code>null</code>,
-     * then it will be used as the return value of the loader's {@link
-     * #getClassAnnotation getClassAnnotation} method.  If
-     * <code>exportAnnotation</code> is <code>null</code>, the
-     * loader's <code>getClassAnnotation</code> method will return a
-     * space-separated list of the URLs in the specified path.  The
-     * <code>exportAnnotation</code> parameter can be used to specify
-     * so-called "export" URLs, from which other parties should load
-     * classes defined by the loader and which are different from the
-     * "import" URLs that the classes are actually loaded from.
-     *
-     * <p>If <code>requireDlPerm</code> is <code>true</code>, the
-     * loader's {@link #getPermissions getPermissions} method will
-     * require that the {@link CodeSource} of any class defined by the
-     * loader is granted {@link DownloadPermission}.
-     *
-     * @param urls the path of URLs to load classes and resources from
-     *
-     * @param parent the parent class loader for delegation
-     *
-     * @param exportAnnotation the export class annotation string to
-     * use for classes defined by this loader, or <code>null</code>
-     *
-     * @param requireDlPerm if <code>true</code>, the loader will only
-     * define classes with a {@link CodeSource} that is granted {@link
-     * DownloadPermission}
-     *
-     * @throws SecurityException if there is a security manager and an
-     * invocation of its {@link SecurityManager#checkCreateClassLoader
-     * checkCreateClassLoader} method fails
-     **/
-    public PreferredClassLoader(URL[] urls,
-				ClassLoader parent,
-				String exportAnnotation,
-				boolean requireDlPerm)
-    {
-	this(urls, parent, exportAnnotation, requireDlPerm, null);
-    }
-
-    /**
-     * Creates a new <code>PreferredClassLoader</code> that loads
-     * classes and resources from the specified path of URLs,
-     * delegates to the specified parent class loader, and uses the
-     * specified {@link URLStreamHandlerFactory} when creating new URL
-     * objects.  This constructor passes <code>factory</code> to the
-     * superclass constructor that has a
-     * <code>URLStreamHandlerFactory</code> parameter.
-     *
-     * <p>If <code>exportAnnotation</code> is not <code>null</code>,
-     * then it will be used as the return value of the loader's {@link
-     * #getClassAnnotation getClassAnnotation} method.  If
-     * <code>exportAnnotation</code> is <code>null</code>, the
-     * loader's <code>getClassAnnotation</code> method will return a
-     * space-separated list of the URLs in the specified path.  The
-     * <code>exportAnnotation</code> parameter can be used to specify
-     * so-called "export" URLs, from which other parties should load
-     * classes defined by the loader and which are different from the
-     * "import" URLs that the classes are actually loaded from.
-     *
-     * <p>If <code>requireDlPerm</code> is <code>true</code>, the
-     * loader's {@link #getPermissions getPermissions} method will
-     * require that the {@link CodeSource} of any class defined by the
-     * loader is granted {@link DownloadPermission}.
-     *
-     * @param urls the path of URLs to load classes and resources from
-     *
-     * @param parent the parent class loader for delegation
-     *
-     * @param exportAnnotation the export class annotation string to
-     * use for classes defined by this loader, or <code>null</code>
-     *
-     * @param requireDlPerm if <code>true</code>, the loader will only
-     * define classes with a {@link CodeSource} that is granted {@link
-     * DownloadPermission}
-     *
-     * @param factory the <code>URLStreamHandlerFactory</code> to use
-     * when creating new URL objects, or <code>null</code>
-     *
-     * @throws SecurityException if there is a security manager and an
-     * invocation of its {@link SecurityManager#checkCreateClassLoader
-     * checkCreateClassLoader} method fails
-     *
-     * @since 2.1
-     **/
-    public PreferredClassLoader(URL[] urls,
-				ClassLoader parent,
-				String exportAnnotation,
-				boolean requireDlPerm,
-				URLStreamHandlerFactory factory)
-    {
-	super(urls, parent, factory);
-	firstURL = (urls.length > 0 ? urls[0] : null);
-	if (exportAnnotation != null) {
-	    this.exportAnnotation = exportAnnotation;
-	} else {
-	    /*
-	     * Caching the value of class annotation string here
-	     * assumes that the protected method addURL() is never
-	     * called on this class loader.
-	     */
-	    this.exportAnnotation = urlsToPath(urls);
-	}
-	this.requireDlPerm = requireDlPerm;
-	if (factory != null) {
-	    jarHandler = factory.createURLStreamHandler("jar");
-	} else {
-	    jarHandler = null;
-	}
-
-	acc = AccessController.getContext();
-	    
-	/*
-	 * Precompute the permissions required to access the loader.
-	 */
-	permissions = new Permissions();
-	addPermissionsForURLs(urls, permissions, false);
-        /*
-         * If a preferred list exists relative to the first URL of this
-         * loader's path, sets this loader's PreferredResources according
-         * to that preferred list.  If no preferred list exists relative
-         * to the first URL, leaves this loader's PreferredResources null.
-         *
-         * Throws IOException if an I/O exception occurs from which the
-         * existence of a preferred list relative to the first URL cannot
-         * be definitely determined.
-         *
-         * This method must only be invoked while synchronized on this
-         * PreferredClassLoader, and it must not be invoked again after it
-         * has completed successfully.
-         * 
-         * This was called from privileged context when it as part of a method,
-         * but we shouldn't need to do that here, any code that can construct
-         * a ClassLoader has privilege.  Note that InputStream is not subject
-         * to deserialization attacks like ObjectInputStream.
-         * 
-         * Also synchronization is not required as it is called from within
-         * the constructor now, this change was made to remove any possiblity
-         * of deadlock by minimising locking.
-         */
-        IOException except = null;
-        PreferredResources pref = null;
-	if (firstURL != null) {
-            InputStream prefIn = null;
-            try {
-                prefIn = getPreferredInputStream(firstURL);
-                if (prefIn != null) {
-                    try {
-                        pref = new PreferredResources(prefIn);
-                    } finally {
-                        try {
-                            prefIn.close();
-                        } catch (IOException e) {
-                        }
-                    }
-                }
-            } catch (IOException ex) {
-                Logger.getLogger(PreferredClassLoader.class.getName()).log(Level.SEVERE, "Unable to access preferred resources", ex);
-                except = ex;
-            } finally {
-                try {
-                    if (prefIn != null){
-                        prefIn.close();
-                    }
-                } catch (IOException ex) {
-                    Logger.getLogger(PreferredClassLoader.class.getName()).log(Level.SEVERE, "Problem closing preferred resources input stream", ex);
-                } 
-                
-            }
-	}
-        exceptionWhileLoadingPreferred = except;
-        preferredResources = pref;
-    }
-
-    /**
-     * Convert an array of URL objects into a corresponding string
-     * containing a space-separated list of URLs.
-     *
-     * Note that if the array has zero elements, the return value is
-     * null, not the empty string.
-     */
-    static String urlsToPath(URL[] urls) {
-	if (urls.length == 0) {
-	    return null;
-	} else if (urls.length == 1) {
-	    return urls[0].toExternalForm();
-	} else {
-	    StringBuffer path = new StringBuffer(urls[0].toExternalForm());
-	    for (int i = 1; i < urls.length; i++) {
-		path.append(' ');
-		path.append(urls[i].toExternalForm());
-	    }
-	    return path.toString();
-	}
-    }
-
-    /**
-     * Creates a new instance of <code>PreferredClassLoader</code>
-     * that loads classes and resources from the specified path of
-     * URLs and delegates to the specified parent class loader.
-     *
-     * <p>The <code>exportAnnotation</code> and
-     * <code>requireDlPerm</code> parameters have the same semantics
-     * as they do for the constructors.
-     *
-     * <p>The {@link #loadClass loadClass} method of the returned
-     * <code>PreferredClassLoader</code> will, if there is a security
-     * manager, invoke its {@link SecurityManager#checkPackageAccess
-     * checkPackageAccess} method with the package name of the class
-     * to load before attempting to load the class; this could result
-     * in a <code>SecurityException</code> being thrown from
-     * <code>loadClass</code>.
-     *
-     * @param urls the path of URLs to load classes and resources from
-     *
-     * @param parent the parent class loader for delegation
-     *
-     * @param exportAnnotation the export class annotation string to
-     * use for classes defined by this loader, or <code>null</code>
-     *
-     * @param requireDlPerm if <code>true</code>, the loader will only
-     * define classes with a {@link CodeSource} that is granted {@link
-     * DownloadPermission}
-     *
-     * @return the new <code>PreferredClassLoader</code> instance
-     *
-     * @throws SecurityException if the current security context does
-     * not have the permissions necessary to connect to all of the
-     * URLs in <code>urls</code>
-     **/
-    public static PreferredClassLoader
-	newInstance(final URL[] urls,
-		    final ClassLoader parent,
-		    final String exportAnnotation,
-		    final boolean requireDlPerm)
-    {
-	/* ensure caller has permission to access all urls */
-	PermissionCollection perms = new Permissions();
-	addPermissionsForURLs(urls, perms, false);
-	checkPermissions(perms);
-
-	AccessControlContext acc = getLoaderAccessControlContext(urls);
-	/* Use privileged status to return a new class loader instance */
-	return (PreferredClassLoader)
-	    AccessController.doPrivileged(new PrivilegedAction() {
-		public Object run() {
-		    return new PreferredFactoryClassLoader(urls, parent,
-			exportAnnotation, requireDlPerm);
-		}
-	    }, acc);
-    }
-
-    
-
-    /**
-     * Returns an InputStream from which the preferred list relative
-     * to the specified URL can be read, or null if the there is
-     * definitely no preferred list relative to the URL.  If the URL's
-     * path ends with "/", then the preferred list is sought at the
-     * location "META-INF/PREFERRED.LIST" relative to the URL;
-     * otherwise, the URL is assumed to refer to a JAR file, and the
-     * preferred list is sought within that JAR file, as the entry
-     * named "META-INF/PREFERRED.LIST".
-     *
-     * Throws IOException if an I/O exception occurs from which the
-     * existence of a preferred list relative to the specified URL
-     * cannot be definitely determined.
-     **/
-    private InputStream getPreferredInputStream(URL firstURL)
-	throws IOException
-    {
-	URL prefListURL = null;
-	try {
-	    URL baseURL;	// base URL to load PREF_NAME relative to
-	    if (firstURL.getFile().endsWith("/")) { // REMIND: track 4915051
-		baseURL = firstURL;
-	    } else {
-		/*
-		 * First try to get a definite answer about the existence of a
-		 * PREFERRED.LIST that doesn't by-pass a JAR file cache, if
-		 * any. If that fails we determine if the JAR file exists by
-		 * attempting to access it directly, without using a "jar:" URL,
-		 * because the "jar:" URL handler can mask the distinction
-		 * between definite lack of existence and less definitive
-		 * errors. Unfortunately, this direct access circumvents the JAR
-		 * file caching done by the "jar:" handler, so it ends up
-		 * causing duplicate requests of the JAR file on first use when
-		 * our first attempt fails. (For HTTP-protocol URLs, the initial
-		 * request will use HEAD instead of GET.)
-		 *
-		 * After determining that the JAR file exists, attempt to
-		 * retrieve the preferred list using a "jar:" URL, like
-		 * URLClassLoader uses to load resources from a JAR file.
-		 */
-		if (jarExists(firstURL)) {
-		    baseURL = getBaseJarURL(firstURL);
-		} else {
-		    return null;
-		}
-	    }
-	    prefListURL = new URL(baseURL, PREF_NAME);
-	    URLConnection preferredConnection =
-		getPreferredConnection(prefListURL, false);
-	    if (preferredConnection != null) {
-		return preferredConnection.getInputStream();
-	    } else {
-		return null;
-	    }
-	} catch (IOException e) {
-	    /*
-	     * Assume that any IOException thrown while attempting to
-	     * access a "file:" URL and any FileNotFoundException
-	     * implies that there is definitely no preferred list
-	     * relative to the specified URL.
-	     */
-	    if (firstURL.getProtocol().equals("file") ||
-		e instanceof FileNotFoundException)
-	    {
-		return null;
-	    } else {
-		throw e;
-	    }
-	}
-    }
-
-    /* cache existence of jar files referenced by codebase urls */
-    private static final Set<String> existSet = new HashSet<String>(11);
-
-    /*
-     * Determine if a jar file in a given URL location exists.  If the
-     * jar exists record the jar file's URL in a cache of URL strings.
-     *
-     * Recording the existence of the jar prevents the need to
-     * re-determine the jar's existence on subsequent downloads of the
-     * jar in potentially different preferred class loaders.
-     */
-    private boolean jarExists(URL firstURL) throws IOException {
-	boolean exists;
-        
-	synchronized (existSet) {
-            // The comment says in a cache of URL strings, URL in Set, bad.
-	    exists = existSet.contains(firstURL.toString());
-	}
-
-	if (!exists) {
-	    /*
-	     * first try to get a definite answer of the existence of a JAR
-	     * file, if no IOException is thrown when obtaining it through the
-	     * "jar:" protocol we can safely assume the JAR file is locally
-	     * available upon the attempt (elsewhere) to obtain the preferred
-	     * list
-	     */
-            URL baseURL = null;
-	    try {
-                baseURL = getBaseJarURL(firstURL);
-		((JarURLConnection) baseURL.openConnection()).getManifest();
-		exists = true;
-	    } catch (IOException e) {
-		// we still have no definite answer on whether the JAR file
-		// and therefore the PREFERRED.LIST exists
-	    } catch (NullPointerException e){
-                // Sun Bug ID: 6536522
-                // NullPointerException is thrown instead of MalformedURLException
-                // Case is the same as above, we have no definite answer on
-                // whether the JAR file and therefore the PREFERRED.LIST exists.
-                System.err.println("NPE thrown while trying to open connection :" +
-                        baseURL);
-                e.printStackTrace(System.err);
-            }
-
-	    if (!exists) {
-		exists = (getPreferredConnection(firstURL, true) != null);
-	    }
-	    if (exists) {
-		synchronized (existSet) {
-		    existSet.add(firstURL.toString());
-		}
-	    }
-	}
-	return exists;
-    }
-
-    /**
-     * Returns a "jar:" URL for the root directory of the JAR file at
-     * the specified URL.  If this loader was constructed with a
-     * URLStreamHandlerFactory, then the returned URL will have a
-     * URLStreamHandler that was created by the factory.
-     **/
-    private URL getBaseJarURL(final URL url) throws MalformedURLException {
-	if (jarHandler == null) {
-	    return new URL("jar", "", -1, url + "!/");
-	} else {
-	    try {
-		return (URL) AccessController.doPrivileged(
-		    new PrivilegedExceptionAction() {
-			public Object run() throws MalformedURLException {
-			    return new URL("jar", "", -1, url + "!/",
-					   jarHandler);
-			}
-		    });
-	    } catch (PrivilegedActionException e) {
-		throw (MalformedURLException) e.getCause();
-	    }
-	}
-    }
-    
-    /**
-     * Obtain a url connection from which an input stream that
-     * contains a preferred list can be obtained.
-     *
-     * For http urls, attempts to use http response codes to
-     * determine if a preferred list exists or is definitely not
-     * found.  Simply attempts to open a connection to other kinds
-     * of non-file urls.  If the attempt fails, an IOException is
-     * thrown to user code.
-     *
-     * Returns null if the preferred list definitely does not
-     * exist.  Rethrows all indefinite IOExceptions generated
-     * while trying to open a connection to the preferred list.
-     *
-     * The caller has the option to close the connection after the
-     * resource has been detected (as will happen when probing for a
-     * PREFERRED.LIST).
-     */
-    private URLConnection getPreferredConnection(URL url, boolean closeAfter)
-	throws IOException
-    {
-	if (url.getProtocol().equals("file")) {
-	    return url.openConnection();
-	}
-	URLConnection closeConn = null;
-	URLConnection conn = null;
-	try {
-	    closeConn = url.openConnection();
-	    conn = closeConn;
-						 
-	    /* check status of http urls  */
-	    if (conn instanceof HttpURLConnection) {
-		HttpURLConnection hconn = (HttpURLConnection) conn;
-		if (closeAfter) {
-		    hconn.setRequestMethod("HEAD");
-		}
-		int responseCode = hconn.getResponseCode();//NPE
-		
-		switch (responseCode) {
-		case HttpURLConnection.HTTP_OK:
-		case HttpURLConnection.HTTP_NOT_AUTHORITATIVE:
-		    /* the preferred list exists */
-		    break;
-
-		    /* 404, not found appears to be handled by
-		     * HttpURLConnection (FileNotFoundException is
-		     * thrown), but to be safe do the right thing here as
-		     * well.
-		     */
-		case HttpURLConnection.HTTP_NOT_FOUND:
-		case HttpURLConnection.HTTP_FORBIDDEN:
-		case HttpURLConnection.HTTP_GONE:
-		    /* list definitely does not exist */
-		    conn = null;
-		    break;
-		default:
-		    /* indefinite response code */
-		    throw new IOException("Indefinite http response for " +
-			"preferred list request:" +
-			hconn.getResponseMessage());
-		}
-	    }
-        } catch (RuntimeException e){
-            if ( e instanceof NullPointerException || e.getCause() instanceof NullPointerException) {
-                // Sun Bug ID: 6536522
-                throw new IOException(url.toString(), e);
-            } else {
-                throw e;
-            }
-	} finally {
-	    if (closeAfter && (closeConn != null)) {
-		/* clean up after... */
-		try {
-		    closeConn.getInputStream().close();//RTE NPE
-		} catch (IOException e) {
-		} catch (RuntimeException e){
-                    if ( e instanceof NullPointerException || e.getCause() instanceof NullPointerException) {
-                        // Sun Bug ID: 6536522
-                        // swallow
-                        e.printStackTrace(System.err);
-                    } else {
-                        throw e;
-                    }
-                }
-	    }
-	}
-
-	return conn;
-    }
-
-    /**
-     * Returns <code>true</code> if a class or resource with the
-     * specified name is preferred for this class loader, and
-     * <code>false</code> if a class or resource with the specified
-     * name is not preferred for this loader.
-     *
-     * <p>If <code>isClass</code> is <code>true</code>, then
-     * <code>name</code> is interpreted as the binary name of a class;
-     * otherwise, <code>name</code> is interpreted as the full path of
-     * a resource.
-     *
-     * <p>This method only returns <code>true</code> if a class or
-     * resource with the specified name exists in the this loader's
-     * path of URLs and the name is preferred in the preferred list.
-     * This method returns <code>false</code> if the name is not
-     * preferred in the preferred list or if the name is preferred
-     * with the default preferred entry or a wildcard preferred entry
-     * and the class or resource does not exist in the path of URLs.
-     *
-     * @param name the name of the class or resource
-     *
-     * @param isClass <code>true</code> if <code>name</code> is a
-     * binary class name, and <code>false</code> if <code>name</code>
-     * is the full path of a resource
-     *
-     * @return <code>true</code> if a class or resource named
-     * <code>name</code> is preferred for this loader, and
-     * <code>false</code> if a class or resource named
-     * <code>name</code> is not preferred for this loader
-     *
-     * @throws IOException if the preferred list cannot definitely be
-     * determined to exist or not exist, or if the preferred list
-     * contains a syntax error, or if the name is preferred with the
-     * default preferred entry or a wildcard preferred entry and the
-     * class or resource cannot definitely be determined to exist or
-     * not exist in the path of URLs, or if the name is preferred with
-     * a non-wildcard entry and the class or resource does not exist
-     * or cannot definitely be determined to exist in the path of URLs
-     **/
-    protected boolean isPreferredResource(final String name,
-					  final boolean isClass)
-	throws IOException
-    {
-        return isPreferredResource0(name, isClass);
-        // No longer need privilege the preferred list is now downloaded
-        // during construction.
-//	try {
-//	    return ((Boolean) AccessController.doPrivileged(
-//	        new PrivilegedExceptionAction() {
-//		    public Object run() throws IOException {
-//			boolean b = isPreferredResource0(name, isClass);
-//			return Boolean.valueOf(b);
-//		    }
-//	        }, acc)).booleanValue();
-//
-//	} catch (PrivilegedActionException e) {
-//	    throw (IOException) e.getException();
-//	}
-    }
-
-    /*
-     * Perform the work to determine if a resource name is preferred.
-     * 
-     * Synchronized removed to avoid possible ClassLoader deadlock.
-     */
-    private boolean isPreferredResource0(String name,
-						      boolean isClass)
-	throws IOException
-    {
-        if (exceptionWhileLoadingPreferred != null) throw exceptionWhileLoadingPreferred;
-	if (preferredResources == null) {
-	    return false;	// no preferred list: nothing is preferred
-	}
-
-	String resourceName = name;
-	if (isClass) {
-	    /* class name -> resource name */
-	    resourceName = name.replace('.', '/') + ".class";
-	}
-
-	/*
-	 * Determine if the class name is preferred. Making this
-	 * distinction is somewhat tricky because we need to cache the
-	 * preferred state (i.e. if the name is preferred and its
-	 * resource exists) in a way that avoids duplication of
-	 * preferred information - state information is stored back
-	 * into the preferred resources object for this class loader
-	 * and not held in a separate preferred settings cache.
-	 */
-	boolean resourcePreferred = false;
-
-	int state = preferredResources.getNameState(resourceName, isClass);
-	switch (state) {
-	case PreferredResources.NAME_NOT_PREFERRED:
-	    resourcePreferred = false;
-	    break;
-
-	case PreferredResources.NAME_PREFERRED_RESOURCE_EXISTS:
-	    resourcePreferred = true;
-	    break;
-
-	case PreferredResources.NAME_NO_PREFERENCE:
-	    Boolean wildcardPref =
-		preferredResources.getWildcardPreference(resourceName);
-	    if (wildcardPref == null) {
-		/* preferredDefault counts as a wild card */
-		wildcardPref = preferredResources.getDefaultPreference();
-	    }
-	    if (wildcardPref.booleanValue()) {
-		resourcePreferred =
-		    findResourceUpdateState(name, resourceName);
-	    }
-	    break;
-
-	case PreferredResources.NAME_PREFERRED:
-	    resourcePreferred =
-		findResourceUpdateState(name, resourceName);
-	    if (!resourcePreferred) {
-		throw new IOException("no resource found for " +
-				      "complete preferred name");
-	    }
-	    break;
-
-	default:
-	    throw new Error("unknown preference state");
-	}
-	return resourcePreferred;
-    }
-
-    /*
-     * Determine if a resource for a given preferred name exists.  If
-     * the resource exists record its new state in the
-     * preferredResources object.
-     */
-    private boolean findResourceUpdateState(String name,
-					    String resourceName)
-	throws IOException
-    {
-	if (findResource(resourceName) != null) {
-	    /* the resource is known to exist */
-	    preferredResources.setNameState(resourceName,
-	        PreferredResources.NAME_PREFERRED_RESOURCE_EXISTS);
-	    return true;
-	}
-
-	return false;
-    }
-
-    /**
-     * Loads a class with the specified name.
-     *
-     * <p><code>PreferredClassLoader</code> implements this method as
-     * follows:
-     *
-     * <p>This method first invokes {@link #findLoadedClass
-     * findLoadedClass} with <code>name</code>; if
-     * <code>findLoadedClass</code> returns a non-<code>null</code>
-     * <code>Class</code>, then this method returns that
-     * <code>Class</code>.
-     *
-     * <p>Otherwise, this method invokes {@link #isPreferredResource
-     * isPreferredResource} with <code>name</code> as the first
-     * argument and <code>true</code> as the second argument:
-     *
-     * <ul>
-     *
-     * <li>If <code>isPreferredResource</code> throws an
-     * <code>IOException</code>, then this method throws a
-     * <code>ClassNotFoundException</code> containing the
-     * <code>IOException</code> as its cause.
-     *
-     * <li>If <code>isPreferredResource</code> returns
-     * <code>true</code>, then this method invokes {@link #findClass
-     * findClass} with <code>name</code>.  If <code>findClass</code>
-     * throws an exception, then this method throws that exception.
-     * Otherwise, this method returns the <code>Class</code> returned
-     * by <code>findClass</code>, and if <code>resolve</code> is
-     * <code>true</code>, {@link #resolveClass resolveClass} is
-     * invoked with the <code>Class</code> before returning.
-     *
-     * <li>If <code>isPreferredResource</code> returns
-     * <code>false</code>, then this method invokes the superclass
-     * implementation of {@link ClassLoader#loadClass(String,boolean)
-     * loadClass} with <code>name</code> and <code>resolve</code> and
-     * returns the result.  If the superclass's <code>loadClass</code>
-     * throws an exception, then this method throws that exception.
-     *
-     * </ul>
-     *
-     * @param name the binary name of the class to load
-     *
-     * @param resolve if <code>true</code>, then {@link #resolveClass
-     * resolveClass} will be invoked with the loaded class before
-     * returning
-     *
-     * @return the loaded class
-     *
-     * @throws ClassNotFoundException if the class could not be found
-     **/
-    protected Class loadClass(String name, boolean resolve)
-	throws ClassNotFoundException
-    {
-	// First, check if the class has already been loaded
-	Class c = findLoadedClass(name);
-	if (c == null) {
-	    boolean preferred;
-	    try {
-		preferred = isPreferredResource(name, true);
-	    } catch (IOException e) {
-		throw new ClassNotFoundException(name +
-		    " (could not determine preferred setting; " +
-		    (firstURL != null ?
-		     "first URL: \"" + firstURL + "\"" : "no URLs") +
-		    ")", e);
-	    }
-	    if (preferred) {
-                synchronized (this){
-                    // Double check again in case the class has been loaded.
-                    c = findLoadedClass(name);
-                    if (c == null){
-                        c = findClass(name);
-                        if (resolve) resolveClass(c);
-                    }
-                }
-	    } else {
-		return super.loadClass(name, resolve);
-	    }
-	}
-	return c;
-    }
-	
-    /**
-     * Gets a resource with the specified name.
-     *
-     * <p><code>PreferredClassLoader</code> implements this method as
-     * follows:
-     *
-     * <p>This method invokes {@link #isPreferredResource
-     * isPreferredResource} with <code>name</code> as the first
-     * argument and <code>false</code> as the second argument:
-     *
-     * <ul>
-     *
-     * <li>If <code>isPreferredResource</code> throws an
-     * <code>IOException</code>, then this method returns
-     * <code>null</code>.
-     *
-     * <li>If <code>isPreferredResource</code> returns
-     * <code>true</code>, then this method invokes {@link
-     * #findResource findResource} with <code>name</code> and returns
-     * the result.
-     *
-     * <li>If <code>isPreferredResource</code> returns
-     * <code>false</code>, then this method invokes the superclass
-     * implementation of {@link ClassLoader#getResource getResource}
-     * with <code>name</code> and returns the result.
-     *
-     * </ul>
-     *
-     * @param name the name of the resource to get
-     *
-     * @return a <code>URL</code> for the resource, or
-     * <code>null</code> if the resource could not be found
-     **/
-    public URL getResource(String name) {
-	try {
-	    return (isPreferredResource(name, false) ?
-	        findResource(name) : super.getResource(name));
-	} catch (IOException e) {
-	}
-	return null;
-    }
-    
-    /**
-     * Gets an Enumeration of resources with the specified name.
-     *
-     * <p><code>PreferredClassLoader</code> implements this method as
-     * follows:
-     *
-     * <p>This method invokes {@link #isPreferredResource
-     * isPreferredResource} with <code>name</code> as the first
-     * argument and <code>false</code> as the second argument:
-     *
-     * <ul>
-     *
-     * <li>If <code>isPreferredResource</code> returns
-     * <code>true</code>, then this method invokes {@link
-     * #findResources findResources} with <code>name</code> and returns
-     * the results.
-     *
-     * <li>If <code>isPreferredResource</code> returns
-     * <code>false</code>, then this method invokes the superclass
-     * implementation of {@link ClassLoader#getResources getResources}
-     * with <code>name</code> and returns the result.
-     *
-     * </ul>
-     *
-     * @param name the name of the resource to get
-     *
-     * @return an <code>Enumeration</code> for the resource, the
-     * <code>Enumeration</code> is empty if the resource could not be found
-     * 
-     * @throws an IOException if isPreferredResource throws an IOException.
-     * 
-     * @since 3.0.0
-     **/
-    public Enumeration<URL> getResources(String name) throws IOException{
-        return (isPreferredResource(name, false) ?
-                findResources(name) : super.getResources(name));
-    }
-
-    /*
-     * Work around 4841786: wrap ClassLoader.definePackage so that if
-     * it throws an IllegalArgumentException because an ancestor
-     * loader "defined" the named package since the last time that
-     * ClassLoader.getPackage was invoked, then just return the
-     * result of invoking ClassLoader.getPackage again here.
-     * Fortunately, URLClassLoader.defineClass ignores the value
-     * returned by this method.
-     */
-    protected Package definePackage(String name, String specTitle,
-				    String specVersion, String specVendor,
-				    String implTitle, String implVersion,
-				    String implVendor, URL sealBase)
-    {
-	try {
-	    return super.definePackage(name, specTitle,
-				       specVersion, specVendor,
-				       implTitle, implVersion,
-				       implVendor, sealBase);
-	} catch (IllegalArgumentException e) {
-	    return getPackage(name);
-	}
-    }
-    
-//    protected Class<?> findClass(final String name)
-//	 throws ClassNotFoundException
-//    {   
-//        /* TODO: Override and create our own CodeSource
-//         * implementation that contains permissions.perm
-//         * After we retrieve the manifest, class bytes and
-//         * certificates, create the CodeSource we call
-//         * defineClass(String name, byte[]b, int off, int len, CodeSource cs)
-//         * 
-//         * This will be utilised by a class that overrides 
-//         * BasicProxyPreparer.getPermissions()
-//         * to retrieve the advisory permissions.
-//         */
-//        return super.findClass(name);
-//    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p><code>PreferredClassLoader</code> implements this method as
-     * follows:
-     *
-     * <p>If this <code>PreferredClassLoader</code> was constructed
-     * with a non-<code>null</code> export class annotation string,
-     * then this method returns that string.  Otherwise, this method
-     * returns a space-separated list of this loader's path of URLs.
-     **/
-    public String getClassAnnotation() {
-	return exportAnnotation;
-    }
-
-    /**
-     * Check that the current access control context has all of the
-     * permissions necessary to load classes from this loader.
-     */
-    void checkPermissions() {
-	checkPermissions(permissions);
-    }
-
-    /**
-     * Check that the current access control context has all of the
-     * given permissions.
-     */
-    private static void checkPermissions(PermissionCollection perms) {
-	SecurityManager sm = System.getSecurityManager();
-	if (sm != null) {		// should never be null?
-	    Enumeration en = perms.elements();
-	    while (en.hasMoreElements()) {
-		sm.checkPermission((Permission) en.nextElement());
-	    }
-	}
-    }
-
-    /**
-     * Returns the static permissions to be automatically granted to
-     * classes loaded from the specified {@link CodeSource} and
-     * defined by this class loader.
-     *
-     * <p><code>PreferredClassLoader</code> implements this method as
-     * follows:
-     *
-     * <p>If there is a security manager and this
-     * <code>PreferredClassLoader</code> was constructed to enforce
-     * {@link DownloadPermission}, then this method checks that the
-     * current security policy grants the specified
-     * <code>CodeSource</code> the permission
-     * <code>DownloadPermission("permit")</code>; if that check fails,
-     * then this method throws a <code>SecurityException</code>.
-     *
-     * <p>Then this method invokes the superclass implementation of
-     * {@link #getPermissions getPermissions} and returns the result.
-     *
-     * @param codeSource the <code>CodeSource</code> to return the
-     * permissions to be granted to
-     *
-     * @return the permissions to be granted to the
-     * <code>CodeSource</code>
-     *
-     * @throws SecurityException if there is a security manager, this
-     * <code>PreferredClassLoader</code> was constructed to enforce
-     * <code>DownloadPermission</code>, and the current security
-     * policy does not grant the specified <code>CodeSource</code> the
-     * permission <code>DownloadPermission("permit")</code>
-     **/
-    protected PermissionCollection getPermissions(CodeSource codeSource) {
-	if (requireDlPerm) {
-	    SecurityManager sm = System.getSecurityManager();
-	    if (sm != null) {
-		ProtectionDomain pd =
-		    new ProtectionDomain(codeSource, null, this, null);
-
-		if (!pd.implies(downloadPermission)) {
-		    throw new SecurityException(
-			"CodeSource not permitted to define class: " +
-			codeSource);
-		}
-	    }
-	}
-	
-  	return super.getPermissions(codeSource);
-    }
-
-    /**
-     * Returns a string representation of this class loader.
-     **/
-    public String toString() {
-	return super.toString() + "[\"" + exportAnnotation + "\"]";
-    }
-
-    /**
-     * Return the access control context that a loader for the given
-     * codebase URL path should execute with.
-     */
-    static AccessControlContext getLoaderAccessControlContext(URL[] urls)
-    {
-	/*
-	 * The approach used here is taken from the similar method
-	 * getAccessControlContext() in the sun.applet.AppletPanel class.
-	 */
-	// begin with permissions granted to all code in current policy
-	PermissionCollection perms = (PermissionCollection)
-	    AccessController.doPrivileged(new PrivilegedAction() {
-		public Object run() {
-		    CodeSource codesource =
-			new CodeSource(null, (Certificate[]) null);
-		    Policy p = java.security.Policy.getPolicy();
-		    if (p != null) {
-			return p.getPermissions(codesource);
-		    } else {
-			return new Permissions();
-		    }
-		}
-	    });
-
-	// createClassLoader permission needed to create loader in context
-	perms.add(new RuntimePermission("createClassLoader"));
-
-	// add permissions to read any "java.*" property
-	perms.add(new java.util.PropertyPermission("java.*","read"));
-
-	// add permissions required to load from codebase URL path
-	addPermissionsForURLs(urls, perms, true);
-
-	/*
-	 * Create an AccessControlContext that consists of a single
-	 * protection domain with only the permissions calculated above.
-         * Comment added 7th May 2010 by Peter Firmstone:
-         * This did call the pre java 1.4 constructor which causes the
-         * ProtectionDomain to not consult the Policy, this
-         * had the effect of not allowing Dynamic Permission changes to be
-         * effected by the Policy.  It doesn't affect the existing
-         * DynamicPolicy implementation as it returns the Permissions
-         * allowing the ProtectionDomain domain combiner to combine
-         * cached permissions with those from the Policy.
-         * ProtectionDomain(CodeSource, PermissionCollection)
-         * By utilising this earlier constructor it also prevents
-         * RevokeableDynamicPolicy, hence the constructor change.  
-	 */
-	ProtectionDomain pd = new ProtectionDomain(
-	    new CodeSource((urls.length > 0 ? urls[0] : null),
-			   (Certificate[]) null), perms, null, null);
-	return new AccessControlContext(new ProtectionDomain[] { pd });
-    }
-
-    /**
-     * Adds to the specified permission collection the permissions
-     * necessary to load classes from a loader with the specified URL
-     * path; if "forLoader" is true, also adds URL-specific
-     * permissions necessary for the security context that such a
-     * loader operates within, such as permissions necessary for
-     * granting automatic permissions to classes defined by the
-     * loader.  A given permission is only added to the collection if
-     * it is not already implied by the collection.
-     **/
-    static void addPermissionsForURLs(URL[] urls,
-				      PermissionCollection perms,
-				      boolean forLoader)
-    {
-	for (int i = 0; i < urls.length; i++) {
-	    URL url = urls[i];
-	    try {
-		URLConnection urlConnection = url.openConnection();
-		Permission p = urlConnection.getPermission();
-		if (p != null) {
-		    if (p instanceof FilePermission) {
-			/*
-			 * If the codebase is a file, the permission required
-			 * to actually read classes from the codebase URL is
-			 * the permission to read all files beneath the last
-			 * directory in the file path, either because JAR
-			 * files can refer to other JAR files in the same
-			 * directory, or because permission to read a
-			 * directory is not implied by permission to read the
-			 * contents of a directory, which all that might be
-			 * granted.
-			 */
-			String path = p.getName();
-			int endIndex = path.lastIndexOf(File.separatorChar);
-			if (endIndex != -1) {
-			    path = path.substring(0, endIndex+1);
-			    if (path.endsWith(File.separator)) {
-				path += "-";
-			    }
-			    Permission p2 = new FilePermission(path, "read");
-			    if (!perms.implies(p2)) {
-				perms.add(p2);
-			    }
-			} else {
-			    /*
-			     * No directory separator: use permission to
-			     * read the file.
-			     */
-			    if (!perms.implies(p)) {
-				perms.add(p);
-			    }
-			}
-		    } else {
-			if (!perms.implies(p)) {
-			    perms.add(p);
-			}
-
-	                /*
-			 * If the purpose of these permissions is to grant
-			 * them to an instance of a URLClassLoader subclass,
-			 * we must add permission to connect to and accept
-			 * from the host of non-"file:" URLs, otherwise the
-			 * getPermissions() method of URLClassLoader will
-			 * throw a security exception.
-			 */
-			if (forLoader) {
-			    // get URL with meaningful host component
-			    URL hostURL = url;
-			    for (URLConnection conn = urlConnection;
-				 conn instanceof JarURLConnection;)
-			    {
-				hostURL =
-				    ((JarURLConnection) conn).getJarFileURL();
-				conn = hostURL.openConnection();
-			    }
-			    String host = hostURL.getHost();
-			    if (host != null &&
-				p.implies(new SocketPermission(host,
-							       "resolve")))
-			    {
-				Permission p2 =
-				    new SocketPermission(host,
-							 "connect,accept");
-				if (!perms.implies(p2)) {
-				    perms.add(p2);
-				}
-			    }
-			}
-		    }
-		}
-	    } catch (IOException e) {
-		/*
-		 * This shouldn't happen, although it is declared to be
-		 * thrown by openConnection() and getPermission().  If it
-		 * does, don't bother granting or requiring any permissions
-		 * for this URL.
-		 */
-	    } catch (NullPointerException e){
-                // Sun Bug ID: 6536522
-            }
-	}
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 net.jini.loader.pref;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.SocketPermission;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.security.cert.Certificate;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.jini.loader.ClassAnnotation;
+import net.jini.loader.DownloadPermission;
+import org.apache.river.api.net.RFC3986URLClassLoader;
+import org.apache.river.api.security.PermissionComparator;
+import org.apache.river.api.security.PermissionGrant;
+
+/**
+ * A class loader that supports preferred classes.
+ *
+ * <p>A preferred class is a class that is to be loaded by a class
+ * loader without the loader delegating to its parent class loader
+ * first.  Resources may also be preferred.
+ *
+ * <p>Like {@link java.net.URLClassLoader},
+ * <code>PreferredClassLoader</code> loads classes and resources from
+ * a search path of URLs.  If a URL in the path ends with a
+ * <code>'/'</code>, it is assumed to refer to a directory; otherwise,
+ * the URL is assumed to refer to a JAR file.
+ *
+ * <p>The location of the first URL in the path can contain a
+ * <i>preferred list</i> for the entire path.  A preferred list
+ * declares names of certain classes and other resources throughout
+ * the path as being <i>preferred</i> or not.  When a
+ * <code>PreferredClassLoader</code> is asked to load a class or
+ * resource that is preferred (according to the preferred list) and
+ * the class or resource exists in the loader's path of URLs, the
+ * loader will not delegate first to its parent class loader as it
+ * otherwise would do; instead, it will attempt to load the class or
+ * resource from its own path of URLs only.
+ *
+ * <p>The preferred list for a path of URLs, if one exists, is located
+ * relative to the first URL in the path.  If the first URL refers to
+ * a JAR file, then the preferred list is the contents of the file
+ * named <code>"META-INF/PREFERRED.LIST"</code> within that JAR file.
+ * If the first URL refers to a directory, then the preferred list is
+ * the contents of the file at the location
+ * <code>"META-INF/PREFERRED.LIST"</code> relative to that directory
+ * URL.  If there is no preferred list at the required location, then
+ * no classes or resources are preferred for the path of URLs.  A
+ * preferred list at any other location (such as relative to one of
+ * the other URLs in the path) is ignored.
+ *
+ * <p>Note that a class or resource is only considered to be preferred
+ * if the preferred list declares the name of the class or resource as
+ * being preferred and the class or resource actually exists in the
+ * path of URLs.
+ *
+ * <h3>Preferred List Syntax</h3>
+ *
+ * A preferred list is a UTF-8 encoded text file, with lines separated
+ * by CR&nbsp;LF, LF, or CR (not followed by an LF).  Multiple
+ * whitespace characters in a line are equivalent to a single
+ * whitespace character, and whitespace characters at the beginning or
+ * end of a line are ignored.  If the first non-whitespace character
+ * of a line is <code>'#'</code>, the line is a comment and is
+ * equivalent to a blank line.
+ *
+ * <p>The first line of a preferred list must contain a version
+ * number in the following format:
+ *
+ * <pre>
+ *     PreferredResources-Version: 1.<i>x</i>
+ * </pre>
+ *
+ * This specification defines only version 1.0, but
+ * <code>PreferredClassLoader</code> will parse any version
+ * 1.<i>x</i>, <i>x</i>>=0 with the format and semantics specified
+ * here.
+ *
+ * <p>After the version number line, a preferred list comprises an
+ * optional default preferred entry followed by zero or more named
+ * preferred entries.  A preferred list must contain either a default
+ * preferred entry or at least one named preferred entry.  Blank lines
+ * are allowed before and after preferred entries, as well as between
+ * the lines of a named preferred entry.
+ *
+ * <p>A default preferred entry is a single line in the following
+ * format:
+ *
+ * <pre>
+ *     Preferred: <i>preferred-setting</i>
+ * </pre>
+ *
+ * where <i>preferred-setting</i> is a non-empty sequence of
+ * characters.  If <i>preferred-setting</i> equals <code>"true"</code>
+ * (case insensitive), then resource names not matched by any of the
+ * named preferred entries are by default preferred; otherwise,
+ * resource names not matched by any of the named preferred entries
+ * are by default not preferred.  If there is no default preferred
+ * entry, then resource names are by default not preferred.
+ *
+ * <p>A named preferred entry is two lines in the following format:
+ *
+ * <pre>
+ *     Name: <i>name-expression</i>
+ *     Preferred: <i>preferred-setting</i>
+ * </pre>
+ *
+ * where <i>name-expression</i> and <i>preferred-setting</i> are
+ * non-empty sequences of characters.  If <i>preferred-setting</i>
+ * equals <code>"true"</code> (case insensitive), then resource names
+ * that are matched by <i>name-expression</i> (and not any more
+ * specific named preferred entries) are preferred; otherwise,
+ * resource names that are matched by <i>name-expression</i> (and not
+ * any more specific named preferred entries) are not preferred.
+ * 
+ * <p>If <i>name-expression</i> ends with <code>".class"</code>, it
+ * matches a class whose binary name is <i>name-expression</i> without
+ * the <code>".class"</code> suffix and with each <code>'/'</code>
+ * character replaced with a <code>'.'</code>.  It also matches any
+ * class whose binary name starts with that same value followed by a
+ * <code>'$'</code>; this rule is intended to match nested classes
+ * that have an enclosing class of that name, so that the preferred
+ * settings of a class and all of its nested classes are the same by
+ * default.  It is possible, but strongly discouraged, to override the
+ * preferred setting of a nested class with a named preferred entry
+ * that explicitly matches the nested class's binary name.
+ *
+ * <p><i>name-expression</i> may match arbitrary resource names as
+ * well as class names, with path elements separated by
+ * <code>'/'</code> characters.
+ *
+ * <p>If <i>name-expression</i> ends with <code>"/"</code> or
+ * <code>"/*"</code>, then the entry is a directory wildcard entry
+ * that matches all resources (including classes) in the named
+ * directory.  If <i>name-expression</i> ends with <code>"/-"</code>,
+ * then the entry is a namespace wildcard entry that matches all
+ * resources (including classes) in the named directory and all of its
+ * subdirectories.
+ *
+ * <p>When more than one named preferred entry matches a class or
+ * resource name, then the most specific entry takes precedence.  A
+ * non-wildcard entry is more specific than a wildcard entry.  A
+ * directory wildcard entry is more specific than a namespace wildcard
+ * entry.  A namespace wildcard entry with more path elements is more
+ * specific than a namespace wildcard entry with fewer path elements.
+ * Given two non-wildcard entries, the entry with the longer
+ * <i>name-expression</i> is more specific (this rule is only
+ * significant when matching a class).  The order of named preferred
+ * entries is insignificant.
+ *
+ * <h3>Example Preferred List</h3>
+ *
+ * <p>Following is an example preferred list:
+ *
+ * <pre>
+ *     PreferredResources-Version: 1.0
+ *     Preferred: false
+ *
+ *     Name: com/foo/FooBar.class
+ *     Preferred: true
+ *
+ *     Name: com/foo/*
+ *     Preferred: false
+ *
+ *     Name: com/foo/-
+ *     Preferred: true
+ *
+ *     Name: image-files/*
+ *     Preferred: mumble
+ * </pre>
+ *
+ * <p>The class <code>com.foo.FooBar</code> is preferred, as well as
+ * any nested classes that have it as an enclosing class.  All other
+ * classes in the <code>com.foo</code> package are not preferred
+ * because of the directory wildcard entry.  Classes in subpackages of
+ * <code>com.foo</code> are preferred because of the namespace
+ * wildcard entry.  Resources in the directory <code>"com/foo/"</code>
+ * are not preferred, and resources in subdirectories of
+ * <code>"com/foo/"</code> are preferred.  Resources in the directory
+ * <code>"image-files/"</code> are not preferred because preferred
+ * settings other than <code>"true"</code> are interpreted as false.
+ * Classes that are in a package named <code>com.bar</code> are not
+ * preferred because of the default preferred entry.
+ *
+ * @author Sun Microsystems, Inc.
+ * @since 2.0
+ **/
+public class PreferredClassLoader extends RFC3986URLClassLoader
+    implements ClassAnnotation
+{
+    /**
+     * well known name of resource that contains the preferred list in
+     * a path of URLs
+     **/
+    private static final String PREF_NAME = "META-INF/PREFERRED.LIST";
+
+    /** first URL in the path, or null if none */
+    private final URL firstURL;
+
+    /** class annotation string for classes defined by this loader */
+    private final String exportAnnotation;
+
+    /** permissions required to access loader through public API */
+    private final Collection<Permission> permissions;
+
+    /** security context for loading classes and resources */
+    private final AccessControlContext acc;
+
+    /** permission required to download code? */
+    private final boolean requireDlPerm;
+
+    /** URLStreamHandler to use when creating new "jar:" URLs */
+    private final URLStreamHandler jarHandler;
+
+    /** PreferredResources for this loader (null if no preferred list) */
+    private final PreferredResources preferredResources;
+    
+    private final IOException exceptionWhileLoadingPreferred;
+
+    private static final Permission downloadPermission =
+	new DownloadPermission();
+
+    /**
+     * Creates a new <code>PreferredClassLoader</code> that loads
+     * classes and resources from the specified path of URLs and
+     * delegates to the specified parent class loader.
+     *
+     * <p>If <code>exportAnnotation</code> is not <code>null</code>,
+     * then it will be used as the return value of the loader's {@link
+     * #getClassAnnotation getClassAnnotation} method.  If
+     * <code>exportAnnotation</code> is <code>null</code>, the
+     * loader's <code>getClassAnnotation</code> method will return a
+     * space-separated list of the URLs in the specified path.  The
+     * <code>exportAnnotation</code> parameter can be used to specify
+     * so-called "export" URLs, from which other parties should load
+     * classes defined by the loader and which are different from the
+     * "import" URLs that the classes are actually loaded from.
+     *
+     * <p>If <code>requireDlPerm</code> is <code>true</code>, the
+     * loader's {@link #getPermissions getPermissions} method will
+     * require that the {@link CodeSource} of any class defined by the
+     * loader is granted {@link DownloadPermission}.
+     *
+     * @param urls the path of URLs to load classes and resources from
+     *
+     * @param parent the parent class loader for delegation
+     *
+     * @param exportAnnotation the export class annotation string to
+     * use for classes defined by this loader, or <code>null</code>
+     *
+     * @param requireDlPerm if <code>true</code>, the loader will only
+     * define classes with a {@link CodeSource} that is granted {@link
+     * DownloadPermission}
+     *
+     * @throws SecurityException if there is a security manager and an
+     * invocation of its {@link SecurityManager#checkCreateClassLoader
+     * checkCreateClassLoader} method fails
+     **/
+    public PreferredClassLoader(URL[] urls,
+				ClassLoader parent,
+				String exportAnnotation,
+				boolean requireDlPerm)
+    {
+	this(urls, parent, exportAnnotation, requireDlPerm, null);
+    }
+
+    /**
+     * Creates a new <code>PreferredClassLoader</code> that loads
+     * classes and resources from the specified path of URLs,
+     * delegates to the specified parent class loader, and uses the
+     * specified {@link URLStreamHandlerFactory} when creating new URL
+     * objects.  This constructor passes <code>factory</code> to the
+     * superclass constructor that has a
+     * <code>URLStreamHandlerFactory</code> parameter.
+     *
+     * <p>If <code>exportAnnotation</code> is not <code>null</code>,
+     * then it will be used as the return value of the loader's {@link
+     * #getClassAnnotation getClassAnnotation} method.  If
+     * <code>exportAnnotation</code> is <code>null</code>, the
+     * loader's <code>getClassAnnotation</code> method will return a
+     * space-separated list of the URLs in the specified path.  The
+     * <code>exportAnnotation</code> parameter can be used to specify
+     * so-called "export" URLs, from which other parties should load
+     * classes defined by the loader and which are different from the
+     * "import" URLs that the classes are actually loaded from.
+     *
+     * <p>If <code>requireDlPerm</code> is <code>true</code>, the
+     * loader's {@link #getPermissions getPermissions} method will
+     * require that the {@link CodeSource} of any class defined by the
+     * loader is granted {@link DownloadPermission}.
+     *
+     * @param urls the path of URLs to load classes and resources from
+     *
+     * @param parent the parent class loader for delegation
+     *
+     * @param exportAnnotation the export class annotation string to
+     * use for classes defined by this loader, or <code>null</code>
+     *
+     * @param requireDlPerm if <code>true</code>, the loader will only
+     * define classes with a {@link CodeSource} that is granted {@link
+     * DownloadPermission}
+     *
+     * @param factory the <code>URLStreamHandlerFactory</code> to use
+     * when creating new URL objects, or <code>null</code>
+     *
+     * @throws SecurityException if there is a security manager and an
+     * invocation of its {@link SecurityManager#checkCreateClassLoader
+     * checkCreateClassLoader} method fails
+     *
+     * @since 2.1
+     **/
+    public PreferredClassLoader(URL[] urls,
+				ClassLoader parent,
+				String exportAnnotation,
+				boolean requireDlPerm,
+				URLStreamHandlerFactory factory)
+    {
+	super(urls, parent, factory);
+	firstURL = (urls.length > 0 ? urls[0] : null);
+	if (exportAnnotation != null) {
+	    this.exportAnnotation = exportAnnotation;
+	} else {
+	    /*
+	     * Caching the value of class annotation string here
+	     * assumes that the protected method addURL() is never
+	     * called on this class loader.
+	     */
+	    this.exportAnnotation = urlsToPath(urls);
+	}
+	this.requireDlPerm = requireDlPerm;
+	if (factory != null) {
+	    jarHandler = factory.createURLStreamHandler("jar");
+	} else {
+	    jarHandler = null;
+	}
+
+	acc = AccessController.getContext();
+	    
+	/*
+	 * Precompute the permissions required to access the loader.
+	 */
+	PermissionCollection permissions = new Permissions();
+	addPermissionsForURLs(urls, permissions, false);
+        /*
+         * If a preferred list exists relative to the first URL of this
+         * loader's path, sets this loader's PreferredResources according
+         * to that preferred list.  If no preferred list exists relative
+         * to the first URL, leaves this loader's PreferredResources null.
+         *
+         * Throws IOException if an I/O exception occurs from which the
+         * existence of a preferred list relative to the first URL cannot
+         * be definitely determined.
+         *
+         * This method must only be invoked while synchronized on this
+         * PreferredClassLoader, and it must not be invoked again after it
+         * has completed successfully.
+         * 
+         * This was called from privileged context when it as part of a method,
+         * but we shouldn't need to do that here, any code that can construct
+         * a ClassLoader has privilege.  Note that InputStream is not subject
+         * to deserialization attacks like ObjectInputStream.
+         * 
+         * Also synchronization is not required as it is called from within
+         * the constructor now, this change was made to remove any possiblity
+         * of deadlock by minimising locking.
+         */
+        IOException except = null;
+        PreferredResources pref = null;
+	if (firstURL != null) {
+            InputStream prefIn = null;
+            try {
+                prefIn = getPreferredInputStream(firstURL);
+                if (prefIn != null) {
+                    try {
+                        pref = new PreferredResources(prefIn);
+                    } finally {
+                        try {
+                            prefIn.close();
+                        } catch (IOException e) {
+                        }
+                    }
+                }
+            } catch (IOException ex) {
+                Logger.getLogger(PreferredClassLoader.class.getName()).log(Level.SEVERE, "Unable to access preferred resources", ex);
+                except = ex;
+            } finally {
+                try {
+                    if (prefIn != null){
+                        prefIn.close();
+                    }
+                } catch (IOException ex) {
+                    Logger.getLogger(PreferredClassLoader.class.getName()).log(Level.SEVERE, "Problem closing preferred resources input stream", ex);
+                } 
+                
+            }
+	}
+        exceptionWhileLoadingPreferred = except;
+        preferredResources = pref;
+        this.permissions = new LinkedList<Permission>();
+        Enumeration<Permission> en = permissions.elements();
+        while(en.hasMoreElements()){
+            this.permissions.add(en.nextElement());
+    }
+    }
+
+    /**
+     * Convert an array of URL objects into a corresponding string
+     * containing a space-separated list of URLs.
+     *
+     * Note that if the array has zero elements, the return value is
+     * null, not the empty string.
+     */
+    static String urlsToPath(URL[] urls) {
+	if (urls.length == 0) {
+	    return null;
+	} else if (urls.length == 1) {
+	    return urls[0].toExternalForm();
+	} else {
+	    StringBuffer path = new StringBuffer(urls[0].toExternalForm());
+	    for (int i = 1; i < urls.length; i++) {
+		path.append(' ');
+		path.append(urls[i].toExternalForm());
+	    }
+	    return path.toString();
+	}
+    }
+
+    /**
+     * Creates a new instance of <code>PreferredClassLoader</code>
+     * that loads classes and resources from the specified path of
+     * URLs and delegates to the specified parent class loader.
+     *
+     * <p>The <code>exportAnnotation</code> and
+     * <code>requireDlPerm</code> parameters have the same semantics
+     * as they do for the constructors.
+     *
+     * <p>The {@link #loadClass loadClass} method of the returned
+     * <code>PreferredClassLoader</code> will, if there is a security
+     * manager, invoke its {@link SecurityManager#checkPackageAccess
+     * checkPackageAccess} method with the package name of the class
+     * to load before attempting to load the class; this could result
+     * in a <code>SecurityException</code> being thrown from
+     * <code>loadClass</code>.
+     *
+     * @param urls the path of URLs to load classes and resources from
+     *
+     * @param parent the parent class loader for delegation
+     *
+     * @param exportAnnotation the export class annotation string to
+     * use for classes defined by this loader, or <code>null</code>
+     *
+     * @param requireDlPerm if <code>true</code>, the loader will only
+     * define classes with a {@link CodeSource} that is granted {@link
+     * DownloadPermission}
+     *
+     * @return the new <code>PreferredClassLoader</code> instance
+     *
+     * @throws SecurityException if the current security context does
+     * not have the permissions necessary to connect to all of the
+     * URLs in <code>urls</code>
+     **/
+    public static PreferredClassLoader
+	newInstance(final URL[] urls,
+		    final ClassLoader parent,
+		    final String exportAnnotation,
+		    final boolean requireDlPerm)
+    {
+	/* ensure caller has permission to access all urls */
+	PermissionCollection perms = new Permissions();
+	addPermissionsForURLs(urls, perms, false);
+	checkPermissions(perms);
+
+	AccessControlContext acc = getLoaderAccessControlContext(urls);
+	/* Use privileged status to return a new class loader instance */
+	return (PreferredClassLoader)
+	    AccessController.doPrivileged(new PrivilegedAction() {
+		public Object run() {
+		    return new PreferredFactoryClassLoader(urls, parent,
+			exportAnnotation, requireDlPerm);
+		}
+	    }, acc);
+    }
+
+    
+
+    /**
+     * Returns an InputStream from which the preferred list relative
+     * to the specified URL can be read, or null if the there is
+     * definitely no preferred list relative to the URL.  If the URL's
+     * path ends with "/", then the preferred list is sought at the
+     * location "META-INF/PREFERRED.LIST" relative to the URL;
+     * otherwise, the URL is assumed to refer to a JAR file, and the
+     * preferred list is sought within that JAR file, as the entry
+     * named "META-INF/PREFERRED.LIST".
+     *
+     * Throws IOException if an I/O exception occurs from which the
+     * existence of a preferred list relative to the specified URL
+     * cannot be definitely determined.
+     **/
+    private InputStream getPreferredInputStream(URL firstURL)
+	throws IOException
+    {
+	URL prefListURL = null;
+	try {
+	    URL baseURL;	// base URL to load PREF_NAME relative to
+	    if (firstURL.getFile().endsWith("/")) { // REMIND: track 4915051
+		baseURL = firstURL;
+	    } else {
+		/*
+		 * First try to get a definite answer about the existence of a
+		 * PREFERRED.LIST that doesn't by-pass a JAR file cache, if
+		 * any. If that fails we determine if the JAR file exists by
+		 * attempting to access it directly, without using a "jar:" URL,
+		 * because the "jar:" URL handler can mask the distinction
+		 * between definite lack of existence and less definitive
+		 * errors. Unfortunately, this direct access circumvents the JAR
+		 * file caching done by the "jar:" handler, so it ends up
+		 * causing duplicate requests of the JAR file on first use when
+		 * our first attempt fails. (For HTTP-protocol URLs, the initial
+		 * request will use HEAD instead of GET.)
+		 *
+		 * After determining that the JAR file exists, attempt to
+		 * retrieve the preferred list using a "jar:" URL, like
+		 * URLClassLoader uses to load resources from a JAR file.
+		 */
+		if (jarExists(firstURL)) {
+		    baseURL = getBaseJarURL(firstURL);
+		} else {
+		    return null;
+		}
+	    }
+	    prefListURL = new URL(baseURL, PREF_NAME);
+	    URLConnection preferredConnection =
+		getPreferredConnection(prefListURL, false);
+	    if (preferredConnection != null) {
+		return preferredConnection.getInputStream();
+	    } else {
+		return null;
+	    }
+	} catch (IOException e) {
+	    /*
+	     * Assume that any IOException thrown while attempting to
+	     * access a "file:" URL and any FileNotFoundException
+	     * implies that there is definitely no preferred list
+	     * relative to the specified URL.
+	     */
+	    if (firstURL.getProtocol().equals("file") ||
+		e instanceof FileNotFoundException)
+	    {
+		return null;
+	    } else {
+		throw e;
+	    }
+	}
+    }
+
+    /* cache existence of jar files referenced by codebase urls */
+    private static final Set<String> existSet = new HashSet<String>(11);
+
+    /*
+     * Determine if a jar file in a given URL location exists.  If the
+     * jar exists record the jar file's URL in a cache of URL strings.
+     *
+     * Recording the existence of the jar prevents the need to
+     * re-determine the jar's existence on subsequent downloads of the
+     * jar in potentially different preferred class loaders.
+     */
+    private boolean jarExists(URL firstURL) throws IOException {
+	boolean exists;
+        
+	synchronized (existSet) {
+            // The comment says in a cache of URL strings, URL in Set, bad.
+	    exists = existSet.contains(firstURL.toString());
+	}
+
+	if (!exists) {
+	    /*
+	     * first try to get a definite answer of the existence of a JAR
+	     * file, if no IOException is thrown when obtaining it through the
+	     * "jar:" protocol we can safely assume the JAR file is locally
+	     * available upon the attempt (elsewhere) to obtain the preferred
+	     * list
+	     */
+            URL baseURL = null;
+	    try {
+                baseURL = getBaseJarURL(firstURL);
+		((JarURLConnection) baseURL.openConnection()).getManifest();
+		exists = true;
+	    } catch (IOException e) {
+		// we still have no definite answer on whether the JAR file
+		// and therefore the PREFERRED.LIST exists
+	    } catch (NullPointerException e){
+                // Sun Bug ID: 6536522
+                // NullPointerException is thrown instead of MalformedURLException
+                // Case is the same as above, we have no definite answer on
+                // whether the JAR file and therefore the PREFERRED.LIST exists.
+                System.err.println("NPE thrown while trying to open connection :" +
+                        baseURL);
+                e.printStackTrace(System.err);
+            }
+
+	    if (!exists) {
+		exists = (getPreferredConnection(firstURL, true) != null);
+	    }
+	    if (exists) {
+		synchronized (existSet) {
+		    existSet.add(firstURL.toString());
+		}
+	    }
+	}
+	return exists;
+    }
+
+    /**
+     * Returns a "jar:" URL for the root directory of the JAR file at
+     * the specified URL.  If this loader was constructed with a
+     * URLStreamHandlerFactory, then the returned URL will have a
+     * URLStreamHandler that was created by the factory.
+     **/
+    private URL getBaseJarURL(final URL url) throws MalformedURLException {
+	if (jarHandler == null) {
+	    return new URL("jar", "", -1, url + "!/");
+	} else {
+	    try {
+		return (URL) AccessController.doPrivileged(
+		    new PrivilegedExceptionAction() {
+			public Object run() throws MalformedURLException {
+			    return new URL("jar", "", -1, url + "!/",
+					   jarHandler);
+			}
+		    });
+	    } catch (PrivilegedActionException e) {
+		throw (MalformedURLException) e.getCause();
+	    }
+	}
+    }
+    
+    /**
+     * Obtain a url connection from which an input stream that
+     * contains a preferred list can be obtained.
+     *
+     * For http urls, attempts to use http response codes to
+     * determine if a preferred list exists or is definitely not
+     * found.  Simply attempts to open a connection to other kinds
+     * of non-file urls.  If the attempt fails, an IOException is
+     * thrown to user code.
+     *
+     * Returns null if the preferred list definitely does not
+     * exist.  Rethrows all indefinite IOExceptions generated
+     * while trying to open a connection to the preferred list.
+     *
+     * The caller has the option to close the connection after the
+     * resource has been detected (as will happen when probing for a
+     * PREFERRED.LIST).
+     */
+    private URLConnection getPreferredConnection(URL url, boolean closeAfter)
+	throws IOException
+    {
+	if (url.getProtocol().equals("file")) {
+	    return url.openConnection();
+	}
+	URLConnection closeConn = null;
+	URLConnection conn = null;
+	try {
+	    closeConn = url.openConnection();
+	    conn = closeConn;
+						 
+	    /* check status of http urls  */
+	    if (conn instanceof HttpURLConnection) {
+		HttpURLConnection hconn = (HttpURLConnection) conn;
+		if (closeAfter) {
+		    hconn.setRequestMethod("HEAD");
+		}
+		int responseCode = hconn.getResponseCode();//NPE
+		
+		switch (responseCode) {
+		case HttpURLConnection.HTTP_OK:
+		case HttpURLConnection.HTTP_NOT_AUTHORITATIVE:
+		    /* the preferred list exists */
+		    break;
+
+		    /* 404, not found appears to be handled by
+		     * HttpURLConnection (FileNotFoundException is
+		     * thrown), but to be safe do the right thing here as
+		     * well.
+		     */
+		case HttpURLConnection.HTTP_NOT_FOUND:
+		case HttpURLConnection.HTTP_FORBIDDEN:
+		case HttpURLConnection.HTTP_GONE:
+		    /* list definitely does not exist */
+		    conn = null;
+		    break;
+		default:
+		    /* indefinite response code */
+		    throw new IOException("Indefinite http response for " +
+			"preferred list request:" +
+			hconn.getResponseMessage());
+		}
+	    }
+        } catch (RuntimeException e){
+            if ( e instanceof NullPointerException || e.getCause() instanceof NullPointerException) {
+                // Sun Bug ID: 6536522
+                throw new IOException(url.toString(), e);
+            } else {
+                throw e;
+            }
+	} finally {
+	    if (closeAfter && (closeConn != null)) {
+		/* clean up after... */
+		try {
+		    closeConn.getInputStream().close();//RTE NPE
+		} catch (IOException e) {
+		} catch (RuntimeException e){
+                    if ( e instanceof NullPointerException || e.getCause() instanceof NullPointerException) {
+                        // Sun Bug ID: 6536522
+                        // swallow
+                        e.printStackTrace(System.err);
+                    } else {
+                        throw e;
+                    }
+                }
+	    }
+	}
+
+	return conn;
+    }
+
+    /**
+     * Returns <code>true</code> if a class or resource with the
+     * specified name is preferred for this class loader, and
+     * <code>false</code> if a class or resource with the specified
+     * name is not preferred for this loader.
+     *
+     * <p>If <code>isClass</code> is <code>true</code>, then
+     * <code>name</code> is interpreted as the binary name of a class;
+     * otherwise, <code>name</code> is interpreted as the full path of
+     * a resource.
+     *
+     * <p>This method only returns <code>true</code> if a class or
+     * resource with the specified name exists in the this loader's
+     * path of URLs and the name is preferred in the preferred list.
+     * This method returns <code>false</code> if the name is not
+     * preferred in the preferred list or if the name is preferred
+     * with the default preferred entry or a wildcard preferred entry
+     * and the class or resource does not exist in the path of URLs.
+     *
+     * @param name the name of the class or resource
+     *
+     * @param isClass <code>true</code> if <code>name</code> is a
+     * binary class name, and <code>false</code> if <code>name</code>
+     * is the full path of a resource
+     *
+     * @return <code>true</code> if a class or resource named
+     * <code>name</code> is preferred for this loader, and
+     * <code>false</code> if a class or resource named
+     * <code>name</code> is not preferred for this loader
+     *
+     * @throws IOException if the preferred list cannot definitely be
+     * determined to exist or not exist, or if the preferred list
+     * contains a syntax error, or if the name is preferred with the
+     * default preferred entry or a wildcard preferred entry and the
+     * class or resource cannot definitely be determined to exist or
+     * not exist in the path of URLs, or if the name is preferred with
+     * a non-wildcard entry and the class or resource does not exist
+     * or cannot definitely be determined to exist in the path of URLs
+     **/
+    protected boolean isPreferredResource(final String name,
+					  final boolean isClass)
+	throws IOException
+    {
+        return isPreferredResource0(name, isClass);
+        // No longer need privilege the preferred list is now downloaded
+        // during construction.
+//	try {
+//	    return ((Boolean) AccessController.doPrivileged(
+//	        new PrivilegedExceptionAction() {
+//		    public Object run() throws IOException {
+//			boolean b = isPreferredResource0(name, isClass);
+//			return Boolean.valueOf(b);
+//		    }
+//	        }, acc)).booleanValue();
+//
+//	} catch (PrivilegedActionException e) {
+//	    throw (IOException) e.getException();
+//	}
+    }
+
+    /*
+     * Perform the work to determine if a resource name is preferred.
+     * 
+     * Synchronized removed to avoid possible ClassLoader deadlock.
+     */
+    private boolean isPreferredResource0(String name,
+						      boolean isClass)
+	throws IOException
+    {
+        if (exceptionWhileLoadingPreferred != null) throw exceptionWhileLoadingPreferred;
+	if (preferredResources == null) {
+	    return false;	// no preferred list: nothing is preferred
+	}
+
+	String resourceName = name;
+	if (isClass) {
+	    /* class name -> resource name */
+	    resourceName = name.replace('.', '/') + ".class";
+	}
+
+	/*
+	 * Determine if the class name is preferred. Making this
+	 * distinction is somewhat tricky because we need to cache the
+	 * preferred state (i.e. if the name is preferred and its
+	 * resource exists) in a way that avoids duplication of
+	 * preferred information - state information is stored back
+	 * into the preferred resources object for this class loader
+	 * and not held in a separate preferred settings cache.
+	 */
+	boolean resourcePreferred = false;
+
+	int state = preferredResources.getNameState(resourceName, isClass);
+	switch (state) {
+	case PreferredResources.NAME_NOT_PREFERRED:
+	    resourcePreferred = false;
+	    break;
+
+	case PreferredResources.NAME_PREFERRED_RESOURCE_EXISTS:
+	    resourcePreferred = true;
+	    break;
+
+	case PreferredResources.NAME_NO_PREFERENCE:
+	    Boolean wildcardPref =
+		preferredResources.getWildcardPreference(resourceName);
+	    if (wildcardPref == null) {
+		/* preferredDefault counts as a wild card */
+		wildcardPref = preferredResources.getDefaultPreference();
+	    }
+	    if (wildcardPref.booleanValue()) {
+		resourcePreferred =
+		    findResourceUpdateState(name, resourceName);
+	    }
+	    break;
+

[... 499 lines stripped ...]