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 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 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(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
90
91
92 if (isExcluded(name)) {
93 try {
94 c= findSystemClass(name);
95 return c;
96 } catch (ClassNotFoundException e) {
97
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") || pathEntry.endsWith(".zip");
129 }
130
131 private byte[] loadFileData(String path, String fileName) {
132 File file= new File(path, fileName);
133 if (file.exists()) {
134 return getClassData(file);
135 }
136 return null;
137 }
138
139 private byte[] getClassData(File f) {
140 try {
141 FileInputStream stream= new FileInputStream(f);
142 ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
143 byte[] b= new byte[1000];
144 int n;
145 while ((n= stream.read(b)) != -1)
146 out.write(b, 0, n);
147 stream.close();
148 out.close();
149 return out.toByteArray();
150
151 } catch (IOException e) {
152 }
153 return null;
154 }
155
156 private byte[] loadJarData(String path, String fileName) {
157 ZipFile zipFile= null;
158 InputStream stream= null;
159 File archive= new File(path);
160 if (!archive.exists())
161 return null;
162 try {
163 zipFile= new ZipFile(archive);
164 } catch(IOException io) {
165 return null;
166 }
167 ZipEntry entry= zipFile.getEntry(fileName);
168 if (entry == null)
169 return null;
170 int size= (int) entry.getSize();
171 try {
172 stream= zipFile.getInputStream(entry);
173 byte[] data= new byte[size];
174 int pos= 0;
175 while (pos < size) {
176 int n= stream.read(data, pos, data.length - pos);
177 pos += n;
178 }
179 zipFile.close();
180 return data;
181 } catch (IOException e) {
182 } finally {
183 try {
184 if (stream != null)
185 stream.close();
186 } catch (IOException e) {
187 }
188 }
189 return null;
190 }
191
192 private void readExcludedPackages() {
193 fExcluded= new Vector(10);
194 for (int i= 0; i < defaultExclusions.length; i++)
195 fExcluded.addElement(defaultExclusions[i]);
196
197 InputStream is= getClass().getResourceAsStream(EXCLUDED_FILE);
198 if (is == null)
199 return;
200 Properties p= new Properties();
201 try {
202 p.load(is);
203 }
204 catch (IOException e) {
205 return;
206 } finally {
207 try {
208 is.close();
209 } catch (IOException e) {
210 }
211 }
212 for (Enumeration e= p.propertyNames(); e.hasMoreElements(); ) {
213 String key= (String)e.nextElement();
214 if (key.startsWith("excluded.")) {
215 String path= p.getProperty(key);
216 path= path.trim();
217 if (path.endsWith("*"))
218 path= path.substring(0, path.length()-1);
219 if (path.length() > 0)
220 fExcluded.addElement(path);
221 }
222 }
223 }
224 }