src/share/classes/org/openjdk/jigsaw/ModuleFileWriter.java

Print this page

        

@@ -33,10 +33,11 @@
 import java.nio.file.Files;
 import java.security.MessageDigest;
 import java.util.*;
 import java.util.jar.*;
 import java.util.zip.*;
+import org.openjdk.jigsaw.ModuleFileParser.Event;
 import static org.openjdk.jigsaw.FileConstants.ModuleFile.*;
 import static org.openjdk.jigsaw.ModuleFile.*;
 
 
 /**

@@ -47,10 +48,11 @@
 
     private final File outfile;
     private final HashType hashtype;
     private final boolean fastestCompression;
     private long usize;
+    private int headerLength;
 
     public ModuleFileWriter(File outfile) {
         this(outfile, false);
     }
 

@@ -61,14 +63,12 @@
     }
 
     /**
      * Generates an unsigned module file.
      */
-    public void writeModule(File mdir,
-                            File nativelibs,
-                            File nativecmds,
-                            File config)
+    public void writeModule(File mdir, File nativelibs, File nativecmds,
+                            File config, ModuleArchitecture modArch)
             throws IOException
     {
         if (!mdir.isDirectory()) {
             throw new IOException("Not a directory: " + mdir);
         }

@@ -75,21 +75,25 @@
 
         try (RandomAccessFile file = new RandomAccessFile(outfile, "rw")) {
             // Truncate the file if it already exists
             file.setLength(0);
 
+            // set appropriate fields in the header before determining the length
+            ModuleFileHeader dummyHeader = ModuleFileHeader.newBuilder()
+                                           .setArchitecture(modArch)
+                                           .setHashType(hashtype)
+                                           .setHash(new byte[hashtype.length()])
+                                           .build();
+
             // Reset module file to right after module header
-            file.seek(ModuleFileHeader.LENGTH);
+            file.seek(headerLength = dummyHeader.getLength());
 
-            // TODO: Why was this after the module info???
-            long remainderStart = file.getFilePointer();
-
             // Write out the Module-Info Section
             File miclass = new File(mdir, "module-info.class");
-            if (!miclass.exists()) {
+            if (!miclass.exists())
                 throw new IOException(miclass + " does not exist");
-            }
+
             writeSection(file,
                          SectionType.MODULE_INFO,
                          mdir,
                          Collections.singletonList(miclass.toPath()),
                          Compressor.NONE);

@@ -96,11 +100,11 @@
 
             // Write out the optional file sections
             writeOptionalSections(file, mdir, nativelibs, nativecmds, config);
 
             // Write out the module file header
-            writeModuleFileHeader(file, remainderStart);
+            writeModuleFileHeader(file, headerLength, modArch);
         }
     }
 
     /*
      * Write a section to the given module file.

@@ -113,11 +117,13 @@
      */
     private void writeSection(RandomAccessFile file,
                               SectionType type,
                               File sourcedir,
                               List<Path> files,
-                              Compressor compressor) throws IOException {
+                              Compressor compressor)
+        throws IOException
+    {
         // Start of section header
         final long start = file.getFilePointer();
 
         // Start of section content
         final long cstart = start + SectionHeader.LENGTH;

@@ -140,12 +146,13 @@
 
     private void writeSectionContent(RandomAccessFile file,
                                      SectionType type,
                                      File sourcedir,
                                      List<Path> files,
-                                     Compressor compressor) throws IOException {
-
+                                     Compressor compressor)
+        throws IOException
+    {
         if (type.hasFiles()) {
             for (Path p : files) {
                 writeSubSection(file, sourcedir, p, compressor);
             }
         } else if (type == SectionType.CLASSES) {

@@ -160,12 +167,13 @@
 
     private void writeSectionHeader(RandomAccessFile file,
                                     SectionType type,
                                     Compressor compressor,
                                     long start, int csize,
-                                    short subsections) throws IOException {
-
+                                    short subsections)
+        throws IOException
+    {
         // Compute hash of content
         MessageDigest md = getHashInstance(hashtype);
         FileChannel channel = file.getChannel();
         ByteBuffer content = ByteBuffer.allocate(csize);
 

@@ -188,11 +196,13 @@
     }
 
     private void writeClassesContent(DataOutput out,
                                      File sourcedir,
                                      List<Path> files,
-                                     Compressor compressor) throws IOException {
+                                     Compressor compressor)
+        throws IOException
+    {
         CompressedClassOutputStream cos =
             CompressedClassOutputStream.newInstance(sourcedir.toPath(),
                                                     files,
                                                     compressor,
                                                     fastestCompression);

@@ -200,11 +210,13 @@
         cos.writeTo(out);
     }
 
     private void writeFileContent(DataOutput out,
                                   Path p,
-                                  Compressor compressor) throws IOException {
+                                  Compressor compressor)
+        throws IOException
+    {
         CompressedOutputStream cos = CompressedOutputStream.newInstance(p, compressor);
         usize += cos.getUSize();
         cos.writeTo(out);
     }
 

@@ -217,11 +229,13 @@
      * @params compressor compression type
      */
     private void writeSubSection(RandomAccessFile file,
                                  File sourcedir,
                                  Path p,
-                                 Compressor compressor) throws IOException {
+                                 Compressor compressor)
+        throws IOException
+    {
         CompressedOutputStream cos = CompressedOutputStream.newInstance(p, compressor);
         usize += cos.getUSize();
 
         String storedpath = relativePath(sourcedir.toPath(), p);
         SubSectionFileHeader header = new SubSectionFileHeader((int)cos.getCSize(), storedpath);

@@ -284,40 +298,54 @@
 
     /*
      * Writes out the module file header.
      */
     private void writeModuleFileHeader(RandomAccessFile file,
-                                       long remainderStart)
+                                       long headerLength,
+                                       ModuleArchitecture modArch)
             throws IOException
     {
+        long csize = file.length() - headerLength;
 
-        long csize = file.length() - remainderStart;
-
         // Header Step 1
         // Write out the module file header (using a dummy file hash value)
-        ModuleFileHeader header =
-                new ModuleFileHeader(csize, usize, hashtype,
-                                     new byte[hashtype.length()]);
+        ModuleFileHeader.Builder hBuilder = ModuleFileHeader.newBuilder()
+                                            .setCSize(csize)
+                                            .setUSize(usize)
+                                            .setArchitecture(modArch)
+                                            .setHashType(hashtype)
+                                            .setHash(new byte[hashtype.length()]);
+        ModuleFileHeader fileHeader = hBuilder.build();
+        assert (fileHeader.getLength() == headerLength);
+
         file.seek(0);
-        header.write(file);
+        fileHeader.write(file);
 
-        // Generate the module file hash
-        byte[] fileHash = generateFileHash(file);
+        // Calculate the correct module file hash, using a non-validating parser
+        file.seek(0);
+        byte[] fileHash = null;
+        ModuleFileParser parser =
+                ModuleFile.newParser(Channels.newInputStream(file.getChannel()));
+        while (parser.hasNext()) {
+            if (parser.next().equals(Event.END_FILE))
+                fileHash =  parser.getHash();
+        }
 
         // Header Step 2
         // Write out the module file header (using correct file hash value)
-        header = new ModuleFileHeader(csize, usize, hashtype, fileHash);
+        hBuilder.setHash(fileHash);
         file.seek(0);
-        header.write(file);
+        fileHeader = hBuilder.build();
+        assert (fileHeader.getLength() == headerLength);
+        fileHeader.write(file);
     }
 
     private void listClassesResources(Path dir,
                                       final List<Path> classes,
                                       final List<Path> resources)
             throws IOException
     {
-
         Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
             @Override
             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                     throws IOException {
                 if (!file.endsWith("module-info.class")) {

@@ -425,11 +453,13 @@
                 p.put(Pack200.Packer.KEEP_FILE_ORDER, Pack200.Packer.FALSE);
                 p.put(Pack200.Packer.MODIFICATION_TIME, Pack200.Packer.LATEST);
                 p.put(Pack200.Packer.DEFLATE_HINT, Pack200.Packer.FALSE);
             }
 
-            void compress(Path sourcepath, List<Path> classes) throws IOException {
+            void compress(Path sourcepath, List<Path> classes)
+                throws IOException
+            {
                 ByteArrayOutputStream os = new ByteArrayOutputStream();
                 try (JarOutputStream jos = new JarOutputStream(os)) {
                     jos.setLevel(0);
                     for (Path file : classes) {
                         // write to the JarInputStream for later pack200 compression

@@ -453,11 +483,13 @@
                 }
             }
         }
     }
 
-    private String relativePath(Path sourcepath, Path path) throws IOException {
+    private String relativePath(Path sourcepath, Path path)
+        throws IOException
+    {
         Path relativepath = sourcepath.relativize(path);
 
         // The '/' character separates nested directories in path names.
         String pathseparator = relativepath.getFileSystem().getSeparator();
         String stored = relativepath.toString().replace(pathseparator, "/");

@@ -468,14 +500,15 @@
         // ## the native libraries in jdk modules are changed
         // ensureShortNativePath(realpath, stored);
         return stored;
     }
 
-    private List<Path> listFiles(Path path) throws IOException {
+    private List<Path> listFiles(Path path)
+        throws IOException
+    {
         final List<Path> list = new ArrayList<>();
         Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
-
             @Override
             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                     throws IOException {
                 list.add(file);
                 return FileVisitResult.CONTINUE;

@@ -483,52 +516,15 @@
         });
         return list;
     }
 
     /*
-     * Generates the hash value for a module file.
-     * Excludes itself (the hash bytes in the module file header).
-     */
-    private byte[] generateFileHash(RandomAccessFile file)
-            throws IOException
-    {
-        MessageDigest md = getHashInstance(hashtype);
-
-        long remainderSize = file.length() - ModuleFileHeader.LENGTH;
-        FileChannel channel = file.getChannel();
-
-        // Module file header without the hash bytes
-        ByteBuffer content = ByteBuffer.allocate(ModuleFileHeader.LENGTH_WITHOUT_HASH);
-        int n = channel.read(content, 0);
-        if (n != ModuleFileHeader.LENGTH_WITHOUT_HASH) {
-            throw new IOException("too few bytes read");
-        }
-        content.position(0);
-        md.update(content);
-
-        // Remainder of file (read in chunks)
-        content = ByteBuffer.allocate(8192);
-        channel.position(ModuleFileHeader.LENGTH);
-        n = channel.read(content);
-        while (n != -1) {
-            content.limit(n);
-            content.position(0);
-            md.update(content);
-            content = ByteBuffer.allocate(8192);
-            n = channel.read(content);
-        }
-
-        return md.digest();
-    }
-
-    /*
      * Check if a given directory is not empty.
      */
     private static boolean directoryIsNotEmpty(File dir)
-            throws IOException {
-        try (DirectoryStream<Path> ds =
-                        Files.newDirectoryStream(dir.toPath())) {
+        throws IOException
+    {
+        try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir.toPath())) {
             return ds.iterator().hasNext();
         }
     }
-
 }