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();
}
}
-
}