226 lines
5.5 KiB
Java
226 lines
5.5 KiB
Java
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);
|
|
}
|
|
}
|
|
}
|
|
}
|