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