Save This Page
Home » openjdk-7 » java » util » jar » [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 java.util.jar;
   27   
   28   import java.io;
   29   import java.lang.ref.SoftReference;
   30   import java.util;
   31   import java.util.zip;
   32   import java.security.CodeSigner;
   33   import java.security.cert.Certificate;
   34   import java.security.AccessController;
   35   import sun.security.action.GetPropertyAction;
   36   import sun.security.util.ManifestEntryVerifier;
   37   import sun.misc.SharedSecrets;
   38   
   39   /**
   40    * The <code>JarFile</code> class is used to read the contents of a jar file
   41    * from any file that can be opened with <code>java.io.RandomAccessFile</code>.
   42    * It extends the class <code>java.util.zip.ZipFile</code> with support
   43    * for reading an optional <code>Manifest</code> entry. The
   44    * <code>Manifest</code> can be used to specify meta-information about the
   45    * jar file and its entries.
   46    *
   47    * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
   48    * or method in this class will cause a {@link NullPointerException} to be
   49    * thrown.
   50    *
   51    * @author  David Connelly
   52    * @see     Manifest
   53    * @see     java.util.zip.ZipFile
   54    * @see     java.util.jar.JarEntry
   55    * @since   1.2
   56    */
   57   public
   58   class JarFile extends ZipFile {
   59       private SoftReference<Manifest> manRef;
   60       private JarEntry manEntry;
   61       private JarVerifier jv;
   62       private boolean jvInitialized;
   63       private boolean verify;
   64       private boolean computedHasClassPathAttribute;
   65       private boolean hasClassPathAttribute;
   66   
   67       // Set up JavaUtilJarAccess in SharedSecrets
   68       static {
   69           SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
   70       }
   71   
   72       /**
   73        * The JAR manifest file name.
   74        */
   75       public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
   76   
   77       /**
   78        * Creates a new <code>JarFile</code> to read from the specified
   79        * file <code>name</code>. The <code>JarFile</code> will be verified if
   80        * it is signed.
   81        * @param name the name of the jar file to be opened for reading
   82        * @throws IOException if an I/O error has occurred
   83        * @throws SecurityException if access to the file is denied
   84        *         by the SecurityManager
   85        */
   86       public JarFile(String name) throws IOException {
   87           this(new File(name), true, ZipFile.OPEN_READ);
   88       }
   89   
   90       /**
   91        * Creates a new <code>JarFile</code> to read from the specified
   92        * file <code>name</code>.
   93        * @param name the name of the jar file to be opened for reading
   94        * @param verify whether or not to verify the jar file if
   95        * it is signed.
   96        * @throws IOException if an I/O error has occurred
   97        * @throws SecurityException if access to the file is denied
   98        *         by the SecurityManager
   99        */
  100       public JarFile(String name, boolean verify) throws IOException {
  101           this(new File(name), verify, ZipFile.OPEN_READ);
  102       }
  103   
  104       /**
  105        * Creates a new <code>JarFile</code> to read from the specified
  106        * <code>File</code> object. The <code>JarFile</code> will be verified if
  107        * it is signed.
  108        * @param file the jar file to be opened for reading
  109        * @throws IOException if an I/O error has occurred
  110        * @throws SecurityException if access to the file is denied
  111        *         by the SecurityManager
  112        */
  113       public JarFile(File file) throws IOException {
  114           this(file, true, ZipFile.OPEN_READ);
  115       }
  116   
  117   
  118       /**
  119        * Creates a new <code>JarFile</code> to read from the specified
  120        * <code>File</code> object.
  121        * @param file the jar file to be opened for reading
  122        * @param verify whether or not to verify the jar file if
  123        * it is signed.
  124        * @throws IOException if an I/O error has occurred
  125        * @throws SecurityException if access to the file is denied
  126        *         by the SecurityManager.
  127        */
  128       public JarFile(File file, boolean verify) throws IOException {
  129           this(file, verify, ZipFile.OPEN_READ);
  130       }
  131   
  132   
  133       /**
  134        * Creates a new <code>JarFile</code> to read from the specified
  135        * <code>File</code> object in the specified mode.  The mode argument
  136        * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
  137        *
  138        * @param file the jar file to be opened for reading
  139        * @param verify whether or not to verify the jar file if
  140        * it is signed.
  141        * @param mode the mode in which the file is to be opened
  142        * @throws IOException if an I/O error has occurred
  143        * @throws IllegalArgumentException
  144        *         if the <tt>mode</tt> argument is invalid
  145        * @throws SecurityException if access to the file is denied
  146        *         by the SecurityManager
  147        * @since 1.3
  148        */
  149       public JarFile(File file, boolean verify, int mode) throws IOException {
  150           super(file, mode);
  151           this.verify = verify;
  152       }
  153   
  154       /**
  155        * Returns the jar file manifest, or <code>null</code> if none.
  156        *
  157        * @return the jar file manifest, or <code>null</code> if none
  158        *
  159        * @throws IllegalStateException
  160        *         may be thrown if the jar file has been closed
  161        */
  162       public Manifest getManifest() throws IOException {
  163           return getManifestFromReference();
  164       }
  165   
  166       private Manifest getManifestFromReference() throws IOException {
  167           Manifest man = manRef != null ? manRef.get() : null;
  168   
  169           if (man == null) {
  170   
  171               JarEntry manEntry = getManEntry();
  172   
  173               // If found then load the manifest
  174               if (manEntry != null) {
  175                   if (verify) {
  176                       byte[] b = getBytes(manEntry);
  177                       man = new Manifest(new ByteArrayInputStream(b));
  178                       if (!jvInitialized) {
  179                           jv = new JarVerifier(b);
  180                       }
  181                   } else {
  182                       man = new Manifest(super.getInputStream(manEntry));
  183                   }
  184                   manRef = new SoftReference(man);
  185               }
  186           }
  187           return man;
  188       }
  189   
  190       private native String[] getMetaInfEntryNames();
  191   
  192       /**
  193        * Returns the <code>JarEntry</code> for the given entry name or
  194        * <code>null</code> if not found.
  195        *
  196        * @param name the jar file entry name
  197        * @return the <code>JarEntry</code> for the given entry name or
  198        *         <code>null</code> if not found.
  199        *
  200        * @throws IllegalStateException
  201        *         may be thrown if the jar file has been closed
  202        *
  203        * @see java.util.jar.JarEntry
  204        */
  205       public JarEntry getJarEntry(String name) {
  206           return (JarEntry)getEntry(name);
  207       }
  208   
  209       /**
  210        * Returns the <code>ZipEntry</code> for the given entry name or
  211        * <code>null</code> if not found.
  212        *
  213        * @param name the jar file entry name
  214        * @return the <code>ZipEntry</code> for the given entry name or
  215        *         <code>null</code> if not found
  216        *
  217        * @throws IllegalStateException
  218        *         may be thrown if the jar file has been closed
  219        *
  220        * @see java.util.zip.ZipEntry
  221        */
  222       public ZipEntry getEntry(String name) {
  223           ZipEntry ze = super.getEntry(name);
  224           if (ze != null) {
  225               return new JarFileEntry(ze);
  226           }
  227           return null;
  228       }
  229   
  230       /**
  231        * Returns an enumeration of the zip file entries.
  232        */
  233       public Enumeration<JarEntry> entries() {
  234           final Enumeration enum_ = super.entries();
  235           return new Enumeration<JarEntry>() {
  236               public boolean hasMoreElements() {
  237                   return enum_.hasMoreElements();
  238               }
  239               public JarFileEntry nextElement() {
  240                   ZipEntry ze = (ZipEntry)enum_.nextElement();
  241                   return new JarFileEntry(ze);
  242               }
  243           };
  244       }
  245   
  246       private class JarFileEntry extends JarEntry {
  247           JarFileEntry(ZipEntry ze) {
  248               super(ze);
  249           }
  250           public Attributes getAttributes() throws IOException {
  251               Manifest man = JarFile.this.getManifest();
  252               if (man != null) {
  253                   return man.getAttributes(getName());
  254               } else {
  255                   return null;
  256               }
  257           }
  258           public Certificate[] getCertificates() {
  259               try {
  260                   maybeInstantiateVerifier();
  261               } catch (IOException e) {
  262                   throw new RuntimeException(e);
  263               }
  264               if (certs == null && jv != null) {
  265                   certs = jv.getCerts(getName());
  266               }
  267               return certs == null ? null : certs.clone();
  268           }
  269           public CodeSigner[] getCodeSigners() {
  270               try {
  271                   maybeInstantiateVerifier();
  272               } catch (IOException e) {
  273                   throw new RuntimeException(e);
  274               }
  275               if (signers == null && jv != null) {
  276                   signers = jv.getCodeSigners(getName());
  277               }
  278               return signers == null ? null : signers.clone();
  279           }
  280       }
  281   
  282       /*
  283        * Ensures that the JarVerifier has been created if one is
  284        * necessary (i.e., the jar appears to be signed.) This is done as
  285        * a quick check to avoid processing of the manifest for unsigned
  286        * jars.
  287        */
  288       private void maybeInstantiateVerifier() throws IOException {
  289           if (jv != null) {
  290               return;
  291           }
  292   
  293           if (verify) {
  294               String[] names = getMetaInfEntryNames();
  295               if (names != null) {
  296                   for (int i = 0; i < names.length; i++) {
  297                       String name = names[i].toUpperCase(Locale.ENGLISH);
  298                       if (name.endsWith(".DSA") ||
  299                           name.endsWith(".RSA") ||
  300                           name.endsWith(".SF")) {
  301                           // Assume since we found a signature-related file
  302                           // that the jar is signed and that we therefore
  303                           // need a JarVerifier and Manifest
  304                           getManifest();
  305                           return;
  306                       }
  307                   }
  308               }
  309               // No signature-related files; don't instantiate a
  310               // verifier
  311               verify = false;
  312           }
  313       }
  314   
  315   
  316       /*
  317        * Initializes the verifier object by reading all the manifest
  318        * entries and passing them to the verifier.
  319        */
  320       private void initializeVerifier() {
  321           ManifestEntryVerifier mev = null;
  322   
  323           // Verify "META-INF/" entries...
  324           try {
  325               String[] names = getMetaInfEntryNames();
  326               if (names != null) {
  327                   for (int i = 0; i < names.length; i++) {
  328                       JarEntry e = getJarEntry(names[i]);
  329                       if (!e.isDirectory()) {
  330                           if (mev == null) {
  331                               mev = new ManifestEntryVerifier
  332                                   (getManifestFromReference());
  333                           }
  334                           byte[] b = getBytes(e);
  335                           if (b != null && b.length > 0) {
  336                               jv.beginEntry(e, mev);
  337                               jv.update(b.length, b, 0, b.length, mev);
  338                               jv.update(-1, null, 0, 0, mev);
  339                           }
  340                       }
  341                   }
  342               }
  343           } catch (IOException ex) {
  344               // if we had an error parsing any blocks, just
  345               // treat the jar file as being unsigned
  346               jv = null;
  347               verify = false;
  348           }
  349   
  350           // if after initializing the verifier we have nothing
  351           // signed, we null it out.
  352   
  353           if (jv != null) {
  354   
  355               jv.doneWithMeta();
  356               if (JarVerifier.debug != null) {
  357                   JarVerifier.debug.println("done with meta!");
  358               }
  359   
  360               if (jv.nothingToVerify()) {
  361                   if (JarVerifier.debug != null) {
  362                       JarVerifier.debug.println("nothing to verify!");
  363                   }
  364                   jv = null;
  365                   verify = false;
  366               }
  367           }
  368       }
  369   
  370       /*
  371        * Reads all the bytes for a given entry. Used to process the
  372        * META-INF files.
  373        */
  374       private byte[] getBytes(ZipEntry ze) throws IOException {
  375           byte[] b = new byte[(int)ze.getSize()];
  376           DataInputStream is = new DataInputStream(super.getInputStream(ze));
  377           is.readFully(b, 0, b.length);
  378           is.close();
  379           return b;
  380       }
  381   
  382       /**
  383        * Returns an input stream for reading the contents of the specified
  384        * zip file entry.
  385        * @param ze the zip file entry
  386        * @return an input stream for reading the contents of the specified
  387        *         zip file entry
  388        * @throws ZipException if a zip file format error has occurred
  389        * @throws IOException if an I/O error has occurred
  390        * @throws SecurityException if any of the jar file entries
  391        *         are incorrectly signed.
  392        * @throws IllegalStateException
  393        *         may be thrown if the jar file has been closed
  394        */
  395       public synchronized InputStream getInputStream(ZipEntry ze)
  396           throws IOException
  397       {
  398           maybeInstantiateVerifier();
  399           if (jv == null) {
  400               return super.getInputStream(ze);
  401           }
  402           if (!jvInitialized) {
  403               initializeVerifier();
  404               jvInitialized = true;
  405               // could be set to null after a call to
  406               // initializeVerifier if we have nothing to
  407               // verify
  408               if (jv == null)
  409                   return super.getInputStream(ze);
  410           }
  411   
  412           // wrap a verifier stream around the real stream
  413           return new JarVerifier.VerifierStream(
  414               getManifestFromReference(),
  415               ze instanceof JarFileEntry ?
  416               (JarEntry) ze : getJarEntry(ze.getName()),
  417               super.getInputStream(ze),
  418               jv);
  419       }
  420   
  421       // Statics for hand-coded Boyer-Moore search in hasClassPathAttribute()
  422       // The bad character shift for "class-path"
  423       private static int[] lastOcc;
  424       // The good suffix shift for "class-path"
  425       private static int[] optoSft;
  426       // Initialize the shift arrays to search for "class-path"
  427       private static char[] src = {'c','l','a','s','s','-','p','a','t','h'};
  428       static {
  429           lastOcc = new int[128];
  430           optoSft = new int[10];
  431           lastOcc[(int)'c']=1;
  432           lastOcc[(int)'l']=2;
  433           lastOcc[(int)'s']=5;
  434           lastOcc[(int)'-']=6;
  435           lastOcc[(int)'p']=7;
  436           lastOcc[(int)'a']=8;
  437           lastOcc[(int)'t']=9;
  438           lastOcc[(int)'h']=10;
  439           for (int i=0; i<9; i++)
  440               optoSft[i]=10;
  441           optoSft[9]=1;
  442       }
  443   
  444       private JarEntry getManEntry() {
  445           if (manEntry == null) {
  446               // First look up manifest entry using standard name
  447               manEntry = getJarEntry(MANIFEST_NAME);
  448               if (manEntry == null) {
  449                   // If not found, then iterate through all the "META-INF/"
  450                   // entries to find a match.
  451                   String[] names = getMetaInfEntryNames();
  452                   if (names != null) {
  453                       for (int i = 0; i < names.length; i++) {
  454                           if (MANIFEST_NAME.equals(
  455                                                    names[i].toUpperCase(Locale.ENGLISH))) {
  456                               manEntry = getJarEntry(names[i]);
  457                               break;
  458                           }
  459                       }
  460                   }
  461               }
  462           }
  463           return manEntry;
  464       }
  465   
  466       // Returns true iff this jar file has a manifest with a class path
  467       // attribute. Returns false if there is no manifest or the manifest
  468       // does not contain a "Class-Path" attribute. Currently exported to
  469       // core libraries via sun.misc.SharedSecrets.
  470       boolean hasClassPathAttribute() throws IOException {
  471           if (computedHasClassPathAttribute) {
  472               return hasClassPathAttribute;
  473           }
  474   
  475           hasClassPathAttribute = false;
  476           if (!isKnownToNotHaveClassPathAttribute()) {
  477               JarEntry manEntry = getManEntry();
  478               if (manEntry != null) {
  479                   byte[] b = new byte[(int)manEntry.getSize()];
  480                   DataInputStream dis = new DataInputStream(
  481                                                             super.getInputStream(manEntry));
  482                   dis.readFully(b, 0, b.length);
  483                   dis.close();
  484   
  485                   int last = b.length - src.length;
  486                   int i = 0;
  487                   next:
  488                   while (i<=last) {
  489                       for (int j=9; j>=0; j--) {
  490                           char c = (char) b[i+j];
  491                           c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
  492                           if (c != src[j]) {
  493                               i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
  494                               continue next;
  495                           }
  496                       }
  497                       hasClassPathAttribute = true;
  498                       break;
  499                   }
  500               }
  501           }
  502           computedHasClassPathAttribute = true;
  503           return hasClassPathAttribute;
  504       }
  505   
  506       private static String javaHome;
  507       private static String[] jarNames;
  508       private boolean isKnownToNotHaveClassPathAttribute() {
  509           // Optimize away even scanning of manifest for jar files we
  510           // deliver which don't have a class-path attribute. If one of
  511           // these jars is changed to include such an attribute this code
  512           // must be changed.
  513           if (javaHome == null) {
  514               javaHome = AccessController.doPrivileged(
  515                   new GetPropertyAction("java.home"));
  516           }
  517           if (jarNames == null) {
  518               String[] names = new String[10];
  519               String fileSep = File.separator;
  520               int i = 0;
  521               names[i++] = fileSep + "rt.jar";
  522               names[i++] = fileSep + "sunrsasign.jar";
  523               names[i++] = fileSep + "jsse.jar";
  524               names[i++] = fileSep + "jce.jar";
  525               names[i++] = fileSep + "charsets.jar";
  526               names[i++] = fileSep + "dnsns.jar";
  527               names[i++] = fileSep + "ldapsec.jar";
  528               names[i++] = fileSep + "localedata.jar";
  529               names[i++] = fileSep + "sunjce_provider.jar";
  530               names[i++] = fileSep + "sunpkcs11.jar";
  531               jarNames = names;
  532           }
  533   
  534           String name = getName();
  535           String localJavaHome = javaHome;
  536           if (name.startsWith(localJavaHome)) {
  537               String[] names = jarNames;
  538               for (int i = 0; i < names.length; i++) {
  539                   if (name.endsWith(names[i])) {
  540                       return true;
  541                   }
  542               }
  543           }
  544           return false;
  545       }
  546   }

Save This Page
Home » openjdk-7 » java » util » jar » [javadoc | source]