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