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