package junit.runner; import java.util.*; import java.io.*; import java.net.URL; import java.util.zip.*; /** * A custom class loader which enables the reloading * of classes for each test run. The class loader * can be configured with a list of package paths that * should be excluded from loading. The loading * of these packages is delegated to the system class * loader. They will be shared across test runs. * <p> * The list of excluded package paths is specified in * a properties file "excluded.properties" that is located in * the same place as the TestCaseClassLoader class. * <p> * <b>Known limitation:</b> the TestCaseClassLoader cannot load classes * from jar files. */ public class TestCaseClassLoader extends ClassLoader { /** scanned class path */ private Vector<String> fPathItems; /** default excluded paths */ private String[] defaultExclusions= { "junit.framework.", "junit.extensions.", "junit.runner." }; /** name of excluded properties file */ static final String EXCLUDED_FILE= "excluded.properties"; /** excluded paths */ private Vector<String> fExcluded; /** * Constructs a TestCaseLoader. It scans the class path * and the excluded package paths */ public TestCaseClassLoader() { this(System.getProperty("java.class.path")); } /** * Constructs a TestCaseLoader. It scans the class path * and the excluded package paths */ public TestCaseClassLoader(String classPath) { scanPath(classPath); readExcludedPackages(); } private void scanPath(String classPath) { String separator= System.getProperty("path.separator"); fPathItems= new Vector<String>(10); StringTokenizer st= new StringTokenizer(classPath, separator); while (st.hasMoreTokens()) { fPathItems.addElement(st.nextToken()); } } public URL getResource(String name) { return ClassLoader.getSystemResource(name); } public InputStream getResourceAsStream(String name) { return ClassLoader.getSystemResourceAsStream(name); } public boolean isExcluded(String name) { for (int i= 0; i < fExcluded.size(); i++) { if (name.startsWith((String) fExcluded.elementAt(i))) { return true; } } return false; } public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class c= findLoadedClass(name); if (c != null) return c; // // Delegate the loading of excluded classes to the // standard class loader. // if (isExcluded(name)) { try { c= findSystemClass(name); return c; } catch (ClassNotFoundException e) { // keep searching } } if (c == null) { byte[] data= lookupClassData(name); if (data == null) throw new ClassNotFoundException(); c= defineClass(name, data, 0, data.length); } if (resolve) resolveClass(c); return c; } private byte[] lookupClassData(String className) throws ClassNotFoundException { byte[] data= null; for (int i= 0; i < fPathItems.size(); i++) { String path= (String) fPathItems.elementAt(i); String fileName= className.replace('.', '/')+".class"; if (isJar(path)) { data= loadJarData(path, fileName); } else { data= loadFileData(path, fileName); } if (data != null) return data; } throw new ClassNotFoundException(className); } boolean isJar(String pathEntry) { return pathEntry.endsWith(".jar") || pathEntry.endsWith(".zip") || pathEntry.endsWith(".apk"); } private byte[] loadFileData(String path, String fileName) { File file= new File(path, fileName); if (file.exists()) { return getClassData(file); } return null; } private byte[] getClassData(File f) { try { FileInputStream stream= new FileInputStream(f); ByteArrayOutputStream out= new ByteArrayOutputStream(1000); byte[] b= new byte[1000]; int n; while ((n= stream.read(b)) != -1) out.write(b, 0, n); stream.close(); out.close(); return out.toByteArray(); } catch (IOException e) { } return null; } private byte[] loadJarData(String path, String fileName) { ZipFile zipFile= null; InputStream stream= null; File archive= new File(path); if (!archive.exists()) return null; try { zipFile= new ZipFile(archive); } catch(IOException io) { return null; } ZipEntry entry= zipFile.getEntry(fileName); if (entry == null) return null; int size= (int) entry.getSize(); try { stream= zipFile.getInputStream(entry); byte[] data= new byte[size]; int pos= 0; while (pos < size) { int n= stream.read(data, pos, data.length - pos); pos += n; } zipFile.close(); return data; } catch (IOException e) { } finally { try { if (stream != null) stream.close(); } catch (IOException e) { } } return null; } private void readExcludedPackages() { fExcluded= new Vector<String>(10); for (int i= 0; i < defaultExclusions.length; i++) fExcluded.addElement(defaultExclusions[i]); InputStream is= getClass().getResourceAsStream(EXCLUDED_FILE); if (is == null) return; Properties p= new Properties(); try { p.load(is); } catch (IOException e) { return; } finally { try { is.close(); } catch (IOException e) { } } for (Enumeration e= p.propertyNames(); e.hasMoreElements(); ) { String key= (String)e.nextElement(); if (key.startsWith("excluded.")) { String path= p.getProperty(key); path= path.trim(); if (path.endsWith("*")) path= path.substring(0, path.length()-1); if (path.length() > 0) fExcluded.addElement(path); } } } }