| 1 |
769 |
jeremybenn |
package gnu.java.net.loader;
|
| 2 |
|
|
|
| 3 |
|
|
import gnu.java.net.IndexListParser;
|
| 4 |
|
|
|
| 5 |
|
|
import java.io.IOException;
|
| 6 |
|
|
import java.net.JarURLConnection;
|
| 7 |
|
|
import java.net.MalformedURLException;
|
| 8 |
|
|
import java.net.URL;
|
| 9 |
|
|
import java.net.URLClassLoader;
|
| 10 |
|
|
import java.net.URLStreamHandlerFactory;
|
| 11 |
|
|
import java.util.ArrayList;
|
| 12 |
|
|
import java.util.Iterator;
|
| 13 |
|
|
import java.util.LinkedHashMap;
|
| 14 |
|
|
import java.util.Map;
|
| 15 |
|
|
import java.util.Set;
|
| 16 |
|
|
import java.util.StringTokenizer;
|
| 17 |
|
|
import java.util.jar.Attributes;
|
| 18 |
|
|
import java.util.jar.JarEntry;
|
| 19 |
|
|
import java.util.jar.JarFile;
|
| 20 |
|
|
import java.util.jar.Manifest;
|
| 21 |
|
|
|
| 22 |
|
|
/**
|
| 23 |
|
|
* A <code>JarURLLoader</code> is a type of <code>URLLoader</code>
|
| 24 |
|
|
* only loading from jar url.
|
| 25 |
|
|
*/
|
| 26 |
|
|
public final class JarURLLoader extends URLLoader
|
| 27 |
|
|
{
|
| 28 |
|
|
// True if we've initialized -- i.e., tried open the jar file.
|
| 29 |
|
|
boolean initialized;
|
| 30 |
|
|
// The jar file for this url.
|
| 31 |
|
|
JarFile jarfile;
|
| 32 |
|
|
// Base jar: url for all resources loaded from jar.
|
| 33 |
|
|
final URL baseJarURL;
|
| 34 |
|
|
// The "Class-Path" attribute of this Jar's manifest.
|
| 35 |
|
|
ArrayList<URLLoader> classPath;
|
| 36 |
|
|
// If not null, a mapping from INDEX.LIST for this jar only.
|
| 37 |
|
|
// This is a set of all prefixes and top-level files that
|
| 38 |
|
|
// ought to be available in this jar.
|
| 39 |
|
|
Set indexSet;
|
| 40 |
|
|
|
| 41 |
|
|
// This constructor is used internally. It purposely does not open
|
| 42 |
|
|
// the jar file -- it defers this until later. This allows us to
|
| 43 |
|
|
// implement INDEX.LIST lazy-loading semantics.
|
| 44 |
|
|
private JarURLLoader(URLClassLoader classloader, URLStreamHandlerCache cache,
|
| 45 |
|
|
URLStreamHandlerFactory factory,
|
| 46 |
|
|
URL baseURL, URL absoluteUrl,
|
| 47 |
|
|
Set indexSet)
|
| 48 |
|
|
{
|
| 49 |
|
|
super(classloader, cache, factory, baseURL, absoluteUrl);
|
| 50 |
|
|
|
| 51 |
|
|
URL newBaseURL = null;
|
| 52 |
|
|
try
|
| 53 |
|
|
{
|
| 54 |
|
|
// Cache url prefix for all resources in this jar url.
|
| 55 |
|
|
String base = baseURL.toExternalForm() + "!/";
|
| 56 |
|
|
newBaseURL = new URL("jar", "", -1, base, cache.get(factory, "jar"));
|
| 57 |
|
|
}
|
| 58 |
|
|
catch (MalformedURLException ignore)
|
| 59 |
|
|
{
|
| 60 |
|
|
// Ignore.
|
| 61 |
|
|
}
|
| 62 |
|
|
this.baseJarURL = newBaseURL;
|
| 63 |
|
|
this.classPath = null;
|
| 64 |
|
|
this.indexSet = indexSet;
|
| 65 |
|
|
}
|
| 66 |
|
|
|
| 67 |
|
|
// This constructor is used by URLClassLoader. It will immediately
|
| 68 |
|
|
// try to read the jar file, in case we've found an index or a class-path
|
| 69 |
|
|
// setting. FIXME: it would be nice to defer this as well, but URLClassLoader
|
| 70 |
|
|
// makes this hard.
|
| 71 |
|
|
public JarURLLoader(URLClassLoader classloader, URLStreamHandlerCache cache,
|
| 72 |
|
|
URLStreamHandlerFactory factory,
|
| 73 |
|
|
URL baseURL, URL absoluteUrl)
|
| 74 |
|
|
{
|
| 75 |
|
|
this(classloader, cache, factory, baseURL, absoluteUrl, null);
|
| 76 |
|
|
initialize();
|
| 77 |
|
|
}
|
| 78 |
|
|
|
| 79 |
|
|
private void initialize()
|
| 80 |
|
|
{
|
| 81 |
|
|
JarFile jarfile = null;
|
| 82 |
|
|
try
|
| 83 |
|
|
{
|
| 84 |
|
|
jarfile =
|
| 85 |
|
|
((JarURLConnection) baseJarURL.openConnection()).getJarFile();
|
| 86 |
|
|
|
| 87 |
|
|
Manifest manifest;
|
| 88 |
|
|
Attributes attributes;
|
| 89 |
|
|
String classPathString;
|
| 90 |
|
|
|
| 91 |
|
|
IndexListParser parser = new IndexListParser(jarfile, baseJarURL,
|
| 92 |
|
|
baseURL);
|
| 93 |
|
|
LinkedHashMap<URL, Set<String>> indexMap = parser.getHeaders();
|
| 94 |
|
|
if (indexMap != null)
|
| 95 |
|
|
{
|
| 96 |
|
|
// Note that the index also computes
|
| 97 |
|
|
// the resulting Class-Path -- there are jars out there
|
| 98 |
|
|
// where the index lists some required jars which do
|
| 99 |
|
|
// not appear in the Class-Path attribute in the manifest.
|
| 100 |
|
|
this.classPath = new ArrayList<URLLoader>();
|
| 101 |
|
|
Iterator<Map.Entry<URL, Set<String>>> it = indexMap.entrySet().iterator();
|
| 102 |
|
|
while (it.hasNext())
|
| 103 |
|
|
{
|
| 104 |
|
|
Map.Entry<URL, Set<String>> entry = it.next();
|
| 105 |
|
|
URL subURL = entry.getKey();
|
| 106 |
|
|
Set<String> prefixes = entry.getValue();
|
| 107 |
|
|
if (subURL.equals(baseURL))
|
| 108 |
|
|
this.indexSet = prefixes;
|
| 109 |
|
|
else
|
| 110 |
|
|
{
|
| 111 |
|
|
JarURLLoader subLoader = new JarURLLoader(classloader,
|
| 112 |
|
|
cache,
|
| 113 |
|
|
factory, subURL,
|
| 114 |
|
|
subURL,
|
| 115 |
|
|
prefixes);
|
| 116 |
|
|
// Note that we don't care if the sub-loader itself has an
|
| 117 |
|
|
// index or a class-path -- only the top-level jar
|
| 118 |
|
|
// file gets this treatment; its index should cover
|
| 119 |
|
|
// everything.
|
| 120 |
|
|
this.classPath.add(subLoader);
|
| 121 |
|
|
}
|
| 122 |
|
|
}
|
| 123 |
|
|
}
|
| 124 |
|
|
else if ((manifest = jarfile.getManifest()) != null
|
| 125 |
|
|
&& (attributes = manifest.getMainAttributes()) != null
|
| 126 |
|
|
&& ((classPathString
|
| 127 |
|
|
= attributes.getValue(Attributes.Name.CLASS_PATH))
|
| 128 |
|
|
!= null))
|
| 129 |
|
|
{
|
| 130 |
|
|
this.classPath = new ArrayList<URLLoader>();
|
| 131 |
|
|
StringTokenizer st = new StringTokenizer(classPathString, " ");
|
| 132 |
|
|
while (st.hasMoreElements ())
|
| 133 |
|
|
{
|
| 134 |
|
|
String e = st.nextToken ();
|
| 135 |
|
|
try
|
| 136 |
|
|
{
|
| 137 |
|
|
URL subURL = new URL(baseURL, e);
|
| 138 |
|
|
// We've seen at least one jar file whose Class-Path
|
| 139 |
|
|
// attribute includes the original jar. If we process
|
| 140 |
|
|
// that normally we end up with infinite recursion.
|
| 141 |
|
|
if (subURL.equals(baseURL))
|
| 142 |
|
|
continue;
|
| 143 |
|
|
JarURLLoader subLoader = new JarURLLoader(classloader,
|
| 144 |
|
|
cache, factory,
|
| 145 |
|
|
subURL, subURL);
|
| 146 |
|
|
this.classPath.add(subLoader);
|
| 147 |
|
|
ArrayList<URLLoader> extra = subLoader.getClassPath();
|
| 148 |
|
|
if (extra != null)
|
| 149 |
|
|
this.classPath.addAll(extra);
|
| 150 |
|
|
}
|
| 151 |
|
|
catch (java.net.MalformedURLException xx)
|
| 152 |
|
|
{
|
| 153 |
|
|
// Give up
|
| 154 |
|
|
}
|
| 155 |
|
|
}
|
| 156 |
|
|
}
|
| 157 |
|
|
}
|
| 158 |
|
|
catch (IOException ioe)
|
| 159 |
|
|
{
|
| 160 |
|
|
/* ignored */
|
| 161 |
|
|
}
|
| 162 |
|
|
|
| 163 |
|
|
this.jarfile = jarfile;
|
| 164 |
|
|
this.initialized = true;
|
| 165 |
|
|
}
|
| 166 |
|
|
|
| 167 |
|
|
/** get resource with the name "name" in the jar url */
|
| 168 |
|
|
public Resource getResource(String name)
|
| 169 |
|
|
{
|
| 170 |
|
|
if (name.startsWith("/"))
|
| 171 |
|
|
name = name.substring(1);
|
| 172 |
|
|
if (indexSet != null)
|
| 173 |
|
|
{
|
| 174 |
|
|
// Trust the index.
|
| 175 |
|
|
String basename = name;
|
| 176 |
|
|
int offset = basename.lastIndexOf('/');
|
| 177 |
|
|
if (offset != -1)
|
| 178 |
|
|
basename = basename.substring(0, offset);
|
| 179 |
|
|
if (! indexSet.contains(basename))
|
| 180 |
|
|
return null;
|
| 181 |
|
|
// FIXME: if the index claim to hold the resource, and jar file
|
| 182 |
|
|
// doesn't have it, we're supposed to throw an exception. However,
|
| 183 |
|
|
// in our model this is tricky to implement, as another URLLoader from
|
| 184 |
|
|
// the same top-level jar may have an overlapping index entry.
|
| 185 |
|
|
}
|
| 186 |
|
|
|
| 187 |
|
|
if (! initialized)
|
| 188 |
|
|
initialize();
|
| 189 |
|
|
if (jarfile == null)
|
| 190 |
|
|
return null;
|
| 191 |
|
|
|
| 192 |
|
|
JarEntry je = jarfile.getJarEntry(name);
|
| 193 |
|
|
if (je != null)
|
| 194 |
|
|
return new JarURLResource(this, name, je);
|
| 195 |
|
|
else
|
| 196 |
|
|
return null;
|
| 197 |
|
|
}
|
| 198 |
|
|
|
| 199 |
|
|
public Manifest getManifest()
|
| 200 |
|
|
{
|
| 201 |
|
|
try
|
| 202 |
|
|
{
|
| 203 |
|
|
return (jarfile == null) ? null : jarfile.getManifest();
|
| 204 |
|
|
}
|
| 205 |
|
|
catch (IOException ioe)
|
| 206 |
|
|
{
|
| 207 |
|
|
return null;
|
| 208 |
|
|
}
|
| 209 |
|
|
}
|
| 210 |
|
|
|
| 211 |
|
|
public ArrayList<URLLoader> getClassPath()
|
| 212 |
|
|
{
|
| 213 |
|
|
return classPath;
|
| 214 |
|
|
}
|
| 215 |
|
|
}
|