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