< prev index next >

src/jdk.jartool/share/classes/sun/tools/jar/Main.java

Print this page
rev 16153 : 8164805: Fail to create a MR modular JAR with a versioned entry of a concealed package
Reviewed-by: chegar, mchung


  63 import jdk.internal.util.jar.JarIndex;
  64 
  65 import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
  66 import static java.util.jar.JarFile.MANIFEST_NAME;
  67 import static java.util.stream.Collectors.joining;
  68 import static java.util.stream.Collectors.toSet;
  69 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
  70 
  71 /**
  72  * This class implements a simple utility for creating files in the JAR
  73  * (Java Archive) file format. The JAR format is based on the ZIP file
  74  * format, with optional meta-information stored in a MANIFEST entry.
  75  */
  76 public
  77 class Main {
  78     String program;
  79     PrintWriter out, err;
  80     String fname, mname, ename;
  81     String zname = "";
  82     String rootjar = null;

  83 
  84     private static final int BASE_VERSION = 0;
  85 
  86     class Entry {
  87         final String basename;
  88         final String entryname;
  89         final File file;
  90         final boolean isDir;
  91 
  92         Entry(int version, File file) {
  93             this.file = file;
  94             String path = file.getPath();
  95             if (file.isDirectory()) {
  96                 isDir = true;
  97                 path = path.endsWith(File.separator) ? path :
  98                             path + File.separator;
  99             } else {
 100                 isDir = false;
 101             }
 102             EntryName en = new EntryName(path, version);


 819             }
 820         }
 821         return true;
 822     }
 823 
 824     private static Set<String> findPackages(ZipFile zf) {
 825         return zf.stream()
 826                  .filter(e -> e.getName().endsWith(".class"))
 827                  .map(e -> toPackageName(e))
 828                  .filter(pkg -> pkg.length() > 0)
 829                  .distinct()
 830                  .collect(Collectors.toSet());
 831     }
 832 
 833     private static String toPackageName(ZipEntry entry) {
 834         return toPackageName(entry.getName());
 835     }
 836 
 837     private static String toPackageName(String path) {
 838         assert path.endsWith(".class");
 839         int index = path.lastIndexOf('/');








 840         if (index != -1) {
 841             return path.substring(0, index).replace('/', '.');
 842         } else {
 843             return "";
 844         }
 845     }
 846 
 847     /**
 848      * Expands list of files to process into full list of all files that
 849      * can be found by recursively descending directories.
 850      */
 851     void expand(File dir,
 852                 String[] files,
 853                 boolean isUpdate,
 854                 Map<String,Path> moduleInfoPaths,
 855                 int version)
 856         throws IOException
 857     {
 858         if (files == null)
 859             return;
 860 
 861         for (int i = 0; i < files.length; i++) {
 862             File f;
 863             if (dir == null)
 864                 f = new File(files[i]);
 865             else
 866                 f = new File(dir, files[i]);
 867 
 868             Entry entry = new Entry(version, f);
 869             String entryName = entry.entryname;
 870 
 871             if (f.isFile()) {
 872                 if (entryName.endsWith(MODULE_INFO)) {
 873                     moduleInfoPaths.put(entryName, f.toPath());
 874                     if (isUpdate)
 875                         entryMap.put(entryName, entry);
 876                 } else if (entries.add(entry)) {
 877                     jarEntries.add(entryName);
 878                     if (entry.basename.endsWith(".class") && !entryName.startsWith(VERSIONS_DIR))
 879                         packages.add(toPackageName(entry.basename));
 880                     if (isUpdate)
 881                         entryMap.put(entryName, entry);
 882                 }
 883             } else if (f.isDirectory()) {
 884                 if (entries.add(entry)) {
 885                     if (isUpdate) {
 886                         entryMap.put(entryName, entry);
 887                     }
 888                     expand(f, f.list(), isUpdate, moduleInfoPaths, version);
 889                 }
 890             } else {
 891                 error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
 892                 ok = false;
 893             }
 894         }
 895     }
 896 
 897     /**
 898      * Creates a new JAR file.


1051                     // do our own compression
1052                     ZipEntry e2 = new ZipEntry(name);
1053                     e2.setMethod(e.getMethod());
1054                     e2.setTime(e.getTime());
1055                     e2.setComment(e.getComment());
1056                     e2.setExtra(e.getExtra());
1057                     if (e.getMethod() == ZipEntry.STORED) {
1058                         e2.setSize(e.getSize());
1059                         e2.setCrc(e.getCrc());
1060                     }
1061                     zos.putNextEntry(e2);
1062                     copy(zis, zos);
1063                 } else { // replace with the new files
1064                     Entry ent = entryMap.get(name);
1065                     addFile(zos, ent);
1066                     entryMap.remove(name);
1067                     entries.remove(ent);
1068                 }
1069 
1070                 jarEntries.add(name);
1071                 if (name.endsWith(".class") && !(name.startsWith(VERSIONS_DIR)))
1072                     packages.add(toPackageName(name));
1073             }
1074         }
1075 
1076         // add the remaining new files
1077         for (Entry entry : entries) {
1078             addFile(zos, entry);
1079         }
1080         if (!foundManifest) {
1081             if (newManifest != null) {
1082                 Manifest m = new Manifest(newManifest);
1083                 updateOk = !isAmbiguousMainClass(m);
1084                 if (updateOk) {
1085                     if (!updateManifest(m, zos)) {
1086                         updateOk = false;
1087                     }
1088                 }
1089             } else if (ename != null) {
1090                 if (!updateManifest(new Manifest(), zos)) {
1091                     updateOk = false;


1745      */
1746     void fatalError(String s) {
1747         error(program + ": " + s);
1748     }
1749 
1750     /**
1751      * Print an output message; like verbose output and the like
1752      */
1753     protected void output(String s) {
1754         out.println(s);
1755     }
1756 
1757     /**
1758      * Print an error message; like something is broken
1759      */
1760     void error(String s) {
1761         err.println(s);
1762     }
1763 
1764     /**







1765      * Main routine to start program.
1766      */
1767     public static void main(String args[]) {
1768         Main jartool = new Main(System.out, System.err, "jar");
1769         System.exit(jartool.run(args) ? 0 : 1);
1770     }
1771 
1772     /**
1773      * An OutputStream that doesn't send its output anywhere, (but could).
1774      * It's here to find the CRC32 of an input file, necessary for STORED
1775      * mode in ZIP.
1776      */
1777     private static class CRC32OutputStream extends java.io.OutputStream {
1778         final CRC32 crc = new CRC32();
1779         long n = 0;
1780 
1781         CRC32OutputStream() {}
1782 
1783         public void write(int r) throws IOException {
1784             crc.update(r);


1958                                 .collect(Collectors.toSet());
1959         if (missing.size() > 0) {
1960             missing.stream().forEach(s -> fatalError(formatMsg("error.missing.provider", s)));
1961             return false;
1962         }
1963         return true;
1964     }
1965 
1966     /**
1967      * Adds extended modules attributes to the given module-info's.  The given
1968      * Map values are updated in-place. Returns false if an error occurs.
1969      */
1970     private boolean addExtendedModuleAttributes(Map<String,byte[]> moduleInfos)
1971         throws IOException
1972     {
1973         assert !moduleInfos.isEmpty() && moduleInfos.get(MODULE_INFO) != null;
1974 
1975         ByteBuffer bb = ByteBuffer.wrap(moduleInfos.get(MODULE_INFO));
1976         ModuleDescriptor rd = ModuleDescriptor.read(bb);
1977 
1978         Set<String> exports = rd.exports()
1979                                 .stream()
1980                                 .map(Exports::source)
1981                                 .collect(toSet());
1982 
1983         Set<String> conceals = packages.stream()
1984                                        .filter(p -> !exports.contains(p))
1985                                        .collect(toSet());
1986 
1987         for (Map.Entry<String,byte[]> e: moduleInfos.entrySet()) {
1988             ModuleDescriptor vd = ModuleDescriptor.read(ByteBuffer.wrap(e.getValue()));
1989             if (!(isValidVersionedDescriptor(vd, rd)))
1990                 return false;
1991             e.setValue(extendedInfoBytes(rd, vd, e.getValue(), conceals));
1992         }
1993         return true;
1994     }
1995 














1996     private static boolean isPlatformModule(String name) {
1997         return name.startsWith("java.") || name.startsWith("jdk.");
1998     }
1999 
2000     /**
2001      * Tells whether or not the given versioned module descriptor's attributes
2002      * are valid when compared against the given root module descriptor.
2003      *
2004      * A versioned module descriptor must be identical to the root module
2005      * descriptor, with two exceptions:
2006      *  - A versioned descriptor can have different non-public `requires`
2007      *    clauses of platform ( `java.*` and `jdk.*` ) modules, and
2008      *  - A versioned descriptor can have different `uses` clauses, even of
2009      *    service types defined outside of the platform modules.
2010      */
2011     private boolean isValidVersionedDescriptor(ModuleDescriptor vd,
2012                                                ModuleDescriptor rd)
2013         throws IOException
2014     {
2015         if (!rd.name().equals(vd.name())) {




  63 import jdk.internal.util.jar.JarIndex;
  64 
  65 import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
  66 import static java.util.jar.JarFile.MANIFEST_NAME;
  67 import static java.util.stream.Collectors.joining;
  68 import static java.util.stream.Collectors.toSet;
  69 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
  70 
  71 /**
  72  * This class implements a simple utility for creating files in the JAR
  73  * (Java Archive) file format. The JAR format is based on the ZIP file
  74  * format, with optional meta-information stored in a MANIFEST entry.
  75  */
  76 public
  77 class Main {
  78     String program;
  79     PrintWriter out, err;
  80     String fname, mname, ename;
  81     String zname = "";
  82     String rootjar = null;
  83     Set<String> concealedPackages = new HashSet<>();
  84 
  85     private static final int BASE_VERSION = 0;
  86 
  87     class Entry {
  88         final String basename;
  89         final String entryname;
  90         final File file;
  91         final boolean isDir;
  92 
  93         Entry(int version, File file) {
  94             this.file = file;
  95             String path = file.getPath();
  96             if (file.isDirectory()) {
  97                 isDir = true;
  98                 path = path.endsWith(File.separator) ? path :
  99                             path + File.separator;
 100             } else {
 101                 isDir = false;
 102             }
 103             EntryName en = new EntryName(path, version);


 820             }
 821         }
 822         return true;
 823     }
 824 
 825     private static Set<String> findPackages(ZipFile zf) {
 826         return zf.stream()
 827                  .filter(e -> e.getName().endsWith(".class"))
 828                  .map(e -> toPackageName(e))
 829                  .filter(pkg -> pkg.length() > 0)
 830                  .distinct()
 831                  .collect(Collectors.toSet());
 832     }
 833 
 834     private static String toPackageName(ZipEntry entry) {
 835         return toPackageName(entry.getName());
 836     }
 837 
 838     private static String toPackageName(String path) {
 839         assert path.endsWith(".class");
 840         int index;
 841         if (path.startsWith(VERSIONS_DIR)) {
 842             index = path.indexOf('/', VERSIONS_DIR.length());
 843             if (index <= 0) {
 844                 return "";
 845             }
 846             path = path.substring(index + 1);
 847         }
 848         index = path.lastIndexOf('/');
 849         if (index != -1) {
 850             return path.substring(0, index).replace('/', '.');
 851         } else {
 852             return "";
 853         }
 854     }
 855 
 856     /**
 857      * Expands list of files to process into full list of all files that
 858      * can be found by recursively descending directories.
 859      */
 860     void expand(File dir,
 861                 String[] files,
 862                 boolean isUpdate,
 863                 Map<String,Path> moduleInfoPaths,
 864                 int version)
 865         throws IOException
 866     {
 867         if (files == null)
 868             return;
 869 
 870         for (int i = 0; i < files.length; i++) {
 871             File f;
 872             if (dir == null)
 873                 f = new File(files[i]);
 874             else
 875                 f = new File(dir, files[i]);
 876 
 877             Entry entry = new Entry(version, f);
 878             String entryName = entry.entryname;
 879 
 880             if (f.isFile()) {
 881                 if (entryName.endsWith(MODULE_INFO)) {
 882                     moduleInfoPaths.put(entryName, f.toPath());
 883                     if (isUpdate)
 884                         entryMap.put(entryName, entry);
 885                 } else if (entries.add(entry)) {
 886                     jarEntries.add(entryName);
 887                     if (entry.basename.endsWith(".class"))
 888                         packages.add(toPackageName(entry.basename));
 889                     if (isUpdate)
 890                         entryMap.put(entryName, entry);
 891                 }
 892             } else if (f.isDirectory()) {
 893                 if (entries.add(entry)) {
 894                     if (isUpdate) {
 895                         entryMap.put(entryName, entry);
 896                     }
 897                     expand(f, f.list(), isUpdate, moduleInfoPaths, version);
 898                 }
 899             } else {
 900                 error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
 901                 ok = false;
 902             }
 903         }
 904     }
 905 
 906     /**
 907      * Creates a new JAR file.


1060                     // do our own compression
1061                     ZipEntry e2 = new ZipEntry(name);
1062                     e2.setMethod(e.getMethod());
1063                     e2.setTime(e.getTime());
1064                     e2.setComment(e.getComment());
1065                     e2.setExtra(e.getExtra());
1066                     if (e.getMethod() == ZipEntry.STORED) {
1067                         e2.setSize(e.getSize());
1068                         e2.setCrc(e.getCrc());
1069                     }
1070                     zos.putNextEntry(e2);
1071                     copy(zis, zos);
1072                 } else { // replace with the new files
1073                     Entry ent = entryMap.get(name);
1074                     addFile(zos, ent);
1075                     entryMap.remove(name);
1076                     entries.remove(ent);
1077                 }
1078 
1079                 jarEntries.add(name);
1080                 if (name.endsWith(".class"))
1081                     packages.add(toPackageName(name));
1082             }
1083         }
1084 
1085         // add the remaining new files
1086         for (Entry entry : entries) {
1087             addFile(zos, entry);
1088         }
1089         if (!foundManifest) {
1090             if (newManifest != null) {
1091                 Manifest m = new Manifest(newManifest);
1092                 updateOk = !isAmbiguousMainClass(m);
1093                 if (updateOk) {
1094                     if (!updateManifest(m, zos)) {
1095                         updateOk = false;
1096                     }
1097                 }
1098             } else if (ename != null) {
1099                 if (!updateManifest(new Manifest(), zos)) {
1100                     updateOk = false;


1754      */
1755     void fatalError(String s) {
1756         error(program + ": " + s);
1757     }
1758 
1759     /**
1760      * Print an output message; like verbose output and the like
1761      */
1762     protected void output(String s) {
1763         out.println(s);
1764     }
1765 
1766     /**
1767      * Print an error message; like something is broken
1768      */
1769     void error(String s) {
1770         err.println(s);
1771     }
1772 
1773     /**
1774      * Print a warning message
1775      */
1776     void warn(String s) {
1777         err.println(s);
1778     }
1779 
1780     /**
1781      * Main routine to start program.
1782      */
1783     public static void main(String args[]) {
1784         Main jartool = new Main(System.out, System.err, "jar");
1785         System.exit(jartool.run(args) ? 0 : 1);
1786     }
1787 
1788     /**
1789      * An OutputStream that doesn't send its output anywhere, (but could).
1790      * It's here to find the CRC32 of an input file, necessary for STORED
1791      * mode in ZIP.
1792      */
1793     private static class CRC32OutputStream extends java.io.OutputStream {
1794         final CRC32 crc = new CRC32();
1795         long n = 0;
1796 
1797         CRC32OutputStream() {}
1798 
1799         public void write(int r) throws IOException {
1800             crc.update(r);


1974                                 .collect(Collectors.toSet());
1975         if (missing.size() > 0) {
1976             missing.stream().forEach(s -> fatalError(formatMsg("error.missing.provider", s)));
1977             return false;
1978         }
1979         return true;
1980     }
1981 
1982     /**
1983      * Adds extended modules attributes to the given module-info's.  The given
1984      * Map values are updated in-place. Returns false if an error occurs.
1985      */
1986     private boolean addExtendedModuleAttributes(Map<String,byte[]> moduleInfos)
1987         throws IOException
1988     {
1989         assert !moduleInfos.isEmpty() && moduleInfos.get(MODULE_INFO) != null;
1990 
1991         ByteBuffer bb = ByteBuffer.wrap(moduleInfos.get(MODULE_INFO));
1992         ModuleDescriptor rd = ModuleDescriptor.read(bb);
1993 
1994         concealedPackages = findConcealedPackages(rd);







1995 
1996         for (Map.Entry<String,byte[]> e: moduleInfos.entrySet()) {
1997             ModuleDescriptor vd = ModuleDescriptor.read(ByteBuffer.wrap(e.getValue()));
1998             if (!(isValidVersionedDescriptor(vd, rd)))
1999                 return false;
2000             e.setValue(extendedInfoBytes(rd, vd, e.getValue(), concealedPackages));
2001         }
2002         return true;
2003     }
2004 
2005     private Set<String> findConcealedPackages(ModuleDescriptor md){
2006         Objects.requireNonNull(md);
2007 
2008         Set<String> exports = md.exports()
2009                 .stream()
2010                 .map(Exports::source)
2011                 .collect(toSet());
2012 
2013         return packages.stream()
2014                 .filter(p -> !p.equals(""))
2015                 .filter(p -> !exports.contains(p))
2016                 .collect(toSet());
2017     }
2018     
2019     private static boolean isPlatformModule(String name) {
2020         return name.startsWith("java.") || name.startsWith("jdk.");
2021     }
2022 
2023     /**
2024      * Tells whether or not the given versioned module descriptor's attributes
2025      * are valid when compared against the given root module descriptor.
2026      *
2027      * A versioned module descriptor must be identical to the root module
2028      * descriptor, with two exceptions:
2029      *  - A versioned descriptor can have different non-public `requires`
2030      *    clauses of platform ( `java.*` and `jdk.*` ) modules, and
2031      *  - A versioned descriptor can have different `uses` clauses, even of
2032      *    service types defined outside of the platform modules.
2033      */
2034     private boolean isValidVersionedDescriptor(ModuleDescriptor vd,
2035                                                ModuleDescriptor rd)
2036         throws IOException
2037     {
2038         if (!rd.name().equals(vd.name())) {


< prev index next >