Home | History | Annotate | Download | only in runner
      1 package junit.runner;
      2 
      3 import java.util.*;
      4 import java.io.*;
      5 import java.net.URL;
      6 import java.util.zip.*;
      7 
      8 /**
      9  * A custom class loader which enables the reloading
     10  * of classes for each test run. The class loader
     11  * can be configured with a list of package paths that
     12  * should be excluded from loading. The loading
     13  * of these packages is delegated to the system class
     14  * loader. They will be shared across test runs.
     15  * <p>
     16  * The list of excluded package paths is specified in
     17  * a properties file "excluded.properties" that is located in
     18  * the same place as the TestCaseClassLoader class.
     19  * <p>
     20  * <b>Known limitation:</b> the TestCaseClassLoader cannot load classes
     21  * from jar files.
     22  * {@hide} - Not needed for 1.0 SDK
     23  */
     24 public class TestCaseClassLoader extends ClassLoader {
     25 	/** scanned class path */
     26 	private Vector fPathItems;
     27 	/** default excluded paths */
     28 	private String[] defaultExclusions= {
     29 		"junit.framework.",
     30 		"junit.extensions.",
     31 		"junit.runner."
     32 	};
     33 	/** name of excluded properties file */
     34 	static final String EXCLUDED_FILE= "excluded.properties";
     35 	/** excluded paths */
     36 	private Vector fExcluded;
     37 
     38 	/**
     39 	 * Constructs a TestCaseLoader. It scans the class path
     40 	 * and the excluded package paths
     41 	 */
     42 	public TestCaseClassLoader() {
     43 		this(System.getProperty("java.class.path"));
     44 	}
     45 
     46 	/**
     47 	 * Constructs a TestCaseLoader. It scans the class path
     48 	 * and the excluded package paths
     49 	 */
     50 	public TestCaseClassLoader(String classPath) {
     51 		scanPath(classPath);
     52 		readExcludedPackages();
     53 	}
     54 
     55 	private void scanPath(String classPath) {
     56 		String separator= System.getProperty("path.separator");
     57 		fPathItems= new Vector(10);
     58 		StringTokenizer st= new StringTokenizer(classPath, separator);
     59 		while (st.hasMoreTokens()) {
     60 			fPathItems.addElement(st.nextToken());
     61 		}
     62 	}
     63 
     64 	public URL getResource(String name) {
     65 		return ClassLoader.getSystemResource(name);
     66 	}
     67 
     68 	public InputStream getResourceAsStream(String name) {
     69 		return ClassLoader.getSystemResourceAsStream(name);
     70 	}
     71 
     72 	public boolean isExcluded(String name) {
     73 		for (int i= 0; i < fExcluded.size(); i++) {
     74 			if (name.startsWith((String) fExcluded.elementAt(i))) {
     75 				return true;
     76 			}
     77 		}
     78 		return false;
     79 	}
     80 
     81 	public synchronized Class loadClass(String name, boolean resolve)
     82 		throws ClassNotFoundException {
     83 
     84 		Class c= findLoadedClass(name);
     85 		if (c != null)
     86 			return c;
     87 		//
     88 		// Delegate the loading of excluded classes to the
     89 		// standard class loader.
     90 		//
     91 		if (isExcluded(name)) {
     92 			try {
     93 				c= findSystemClass(name);
     94 				return c;
     95 			} catch (ClassNotFoundException e) {
     96 				// keep searching
     97 			}
     98 		}
     99 		if (c == null) {
    100 			byte[] data= lookupClassData(name);
    101 			if (data == null)
    102 				throw new ClassNotFoundException();
    103 			c= defineClass(name, data, 0, data.length);
    104 		}
    105 		if (resolve)
    106 			resolveClass(c);
    107 		return c;
    108 	}
    109 
    110 	private byte[] lookupClassData(String className) throws ClassNotFoundException {
    111 		byte[] data= null;
    112 		for (int i= 0; i < fPathItems.size(); i++) {
    113 			String path= (String) fPathItems.elementAt(i);
    114 			String fileName= className.replace('.', '/')+".class";
    115 			if (isJar(path)) {
    116 				data= loadJarData(path, fileName);
    117 			} else {
    118 				data= loadFileData(path, fileName);
    119 			}
    120 			if (data != null)
    121 				return data;
    122 		}
    123 		throw new ClassNotFoundException(className);
    124 	}
    125 
    126 	boolean isJar(String pathEntry) {
    127 		return pathEntry.endsWith(".jar") ||
    128 		       pathEntry.endsWith(".apk") ||
    129                        pathEntry.endsWith(".zip");
    130 	}
    131 
    132 	private byte[] loadFileData(String path, String fileName) {
    133 		File file= new File(path, fileName);
    134 		if (file.exists()) {
    135 			return getClassData(file);
    136 		}
    137 		return null;
    138 	}
    139 
    140 	private byte[] getClassData(File f) {
    141 		try {
    142 			FileInputStream stream= new FileInputStream(f);
    143 			ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
    144 			byte[] b= new byte[1000];
    145 			int n;
    146 			while ((n= stream.read(b)) != -1)
    147 				out.write(b, 0, n);
    148 			stream.close();
    149 			out.close();
    150 			return out.toByteArray();
    151 
    152 		} catch (IOException e) {
    153 		}
    154 		return null;
    155 	}
    156 
    157 	private byte[] loadJarData(String path, String fileName) {
    158 		ZipFile zipFile= null;
    159 		InputStream stream= null;
    160 		File archive= new File(path);
    161 		if (!archive.exists())
    162 			return null;
    163 		try {
    164 			zipFile= new ZipFile(archive);
    165 		} catch(IOException io) {
    166 			return null;
    167 		}
    168 		ZipEntry entry= zipFile.getEntry(fileName);
    169 		if (entry == null)
    170 			return null;
    171 		int size= (int) entry.getSize();
    172 		try {
    173 			stream= zipFile.getInputStream(entry);
    174 			byte[] data= new byte[size];
    175 			int pos= 0;
    176 			while (pos < size) {
    177 				int n= stream.read(data, pos, data.length - pos);
    178 				pos += n;
    179 			}
    180 			zipFile.close();
    181 			return data;
    182 		} catch (IOException e) {
    183 		} finally {
    184 			try {
    185 				if (stream != null)
    186 					stream.close();
    187 			} catch (IOException e) {
    188 			}
    189 		}
    190 		return null;
    191 	}
    192 
    193 	private void readExcludedPackages() {
    194 		fExcluded= new Vector(10);
    195 		for (int i= 0; i < defaultExclusions.length; i++)
    196 			fExcluded.addElement(defaultExclusions[i]);
    197 
    198 		InputStream is= getClass().getResourceAsStream(EXCLUDED_FILE);
    199 		if (is == null)
    200 			return;
    201 		Properties p= new Properties();
    202 		try {
    203 			p.load(is);
    204 		}
    205 		catch (IOException e) {
    206 			return;
    207 		} finally {
    208 			try {
    209 				is.close();
    210 			} catch (IOException e) {
    211 			}
    212 		}
    213 		for (Enumeration e= p.propertyNames(); e.hasMoreElements(); ) {
    214 			String key= (String)e.nextElement();
    215 			if (key.startsWith("excluded.")) {
    216 				String path= p.getProperty(key);
    217 				path= path.trim();
    218 				if (path.endsWith("*"))
    219 					path= path.substring(0, path.length()-1);
    220 				if (path.length() > 0)
    221 					fExcluded.addElement(path);
    222 			}
    223 		}
    224 	}
    225 }
    226