001: package com.sun.istack.tools;
002:
003: import java.io.InputStream;
004: import java.io.ByteArrayOutputStream;
005: import java.io.IOException;
006: import java.net.URL;
007: import java.net.MalformedURLException;
008: import java.util.Enumeration;
009:
010: /**
011: * Load classes/resources from a side folder, so that
012: * classes of the same package can live in a single jar file.
013: *
014: * <p>
015: * For example, with the following jar file:
016: * <pre>
017: * /
018: * +- foo
019: * +- X.class
020: * +- bar
021: * +- X.class
022: * </pre>
023: * <p>
024: * {@link ParallelWorldClassLoader}("foo/") would load <tt>X.class<tt> from
025: * <tt>/foo/X.class</tt> (note that X is defined in the root package, not
026: * <tt>foo.X</tt>.
027: *
028: * <p>
029: * This can be combined with {@link MaskingClassLoader} to mask classes which are loaded by the parent
030: * class loader so that the child class loader
031: * classes living in different folders are loaded
032: * before the parent class loader loads classes living the jar file publicly
033: * visible
034: * For example, with the following jar file:
035: * <pre>
036: * /
037: * +- foo
038: * +- X.class
039: * +- bar
040: * +-foo
041: * +- X.class
042: * </pre>
043: * <p>
044: * {@link ParallelWorldClassLoader}(MaskingClassLoader.class.getClassLoader()) would load <tt>foo.X.class<tt> from
045: * <tt>/bar/foo.X.class</tt> not the <tt>foo.X.class<tt> in the publicly visible place in the jar file, thus
046: * masking the parent classLoader from loading the class from <tt>foo.X.class<tt>
047: * (note that X is defined in the package foo, not
048: * <tt>bar.foo.X</tt>.
049: *
050: * @author Kohsuke Kawaguchi
051: */
052: public class ParallelWorldClassLoader extends ClassLoader {
053:
054: /**
055: * Strings like "prefix/", "abc/", or "" to indicate
056: * classes should be loaded normally.
057: */
058: private final String prefix;
059:
060: public ParallelWorldClassLoader(ClassLoader parent, String prefix) {
061: super (parent);
062: this .prefix = prefix;
063: }
064:
065: protected Class findClass(String name)
066: throws ClassNotFoundException {
067: StringBuffer sb = new StringBuffer(name.length()
068: + prefix.length() + 6);
069: sb.append(prefix).append(name.replace('.', '/')).append(
070: ".class");
071:
072: InputStream is = getParent().getResourceAsStream(sb.toString());
073: if (is == null)
074: throw new ClassNotFoundException(name);
075:
076: try {
077: ByteArrayOutputStream baos = new ByteArrayOutputStream();
078: byte[] buf = new byte[1024];
079: int len;
080: while ((len = is.read(buf)) >= 0)
081: baos.write(buf, 0, len);
082:
083: buf = baos.toByteArray();
084: int packIndex = name.lastIndexOf('.');
085: if (packIndex != -1) {
086: String pkgname = name.substring(0, packIndex);
087: // Check if package already loaded.
088: Package pkg = getPackage(pkgname);
089: if (pkg == null) {
090: definePackage(pkgname, null, null, null, null,
091: null, null, null);
092: }
093: }
094: return defineClass(name, buf, 0, buf.length);
095: } catch (IOException e) {
096: throw new ClassNotFoundException(name, e);
097: }
098: }
099:
100: protected URL findResource(String name) {
101: return getParent().getResource(prefix + name);
102: }
103:
104: protected Enumeration<URL> findResources(String name)
105: throws IOException {
106: return getParent().getResources(prefix + name);
107: }
108:
109: /**
110: * Given the URL inside jar, returns the URL to the jar itself.
111: */
112: public static URL toJarUrl(URL res) throws ClassNotFoundException,
113: MalformedURLException {
114: String url = res.toExternalForm();
115: if (!url.startsWith("jar:"))
116: throw new ClassNotFoundException("Loaded outside a jar "
117: + url);
118: url = url.substring(4); // cut off jar:
119: url = url.substring(0, url.lastIndexOf('!')); // cut off everything after '!'
120: return new URL(url);
121: }
122: }
|