Save This Page
Home » openjdk-7 » sun » misc » [javadoc | source]
    1   /*
    2    * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package sun.misc;
   27   
   28   import java.util.Enumeration;
   29   import java.util.HashMap;
   30   import java.util.LinkedList;
   31   import java.util.Hashtable;
   32   import java.util.NoSuchElementException;
   33   import java.util.Stack;
   34   import java.util.Set;
   35   import java.util.HashSet;
   36   import java.util.StringTokenizer;
   37   import java.util.ArrayList;
   38   import java.util.Iterator;
   39   import java.util.jar.JarFile;
   40   import sun.misc.JarIndex;
   41   import sun.misc.InvalidJarIndexException;
   42   import sun.net.www.ParseUtil;
   43   import java.util.zip.ZipEntry;
   44   import java.util.jar.JarEntry;
   45   import java.util.jar.Manifest;
   46   import java.util.jar.Attributes;
   47   import java.util.jar.Attributes.Name;
   48   import java.net.JarURLConnection;
   49   import java.net.MalformedURLException;
   50   import java.net.URL;
   51   import java.net.URLConnection;
   52   import java.net.HttpURLConnection;
   53   import java.net.URLStreamHandler;
   54   import java.net.URLStreamHandlerFactory;
   55   import java.io.File;
   56   import java.io.FileInputStream;
   57   import java.io.FileNotFoundException;
   58   import java.io.InputStream;
   59   import java.io.DataOutputStream;
   60   import java.io.IOException;
   61   import java.security.AccessController;
   62   import java.security.AccessControlException;
   63   import java.security.CodeSigner;
   64   import java.security.Permission;
   65   import java.security.PrivilegedAction;
   66   import java.security.PrivilegedExceptionAction;
   67   import java.security.cert.Certificate;
   68   import sun.misc.FileURLMapper;
   69   
   70   /**
   71    * This class is used to maintain a search path of URLs for loading classes
   72    * and resources from both JAR files and directories.
   73    *
   74    * @author  David Connelly
   75    */
   76   public class URLClassPath {
   77       final static String USER_AGENT_JAVA_VERSION = "UA-Java-Version";
   78       final static String JAVA_VERSION;
   79       private static final boolean DEBUG;
   80   
   81       static {
   82           JAVA_VERSION = java.security.AccessController.doPrivileged(
   83               new sun.security.action.GetPropertyAction("java.version"));
   84           DEBUG        = (java.security.AccessController.doPrivileged(
   85               new sun.security.action.GetPropertyAction("sun.misc.URLClassPath.debug")) != null);
   86       }
   87   
   88       /* The original search path of URLs. */
   89       private ArrayList<URL> path = new ArrayList<URL>();
   90   
   91       /* The stack of unopened URLs */
   92       Stack<URL> urls = new Stack<URL>();
   93   
   94       /* The resulting search path of Loaders */
   95       ArrayList<Loader> loaders = new ArrayList<Loader>();
   96   
   97       /* Map of each URL opened to its corresponding Loader */
   98       HashMap<URL, Loader> lmap = new HashMap<URL, Loader>();
   99   
  100       /* The jar protocol handler to use when creating new URLs */
  101       private URLStreamHandler jarHandler;
  102   
  103       /**
  104        * Creates a new URLClassPath for the given URLs. The URLs will be
  105        * searched in the order specified for classes and resources. A URL
  106        * ending with a '/' is assumed to refer to a directory. Otherwise,
  107        * the URL is assumed to refer to a JAR file.
  108        *
  109        * @param urls the directory and JAR file URLs to search for classes
  110        *        and resources
  111        * @param factory the URLStreamHandlerFactory to use when creating new URLs
  112        */
  113       public URLClassPath(URL[] urls, URLStreamHandlerFactory factory) {
  114           for (int i = 0; i < urls.length; i++) {
  115               path.add(urls[i]);
  116           }
  117           push(urls);
  118           if (factory != null) {
  119               jarHandler = factory.createURLStreamHandler("jar");
  120           }
  121       }
  122   
  123       public URLClassPath(URL[] urls) {
  124           this(urls, null);
  125       }
  126   
  127       /**
  128        * Appends the specified URL to the search path of directory and JAR
  129        * file URLs from which to load classes and resources.
  130        * <p>
  131        * If the URL specified is null or is already in the list of
  132        * URLs, then invoking this method has no effect.
  133        */
  134       public void addURL(URL url) {
  135           synchronized (urls) {
  136               if (url == null || path.contains(url))
  137                   return;
  138   
  139               urls.add(0, url);
  140               path.add(url);
  141           }
  142       }
  143   
  144       /**
  145        * Returns the original search path of URLs.
  146        */
  147       public URL[] getURLs() {
  148           synchronized (urls) {
  149               return path.toArray(new URL[path.size()]);
  150           }
  151       }
  152   
  153       /**
  154        * Finds the resource with the specified name on the URL search path
  155        * or null if not found or security check fails.
  156        *
  157        * @param name      the name of the resource
  158        * @param check     whether to perform a security check
  159        * @return a <code>URL</code> for the resource, or <code>null</code>
  160        * if the resource could not be found.
  161        */
  162       public URL findResource(String name, boolean check) {
  163           Loader loader;
  164           for (int i = 0; (loader = getLoader(i)) != null; i++) {
  165               URL url = loader.findResource(name, check);
  166               if (url != null) {
  167                   return url;
  168               }
  169           }
  170           return null;
  171       }
  172   
  173       /**
  174        * Finds the first Resource on the URL search path which has the specified
  175        * name. Returns null if no Resource could be found.
  176        *
  177        * @param name the name of the Resource
  178        * @param check     whether to perform a security check
  179        * @return the Resource, or null if not found
  180        */
  181       public Resource getResource(String name, boolean check) {
  182           if (DEBUG) {
  183               System.err.println("URLClassPath.getResource(\"" + name + "\")");
  184           }
  185   
  186           Loader loader;
  187           for (int i = 0; (loader = getLoader(i)) != null; i++) {
  188               Resource res = loader.getResource(name, check);
  189               if (res != null) {
  190                   return res;
  191               }
  192           }
  193           return null;
  194       }
  195   
  196       /**
  197        * Finds all resources on the URL search path with the given name.
  198        * Returns an enumeration of the URL objects.
  199        *
  200        * @param name the resource name
  201        * @return an Enumeration of all the urls having the specified name
  202        */
  203       public Enumeration<URL> findResources(final String name,
  204                                        final boolean check) {
  205           return new Enumeration<URL>() {
  206               private int index = 0;
  207               private URL url = null;
  208   
  209               private boolean next() {
  210                   if (url != null) {
  211                       return true;
  212                   } else {
  213                       Loader loader;
  214                       while ((loader = getLoader(index++)) != null) {
  215                           url = loader.findResource(name, check);
  216                           if (url != null) {
  217                               return true;
  218                           }
  219                       }
  220                       return false;
  221                   }
  222               }
  223   
  224               public boolean hasMoreElements() {
  225                   return next();
  226               }
  227   
  228               public URL nextElement() {
  229                   if (!next()) {
  230                       throw new NoSuchElementException();
  231                   }
  232                   URL u = url;
  233                   url = null;
  234                   return u;
  235               }
  236           };
  237       }
  238   
  239       public Resource getResource(String name) {
  240           return getResource(name, true);
  241       }
  242   
  243       /**
  244        * Finds all resources on the URL search path with the given name.
  245        * Returns an enumeration of the Resource objects.
  246        *
  247        * @param name the resource name
  248        * @return an Enumeration of all the resources having the specified name
  249        */
  250       public Enumeration<Resource> getResources(final String name,
  251                                       final boolean check) {
  252           return new Enumeration<Resource>() {
  253               private int index = 0;
  254               private Resource res = null;
  255   
  256               private boolean next() {
  257                   if (res != null) {
  258                       return true;
  259                   } else {
  260                       Loader loader;
  261                       while ((loader = getLoader(index++)) != null) {
  262                           res = loader.getResource(name, check);
  263                           if (res != null) {
  264                               return true;
  265                           }
  266                       }
  267                       return false;
  268                   }
  269               }
  270   
  271               public boolean hasMoreElements() {
  272                   return next();
  273               }
  274   
  275               public Resource nextElement() {
  276                   if (!next()) {
  277                       throw new NoSuchElementException();
  278                   }
  279                   Resource r = res;
  280                   res = null;
  281                   return r;
  282               }
  283           };
  284       }
  285   
  286       public Enumeration<Resource> getResources(final String name) {
  287           return getResources(name, true);
  288       }
  289   
  290       /*
  291        * Returns the Loader at the specified position in the URL search
  292        * path. The URLs are opened and expanded as needed. Returns null
  293        * if the specified index is out of range.
  294        */
  295        private synchronized Loader getLoader(int index) {
  296            // Expand URL search path until the request can be satisfied
  297            // or the URL stack is empty.
  298           while (loaders.size() < index + 1) {
  299               // Pop the next URL from the URL stack
  300               URL url;
  301               synchronized (urls) {
  302                   if (urls.empty()) {
  303                       return null;
  304                   } else {
  305                       url = urls.pop();
  306                   }
  307               }
  308               // Skip this URL if it already has a Loader. (Loader
  309               // may be null in the case where URL has not been opened
  310               // but is referenced by a JAR index.)
  311               if (lmap.containsKey(url)) {
  312                   continue;
  313               }
  314               // Otherwise, create a new Loader for the URL.
  315               Loader loader;
  316               try {
  317                   loader = getLoader(url);
  318                   // If the loader defines a local class path then add the
  319                   // URLs to the list of URLs to be opened.
  320                   URL[] urls = loader.getClassPath();
  321                   if (urls != null) {
  322                       push(urls);
  323                   }
  324               } catch (IOException e) {
  325                   // Silently ignore for now...
  326                   continue;
  327               }
  328               // Finally, add the Loader to the search path.
  329               loaders.add(loader);
  330               lmap.put(url, loader);
  331           }
  332           return loaders.get(index);
  333       }
  334   
  335       /*
  336        * Returns the Loader for the specified base URL.
  337        */
  338       private Loader getLoader(final URL url) throws IOException {
  339           try {
  340               return java.security.AccessController.doPrivileged(
  341                   new java.security.PrivilegedExceptionAction<Loader>() {
  342                   public Loader run() throws IOException {
  343                       String file = url.getFile();
  344                       if (file != null && file.endsWith("/")) {
  345                           if ("file".equals(url.getProtocol())) {
  346                               return new FileLoader(url);
  347                           } else {
  348                               return new Loader(url);
  349                           }
  350                       } else {
  351                           return new JarLoader(url, jarHandler, lmap);
  352                       }
  353                   }
  354               });
  355           } catch (java.security.PrivilegedActionException pae) {
  356               throw (IOException)pae.getException();
  357           }
  358       }
  359   
  360       /*
  361        * Pushes the specified URLs onto the list of unopened URLs.
  362        */
  363       private void push(URL[] us) {
  364           synchronized (urls) {
  365               for (int i = us.length - 1; i >= 0; --i) {
  366                   urls.push(us[i]);
  367               }
  368           }
  369       }
  370   
  371       /**
  372        * Convert class path specification into an array of file URLs.
  373        *
  374        * The path of the file is encoded before conversion into URL
  375        * form so that reserved characters can safely appear in the path.
  376        */
  377       public static URL[] pathToURLs(String path) {
  378           StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
  379           URL[] urls = new URL[st.countTokens()];
  380           int count = 0;
  381           while (st.hasMoreTokens()) {
  382               File f = new File(st.nextToken());
  383               try {
  384                   f = new File(f.getCanonicalPath());
  385               } catch (IOException x) {
  386                   // use the non-canonicalized filename
  387               }
  388               try {
  389                   urls[count++] = ParseUtil.fileToEncodedURL(f);
  390               } catch (IOException x) { }
  391           }
  392   
  393           if (urls.length != count) {
  394               URL[] tmp = new URL[count];
  395               System.arraycopy(urls, 0, tmp, 0, count);
  396               urls = tmp;
  397           }
  398           return urls;
  399       }
  400   
  401       /*
  402        * Check whether the resource URL should be returned.
  403        * Return null on security check failure.
  404        * Called by java.net.URLClassLoader.
  405        */
  406       public URL checkURL(URL url) {
  407           try {
  408               check(url);
  409           } catch (Exception e) {
  410               return null;
  411           }
  412   
  413           return url;
  414       }
  415   
  416       /*
  417        * Check whether the resource URL should be returned.
  418        * Throw exception on failure.
  419        * Called internally within this file.
  420        */
  421       static void check(URL url) throws IOException {
  422           SecurityManager security = System.getSecurityManager();
  423           if (security != null) {
  424               URLConnection urlConnection = url.openConnection();
  425               Permission perm = urlConnection.getPermission();
  426               if (perm != null) {
  427                   try {
  428                       security.checkPermission(perm);
  429                   } catch (SecurityException se) {
  430                       // fallback to checkRead/checkConnect for pre 1.2
  431                       // security managers
  432                       if ((perm instanceof java.io.FilePermission) &&
  433                           perm.getActions().indexOf("read") != -1) {
  434                           security.checkRead(perm.getName());
  435                       } else if ((perm instanceof
  436                           java.net.SocketPermission) &&
  437                           perm.getActions().indexOf("connect") != -1) {
  438                           URL locUrl = url;
  439                           if (urlConnection instanceof JarURLConnection) {
  440                               locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
  441                           }
  442                           security.checkConnect(locUrl.getHost(),
  443                                                 locUrl.getPort());
  444                       } else {
  445                           throw se;
  446                       }
  447                   }
  448               }
  449           }
  450       }
  451   
  452       /**
  453        * Inner class used to represent a loader of resources and classes
  454        * from a base URL.
  455        */
  456       private static class Loader {
  457           private final URL base;
  458   
  459           /*
  460            * Creates a new Loader for the specified URL.
  461            */
  462           Loader(URL url) {
  463               base = url;
  464           }
  465   
  466           /*
  467            * Returns the base URL for this Loader.
  468            */
  469           URL getBaseURL() {
  470               return base;
  471           }
  472   
  473           URL findResource(final String name, boolean check) {
  474               URL url;
  475               try {
  476                   url = new URL(base, ParseUtil.encodePath(name, false));
  477               } catch (MalformedURLException e) {
  478                   throw new IllegalArgumentException("name");
  479               }
  480   
  481               try {
  482                   if (check) {
  483                       URLClassPath.check(url);
  484                   }
  485   
  486                   /*
  487                    * For a HTTP connection we use the HEAD method to
  488                    * check if the resource exists.
  489                    */
  490                   URLConnection uc = url.openConnection();
  491                   if (uc instanceof HttpURLConnection) {
  492                       HttpURLConnection hconn = (HttpURLConnection)uc;
  493                       hconn.setRequestMethod("HEAD");
  494                       if (hconn.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
  495                           return null;
  496                       }
  497                   } else {
  498                       // our best guess for the other cases
  499                       InputStream is = url.openStream();
  500                       is.close();
  501                   }
  502                   return url;
  503               } catch (Exception e) {
  504                   return null;
  505               }
  506           }
  507   
  508           Resource getResource(final String name, boolean check) {
  509               final URL url;
  510               try {
  511                   url = new URL(base, ParseUtil.encodePath(name, false));
  512               } catch (MalformedURLException e) {
  513                   throw new IllegalArgumentException("name");
  514               }
  515               final URLConnection uc;
  516               try {
  517                   if (check) {
  518                       URLClassPath.check(url);
  519                   }
  520                   uc = url.openConnection();
  521                   InputStream in = uc.getInputStream();
  522               } catch (Exception e) {
  523                   return null;
  524               }
  525               return new Resource() {
  526                   public String getName() { return name; }
  527                   public URL getURL() { return url; }
  528                   public URL getCodeSourceURL() { return base; }
  529                   public InputStream getInputStream() throws IOException {
  530                       return uc.getInputStream();
  531                   }
  532                   public int getContentLength() throws IOException {
  533                       return uc.getContentLength();
  534                   }
  535               };
  536           }
  537   
  538           /*
  539            * Returns the Resource for the specified name, or null if not
  540            * found or the caller does not have the permission to get the
  541            * resource.
  542            */
  543           Resource getResource(final String name) {
  544               return getResource(name, true);
  545           }
  546   
  547           /*
  548            * Returns the local class path for this loader, or null if none.
  549            */
  550           URL[] getClassPath() throws IOException {
  551               return null;
  552           }
  553       }
  554   
  555       /*
  556        * Inner class used to represent a Loader of resources from a JAR URL.
  557        */
  558       static class JarLoader extends Loader {
  559           private JarFile jar;
  560           private URL csu;
  561           private JarIndex index;
  562           private MetaIndex metaIndex;
  563           private URLStreamHandler handler;
  564           private HashMap<URL, Loader> lmap;
  565   
  566           /*
  567            * Creates a new JarLoader for the specified URL referring to
  568            * a JAR file.
  569            */
  570           JarLoader(URL url, URLStreamHandler jarHandler,
  571                     HashMap<URL, Loader> loaderMap)
  572               throws IOException
  573           {
  574               super(new URL("jar", "", -1, url + "!/", jarHandler));
  575               csu = url;
  576               handler = jarHandler;
  577               lmap = loaderMap;
  578   
  579               if (!isOptimizable(url)) {
  580                   ensureOpen();
  581               } else {
  582                    String fileName = url.getFile();
  583                   if (fileName != null) {
  584                       fileName = ParseUtil.decode(fileName);
  585                       File f = new File(fileName);
  586                       metaIndex = MetaIndex.forJar(f);
  587                       // If the meta index is found but the file is not
  588                       // installed, set metaIndex to null. A typical
  589                       // senario is charsets.jar which won't be installed
  590                       // when the user is running in certain locale environment.
  591                       // The side effect of null metaIndex will cause
  592                       // ensureOpen get called so that IOException is thrown.
  593                       if (metaIndex != null && !f.exists()) {
  594                           metaIndex = null;
  595                       }
  596                   }
  597   
  598                   // metaIndex is null when either there is no such jar file
  599                   // entry recorded in meta-index file or such jar file is
  600                   // missing in JRE. See bug 6340399.
  601                   if (metaIndex == null) {
  602                       ensureOpen();
  603                   }
  604               }
  605           }
  606   
  607           JarFile getJarFile () {
  608               return jar;
  609           }
  610   
  611           private boolean isOptimizable(URL url) {
  612               return "file".equals(url.getProtocol());
  613           }
  614   
  615           private void ensureOpen() throws IOException {
  616               if (jar == null) {
  617                   try {
  618                       java.security.AccessController.doPrivileged(
  619                           new java.security.PrivilegedExceptionAction<Void>() {
  620                               public Void run() throws IOException {
  621                                   if (DEBUG) {
  622                                       System.err.println("Opening " + csu);
  623                                       Thread.dumpStack();
  624                                   }
  625   
  626                                   jar = getJarFile(csu);
  627                                   index = JarIndex.getJarIndex(jar, metaIndex);
  628                                   if (index != null) {
  629                                       String[] jarfiles = index.getJarFiles();
  630                                   // Add all the dependent URLs to the lmap so that loaders
  631                                   // will not be created for them by URLClassPath.getLoader(int)
  632                                   // if the same URL occurs later on the main class path.  We set
  633                                   // Loader to null here to avoid creating a Loader for each
  634                                   // URL until we actually need to try to load something from them.
  635                                       for(int i = 0; i < jarfiles.length; i++) {
  636                                           try {
  637                                               URL jarURL = new URL(csu, jarfiles[i]);
  638                                               // If a non-null loader already exists, leave it alone.
  639                                               if (!lmap.containsKey(jarURL)) {
  640                                                   lmap.put(jarURL, null);
  641                                               }
  642                                           } catch (MalformedURLException e) {
  643                                               continue;
  644                                           }
  645                                       }
  646                                   }
  647                                   return null;
  648                               }
  649                           }
  650                       );
  651                   } catch (java.security.PrivilegedActionException pae) {
  652                       throw (IOException)pae.getException();
  653                   }
  654               }
  655           }
  656   
  657           private JarFile getJarFile(URL url) throws IOException {
  658               // Optimize case where url refers to a local jar file
  659               if (isOptimizable(url)) {
  660                   FileURLMapper p = new FileURLMapper (url);
  661                   if (!p.exists()) {
  662                       throw new FileNotFoundException(p.getPath());
  663                   }
  664                   return new JarFile (p.getPath());
  665               }
  666               URLConnection uc = getBaseURL().openConnection();
  667               uc.setRequestProperty(USER_AGENT_JAVA_VERSION, JAVA_VERSION);
  668               return ((JarURLConnection)uc).getJarFile();
  669           }
  670   
  671           /*
  672            * Returns the index of this JarLoader if it exists.
  673            */
  674           JarIndex getIndex() {
  675               try {
  676                   ensureOpen();
  677               } catch (IOException e) {
  678                   throw (InternalError) new InternalError().initCause(e);
  679               }
  680               return index;
  681           }
  682   
  683           /*
  684            * Creates the resource and if the check flag is set to true, checks if
  685            * is its okay to return the resource.
  686            */
  687           Resource checkResource(final String name, boolean check,
  688               final JarEntry entry) {
  689   
  690               final URL url;
  691               try {
  692                   url = new URL(getBaseURL(), ParseUtil.encodePath(name, false));
  693                   if (check) {
  694                       URLClassPath.check(url);
  695                   }
  696               } catch (MalformedURLException e) {
  697                   return null;
  698                   // throw new IllegalArgumentException("name");
  699               } catch (IOException e) {
  700                   return null;
  701               } catch (AccessControlException e) {
  702                   return null;
  703               }
  704   
  705               return new Resource() {
  706                   public String getName() { return name; }
  707                   public URL getURL() { return url; }
  708                   public URL getCodeSourceURL() { return csu; }
  709                   public InputStream getInputStream() throws IOException
  710                       { return jar.getInputStream(entry); }
  711                   public int getContentLength()
  712                       { return (int)entry.getSize(); }
  713                   public Manifest getManifest() throws IOException
  714                       { return jar.getManifest(); };
  715                   public Certificate[] getCertificates()
  716                       { return entry.getCertificates(); };
  717                   public CodeSigner[] getCodeSigners()
  718                       { return entry.getCodeSigners(); };
  719               };
  720           }
  721   
  722   
  723           /*
  724            * Returns true iff atleast one resource in the jar file has the same
  725            * package name as that of the specified resource name.
  726            */
  727           boolean validIndex(final String name) {
  728               String packageName = name;
  729               int pos;
  730               if((pos = name.lastIndexOf("/")) != -1) {
  731                   packageName = name.substring(0, pos);
  732               }
  733   
  734               String entryName;
  735               ZipEntry entry;
  736               Enumeration<JarEntry> enum_ = jar.entries();
  737               while (enum_.hasMoreElements()) {
  738                   entry = enum_.nextElement();
  739                   entryName = entry.getName();
  740                   if((pos = entryName.lastIndexOf("/")) != -1)
  741                       entryName = entryName.substring(0, pos);
  742                   if (entryName.equals(packageName)) {
  743                       return true;
  744                   }
  745               }
  746               return false;
  747           }
  748   
  749           /*
  750            * Returns the URL for a resource with the specified name
  751            */
  752           URL findResource(final String name, boolean check) {
  753               Resource rsc = getResource(name, check);
  754               if (rsc != null) {
  755                   return rsc.getURL();
  756               }
  757               return null;
  758           }
  759   
  760           /*
  761            * Returns the JAR Resource for the specified name.
  762            */
  763           Resource getResource(final String name, boolean check) {
  764               if (metaIndex != null) {
  765                   if (!metaIndex.mayContain(name)) {
  766                       return null;
  767                   }
  768               }
  769   
  770               try {
  771                   ensureOpen();
  772               } catch (IOException e) {
  773                   throw (InternalError) new InternalError().initCause(e);
  774               }
  775               final JarEntry entry = jar.getJarEntry(name);
  776               if (entry != null)
  777                   return checkResource(name, check, entry);
  778   
  779               if (index == null)
  780                   return null;
  781   
  782               HashSet<URL> visited = new HashSet<URL>();
  783               return getResource(name, check, visited);
  784           }
  785   
  786           /*
  787            * Version of getResource() that tracks the jar files that have been
  788            * visited by linking through the index files. This helper method uses
  789            * a HashSet to store the URLs of jar files that have been searched and
  790            * uses it to avoid going into an infinite loop, looking for a
  791            * non-existent resource
  792            */
  793           Resource getResource(final String name, boolean check,
  794                                Set<URL> visited) {
  795   
  796               Resource res;
  797               Object[] jarFiles;
  798               boolean done = false;
  799               int count = 0;
  800               LinkedList jarFilesList = null;
  801   
  802               /* If there no jar files in the index that can potential contain
  803                * this resource then return immediately.
  804                */
  805               if((jarFilesList = index.get(name)) == null)
  806                   return null;
  807   
  808               do {
  809                   jarFiles = jarFilesList.toArray();
  810                   int size = jarFilesList.size();
  811                   /* loop through the mapped jar file list */
  812                   while(count < size) {
  813                       String jarName = (String)jarFiles[count++];
  814                       JarLoader newLoader;
  815                       final URL url;
  816   
  817                       try{
  818                           url = new URL(csu, jarName);
  819                           if ((newLoader = (JarLoader)lmap.get(url)) == null) {
  820                               /* no loader has been set up for this jar file
  821                                * before
  822                                */
  823                               newLoader = AccessController.doPrivileged(
  824                                   new PrivilegedExceptionAction<JarLoader>() {
  825                                       public JarLoader run() throws IOException {
  826                                           return new JarLoader(url, handler,
  827                                               lmap);
  828                                       }
  829                                   });
  830   
  831                               /* this newly opened jar file has its own index,
  832                                * merge it into the parent's index, taking into
  833                                * account the relative path.
  834                                */
  835                               JarIndex newIndex = newLoader.getIndex();
  836                               if(newIndex != null) {
  837                                   int pos = jarName.lastIndexOf("/");
  838                                   newIndex.merge(this.index, (pos == -1 ?
  839                                       null : jarName.substring(0, pos + 1)));
  840                               }
  841   
  842                               /* put it in the global hashtable */
  843                               lmap.put(url, newLoader);
  844                           }
  845                       } catch (java.security.PrivilegedActionException pae) {
  846                           continue;
  847                       } catch (MalformedURLException e) {
  848                           continue;
  849                       }
  850   
  851   
  852                       /* Note that the addition of the url to the list of visited
  853                        * jars incorporates a check for presence in the hashmap
  854                        */
  855                       boolean visitedURL = !visited.add(url);
  856                       if (!visitedURL) {
  857                           try {
  858                               newLoader.ensureOpen();
  859                           } catch (IOException e) {
  860                               throw (InternalError) new InternalError().initCause(e);
  861                           }
  862                           final JarEntry entry = newLoader.jar.getJarEntry(name);
  863                           if (entry != null) {
  864                               return newLoader.checkResource(name, check, entry);
  865                           }
  866   
  867                           /* Verify that at least one other resource with the
  868                            * same package name as the lookedup resource is
  869                            * present in the new jar
  870                            */
  871                           if (!newLoader.validIndex(name)) {
  872                               /* the mapping is wrong */
  873                               throw new InvalidJarIndexException("Invalid index");
  874                           }
  875                       }
  876   
  877                       /* If newLoader is the current loader or if it is a
  878                        * loader that has already been searched or if the new
  879                        * loader does not have an index then skip it
  880                        * and move on to the next loader.
  881                        */
  882                       if (visitedURL || newLoader == this ||
  883                               newLoader.getIndex() == null) {
  884                           continue;
  885                       }
  886   
  887                       /* Process the index of the new loader
  888                        */
  889                       if((res = newLoader.getResource(name, check, visited))
  890                               != null) {
  891                           return res;
  892                       }
  893                   }
  894                   // Get the list of jar files again as the list could have grown
  895                   // due to merging of index files.
  896                   jarFilesList = index.get(name);
  897   
  898               // If the count is unchanged, we are done.
  899               } while(count < jarFilesList.size());
  900               return null;
  901           }
  902   
  903   
  904           /*
  905            * Returns the JAR file local class path, or null if none.
  906            */
  907           URL[] getClassPath() throws IOException {
  908               if (index != null) {
  909                   return null;
  910               }
  911   
  912               if (metaIndex != null) {
  913                   return null;
  914               }
  915   
  916               ensureOpen();
  917               parseExtensionsDependencies();
  918               if (SharedSecrets.javaUtilJarAccess().jarFileHasClassPathAttribute(jar)) { // Only get manifest when necessary
  919                   Manifest man = jar.getManifest();
  920                   if (man != null) {
  921                       Attributes attr = man.getMainAttributes();
  922                       if (attr != null) {
  923                           String value = attr.getValue(Name.CLASS_PATH);
  924                           if (value != null) {
  925                               return parseClassPath(csu, value);
  926                           }
  927                       }
  928                   }
  929               }
  930               return null;
  931           }
  932   
  933           /*
  934            * parse the standard extension dependencies
  935            */
  936           private void  parseExtensionsDependencies() throws IOException {
  937               ExtensionDependency.checkExtensionsDependencies(jar);
  938           }
  939   
  940           /*
  941            * Parses value of the Class-Path manifest attribute and returns
  942            * an array of URLs relative to the specified base URL.
  943            */
  944           private URL[] parseClassPath(URL base, String value)
  945               throws MalformedURLException
  946           {
  947               StringTokenizer st = new StringTokenizer(value);
  948               URL[] urls = new URL[st.countTokens()];
  949               int i = 0;
  950               while (st.hasMoreTokens()) {
  951                   String path = st.nextToken();
  952                   urls[i] = new URL(base, path);
  953                   i++;
  954               }
  955               return urls;
  956           }
  957       }
  958   
  959       /*
  960        * Inner class used to represent a loader of classes and resources
  961        * from a file URL that refers to a directory.
  962        */
  963       private static class FileLoader extends Loader {
  964           /* Canonicalized File */
  965           private File dir;
  966   
  967           FileLoader(URL url) throws IOException {
  968               super(url);
  969               if (!"file".equals(url.getProtocol())) {
  970                   throw new IllegalArgumentException("url");
  971               }
  972               String path = url.getFile().replace('/', File.separatorChar);
  973               path = ParseUtil.decode(path);
  974               dir = (new File(path)).getCanonicalFile();
  975           }
  976   
  977           /*
  978            * Returns the URL for a resource with the specified name
  979            */
  980           URL findResource(final String name, boolean check) {
  981               Resource rsc = getResource(name, check);
  982               if (rsc != null) {
  983                   return rsc.getURL();
  984               }
  985               return null;
  986           }
  987   
  988           Resource getResource(final String name, boolean check) {
  989               final URL url;
  990               try {
  991                   URL normalizedBase = new URL(getBaseURL(), ".");
  992                   url = new URL(getBaseURL(), ParseUtil.encodePath(name, false));
  993   
  994                   if (url.getFile().startsWith(normalizedBase.getFile()) == false) {
  995                       // requested resource had ../..'s in path
  996                       return null;
  997                   }
  998   
  999                   if (check)
 1000                       URLClassPath.check(url);
 1001   
 1002                   final File file;
 1003                   if (name.indexOf("..") != -1) {
 1004                       file = (new File(dir, name.replace('/', File.separatorChar)))
 1005                             .getCanonicalFile();
 1006                       if ( !((file.getPath()).startsWith(dir.getPath())) ) {
 1007                           /* outside of base dir */
 1008                           return null;
 1009                       }
 1010                   } else {
 1011                       file = new File(dir, name.replace('/', File.separatorChar));
 1012                   }
 1013   
 1014                   if (file.exists()) {
 1015                       return new Resource() {
 1016                           public String getName() { return name; };
 1017                           public URL getURL() { return url; };
 1018                           public URL getCodeSourceURL() { return getBaseURL(); };
 1019                           public InputStream getInputStream() throws IOException
 1020                               { return new FileInputStream(file); };
 1021                           public int getContentLength() throws IOException
 1022                               { return (int)file.length(); };
 1023                       };
 1024                   }
 1025               } catch (Exception e) {
 1026                   return null;
 1027               }
 1028               return null;
 1029           }
 1030       }
 1031   }

Save This Page
Home » openjdk-7 » sun » misc » [javadoc | source]