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 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 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(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"); } 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(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); } } } }