Source code: com/simscomputing/testbed/FileSystemClassLoader.java
1 package com.simscomputing.testbed;
2
3 import java.io.*;
4 import java.net.*;
5 import java.util.*;
6 import java.util.jar.JarEntry;
7 import java.util.jar.JarFile;
8 import java.util.jar.JarInputStream;
9 import java.util.zip.*;
10
11 import java.io.File;
12 import java.io.FileNotFoundException;
13 import java.io.FileInputStream;
14 import java.io.IOException;
15 import java.security.AllPermission;
16 import java.security.ProtectionDomain;
17 import java.security.Permissions;
18 import java.security.*;
19
20 /**
21 This class is almost a trivial subclass of URLClassLoader.
22
23 The one thing that I'm baffled by is that if I overload loadClass(),
24 then newly loaded classes have their class loader set to this class.
25 If I don't, then new classes have their class loader set to the
26 primordial class loader.
27
28 I've no idea why this behaves this way. I've tested it under Linux JDK 1.2
29 and Windows JDK 1.3 beta.
30
31 I need newly loaded classes to have their class loader set to
32 FileSystemClassLoader so that can be reloaded when the user clicks
33 "Re-Exercise".
34
35 Why not just find the class file in the file system and call
36 defineClass() like almost all other custom class loaders do? Because
37 it gets real tricky when you're trying to load classes from signed jar
38 files and the like. I figured that since URLClassLoader has already
39 gone to all the trouble to do these things for me, why not use it?
40
41 The only downside so far is that classes loaded from the CLASSPATH,
42 and not the TestClassPath, have their class loader set to the
43 primordial one, which means they can't be re-exercised properly. This
44 is why we have the concept of the TestClassPath.
45
46 But even with this problem, the benefits outweigh the drawbacks. I get
47 to reuse a lot of JDK code which works properly even under tricky
48 security situations.
49
50 Please let me know if you have some insight into this
51 situation. david@simscomputing.com
52
53 @author David Sims
54 @version $Revision: 1.1.1.1 $ $Date: 2000/02/21 21:22:33 $ */
55 public class FileSystemClassLoader extends URLClassLoader {
56 private String classPath;
57 private ArrayList storageUnits;
58 private boolean debug = true;
59 private ClassLoader exemptClassLoader;
60 private ArrayList exemptPackagePrefixList = new ArrayList();
61 private boolean second = false;
62
63 public FileSystemClassLoader(URL[] urls, ClassLoader exemptClassLoader) {
64 super(urls, exemptClassLoader);
65 this.exemptClassLoader = exemptClassLoader;
66 // System.out.println("FSCL's excempt CL = " + exemptClassLoader.getClass().getName());
67 String classPath = System.getProperty("java.class.path", ".");
68 // if (debug) System.out.println("*** CP is " + classPath);
69 // setClassPath(classPath);
70 } // constructor
71
72 // public FileSystemClassLoader(URL[] urls) {
73 // super(urls);
74 // second = true;
75 // } // constructor
76
77
78 // public void exemptPackagePrefix(String prefix) {
79 // exemptPackagePrefixList.add(prefix + ".");
80 // }
81
82 public Class loadClass(File file) throws ClassNotFoundException {
83 try {
84 final FileInputStream inputStream = new FileInputStream(file);
85 final long length = file.length();
86
87 if (length > Integer.MAX_VALUE) {
88 throw new ClassNotFoundException();
89 } // if
90
91 // a safe cast
92 final int size = (int) length;
93
94 final byte[] bytes = new byte[ (int) size];
95
96 if (inputStream.read(bytes) != size) {
97 throw new ClassNotFoundException();
98 } // if
99
100 Class result = defineClass(null, bytes, 0, size);
101 resolveClass(result);
102 return result;
103 } // try
104 catch (FileNotFoundException e) {
105 throw new ClassNotFoundException(e.getMessage());
106 } // catch
107 catch (IOException e) {
108 throw new ClassNotFoundException(e.getMessage());
109 } // catch
110 } // loadClass()
111
112 // public Class loadClass(String name) throws ClassNotFoundException {
113 // // System.out.println("$$$ loadClass(" + name + ")");
114 // return super.loadClass(name, true);
115 // } // loadClass()
116
117 // public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
118 // System.out.println("loadClass(" + name + ", " + resolve + ")");
119 // return super.loadClass(name, resolve);
120 // } // loadClass()
121
122
123 public String toString() { return "FileSystemClassLoader"; }
124
125 // public void setClassPath(String classPath) {
126 // if (!classPath.equals(this.classPath)) {
127 // this.classPath = classPath;
128 // storageUnits = new ArrayList();
129
130 // // turn the class paths, separated by File.pathSeparator, into an ArrayList of .jar files.
131 // StringTokenizer tokens = new StringTokenizer(classPath, File.pathSeparator);
132 // while (tokens.hasMoreTokens()) {
133 // StorageUnit unit;
134 // String nextToken = tokens.nextToken();
135 // File f = new File(nextToken);
136 // if (f.isDirectory()) {
137 // String newurl = "file:" + nextToken;
138 // if (nextToken.length() > 0 && nextToken.charAt(nextToken.length() - 1) != File.separatorChar) {
139 // newurl = newurl + File.separatorChar;
140 // } // if
141
142 // // System.out.println("adding directory url: " + newurl);
143 // // try {
144 // // addURL(new URL(newurl));
145 // // } // try
146 // // catch (Exception e) {
147 // // e.printStackTrace();
148 // // } // catch
149
150 // // System.out.println("CP directory: " + nextToken);
151 // unit = new DirectoryStorageUnit(f);
152 // } // if
153 // else {
154 // String newurl = "file:" + nextToken;
155 // // System.out.println("adding file url: " + newurl);
156 // // try {
157 // // addURL(new URL(newurl));
158 // // } // try
159 // // catch (Exception e) {
160 // // e.printStackTrace();
161 // // } // catch
162
163 // // System.out.println("CP jar file: " + nextToken);
164 // unit = new JarStorageUnit(nextToken);
165 // } // else
166 // storageUnits.add(unit);
167 // } // while
168 // } // if
169 // } // setClassPath()
170
171 // /**
172 // Enumerates all known storage units, searching for the named file, and returns
173 // a non-null stream if it can be found.
174 // */
175 // private byte[] /*InputStream*/ getStream(String path, CodeSourceHolder csh) {
176 // final int size = storageUnits.size();
177
178 // for (int index = 0; index < size; ++index) {
179 // StorageUnit unit = (StorageUnit) storageUnits.get(index);
180 // // InputStream stream = unit.getStream(path, oi);
181 // byte[] stream = unit.getStream(path, csh);
182
183 // if (stream != null) {
184 // // System.out.println("getStream() success on " +
185 // // ((StorageUnit) storageUnits.get(index)).getName());
186 // return stream;
187 // } // if
188 // } // for index
189
190 // return null;
191 // } // getStream()
192
193 // private byte[] loadClassData(String name, CodeSourceHolder csh) {
194 // System.out.println("*** loadClassData(" + name + ")");
195 // // load the class data from the class path.
196 // InputStream stream = null;
197
198 // try {
199 // String entryName = name.replace('.', '/') + ".class";
200 // // OutInteger oi = new OutInteger();
201 // // stream = getStream(entryName, oi);
202 // return getStream(entryName, csh);//, oi);
203
204 // if (stream != null) {
205 // if (name.startsWith("BigDummy")) System.out.println("@@@@ trying to read from jar");
206 // long size = oi.getSize();
207 // if (name.startsWith("BigDummy")) System.out.println("@@@@ size = " + size);
208 // byte[] data = new byte[ (int) size];
209 // int dataread = stream.read(data);
210 // if (name.startsWith("BigDummy")) System.out.println("@@@@ dataread = " + dataread);
211 // return data;
212 // // DataInputStream input = new DataInputStream(stream);
213 // // input.readFully(data);
214 // // input.close();
215 // // return data;
216 // } // if
217 // else System.out.println("*** failed to load " + entryName);
218
219
220
221
222
223 // } // if
224 // else {
225
226 // System.out.println("*** trying to load: " + entryName);
227
228 // totalBytes = read(byte[] b,
229 // int off,
230 // int len)
231 // throws IOException
232
233 // final File f = new File(entryName);
234 // if (f.exists()) {
235 // final int size = (int) f.length();
236 // if (size > 0) {
237 // byte[] data = new byte[size];
238 // DataInputStream input = new DataInputStream(new FileInputStream(f));
239 // input.readFully(data);
240 // input.close();
241 // return data;
242 // } // if
243 // } // if
244 // // } // else
245 // } // try
246 // // catch (IOException ioe) {
247 // // ioe.printStackTrace();
248 // // // return null below
249 // // } // catch
250 // finally {
251 // if (stream != null) {
252 // try {
253 // stream.close();
254 // } // try
255 // catch (IOException e) {
256 // // ignore. do nothing.
257 // } // catch
258 // } // if
259 // }
260
261 // // return null;
262 // } // loadClassData()
263
264 // public Class findClass(String name) throws ClassNotFoundException {
265 // System.out.println("************ findClass(" + name + ") ************");
266 // return Integer.class;
267 // }
268
269 /**
270 According the JDK source, loadClass is supposed to be declared as synchronzied.
271 For reasons unknown, if this method is commented out, all classes loaded by
272 this class loader will have their class loader set to primordial class loader,
273 NOT this class (FileSystemClassLoader).
274 */
275 public synchronized Class loadClass(String name, boolean resolve)
276 throws ClassNotFoundException {
277 return super.loadClass(name, resolve);
278 } // loadClass()
279
280
281 // System.out.println("$$$$ loadClass(" + name + ", " + resolve + ")");
282 // return super.loadClass(name, resolve);
283
284 // first, check if class name's package matches one of the exempt package prefixes
285 // final int size = exemptPackagePrefixList.size();
286 // for (int index = 0; index < size; ++index) {
287 // if (name.startsWith( (String) exemptPackagePrefixList.get(index)) ) {
288 // // name is exempt from special class loading. Use the stock class loader.
289 // if (debug) System.out.println("*** exempt: " + name);
290 // Class result = exemptClassLoader.loadClass(name);
291 // System.out.println("exempt: " + result.getName() + ", CL = " + result.getClassLoader().getClass().getName());
292 // return result;
293 // // return super.findClass(name);
294 // } // if
295 // } // for index
296
297 // System.out.println("loading " + name);
298
299 // /*
300
301 // Class c = findLoadedClass(name);
302 // if (c == null) {
303 // // don't look load system classes in our class loader, because this can mess up Java.
304 // boolean direct = true;
305 // if ((!name.startsWith("java.")) && (!name.startsWith("javax."))) {
306 // direct = false;
307 // CodeSourceHolder csh = new CodeSourceHolder();
308 // byte[] data = loadClassData(name, csh);
309 // if (data == null) System.out.println("*********** loadClassData failed on " + name);
310 // if (debug && name.equals("BigDummy")) System.out.println("###(loadClassData(), got back: " + data);
311 // if (data != null) {
312 // System.out.println("defineClass on data length " + data.length);
313 // // try {
314 // // FileOutputStream fos = new FileOutputStream("testme");
315 // // fos.write(data);
316 // // fos.close();
317 // // } catch (IOException e) {e.printStackTrace();}
318 // // URL u = null;
319 // // try {
320 // // u = new URL("file:.");
321 // // }
322 // // catch (MalformedURLException e) {
323 // // // do nothing
324 // // }
325 // // CodeSource codeSource = new CodeSource(u,
326
327 // Permissions permissions = new Permissions();
328 // permissions.add(new AllPermission());
329 // ProtectionDomain pd = new ProtectionDomain(csh.codeSource, permissions);
330
331 // // SecureClassLoader scl = new SecureClassLoader(this);
332 // // c = scl.defineClass(name, data, 0, data.length, 0);
333 // CodeSource cs = null;
334
335 // c = defineClass(name, data, 0, data.length, null); // null was cs
336
337 // // Class superc = super.loadClass(name, resolve);
338 // // if (c == superc) System.out.println("c == superc");
339 // // try {
340 // // Object o = c.newInstance();
341 // // System.out.println("c.newInstance() = " + o);
342 // // }
343 // // catch (Exception e) {e.printStackTrace();}
344 // // System.out.println("c = " + c + ", superc = " + superc);
345
346 // if (c == null) System.out.println("******* defineClass() returned null");
347 // if (debug && name.equals("BigDummy")) System.out.println("#### after defineClass(), got back c: " + c);
348 // } // if
349 // } // if
350 // if (c == null) {
351 // // System.out.println("*** using stock class loader on " + name);// + ", direct = " + direct);
352
353
354
355 // // System.out.println("*** using stock class loader (" + FileSystemClassLoader.class.getClassLoader() + ") on " + name);// + ", direct = " + direct);
356 // return super.loadClass(name, resolve);
357 // // ClassLoader loader = FileSystemClassLoader.class.getClassLoader();
358 // // if (loader != null) {
359 // // return loader.loadClass(name);
360 // // } // if
361 // } // if
362 // if (c == null) {
363 // System.out.println("*** findSystemClass on " + name);
364 // c = findSystemClass(name);
365 // } // if
366 // if (c == null) {
367 // System.out.println("*** throwing ClassNotFoundException");
368 // throw new ClassNotFoundException(name);
369 // } // if
370 // } // if
371 // else {
372 // System.out.println("*** found loaded class: " + name);
373 // } // else
374
375 // if (resolve) {
376 // resolveClass(c);
377 // } // if
378
379 // if (c == null) {
380 // System.out.println("*** loadClass() returns null on: " + name);
381 // }
382
383 // return c;
384
385 // */
386
387 // } // loadClass()
388
389
390 ///////////////////
391 // inner classes //
392 ///////////////////
393
394 // private abstract class StorageUnit {
395 // abstract byte[] /*InputStream*/ getStream(String path, CodeSourceHolder csh);
396 // abstract String getName();
397 // } // class StorageUnit
398
399 // /**
400 // Retrieves class files from a directory.
401 // */
402 // private class DirectoryStorageUnit extends StorageUnit {
403 // private File directory;
404
405 // public String getName() { return "dir storage: " + directory; }
406
407 // DirectoryStorageUnit(File directory) {
408 // this.directory = directory;
409 // } // constructor
410
411 // byte[] /*InputStream*/ getStream(String path, CodeSourceHolder csh) {
412 // try {
413 // File f = new File(directory, path);
414 // if (f.exists()) {
415 // // oi.setSize(f.length());
416 // FileInputStream fis = new FileInputStream(f);
417 // if (fis != null) {
418 // byte[] result = new byte[ (int) f.length()];
419 // fis.read(result);
420 // fis.close();
421 // return result;
422 // } // if
423 // } // if
424 // }
425 // catch (IOException e) {
426 // // return null below
427 // }
428
429 // return null;
430
431 // // return (f.exists() ? new FileInputStream(f) : null);
432 // } // getStream()
433 // } // class DirectoryStorageUnit
434
435 // /**
436 // Retrieves class files from a jar file.
437 // */
438 // private class JarStorageUnit extends StorageUnit {
439 // // private File archivePath;
440 // // private ZipFile archive;
441 // String jarFile;
442
443 // public String getName() { return "jar storage: " + jarFile; }
444
445 // JarStorageUnit(String jarFile) {
446 // this.jarFile = jarFile;
447 // // archive = null;
448 // } // constructor
449
450 // byte[] /*InputStream*/ getStream(String path, CodeSourceHolder csh) {
451 // // System.out.println("#### jar " + jarFile + " getStream: " + path);
452 // try {
453
454 // // extracts just sizes only.
455 // final HashMap htSizes = new HashMap();
456 // final ZipFile zf = new ZipFile(jarFile);
457 // final Enumeration e = zf.entries();
458 // while (e.hasMoreElements()) {
459 // ZipEntry ze=(ZipEntry)e.nextElement();
460 // htSizes.put(ze.getName(), new Integer((int)ze.getSize()));
461 // } // while
462 // zf.close();
463 // // end
464
465
466
467 // // have to reload the archive from disk every time in case it changes
468 // //csh ZipFile archive = new ZipFile(jarFile);
469 // JarFile archive = new JarFile(jarFile);
470
471 // FileInputStream fis = new FileInputStream(jarFile);
472 // //csh ZipInputStream zis = new ZipInputStream(fis);
473 // //csh ZipEntry ze;
474 // JarInputStream zis = new JarInputStream(fis);
475 // JarEntry ze;
476
477 // //csh while ((ze = zis.getNextEntry()) != null) {
478 // while ((ze = zis.getNextJarEntry()) != null) {
479 // if (ze.isDirectory()) continue;
480
481 // if (ze.getName().equals(path)) {
482 // // found our boy
483 // // System.out.println("!!!! ze name: " + ze.getName());
484 // // System.out.println("!!!! archive size = " + new File(jarFile).length());
485 // // System.out.println("!!!! entry size = " + ze.getSize());
486 // // System.out.println("!!!! entry compressed size = " + ze.getCompressedSize());
487
488 // // oi.setSize(ze.getSize());
489
490 // // java.security.cert.Certificate[] certs = ze.getCertificates();
491 // // int len = certs == null ? 0 : certs.length;
492 // // if (len != 0)
493 // // System.out.println("got " + len + " certs on " + ze.getName());
494 // // csh.codeSource = new CodeSource(null, certs);
495
496 // int size = (int) ze.getSize();
497 // if (size == -1) {
498 // size = ((Integer) htSizes.get(ze.getName())).intValue();
499 // // oi.setSize(size);
500 // // System.out.println("!!!! remapped size to " + size);
501 // } // if
502 // byte[] result = new byte[size];
503
504 // int rb=0;
505 // int chunk=0;
506
507 // // fixme: why on earth do we have to loop like this?
508 // while ( ((int)size - rb) > 0) {
509 // chunk = zis.read(result, rb,(int)size - rb);
510 // if (chunk==-1) {
511 // break;
512 // }
513 // rb += chunk;
514 // } // while
515
516 // // int got = zis.read(result);
517 // // System.out.println("!!!! read " + rb + " bytes from " + path);
518 // zis.close();
519 // fis.close();
520 // archive.close();
521
522 // return result;
523 // } // if
524
525 // } // while
526 // } // try
527 // catch (IOException e) {
528 // // return null below
529 // }
530
531 // /*
532 // ZipEntry entry = archive.getEntry(path);
533
534 // if (entry != null) {
535 // if (debug) System.out.println("!!!! archive size = " + new File(jarFile).length());
536 // if (debug) System.out.println("!!!! entry size = " + entry.getSize());
537 // oi.setSize(entry.getSize());
538 // InputStream input = archive.getInputStream(entry);
539 // ZipInputStream zis = new ZipInputStream(input);
540
541 // final int size = (int) entry.getSize();
542 // byte[] result = new byte[size];
543
544 // int rb=0;
545 // int chunk=0;
546
547 // // fixme: why on earth do we have to loop like this?
548 // while ( ((int)size - rb) > 0) {
549 // chunk = zis.read(result, rb,(int)size - rb);
550 // if (chunk==-1) {
551 // break;
552 // }
553 // rb += chunk;
554 // } // while
555
556
557 // // int got = input.read(result);
558 // if (debug) System.out.println("read " + rb + " bytes from " + path);
559 // input.close();
560 // zis.close();
561 // archive.close();
562 // return result;
563 // } // if
564 // else {
565 // if (debug) System.out.println("!!!! ZipEntry is null");
566 // } // else
567
568 // archive.close();
569 // */
570
571 // return null;
572 // } // getStream()
573 // } // class JarStorageUnit
574
575 // class OutInteger {
576 // long size;
577
578 // void setSize(long newsize) { size = newsize; }
579 // long getSize() { return size; }
580 // } // class OutInteger
581
582 //////////////////////////
583
584 // public java.net.URL findResource(String name) {
585 // System.out.println("#### findResource(" + name + ") called");
586 // return super.findResource(name);
587 // }
588
589 // public Enumeration findResources(String name) throws IOException {
590 // System.out.println("#### findResources(" + name + ") called");
591 // return super.findResources(name);
592 // }
593
594 // public java.net.URL getResource(String name) {
595 // System.out.println("#### getResource(" + name + ") called");
596 // return super.getResource(name);
597 // }
598
599 // public Enumeration getResources(String name) throws IOException {
600 // System.out.println("#### getResources(" + name + ") called");
601 // return super.getResources(name);
602 // }
603
604 // public InputStream getResourceAsStrream(String name) {
605 // System.out.println("#### getResourceAsStream(" + name + ") called");
606 // return super.getResourceAsStream(name);
607 // }
608
609 // public String findLibrary(String library) {
610 // System.out.println("#### findLibrary(" + library + ") called");
611 // return super.findLibrary(library);
612 // }
613
614
615 // public Class findClass(String name) throws ClassNotFoundException {
616 // // try {
617 // System.out.println("************* findClass() called: " + name);
618 // if (true || second) {
619 // System.out.println("************* calling super.findClass()");
620 // Class c = super.findClass(name);
621 // System.out.println("************* super.findClass() returned: " + c.getName());
622 // return c;
623 // }
624 // throw new ClassNotFoundException();
625 // }
626
627
628 // final String testClassPath = System.getProperty("TestClassPath");
629 // System.out.println("*** TestClassPath = " + testClassPath);
630 // FileSystemClassLoader fileSystemClassLoader;
631 // if (testClassPath != null) {
632 // final StringTokenizer tokenizer = new StringTokenizer(testClassPath, File.pathSeparator);
633 // String token;
634 // final URL[] urls = new URL[tokenizer.countTokens()];
635 // int index = 0;
636 // while (tokenizer.hasMoreTokens()) {
637 // token = tokenizer.nextToken();
638
639 // String fileUrl = "file:" + token;
640 // if (token.length() > 0 && token.charAt(token.length() - 1) != File.separatorChar) {
641 // File fileToken = new File(token);
642 // if (fileToken.isDirectory()) {
643 // fileUrl = fileUrl + File.separatorChar;
644 // } // if
645 // } // if
646
647 // urls[index] = new URL(fileUrl);
648 // System.out.println(urls[index]);
649 // ++index;
650 // } // while
651 // fileSystemClassLoader = new FileSystemClassLoader(urls);
652 // } // if
653 // else {
654 // fileSystemClassLoader = new FileSystemClassLoader(new URL[0]);
655 // } // else
656 // System.out.println("*** calling loadClass() on " + name);
657 // return fileSystemClassLoader.loadClass(name);
658 // }
659 // catch (Exception e) {
660 // e.printStackTrace();
661 // throw new ClassNotFoundException(e.getMessage());
662 // } // catch
663 // } // findClass()
664
665 // public Class findClass(String name) throws ClassNotFoundException {
666 // System.out.println("#### findClass(" + name + ") called");
667 // return super.findClass(name);
668 // }
669
670 // class CodeSourceHolder {
671 // public CodeSource codeSource = null;
672 // }
673
674 } // class FileSystemClassLoader