View Javadoc

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  		// 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") || 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 }