jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java
changeset 40251 481b890e50a3
parent 39313 0970a9a1de13
child 41221 eb0dc5ea2302
equal deleted inserted replaced
40250:e645cab45a32 40251:481b890e50a3
    40 import java.net.URI;
    40 import java.net.URI;
    41 import java.nio.ByteBuffer;
    41 import java.nio.ByteBuffer;
    42 import java.nio.file.Path;
    42 import java.nio.file.Path;
    43 import java.nio.file.Files;
    43 import java.nio.file.Files;
    44 import java.nio.file.Paths;
    44 import java.nio.file.Paths;
       
    45 import java.nio.file.StandardCopyOption;
    45 import java.util.*;
    46 import java.util.*;
    46 import java.util.function.Consumer;
    47 import java.util.function.Consumer;
    47 import java.util.function.Function;
    48 import java.util.function.Function;
    48 import java.util.function.Supplier;
    49 import java.util.function.Supplier;
    49 import java.util.regex.Pattern;
    50 import java.util.regex.Pattern;
   276                     if (zname.startsWith("./")) {
   277                     if (zname.startsWith("./")) {
   277                         zname = zname.substring(2);
   278                         zname = zname.substring(2);
   278                     }
   279                     }
   279                 }
   280                 }
   280             }
   281             }
       
   282 
   281             if (cflag) {
   283             if (cflag) {
   282                 Manifest manifest = null;
   284                 Manifest manifest = null;
   283                 InputStream in = null;
       
   284 
       
   285                 if (!Mflag) {
   285                 if (!Mflag) {
   286                     if (mname != null) {
   286                     if (mname != null) {
   287                         in = new FileInputStream(mname);
   287                         try (InputStream in = new FileInputStream(mname)) {
   288                         manifest = new Manifest(new BufferedInputStream(in));
   288                             manifest = new Manifest(new BufferedInputStream(in));
       
   289                         }
   289                     } else {
   290                     } else {
   290                         manifest = new Manifest();
   291                         manifest = new Manifest();
   291                     }
   292                     }
   292                     addVersion(manifest);
   293                     addVersion(manifest);
   293                     addCreatedBy(manifest);
   294                     addCreatedBy(manifest);
   294                     if (isAmbiguousMainClass(manifest)) {
   295                     if (isAmbiguousMainClass(manifest)) {
   295                         if (in != null) {
       
   296                             in.close();
       
   297                         }
       
   298                         return false;
   296                         return false;
   299                     }
   297                     }
   300                     if (ename != null) {
   298                     if (ename != null) {
   301                         addMainClass(manifest, ename);
   299                         addMainClass(manifest, ename);
   302                     }
   300                     }
   303                     if (isMultiRelease) {
   301                     if (isMultiRelease) {
   304                         addMultiRelease(manifest);
   302                         addMultiRelease(manifest);
   305                     }
   303                     }
   306                 }
   304                 }
       
   305 
   307                 Map<String,Path> moduleInfoPaths = new HashMap<>();
   306                 Map<String,Path> moduleInfoPaths = new HashMap<>();
   308                 for (int version : filesMap.keySet()) {
   307                 for (int version : filesMap.keySet()) {
   309                     String[] files = filesMap.get(version);
   308                     String[] files = filesMap.get(version);
   310                     expand(null, files, false, moduleInfoPaths, version);
   309                     expand(null, files, false, moduleInfoPaths, version);
   311                 }
   310                 }
       
   311 
   312                 Map<String,byte[]> moduleInfos = new LinkedHashMap<>();
   312                 Map<String,byte[]> moduleInfos = new LinkedHashMap<>();
   313                 if (!moduleInfoPaths.isEmpty()) {
   313                 if (!moduleInfoPaths.isEmpty()) {
   314                     if (!checkModuleInfos(moduleInfoPaths))
   314                     if (!checkModuleInfos(moduleInfoPaths))
   315                         return false;
   315                         return false;
   316 
   316 
   330                 } else if (moduleVersion != null || modulesToHash != null) {
   330                 } else if (moduleVersion != null || modulesToHash != null) {
   331                     error(getMsg("error.module.options.without.info"));
   331                     error(getMsg("error.module.options.without.info"));
   332                     return false;
   332                     return false;
   333                 }
   333                 }
   334 
   334 
   335                 OutputStream out;
   335                 if (vflag && fname == null) {
   336                 if (fname != null) {
   336                     // Disable verbose output so that it does not appear
   337                     out = new FileOutputStream(fname);
   337                     // on stdout along with file data
   338                 } else {
   338                     // error("Warning: -v option ignored");
   339                     out = new FileOutputStream(FileDescriptor.out);
   339                     vflag = false;
   340                     if (vflag) {
   340                 }
   341                         // Disable verbose output so that it does not appear
   341 
   342                         // on stdout along with file data
       
   343                         // error("Warning: -v option ignored");
       
   344                         vflag = false;
       
   345                     }
       
   346                 }
       
   347                 File tmpfile = null;
       
   348                 final OutputStream finalout = out;
       
   349                 final String tmpbase = (fname == null)
   342                 final String tmpbase = (fname == null)
   350                         ? "tmpjar"
   343                         ? "tmpjar"
   351                         : fname.substring(fname.indexOf(File.separatorChar) + 1);
   344                         : fname.substring(fname.indexOf(File.separatorChar) + 1);
       
   345                 File tmpfile = createTemporaryFile(tmpbase, ".jar");
       
   346 
       
   347                 try (OutputStream out = new FileOutputStream(tmpfile)) {
       
   348                     create(new BufferedOutputStream(out, 4096), manifest, moduleInfos);
       
   349                 }
       
   350 
   352                 if (nflag) {
   351                 if (nflag) {
   353                     tmpfile = createTemporaryFile(tmpbase, ".jar");
   352                     File packFile = createTemporaryFile(tmpbase, ".pack");
   354                     out = new FileOutputStream(tmpfile);
       
   355                 }
       
   356                 create(new BufferedOutputStream(out, 4096), manifest, moduleInfos);
       
   357 
       
   358                 if (in != null) {
       
   359                     in.close();
       
   360                 }
       
   361                 out.close();
       
   362                 if (nflag) {
       
   363                     JarFile jarFile = null;
       
   364                     File packFile = null;
       
   365                     JarOutputStream jos = null;
       
   366                     try {
   353                     try {
   367                         Packer packer = Pack200.newPacker();
   354                         Packer packer = Pack200.newPacker();
   368                         Map<String, String> p = packer.properties();
   355                         Map<String, String> p = packer.properties();
   369                         p.put(Packer.EFFORT, "1"); // Minimal effort to conserve CPU
   356                         p.put(Packer.EFFORT, "1"); // Minimal effort to conserve CPU
   370                         jarFile = new JarFile(tmpfile.getCanonicalPath());
   357                         try (
   371                         packFile = createTemporaryFile(tmpbase, ".pack");
   358                                 JarFile jarFile = new JarFile(tmpfile.getCanonicalPath());
   372                         out = new FileOutputStream(packFile);
   359                                 OutputStream pack = new FileOutputStream(packFile)
   373                         packer.pack(jarFile, out);
   360                         ) {
   374                         jos = new JarOutputStream(finalout);
   361                             packer.pack(jarFile, pack);
   375                         Unpacker unpacker = Pack200.newUnpacker();
       
   376                         unpacker.unpack(packFile, jos);
       
   377                     } catch (IOException ioe) {
       
   378                         fatalError(ioe);
       
   379                     } finally {
       
   380                         if (jarFile != null) {
       
   381                             jarFile.close();
       
   382                         }
   362                         }
   383                         if (out != null) {
   363                         if (tmpfile.exists()) {
   384                             out.close();
       
   385                         }
       
   386                         if (jos != null) {
       
   387                             jos.close();
       
   388                         }
       
   389                         if (tmpfile != null && tmpfile.exists()) {
       
   390                             tmpfile.delete();
   364                             tmpfile.delete();
   391                         }
   365                         }
   392                         if (packFile != null && packFile.exists()) {
   366                         tmpfile = createTemporaryFile(tmpbase, ".jar");
   393                             packFile.delete();
   367                         try (
       
   368                                 OutputStream out = new FileOutputStream(tmpfile);
       
   369                                 JarOutputStream jos = new JarOutputStream(out)
       
   370                         ) {
       
   371                             Unpacker unpacker = Pack200.newUnpacker();
       
   372                             unpacker.unpack(packFile, jos);
   394                         }
   373                         }
   395                     }
   374                     } finally {
   396                 }
   375                         Files.deleteIfExists(packFile.toPath());
       
   376                     }
       
   377                 }
       
   378 
       
   379                 validateAndClose(tmpfile);
       
   380 
   397             } else if (uflag) {
   381             } else if (uflag) {
   398                 File inputFile = null, tmpFile = null;
   382                 File inputFile = null, tmpFile = null;
   399                 FileInputStream in;
       
   400                 FileOutputStream out;
       
   401                 if (fname != null) {
   383                 if (fname != null) {
   402                     inputFile = new File(fname);
   384                     inputFile = new File(fname);
   403                     tmpFile = createTempFileInSameDirectoryAs(inputFile);
   385                     tmpFile = createTempFileInSameDirectoryAs(inputFile);
   404                     in = new FileInputStream(inputFile);
       
   405                     out = new FileOutputStream(tmpFile);
       
   406                 } else {
   386                 } else {
   407                     in = new FileInputStream(FileDescriptor.in);
       
   408                     out = new FileOutputStream(FileDescriptor.out);
       
   409                     vflag = false;
   387                     vflag = false;
   410                 }
   388                     tmpFile = createTemporaryFile("tmpjar", ".jar");
   411                 InputStream manifest = (!Mflag && (mname != null)) ?
   389                 }
   412                     (new FileInputStream(mname)) : null;
       
   413 
   390 
   414                 Map<String,Path> moduleInfoPaths = new HashMap<>();
   391                 Map<String,Path> moduleInfoPaths = new HashMap<>();
   415                 for (int version : filesMap.keySet()) {
   392                 for (int version : filesMap.keySet()) {
   416                     String[] files = filesMap.get(version);
   393                     String[] files = filesMap.get(version);
   417                     expand(null, files, true, moduleInfoPaths, version);
   394                     expand(null, files, true, moduleInfoPaths, version);
   419 
   396 
   420                 Map<String,byte[]> moduleInfos = new HashMap<>();
   397                 Map<String,byte[]> moduleInfos = new HashMap<>();
   421                 for (Map.Entry<String,Path> e : moduleInfoPaths.entrySet())
   398                 for (Map.Entry<String,Path> e : moduleInfoPaths.entrySet())
   422                     moduleInfos.put(e.getKey(), readModuleInfo(e.getValue()));
   399                     moduleInfos.put(e.getKey(), readModuleInfo(e.getValue()));
   423 
   400 
   424                 boolean updateOk = update(in, new BufferedOutputStream(out),
   401                 try (
   425                                           manifest, moduleInfos, null);
   402                         FileInputStream in = (fname != null) ? new FileInputStream(inputFile)
       
   403                                 : new FileInputStream(FileDescriptor.in);
       
   404                         FileOutputStream out = new FileOutputStream(tmpFile);
       
   405                         InputStream manifest = (!Mflag && (mname != null)) ?
       
   406                                 (new FileInputStream(mname)) : null;
       
   407                 ) {
       
   408                         boolean updateOk = update(in, new BufferedOutputStream(out),
       
   409                                 manifest, moduleInfos, null);
       
   410                         if (ok) {
       
   411                             ok = updateOk;
       
   412                         }
       
   413                 }
   426 
   414 
   427                 // Consistency checks for modular jars.
   415                 // Consistency checks for modular jars.
   428                 if (!moduleInfos.isEmpty()) {
   416                 if (!moduleInfos.isEmpty()) {
   429                     if(!checkServices(moduleInfos.get(MODULE_INFO)))
   417                     if(!checkServices(moduleInfos.get(MODULE_INFO)))
   430                         return false;
   418                         return false;
   431                 }
   419                 }
   432 
   420 
   433                 if (ok) {
   421                 validateAndClose(tmpFile);
   434                     ok = updateOk;
   422 
   435                 }
       
   436                 in.close();
       
   437                 out.close();
       
   438                 if (manifest != null) {
       
   439                     manifest.close();
       
   440                 }
       
   441                 if (ok && fname != null) {
       
   442                     // on Win32, we need this delete
       
   443                     inputFile.delete();
       
   444                     if (!tmpFile.renameTo(inputFile)) {
       
   445                         tmpFile.delete();
       
   446                         throw new IOException(getMsg("error.write.file"));
       
   447                     }
       
   448                     tmpFile.delete();
       
   449                 }
       
   450             } else if (tflag) {
   423             } else if (tflag) {
   451                 replaceFSC(filesMap);
   424                 replaceFSC(filesMap);
   452                 // For the "list table contents" action, access using the
   425                 // For the "list table contents" action, access using the
   453                 // ZipFile class is always most efficient since only a
   426                 // ZipFile class is always most efficient since only a
   454                 // "one-finger" scan through the central directory is required.
   427                 // "one-finger" scan through the central directory is required.
   518         out.flush();
   491         out.flush();
   519         err.flush();
   492         err.flush();
   520         return ok;
   493         return ok;
   521     }
   494     }
   522 
   495 
       
   496     private void validateAndClose(File tmpfile) throws IOException {
       
   497         if (ok && isMultiRelease) {
       
   498             ok = validate(tmpfile.getCanonicalPath());
       
   499             if (!ok) {
       
   500                 error(formatMsg("error.validator.jarfile.invalid", fname));
       
   501             }
       
   502         }
       
   503 
       
   504         Path path = tmpfile.toPath();
       
   505         try {
       
   506             if (ok) {
       
   507                 if (fname != null) {
       
   508                     Files.move(path, Paths.get(fname), StandardCopyOption.REPLACE_EXISTING);
       
   509                 } else {
       
   510                     Files.copy(path, new FileOutputStream(FileDescriptor.out));
       
   511                 }
       
   512             }
       
   513         } finally {
       
   514             Files.deleteIfExists(path);
       
   515         }
       
   516     }
       
   517 
   523     private String[] filesMapToFiles(Map<Integer,String[]> filesMap) {
   518     private String[] filesMapToFiles(Map<Integer,String[]> filesMap) {
   524         if (filesMap.isEmpty()) return null;
   519         if (filesMap.isEmpty()) return null;
   525         return filesMap.entrySet()
   520         return filesMap.entrySet()
   526                 .stream()
   521                 .stream()
   527                 .flatMap(this::filesToEntryNames)
   522                 .flatMap(this::filesToEntryNames)
   530 
   525 
   531     Stream<String> filesToEntryNames(Map.Entry<Integer,String[]> fileEntries) {
   526     Stream<String> filesToEntryNames(Map.Entry<Integer,String[]> fileEntries) {
   532         int version = fileEntries.getKey();
   527         int version = fileEntries.getKey();
   533         return Stream.of(fileEntries.getValue())
   528         return Stream.of(fileEntries.getValue())
   534                 .map(f -> (new EntryName(f, version)).entryName);
   529                 .map(f -> (new EntryName(f, version)).entryName);
       
   530     }
       
   531 
       
   532     // sort base entries before versioned entries, and sort entry classes with
       
   533     // nested classes so that the top level class appears before the associated
       
   534     // nested class
       
   535     private Comparator<JarEntry> entryComparator = (je1, je2) ->  {
       
   536         String s1 = je1.getName();
       
   537         String s2 = je2.getName();
       
   538         if (s1.equals(s2)) return 0;
       
   539         boolean b1 = s1.startsWith(VERSIONS_DIR);
       
   540         boolean b2 = s2.startsWith(VERSIONS_DIR);
       
   541         if (b1 && !b2) return 1;
       
   542         if (!b1 && b2) return -1;
       
   543         int n = 0; // starting char for String compare
       
   544         if (b1 && b2) {
       
   545             // normally strings would be sorted so "10" goes before "9", but
       
   546             // version number strings need to be sorted numerically
       
   547             n = VERSIONS_DIR.length();   // skip the common prefix
       
   548             int i1 = s1.indexOf('/', n);
       
   549             int i2 = s1.indexOf('/', n);
       
   550             if (i1 == -1) throw new InvalidJarException(s1);
       
   551             if (i2 == -1) throw new InvalidJarException(s2);
       
   552             // shorter version numbers go first
       
   553             if (i1 != i2) return i1 - i2;
       
   554             // otherwise, handle equal length numbers below
       
   555         }
       
   556         int l1 = s1.length();
       
   557         int l2 = s2.length();
       
   558         int lim = Math.min(l1, l2);
       
   559         for (int k = n; k < lim; k++) {
       
   560             char c1 = s1.charAt(k);
       
   561             char c2 = s2.charAt(k);
       
   562             if (c1 != c2) {
       
   563                 // change natural ordering so '.' comes before '$'
       
   564                 // i.e. top level classes come before nested classes
       
   565                 if (c1 == '$' && c2 == '.') return 1;
       
   566                 if (c1 == '.' && c2 == '$') return -1;
       
   567                 return c1 - c2;
       
   568             }
       
   569         }
       
   570         return l1 - l2;
       
   571     };
       
   572 
       
   573     private boolean validate(String fname) {
       
   574         boolean valid;
       
   575 
       
   576         try (JarFile jf = new JarFile(fname)) {
       
   577             Validator validator = new Validator(this, jf);
       
   578             jf.stream()
       
   579                     .filter(e -> !e.isDirectory())
       
   580                     .filter(e -> !e.getName().equals(MANIFEST_NAME))
       
   581                     .filter(e -> !e.getName().endsWith(MODULE_INFO))
       
   582                     .sorted(entryComparator)
       
   583                     .forEachOrdered(validator);
       
   584              valid = validator.isValid();
       
   585         } catch (IOException e) {
       
   586             error(formatMsg2("error.validator.jarfile.exception", fname, e.getMessage()));
       
   587             valid = false;
       
   588         } catch (InvalidJarException e) {
       
   589             error(formatMsg("error.validator.bad.entry.name", e.getMessage()));
       
   590             valid = false;
       
   591         }
       
   592         return valid;
       
   593     }
       
   594 
       
   595     private static class InvalidJarException extends RuntimeException {
       
   596         private static final long serialVersionUID = -3642329147299217726L;
       
   597         InvalidJarException(String msg) {
       
   598             super(msg);
       
   599         }
   535     }
   600     }
   536 
   601 
   537     /**
   602     /**
   538      * Parses command line arguments.
   603      * Parses command line arguments.
   539      */
   604      */