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

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