| 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. |
| * {@hide} - Not needed for 1.0 SDK |
| */ |
| 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(".apk") || |
| 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); |
| } |
| } |
| } |
| } |