Initial Contribution
diff --git a/test-runner/junit/runner/TestCaseClassLoader.java b/test-runner/junit/runner/TestCaseClassLoader.java
new file mode 100644
index 0000000..3a510c6
--- /dev/null
+++ b/test-runner/junit/runner/TestCaseClassLoader.java
@@ -0,0 +1,225 @@
+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);				
+			}
+		}
+	}
+}