From: <le...@us...> - 2010-01-30 16:07:07
|
Revision: 6970 http://jython.svn.sourceforge.net/jython/?rev=6970&view=rev Author: leosoto Date: 2010-01-30 16:06:59 +0000 (Sat, 30 Jan 2010) Log Message: ----------- Cleanup of class-loading strategy SyspathJavaLoader is now a delegating classloader. Its parent is the context class loader, for backwards compatibility. And when sys.classLoader is set, it willdelegate to it, WITHOUT searching on sys.path, but after the regular parent delegation. The parent of SyspathJavaLoader is determined by imp.getParentClassLoader, to allow other class loading components to use a consistent parent. Refactored findClass/findClassEx to eliminate code duplications and to use: - only sys.classLoader if it is set - SyspathJavaLoader if it can be used - SyspathJavaLoader's parent if SyspathJavaLoader can't be used - Class.forName(String) as a last resort Refactored ClassPathImporter to use SyspathJavaLoader's parent if sys.classLoader is not set. Modified Paths: -------------- trunk/jython/src/org/python/core/ClasspathPyImporter.java trunk/jython/src/org/python/core/Py.java trunk/jython/src/org/python/core/SyspathJavaLoader.java trunk/jython/src/org/python/core/imp.java Modified: trunk/jython/src/org/python/core/ClasspathPyImporter.java =================================================================== --- trunk/jython/src/org/python/core/ClasspathPyImporter.java 2010-01-30 10:14:30 UTC (rev 6969) +++ trunk/jython/src/org/python/core/ClasspathPyImporter.java 2010-01-30 16:06:59 UTC (rev 6970) @@ -89,13 +89,12 @@ if (entries.containsKey(filename)) { return filename; } - InputStream is = tryClassLoader(filename, Py.getSystemState().getClassLoader(), "sys"); - if (is == null) { - is = tryClassLoader(filename, Thread.currentThread().getContextClassLoader(), "context"); + InputStream is; + if (Py.getSystemState().getClassLoader() != null) { + is = tryClassLoader(filename, Py.getSystemState().getClassLoader(), "sys"); + } else { + is = tryClassLoader(filename, imp.getParentClassLoader(), "parent"); } - if (is == null) { - is = tryClassLoader(filename, ClasspathPyImporter.class.getClassLoader(), "current"); - } if (is != null) { entries.put(filename, is); return filename; Modified: trunk/jython/src/org/python/core/Py.java =================================================================== --- trunk/jython/src/org/python/core/Py.java 2010-01-30 10:14:30 UTC (rev 6969) +++ trunk/jython/src/org/python/core/Py.java 2010-01-30 16:06:59 UTC (rev 6970) @@ -780,35 +780,68 @@ return true; } - private static boolean secEnv = false; + private static boolean syspathJavaLoaderRestricted = false; - public static Class<?> findClass(String name) { - try { - ClassLoader classLoader = Py.getSystemState().getClassLoader(); + /** + * Common code for findClass and findClassEx + * @param name Name of the Java class to load and initialize + * @param reason Reason for loading it, used for debugging. No debug output + * is generated if it is null + * @return the loaded class, or null if no class loader is accessible + * @throws ClassNotFoundException if the class wasn't found by the class loader + */ + private static Class<?> findClassInternal(String name, String reason) throws ClassNotFoundException { + ClassLoader classLoader = Py.getSystemState().getClassLoader(); + if (classLoader != null) { + if (reason != null) { + writeDebug("import", "trying " + name + " as " + reason + + " in sys.classLoader"); + } + return loadAndInitClass(name, classLoader); + } + + if (!syspathJavaLoaderRestricted) { + try { + classLoader = imp.getSyspathJavaLoader(); + } catch (SecurityException e) { + syspathJavaLoaderRestricted = true; + } if (classLoader != null) { - return classLoader.loadClass(name); - } - - if (!secEnv) { + if (reason != null) { + writeDebug("import", "trying " + name + " as " + reason + + " in SysPathJavaLoader"); + } try { - classLoader = imp.getSyspathJavaLoader(); - } catch (SecurityException e) { - secEnv = true; + return loadAndInitClass(name, classLoader); + } catch (ClassNotFoundException cnfe) { + // let the default classloader try } - if (classLoader != null) { - try { - return classLoader.loadClass(name); - } catch (ClassNotFoundException cnfe) { - // let the context classloader try - } - } } - - classLoader = Thread.currentThread().getContextClassLoader(); - if (classLoader != null) { - return classLoader.loadClass(name); - } - return null; + } + if (reason != null) { + writeDebug("import", "trying " + name + " as " + reason + + " in Jython's parent class loader"); + } + classLoader = imp.getParentClassLoader(); + if (classLoader != null) { + return loadAndInitClass(name, classLoader); + } + + if (reason != null) { + writeDebug("import", "trying " + name + " as " + reason + + " in Class.forName"); + } + return Class.forName(name); + } + + /** + * Tries to find a Java class. + * @param name Name of the Java class. + * @return The class, or null if it wasn't found + */ + public static Class<?> findClass(String name) { + try { + return findClassInternal(name, null); } catch (ClassNotFoundException e) { // e.printStackTrace(); return null; @@ -821,39 +854,20 @@ } } + /** + * Tries to find a Java class. + * + * Unless {@link #findClass(String)}, it raises a JavaError + * if the class was found but there were problems loading it. + * @param name Name of the Java class. + * @param reason Reason for finding the class. Used for debugging messages. + * @return The class, or null if it wasn't found + * @throws JavaError wrapping LinkageErrors/IllegalArgumentExceptions + * occurred when the class is found but can't be loaded. + */ public static Class<?> findClassEx(String name, String reason) { - try { - ClassLoader classLoader = Py.getSystemState().getClassLoader(); - if (classLoader != null) { - writeDebug("import", "trying " + name + " as " + reason + - " in classLoader"); - return classLoader.loadClass(name); - } - - if (!secEnv) { - try { - classLoader = imp.getSyspathJavaLoader(); - } catch (SecurityException e) { - secEnv = true; - } - if (classLoader != null) { - writeDebug("import", "trying " + name + " as " + reason + - " in syspath loader"); - try { - return classLoader.loadClass(name); - } catch (ClassNotFoundException cnfe) { - // let the context classloader try - } - } - } - - writeDebug("import", "trying " + name + " as " + reason + - " in Class.forName"); - classLoader = Thread.currentThread().getContextClassLoader(); - if (classLoader != null) { - return classLoader.loadClass(name); - } - return null; + try { + return findClassInternal(name, reason); } catch (ClassNotFoundException e) { return null; } catch (IllegalArgumentException e) { @@ -863,6 +877,14 @@ } } + // An alias to express intent (since boolean flags aren't exactly obvious). + // We *need* to initialize classes on findClass/findClassEx, so that import + // statements can trigger static initializers + private static Class<?> loadAndInitClass(String name, ClassLoader loader) throws ClassNotFoundException { + return Class.forName(name, true, loader); + } + + public static void initProxy(PyProxy proxy, String module, String pyclass, Object[] args) { if (proxy._getPyInstance() != null) Modified: trunk/jython/src/org/python/core/SyspathJavaLoader.java =================================================================== --- trunk/jython/src/org/python/core/SyspathJavaLoader.java 2010-01-30 10:14:30 UTC (rev 6969) +++ trunk/jython/src/org/python/core/SyspathJavaLoader.java 2010-01-30 16:06:59 UTC (rev 6970) @@ -9,40 +9,118 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; import java.util.StringTokenizer; import java.util.zip.ZipEntry; +import javax.management.RuntimeErrorException; + import org.python.core.util.RelativeFile; public class SyspathJavaLoader extends ClassLoader { private static final char SLASH_CHAR = '/'; - public InputStream getResourceAsStream(String res) { - Py.writeDebug("resource", "trying resource: " + res); - PySystemState sys = Py.getSystemState(); - ClassLoader classLoader = sys.getClassLoader(); - if (classLoader != null) { - return classLoader.getResourceAsStream(res); + public SyspathJavaLoader(ClassLoader parent) { + super(parent); + } + + + /** + * Returns a byte[] with the contents read from an InputStream. + * + * The stream is closed after reading the bytes. + * + * @param input The input stream + * @param size The number of bytes to read + * + * @return an array of byte[size] with the contents read + * */ + private byte[] getBytesFromInputStream(InputStream input, int size) { + try { + byte[] buffer = new byte[size]; + int nread = 0; + while(nread < size) { + nread += input.read(buffer, nread, size - nread); + } + return buffer; + } catch (IOException exc) { + return null; + } finally { + try { + input.close(); + } catch (IOException e) { + // Nothing to do + } + } + } + + private byte[] getBytesFromDir(String dir, String name) { + try { + File file = getFile(dir, name); + if (file == null) { + return null; + } + return getBytesFromInputStream(new FileInputStream(file), (int)file.length()); + } catch (FileNotFoundException e) { + return null; + } catch(SecurityException e) { + return null; } - classLoader = Thread.currentThread().getContextClassLoader(); - - InputStream ret; - - if (classLoader != null) { - ret = classLoader.getResourceAsStream(res); - } else { - ret = ClassLoader.getSystemResourceAsStream(res); + } + + private byte[] getBytesFromArchive(SyspathArchive archive, String name) { + String entryname = name.replace('.', SLASH_CHAR) + ".class"; + ZipEntry ze = archive.getEntry(entryname); + if (ze == null) { + return null; } - if (ret != null) { - return ret; + try { + return getBytesFromInputStream(archive.getInputStream(ze), + (int)ze.getSize()); + } catch (IOException e) { + return null; + } + } + + @Override + protected Class<?> findClass(String name) throws ClassNotFoundException { + PySystemState sys = Py.getSystemState(); + ClassLoader sysClassLoader = sys.getClassLoader(); + if (sysClassLoader != null) { + // sys.classLoader overrides this class loader! + return sysClassLoader.loadClass(name); + } + // Search the sys.path for a .class file matching the named class. + PyList path = sys.path; + for (int i = 0; i < path.__len__(); i++) { + byte[] buffer; + PyObject entry = replacePathItem(sys, i, path); + if (entry instanceof SyspathArchive) { + SyspathArchive archive = (SyspathArchive)entry; + buffer = getBytesFromArchive(archive, name); + } else { + String dir = entry.__str__().toString(); + buffer = getBytesFromDir(dir, name); + } + if (buffer != null) { + return defineClass(name, buffer, 0, buffer.length); + } } - - if (res.charAt(0) == SLASH_CHAR) { + // couldn't find the .class file on sys.path + throw new ClassNotFoundException(name); + } + + @Override + protected URL findResource(String res) { + PySystemState sys = Py.getSystemState(); + + if (res.charAt(0) == SLASH_CHAR) { res = res.substring(1); } - String entryRes = res; + String entryRes = res; if (File.separatorChar != SLASH_CHAR) { res = res.replace(SLASH_CHAR, File.separatorChar); entryRes = entryRes.replace(File.separatorChar, SLASH_CHAR); @@ -55,25 +133,25 @@ SyspathArchive archive = (SyspathArchive) entry; ZipEntry ze = archive.getEntry(entryRes); if (ze != null) { - try { - return archive.getInputStream(ze); - } catch (IOException e) { - ; - } + try { + return new URL("jar:" + entry.__str__().toString() + "!/" + entryRes); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } } continue; } String dir = sys.getPath(entry.__str__().toString()); try { - return new BufferedInputStream(new FileInputStream(new File(dir, res))); - } catch (IOException e) { - continue; - } + return new File(dir, res).toURI().toURL(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } } - return null; } + static PyObject replacePathItem(PySystemState sys, int idx, PyList paths) { PyObject path = paths.__getitem__(idx); if (path instanceof SyspathArchive) { @@ -92,91 +170,6 @@ return path; } - // override from abstract base class - protected Class<?> loadClass(String name, boolean resolve) - throws ClassNotFoundException { - // First, if the Python runtime system has a default class loader, defer to it. - PySystemState sys = Py.getSystemState(); - ClassLoader classLoader = sys.getClassLoader(); - if (classLoader != null) { - return classLoader.loadClass(name); - } - - // Search the sys.path for a .class file matching the named class. - try { - return Class.forName(name); - } catch(ClassNotFoundException e) { - } - - // The current class loader may be null (e.g., when Jython is loaded - // from the boot classpath); try the system class loader. - try { - return Class.forName(name, true, ClassLoader.getSystemClassLoader()); - } catch(ClassNotFoundException e) { - } catch (SecurityException se) { - } - - Class<?> c = findLoadedClass(name); - if(c != null) { - return c; - } - - PyList path = sys.path; - for(int i = 0; i < path.__len__(); i++) { - - InputStream fis; - int size; - PyObject entry = replacePathItem(sys, i, path); - if(entry instanceof SyspathArchive) { - SyspathArchive archive = (SyspathArchive)entry; - String entryname = name.replace('.', SLASH_CHAR) + ".class"; - ZipEntry ze = archive.getEntry(entryname); - if(ze == null) { - continue; - } - try { - fis = archive.getInputStream(ze); - size = (int)ze.getSize(); - } catch (IOException exc) { - continue; - } - } else { - String dir = entry.__str__().toString(); - File file = getFile(dir, name); - if (file == null) { - continue; - } - try { - size = (int)file.length(); - fis = new FileInputStream(file); - } catch (FileNotFoundException e) { - continue; - } catch(SecurityException e) { - continue; - } - } - try { - byte[] buffer = new byte[size]; - int nread = 0; - while(nread < size) { - nread += fis.read(buffer, nread, size - nread); - } - fis.close(); - return loadClassFromBytes(name, buffer); - } catch (IOException e) { - - } finally { - try { - fis.close(); - } catch (IOException e) { - } - } - } - - // couldn't find the .class file on sys.path - throw new ClassNotFoundException(name); - } - private File getFile(String dir, String name) { String accum = ""; boolean first = true; @@ -192,12 +185,5 @@ return new RelativeFile(dir, accum + ".class"); } - private Class<?> loadClassFromBytes(String name, byte[] data) { - // System.err.println("loadClassFromBytes("+name+", byte[])"); - Class<?> c = defineClass(name, data, 0, data.length); - resolveClass(c); - Compiler.compileClass(c); - return c; - } } Modified: trunk/jython/src/org/python/core/imp.java =================================================================== --- trunk/jython/src/org/python/core/imp.java 2010-01-30 10:14:30 UTC (rev 6969) +++ trunk/jython/src/org/python/core/imp.java 2010-01-30 16:06:59 UTC (rev 6970) @@ -41,11 +41,41 @@ public static ClassLoader getSyspathJavaLoader() { synchronized (syspathJavaLoaderLock) { if (syspathJavaLoader == null) { - syspathJavaLoader = new SyspathJavaLoader(); - } + syspathJavaLoader = new SyspathJavaLoader(getParentClassLoader()); + } } return syspathJavaLoader; } + + /** + * Returns the parent class loader for Jython. In most environments + * is just the class loader that loaded this class (imp.class). However, + * when that class loader is null (e.g., when Jython has been + * loaded using the boot loader) we return the Thread's context class + * loader (which can also be null, but in such case we return null anyway). + * + * @return the parent class loader for Jython + */ + public static ClassLoader getParentClassLoader() { + ClassLoader parent = null; + // XXX: While trying the current class loader before using the context class + // loader seems like the saner approach, there may be legacy code + // expecting Jython to use the context class loader, + // + // Se we should uncomment the following line when we feel like + // doing backwards-incompatible changes (3.0?) + // + // parent = imp.class.getClassLoader(); + if (parent == null) { + // Try the context class loader + try { + parent = Thread.currentThread().getContextClassLoader(); + } catch (SecurityException e) { + // We just give up + } + } + return parent; + } private imp() { } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |