Source for gnu.java.net.loader.JarURLLoader

   1: 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: }