/* * $Id: ClassLoader.java 5226 2009-04-06 14:55:27Z lsantha $ * * Copyright (C) 2003-2009 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package java.lang; import gnu.classpath.SystemProperties; import gnu.java.util.EmptyEnumeration; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.nio.ByteBuffer; import java.security.AccessController; import java.security.CodeSource; import java.security.PermissionCollection; import java.security.Policy; import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import sun.reflect.Reflection; public abstract class ClassLoader { private final ClassLoader parent; //this is always a VmClassLoaderInstance final Object vmClassLoader; private ProtectionDomain defaultProtectionDomain; /** * All packages defined by this classloader. It is not private in order to * allow native code (and trusted subclasses) access to this field. */ final HashMap<String, Package> definedPackages = new HashMap<String, Package>(); /** * The desired assertion status of classes loaded by this loader, if not * overridden by package or class instructions. */ // Package visible for use by Class. boolean defaultAssertionStatus = VMClassLoader.defaultAssertionStatus(); /** * The map of package assertion status overrides, or null if no package * overrides have been specified yet. The values of the map should be * Boolean.TRUE or Boolean.FALSE, and the unnamed package is represented by * the null key. This map must be synchronized on this instance. */ // Package visible for use by Class. Map<String, Boolean> packageAssertionStatus; /** * The map of class assertion status overrides, or null if no class * overrides have been specified yet. The values of the map should be * Boolean.TRUE or Boolean.FALSE. This map must be synchronized on this * instance. */ // Package visible for use by Class. Map<String, Boolean> classAssertionStatus; static class StaticData { /** * The System Class Loader (a.k.a. Application Class Loader). The one * returned by ClassLoader.getSystemClassLoader. */ static final ClassLoader systemClassLoader = VMClassLoader.getSystemClassLoader(); static { // Find out if we have to install a default security manager. Note // that // this is done here because we potentially need the system class // loader // to load the security manager and note also that we don't need the // security manager until the system class loader is created. // If the runtime chooses to use a class loader that doesn't have // the // system class loader as its parent, it is responsible for setting // up a security manager before doing so. String secman = SystemProperties .getProperty("java.security.manager"); if (secman != null && SecurityManager.current == null) { if (secman.equals("") || secman.equals("default")) { SecurityManager.current = new SecurityManager(); } else { try { Class< ? > cl = Class.forName(secman, false, StaticData.systemClassLoader); SecurityManager.current = (SecurityManager) cl .newInstance(); } catch (Exception x) { throw (InternalError) new InternalError( "Unable to create SecurityManager") .initCause(x); } } } } /** * The default protection domain, used when defining a class with a null * parameter for the domain. */ static final ProtectionDomain defaultProtectionDomain; static { final CodeSource cs = new CodeSource(null, (Certificate[])null); PermissionCollection perm = AccessController .doPrivileged(new PrivilegedAction<PermissionCollection>() { public PermissionCollection run() { return Policy.getPolicy().getPermissions(cs); } }); defaultProtectionDomain = new ProtectionDomain(cs, perm); } /** * The command-line state of the package assertion status overrides. * This map is never modified, so it does not need to be synchronized. */ // Package visible for use by Class. static final Map<String, Boolean> systemPackageAssertionStatus = VMClassLoader.packageAssertionStatus(); /** * The command-line state of the class assertion status overrides. This * map is never modified, so it does not need to be synchronized. */ // Package visible for use by Class. static final Map<String, Boolean> systemClassAssertionStatus = VMClassLoader.classAssertionStatus(); } /** * Create a new ClassLoader with the specified parent. The parent will be * consulted when a class or resource is requested through * <code>loadClass()</code> or <code>getResource()</code>. Only when * the parent classloader cannot provide the requested class or resource the * <code>findClass()</code> or <code>findResource()</code> method of * this classloader will be called. There may be a security check for * <code>checkCreateClassLoader</code>. * * @param parent * the classloader's parent * @throws SecurityException * if the security check fails * @since 1.2 */ protected ClassLoader(ClassLoader parent) { /* May we create a new classloader? */ SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkCreateClassLoader(); } this.parent = parent; this.vmClassLoader = createVmJavaClassLoader0(this); } private static native Object createVmJavaClassLoader0(ClassLoader instance); /** * Create a new instance * */ protected ClassLoader() { this(getSystemClassLoader()); } /** * Create a new classloader wrapped around a given VmClassLoader. * * @param vmClassLoader */ protected ClassLoader(Object vmClassLoader, int discriminator) { /* May we create a new classloader? */ SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkCreateClassLoader(); } if (vmClassLoader == null) { throw new IllegalArgumentException("vmClassLoader cannot be null"); } checkArg0(vmClassLoader); this.parent = null; this.vmClassLoader = vmClassLoader; } private static native void checkArg0(Object vmClassLoader); /** * Create a new classloader wrapped around a given VmClassLoader, * with a given parent loader. * * @param vmClassLoader */ protected ClassLoader(ClassLoader parent, Object vmClassLoader) { /* May we create a new classloader? */ checkArgs0(vmClassLoader); this.parent = parent; this.vmClassLoader = vmClassLoader; } private static native void checkArgs0(Object vmClassLoader); /** * Gets the VmClassLoader that is used by this classloader. * This method requires special permission. * @return */ public final Object getVmClassLoader() { return getVmClassLoader0(); } private native Object getVmClassLoader0(); /** * Load and resolve a class with a given name. * * @param name * @return Class * @throws ClassNotFoundException */ public Class loadClass(String name) throws ClassNotFoundException { // return vmClassLoader.loadClass(name, true).asClass(); return loadClass(name, true); } /** * Load and optionally resolve a class with a given name. * * @param name * @param resolve * @return Class * @throws ClassNotFoundException */ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { /* Have we already loaded this class? */ final Class cls = findLoadedClass(name); if (cls != null) { return cls; } /* Can the class been loaded by a parent? */ try { if ((parent == null) || skipParentLoader(name)) { return loadClass0(name, resolve); } else { return parent.loadClass(name, resolve); } } catch (ClassNotFoundException e) { // e.printStackTrace(); } /* Still not found, we have to do it ourself. */ final Class c = findClass(name); if (resolve) { resolveClass(c); } return c; } private static native Class loadClass0(String name, boolean resolve) throws ClassNotFoundException; /** * Define a byte-array of class data into a loaded class. * * @param data * @param offset * @param length * @return Class * @deprecated Replaced by {@link #defineClass(String, byte[], int, int)} */ protected final Class defineClass(byte[] data, int offset, int length) { return defineClass(null, data, offset, length, null); } /** * Define a byte-array of class data into a loaded class. * * @param name * @param data * @param offset * @param length * @return Class */ protected final Class defineClass(String name, byte[] data, int offset, int length) { return defineClass(name, data, offset, length, null); } /** * Defines a new package and creates a Package object. The package should be * defined before any class in the package is defined with * <code>defineClass()</code>. The package should not yet be defined * before in this classloader or in one of its parents (which means that * <code>getPackage()</code> should return <code>null</code>). All * parameters except the <code>name</code> of the package may be * <code>null</code>. * * <p> * Subclasses should call this method from their <code>findClass()</code> * implementation before calling <code>defineClass()</code> on a Class in * a not yet defined Package (which can be checked by calling * <code>getPackage()</code>). * * @param name * the name of the Package * @param specTitle * the name of the specification * @param specVendor * the name of the specification designer * @param specVersion * the version of this specification * @param implTitle * the name of the implementation * @param implVendor * the vendor that wrote this implementation * @param implVersion * the version of this implementation * @param sealed * if sealed the origin of the package classes * @return the Package object for the specified package * @throws IllegalArgumentException * if the package name is null or it was already defined by this * classloader or one of its parents * @see Package * @since 1.2 */ protected Package definePackage(String name, String specTitle, String specVendor, String specVersion, String implTitle, String implVendor, String implVersion, URL sealed) { if (getPackage(name) != null) throw new IllegalArgumentException("Package " + name + " already defined"); Package p = new Package(name, specTitle, specVendor, specVersion, implTitle, implVendor, implVersion, sealed, this); synchronized (definedPackages) { definedPackages.put(name, p); } return p; } /** * Define a byte-array of class data into a loaded class. * * @param name * @param data * @param offset * @param length * @param protDomain * @return Class */ protected final Class defineClass(String name, byte[] data, int offset, int length, ProtectionDomain protDomain) { if (data == null) { throw new NullPointerException(); } if (offset < 0 || length < 0 || (offset + length) > data.length) { throw new IndexOutOfBoundsException(); } if (protDomain == null) { protDomain = AccessController .doPrivileged(new PrivilegedAction<ProtectionDomain>() { public ProtectionDomain run() { return getDefaultProtectionDomain(); } }); } return defineClass0(name, data, offset, length, protDomain); } private native Class defineClass0(String name, byte[] data, int offset, int length, ProtectionDomain protDomain); /** * Define a byte-array of class data into a loaded class. * * @param name * @param data * @param protDomain * @return Class */ protected final Class defineClass(String name, ByteBuffer data, ProtectionDomain protDomain) { if (data == null) { throw new NullPointerException(); } if (protDomain == null) { protDomain = AccessController.doPrivileged( new PrivilegedAction<ProtectionDomain>() { public ProtectionDomain run() { return getDefaultProtectionDomain(); } }); } return defineClass0(name, data, protDomain); } private native Class defineClass0(String name, ByteBuffer data, ProtectionDomain protDomain); private ProtectionDomain getDefaultProtectionDomain() { if (defaultProtectionDomain == null) { final CodeSource cs = new CodeSource(null, (Certificate[])null); defaultProtectionDomain = new ProtectionDomain(cs, Policy .getPolicy().getPermissions(cs)); } return defaultProtectionDomain; } /** * Resolve all references in the given class. * * @param c */ protected final void resolveClass(Class c) { if (c == null) { throw new NullPointerException(); } } /** * Finds the class with the given name if it had been previously loaded * through this class loader. * * @param name * @return the Class object, or null if the class has not been loaded */ protected native final Class findLoadedClass(String name); /** * Finds the specified class. This method should be overridden by class * loader implementations that follow the new delegation model for loading * classes, and will be called by the loadClass method after checking the * parent class loader for the requested class. The default implementation * throws ClassNotFoundException. * * @param name * @return Class * @throws ClassNotFoundException */ protected Class findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); } /** * Find a system class. * * @param name * @return Class * @throws ClassNotFoundException */ protected final Class findSystemClass(String name) throws ClassNotFoundException { if (name == null) { throw new NullPointerException(); } else { return loadClass0(name, true); } } /** * Gets a system resource as stream by name. * * @param name * @return InputStream */ public static final InputStream getSystemResourceAsStream(String name) { try { return getSystemResourceAsStream0(name); } catch (IOException ex) { return null; } } private static native InputStream getSystemResourceAsStream0(String name) throws IOException; /** * Gets a resource as stream by name. * * @param name * @return InputStream */ public InputStream getResourceAsStream(String name) { URL url = getResource(name); if (url != null) { try { return url.openStream(); } catch (IOException ex) { // Syslog.debug("Cannot load resource " + name, ex); return null; } } else { return null; } } /** * Gets a URL to a system resource by name. * * @param name * @return URL */ public static final URL getSystemResource(String name) { try { if (name.startsWith("/")) { return new URL("system://" + name); } else { return new URL("system:///" + name); } } catch (MalformedURLException ex) { ex.printStackTrace(); return null; } } /** * Gets an URL to a resource by name. * * @param name * @return URL */ public URL getResource(String name) { URL result = null; if (parent == null) { if (resourceExists0(name)) { try { if (name.startsWith("/")) { result = new URL("system://" + name); } else { result = new URL("system:///" + name); } } catch (MalformedURLException ex) { ex.printStackTrace(); result = null; } } } else { result = parent.getResource(name); } if (result == null) { result = findResource(name); } return result; } private native boolean resourceExists0(String name); public Enumeration getResources(String name) throws IOException { final List<URL> urls = new ArrayList<URL>(); getResourcesImpl(name, urls); return new Enumeration<URL>() { private Iterator<URL> it = urls.iterator(); public boolean hasMoreElements() { return it.hasNext(); } public URL nextElement() { return it.next(); } }; } protected boolean getResourcesImpl(String name, List<URL> urls) throws IOException { URL result = null; if (parent == null) { if (resourceExists0(name)) { try { if (name.startsWith("/")) { //todo: adjust the rt.jar path to match the future configurations //todo: one possibility is ${java.home}/lib/rt.jar result = new URL("jar:file:/jifs/lib/rt.jar!" + name); //result = new URL("system://" + name); } else { //todo: adjust the rt.jar path to match the future configurations //todo: one possibility is ${java.home}/lib/rt.jar result = new URL("jar:file:/jifs/lib/rt.jar!/" + name); //result = new URL("system:///" + name); } } catch (MalformedURLException ex) { ex.printStackTrace(); result = null; } if(result != null) { if(!urls.contains(result)) urls.add(result); } } } else { parent.getResourcesImpl(name, urls); } if (result == null) { result = findResource(name); if(result != null) { if(!urls.contains(result)) urls.add(result); } } return (result != null); } /** * Finds the resource with the given name. Class loader implementations * should override this method to specify where to find resources. * * @param name * @return URL */ protected URL findResource(String name) { return null; } /** * Called whenever all locations of a named resource are needed. * It is called by <code>getResources()</code> after it has called * <code>parent.getResources()</code>. The results are combined by * the <code>getResources()</code> method. * * <p>The default implementation always returns an empty Enumeration. * Subclasses should override it when they can provide an Enumeration of * URLs (possibly just one element) to the named resource. * The first URL of the Enumeration should be the same as the one * returned by <code>findResource</code>. * * @param name the name of the resource to be found * @return a possibly empty Enumeration of URLs to the named resource * @throws IOException if I/O errors occur in the process * @since 1.2 */ protected Enumeration findResources(String name) throws IOException { return EmptyEnumeration.getInstance(); } /** * Returns the Package object for the requested package name. It returns * null when the package is not defined by this classloader or one of its * parents. * * @param name * the package name to find * @return the package, if defined * @since 1.2 */ protected Package getPackage(String name) { Package p; if (parent == null) { p = VMClassLoader.getPackage(name); } else { p = parent.getPackage(name); } if (p == null) { synchronized (definedPackages) { p = definedPackages.get(name); } } return p; } /** * Returns all Package objects defined by this classloader and its parents. * * @return an array of all defined packages * @since 1.2 */ protected Package[] getPackages() { // Get all our packages. Package[] packages; synchronized (definedPackages) { packages = new Package[definedPackages.size()]; definedPackages.values().toArray(packages); } // If we have a parent get all packages defined by our parents. Package[] parentPackages; if (parent == null) parentPackages = VMClassLoader.getPackages(); else parentPackages = parent.getPackages(); Package[] allPackages = new Package[parentPackages.length + packages.length]; System.arraycopy(parentPackages, 0, allPackages, 0, parentPackages.length); System.arraycopy(packages, 0, allPackages, parentPackages.length, packages.length); return allPackages; } /** * Avoid trying to load the given class via its parent classloader? * * @param name * @return {@code true} if the parent classloader should be skipped. */ public boolean skipParentLoader(String name) { return false; } /** * Gets the system classloader. * * @return ClassLoader */ public static native ClassLoader getSystemClassLoader(); public static Enumeration getSystemResources(String name) throws IOException { return EmptyEnumeration.getInstance(); } /** * @return */ public ClassLoader getParent() { return parent; } /** * Called by <code>Runtime.loadLibrary()</code> to get an absolute path to * a (system specific) library that was requested by a class loaded by this * classloader. The default implementation returns <code>null</code>. It * should be implemented by subclasses when they have a way to find the * absolute path to a library. If this method returns null the library is * searched for in the default locations (the directories listed in the * <code>java.library.path</code> system property). * * @param name * the (system specific) name of the requested library * @return the full pathname to the requested library, or null * @see Runtime#loadLibrary(String) * @since 1.2 */ protected String findLibrary(String name) { return null; } /** * Sets the signers of a class. This should be invoked after defining a * class. * * @param clazz * The class object. * @param signers * The signers. */ protected final void setSigners(Class clazz, Object[] signers) { // TODO implement me } /** * Set the default assertion status for classes loaded by this classloader, * used unless overridden by a package or class request. * * @param enabled * true to set the default to enabled * @see #setClassAssertionStatus(String, boolean) * @see #setPackageAssertionStatus(String, boolean) * @see #clearAssertionStatus() * @since 1.4 */ public void setDefaultAssertionStatus(boolean enabled) { defaultAssertionStatus = enabled; } /** * Set the default assertion status for packages, used unless overridden by * a class request. This default also covers subpackages, unless they are * also specified. The unnamed package should use null for the name. * * @param name * the package (and subpackages) to affect * @param enabled * true to set the default to enabled * @see #setDefaultAssertionStatus(boolean) * @see #setClassAssertionStatus(String, boolean) * @see #clearAssertionStatus() * @since 1.4 */ public synchronized void setPackageAssertionStatus(String name, boolean enabled) { if (packageAssertionStatus == null) packageAssertionStatus = new HashMap<String, Boolean>( StaticData.systemPackageAssertionStatus); packageAssertionStatus.put(name, Boolean.valueOf(enabled)); } /** * Set the default assertion status for a class. This only affects the * status of top-level classes, any other string is harmless. * * @param name * the class to affect * @param enabled * true to set the default to enabled * @throws NullPointerException * if name is null * @see #setDefaultAssertionStatus(boolean) * @see #setPackageAssertionStatus(String, boolean) * @see #clearAssertionStatus() * @since 1.4 */ public synchronized void setClassAssertionStatus(String name, boolean enabled) { if (classAssertionStatus == null) classAssertionStatus = new HashMap<String, Boolean>( StaticData.systemClassAssertionStatus); // The toString() hack catches null, as required. classAssertionStatus.put(name.toString(), Boolean.valueOf(enabled)); } /** * Resets the default assertion status of this classloader, its packages and * classes, all to false. This allows overriding defaults inherited from the * command line. * * @see #setDefaultAssertionStatus(boolean) * @see #setClassAssertionStatus(String, boolean) * @see #setPackageAssertionStatus(String, boolean) * @since 1.4 */ public synchronized void clearAssertionStatus() { defaultAssertionStatus = false; packageAssertionStatus = null; classAssertionStatus = null; } //jnod+openjdk // Returns the invoker's class loader, or null if none. // NOTE: This must always be invoked when there is exactly one intervening // frame from the core libraries on the stack between this method's // invocation and the desired invoker. static ClassLoader getCallerClassLoader() { // NOTE use of more generic Reflection.getCallerClass() Class caller = Reflection.getCallerClass(3); // This can be null if the VM is requesting it if (caller == null) { return null; } // Circumvent security check since this is package-private return caller.getClassLoader0(); } // Returns true if the specified class loader can be found in this class // loader's delegation chain. boolean isAncestor(ClassLoader cl) { ClassLoader acl = this; do { acl = acl.parent; if (cl == acl) { return true; } } while (acl != null); return false; } /** * Returns the assertion status that would be assigned to the specified * class if it were to be initialized at the time this method is invoked. * If the named class has had its assertion status set, the most recent * setting will be returned; otherwise, if any package default assertion * status pertains to this class, the most recent setting for the most * specific pertinent package default assertion status is returned; * otherwise, this class loader's default assertion status is returned. * </p> * * @param className * The fully qualified class name of the class whose desired * assertion status is being queried. * * @return The desired assertion status of the specified class. * * @see #setClassAssertionStatus(String, boolean) * @see #setPackageAssertionStatus(String, boolean) * @see #setDefaultAssertionStatus(boolean) * * @since 1.4 */ synchronized boolean desiredAssertionStatus(String className) { Boolean result; // assert classAssertionStatus != null; // assert packageAssertionStatus != null; // Check for a class entry result = (Boolean)classAssertionStatus.get(className); if (result != null) return result.booleanValue(); // Check for most specific package entry int dotIndex = className.lastIndexOf("."); if (dotIndex < 0) { // default package result = (Boolean)packageAssertionStatus.get(null); if (result != null) return result.booleanValue(); } while(dotIndex > 0) { className = className.substring(0, dotIndex); result = (Boolean)packageAssertionStatus.get(className); if (result != null) return result.booleanValue(); dotIndex = className.lastIndexOf(".", dotIndex-1); } // Return the classloader default return defaultAssertionStatus; } { packageAssertionStatus = new HashMap(); } public static void loadLibrary(Class fromClass, String libname, boolean b) { //do nothing } }