8166860: Add magic number to jmod file
Reviewed-by: alanb, jjg
This commit is contained in:
parent
e4d6418631
commit
6504983459
@ -56,6 +56,8 @@ import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import jdk.internal.jmod.JmodFile;
|
||||
import jdk.internal.jmod.JmodFile.Section;
|
||||
import jdk.internal.module.ConfigurableModuleFinder;
|
||||
import jdk.internal.perf.PerfCounter;
|
||||
|
||||
@ -294,11 +296,11 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
|
||||
// -- jmod files --
|
||||
|
||||
private Set<String> jmodPackages(ZipFile zf) {
|
||||
return zf.stream()
|
||||
.filter(e -> e.getName().startsWith("classes/") &&
|
||||
e.getName().endsWith(".class"))
|
||||
.map(e -> toPackageName(e.getName().substring(8)))
|
||||
private Set<String> jmodPackages(JmodFile jf) {
|
||||
return jf.stream()
|
||||
.filter(e -> e.section() == Section.CLASSES)
|
||||
.map(JmodFile.Entry::name)
|
||||
.map(this::toPackageName)
|
||||
.filter(pkg -> pkg.length() > 0) // module-info
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
@ -311,14 +313,10 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
* @throws InvalidModuleDescriptorException
|
||||
*/
|
||||
private ModuleReference readJMod(Path file) throws IOException {
|
||||
try (ZipFile zf = new ZipFile(file.toString())) {
|
||||
ZipEntry ze = zf.getEntry("classes/" + MODULE_INFO);
|
||||
if (ze == null) {
|
||||
throw new IOException(MODULE_INFO + " is missing: " + file);
|
||||
}
|
||||
try (JmodFile jf = new JmodFile(file)) {
|
||||
ModuleDescriptor md;
|
||||
try (InputStream in = zf.getInputStream(ze)) {
|
||||
md = ModuleDescriptor.read(in, () -> jmodPackages(zf));
|
||||
try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
|
||||
md = ModuleDescriptor.read(in, () -> jmodPackages(jf));
|
||||
}
|
||||
return ModuleReferences.newJModModule(md, file);
|
||||
}
|
||||
|
206
jdk/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java
Normal file
206
jdk/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.jmod;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Helper class to read JMOD file
|
||||
*/
|
||||
public class JmodFile implements AutoCloseable {
|
||||
// jmod magic number and version number
|
||||
public static final int JMOD_MAJOR_VERSION = 0x01;
|
||||
public static final int JMOD_MINOR_VERSION = 0x00;
|
||||
public static final byte[] JMOD_MAGIC_NUMBER = {
|
||||
0x4A, 0x4D, /* JM */
|
||||
JMOD_MAJOR_VERSION, JMOD_MINOR_VERSION, /* version 1.0 */
|
||||
};
|
||||
|
||||
public static void checkMagic(Path file) throws IOException {
|
||||
try (InputStream in = Files.newInputStream(file);
|
||||
BufferedInputStream bis = new BufferedInputStream(in)) {
|
||||
// validate the header
|
||||
byte[] magic = new byte[4];
|
||||
bis.read(magic);
|
||||
if (magic[0] != JMOD_MAGIC_NUMBER[0] ||
|
||||
magic[1] != JMOD_MAGIC_NUMBER[1]) {
|
||||
throw new IOException("Invalid jmod file: " + file.toString());
|
||||
}
|
||||
if (magic[2] > JMOD_MAJOR_VERSION ||
|
||||
(magic[2] == JMOD_MAJOR_VERSION && magic[3] > JMOD_MINOR_VERSION)) {
|
||||
throw new IOException("Unsupported jmod version: " +
|
||||
magic[2] + "." + magic[3] + " in " + file.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JMOD sections
|
||||
*/
|
||||
public static enum Section {
|
||||
NATIVE_LIBS("native"),
|
||||
NATIVE_CMDS("bin"),
|
||||
CLASSES("classes"),
|
||||
CONFIG("conf");
|
||||
|
||||
private final String jmodDir;
|
||||
private Section(String jmodDir) {
|
||||
this.jmodDir = jmodDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory name in the JMOD file corresponding to
|
||||
* this section
|
||||
*/
|
||||
public String jmodDir() { return jmodDir; }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* JMOD file entry.
|
||||
*
|
||||
* Each entry corresponds to a ZipEntry whose name is:
|
||||
* Section::jmodDir + '/' + name
|
||||
*/
|
||||
public static class Entry {
|
||||
private final ZipEntry zipEntry;
|
||||
private final Section section;
|
||||
private final String name;
|
||||
|
||||
private Entry(ZipEntry e) {
|
||||
String name = e.getName();
|
||||
int i = name.indexOf('/');
|
||||
if (i <= 1) {
|
||||
throw new RuntimeException("invalid jmod entry: " + name);
|
||||
}
|
||||
|
||||
this.zipEntry = e;
|
||||
this.section = section(name);
|
||||
this.name = name.substring(i+1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the section of this entry.
|
||||
*/
|
||||
public Section section() {
|
||||
return section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this entry.
|
||||
*/
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of this entry.
|
||||
*/
|
||||
public long size() {
|
||||
return zipEntry.getSize();
|
||||
}
|
||||
|
||||
public ZipEntry zipEntry() {
|
||||
return zipEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return section.jmodDir() + "/" + name;
|
||||
}
|
||||
|
||||
static Section section(String name) {
|
||||
int i = name.indexOf('/');
|
||||
String s = name.substring(0, i);
|
||||
switch (s) {
|
||||
case "native":
|
||||
return Section.NATIVE_LIBS;
|
||||
case "bin":
|
||||
return Section.NATIVE_CMDS;
|
||||
case "classes":
|
||||
return Section.CLASSES;
|
||||
case "conf":
|
||||
return Section.CONFIG;
|
||||
default:
|
||||
throw new IllegalArgumentException("invalid section: " + s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Path file;
|
||||
private final ZipFile zipfile;
|
||||
|
||||
/**
|
||||
* Constructs a {@code JmodFile} from a given path.
|
||||
*/
|
||||
public JmodFile(Path file) throws IOException {
|
||||
checkMagic(file);
|
||||
this.file = file;
|
||||
this.zipfile = new ZipFile(file.toFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an {@code InputStream} for reading the named entry of the given
|
||||
* section in this jmod file.
|
||||
*
|
||||
* @throws IOException if the named entry is not found, or I/O error
|
||||
* occurs when reading it
|
||||
*/
|
||||
public InputStream getInputStream(Section section, String name)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
String entry = section.jmodDir() + "/" + name;
|
||||
ZipEntry e = zipfile.getEntry(entry);
|
||||
if (e == null) {
|
||||
throw new IOException(name + " not found: " + file);
|
||||
}
|
||||
return zipfile.getInputStream(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream of non-directory entries in this jmod file.
|
||||
*/
|
||||
public Stream<Entry> stream() {
|
||||
return zipfile.stream()
|
||||
.filter(e -> !e.isDirectory())
|
||||
.map(Entry::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (zipfile != null) {
|
||||
zipfile.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -163,6 +163,16 @@ public final class ModuleInfoExtender {
|
||||
* be discarded.
|
||||
*/
|
||||
public void write(OutputStream out) throws IOException {
|
||||
// emit to the output stream
|
||||
out.write(toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bytes of the modified module-info.class.
|
||||
* Once this method has been called then the Extender object should
|
||||
* be discarded.
|
||||
*/
|
||||
public byte[] toByteArray() throws IOException {
|
||||
ClassWriter cw
|
||||
= new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
|
||||
@ -197,8 +207,7 @@ public final class ModuleInfoExtender {
|
||||
// add any attributes that didn't replace previous attributes
|
||||
cv.finish();
|
||||
|
||||
// emit to the output stream
|
||||
out.write(cw.toByteArray());
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,6 +124,9 @@ module java.base {
|
||||
jdk.jlink;
|
||||
exports jdk.internal.jimage.decompressor to
|
||||
jdk.jlink;
|
||||
exports jdk.internal.jmod to
|
||||
jdk.compiler,
|
||||
jdk.jlink;
|
||||
exports jdk.internal.logger to
|
||||
java.logging;
|
||||
exports jdk.internal.org.objectweb.asm to
|
||||
|
@ -55,6 +55,13 @@ public interface Archive {
|
||||
private final Archive archive;
|
||||
private final String path;
|
||||
|
||||
/**
|
||||
* Constructs an entry of the given archive
|
||||
* @param archive archive
|
||||
* @param path
|
||||
* @param name an entry name that does not contain the module name
|
||||
* @param type
|
||||
*/
|
||||
public Entry(Archive archive, String path, String name, EntryType type) {
|
||||
this.archive = Objects.requireNonNull(archive);
|
||||
this.path = Objects.requireNonNull(path);
|
||||
@ -62,25 +69,29 @@ public interface Archive {
|
||||
this.type = Objects.requireNonNull(type);
|
||||
}
|
||||
|
||||
public Archive archive() {
|
||||
public final Archive archive() {
|
||||
return archive;
|
||||
}
|
||||
|
||||
public String path() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public EntryType type() {
|
||||
public final EntryType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Returns the name of this entry.
|
||||
*/
|
||||
public String name() {
|
||||
public final String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name representing a ResourcePoolEntry in the form of:
|
||||
* /$MODULE/$ENTRY_NAME
|
||||
*/
|
||||
public final String getResourcePoolEntryName() {
|
||||
return "/" + archive.moduleName() + "/" + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "type " + type.name() + " path " + path;
|
||||
|
@ -50,7 +50,7 @@ public class DirArchive implements Archive {
|
||||
|
||||
FileEntry(Path path, String name) {
|
||||
super(DirArchive.this, getPathName(path), name,
|
||||
Archive.Entry.EntryType.CLASS_OR_RESOURCE);
|
||||
Archive.Entry.EntryType.CLASS_OR_RESOURCE);
|
||||
this.path = path;
|
||||
try {
|
||||
size = Files.size(path);
|
||||
@ -124,13 +124,7 @@ public class DirArchive implements Archive {
|
||||
return null;
|
||||
}
|
||||
String name = getPathName(p).substring(chop);
|
||||
if (name.startsWith("_")) {
|
||||
return null;
|
||||
}
|
||||
log.accept(moduleName + "/" + name);
|
||||
if (name.equals(MODULE_INFO)) {
|
||||
name = moduleName + "/" + MODULE_INFO;
|
||||
}
|
||||
return new FileEntry(p, name);
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.tools.jlink.internal.Archive.Entry;
|
||||
import jdk.tools.jlink.internal.Archive.Entry.EntryType;
|
||||
import jdk.tools.jlink.internal.ResourcePoolManager.CompressedModuleData;
|
||||
@ -122,10 +123,6 @@ public final class ImageFileCreator {
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isClassPackage(String path) {
|
||||
return path.endsWith(".class") && !path.endsWith("module-info.class");
|
||||
}
|
||||
|
||||
public static void recreateJimage(Path jimageFile,
|
||||
Set<Archive> archives,
|
||||
ImagePluginStack pluginSupport)
|
||||
@ -265,26 +262,13 @@ public final class ImageFileCreator {
|
||||
return writer.getString(id);
|
||||
}
|
||||
});
|
||||
|
||||
for (Archive archive : archives) {
|
||||
String mn = archive.moduleName();
|
||||
for (Entry entry : entriesForModule.get(mn)) {
|
||||
String path;
|
||||
if (entry.type() == EntryType.CLASS_OR_RESOURCE) {
|
||||
// Removal of "classes/" radical.
|
||||
path = entry.name();
|
||||
if (path.endsWith("module-info.class")) {
|
||||
path = "/" + path;
|
||||
} else {
|
||||
path = "/" + mn + "/" + path;
|
||||
}
|
||||
} else {
|
||||
// Entry.path() contains the kind of file native, conf, bin, ...
|
||||
// Keep it to avoid naming conflict (eg: native/jvm.cfg and config/jvm.cfg
|
||||
path = "/" + mn + "/" + entry.path();
|
||||
}
|
||||
|
||||
resources.add(new ArchiveEntryResourcePoolEntry(mn, path, entry));
|
||||
}
|
||||
entriesForModule.get(mn).stream()
|
||||
.map(e -> new ArchiveEntryResourcePoolEntry(mn,
|
||||
e.getResourcePoolEntryName(), e))
|
||||
.forEach(resources::add);
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
@ -320,6 +304,20 @@ public final class ImageFileCreator {
|
||||
return result.toArray(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of the resource.
|
||||
*/
|
||||
public static String resourceName(String path) {
|
||||
Objects.requireNonNull(path);
|
||||
String s = path.substring(1);
|
||||
int index = s.indexOf("/");
|
||||
return s.substring(index + 1);
|
||||
}
|
||||
|
||||
public static String toPackage(String name) {
|
||||
return toPackage(name, false);
|
||||
}
|
||||
|
||||
private static String toPackage(String name, boolean log) {
|
||||
int index = name.lastIndexOf('/');
|
||||
if (index > 0) {
|
||||
|
@ -43,7 +43,7 @@ public abstract class JarArchive implements Archive {
|
||||
/**
|
||||
* An entry located in a jar file.
|
||||
*/
|
||||
private class JarEntry extends Entry {
|
||||
public class JarEntry extends Entry {
|
||||
|
||||
private final long size;
|
||||
private final ZipEntry entry;
|
||||
@ -70,12 +70,10 @@ public abstract class JarArchive implements Archive {
|
||||
}
|
||||
}
|
||||
|
||||
private static final String MODULE_INFO = "module-info.class";
|
||||
|
||||
private final Path file;
|
||||
private final String moduleName;
|
||||
// currently processed ZipFile
|
||||
private ZipFile zipFile;
|
||||
protected ZipFile zipFile;
|
||||
|
||||
protected JarArchive(String mn, Path file) {
|
||||
Objects.requireNonNull(mn);
|
||||
@ -110,21 +108,7 @@ public abstract class JarArchive implements Archive {
|
||||
|
||||
abstract String getFileName(String entryName);
|
||||
|
||||
private Entry toEntry(ZipEntry ze) {
|
||||
String name = ze.getName();
|
||||
String fn = getFileName(name);
|
||||
|
||||
if (ze.isDirectory() || fn.startsWith("_")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
EntryType rt = toEntryType(name);
|
||||
|
||||
if (fn.equals(MODULE_INFO)) {
|
||||
fn = moduleName + "/" + MODULE_INFO;
|
||||
}
|
||||
return new JarEntry(ze.getName(), fn, rt, zipFile, ze);
|
||||
}
|
||||
abstract Entry toEntry(ZipEntry ze);
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
|
@ -25,34 +25,106 @@
|
||||
|
||||
package jdk.tools.jlink.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.jmod.JmodFile;
|
||||
import jdk.tools.jlink.internal.Archive.Entry.EntryType;
|
||||
|
||||
/**
|
||||
* An Archive backed by a jmod file.
|
||||
*/
|
||||
public class JmodArchive extends JarArchive {
|
||||
|
||||
public class JmodArchive implements Archive {
|
||||
private static final String JMOD_EXT = ".jmod";
|
||||
private static final String MODULE_NAME = "module";
|
||||
private static final String MODULE_INFO = "module-info.class";
|
||||
private static final String CLASSES = "classes";
|
||||
private static final String NATIVE_LIBS = "native";
|
||||
private static final String NATIVE_CMDS = "bin";
|
||||
private static final String CONFIG = "conf";
|
||||
|
||||
public JmodArchive(String mn, Path jmod) {
|
||||
super(mn, jmod);
|
||||
String filename = Objects.requireNonNull(jmod.getFileName()).toString();
|
||||
if (!filename.endsWith(JMOD_EXT)) {
|
||||
throw new UnsupportedOperationException("Unsupported format: " + filename);
|
||||
/**
|
||||
* An entry located in a jmod file.
|
||||
*/
|
||||
public class JmodEntry extends Entry {
|
||||
private final JmodFile.Entry entry;
|
||||
|
||||
JmodEntry(String path, String name, EntryType type,
|
||||
JmodFile.Entry entry) {
|
||||
super(JmodArchive.this, path, name, type);
|
||||
this.entry = Objects.requireNonNull(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of uncompressed bytes for this entry.
|
||||
*/
|
||||
@Override
|
||||
public long size() {
|
||||
return entry.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream stream() throws IOException {
|
||||
return jmodFile.getInputStream(entry.section(), entry.name());
|
||||
}
|
||||
}
|
||||
|
||||
private final Path file;
|
||||
private final String moduleName;
|
||||
private JmodFile jmodFile;
|
||||
|
||||
public JmodArchive(String mn, Path jmod) {
|
||||
Objects.requireNonNull(mn);
|
||||
Objects.requireNonNull(jmod.getFileName());
|
||||
String filename = jmod.toString();
|
||||
if (!filename.endsWith(JMOD_EXT)) {
|
||||
throw new UnsupportedOperationException("Unsupported format: " + filename);
|
||||
}
|
||||
this.moduleName = mn;
|
||||
this.file = jmod;
|
||||
}
|
||||
|
||||
@Override
|
||||
EntryType toEntryType(String entryName) {
|
||||
String section = getSection(entryName.replace('\\', '/'));
|
||||
public String moduleName() {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPath() {
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Entry> entries() {
|
||||
ensureOpen();
|
||||
return jmodFile.stream()
|
||||
.map(this::toEntry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
if (jmodFile != null) {
|
||||
jmodFile.close();
|
||||
}
|
||||
this.jmodFile = new JmodFile(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (jmodFile != null) {
|
||||
jmodFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureOpen() {
|
||||
if (jmodFile == null) {
|
||||
try {
|
||||
open();
|
||||
} catch(IOException ioe){
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EntryType toEntryType(JmodFile.Section section) {
|
||||
switch (section) {
|
||||
case CLASSES:
|
||||
return EntryType.CLASS_OR_RESOURCE;
|
||||
@ -62,26 +134,23 @@ public class JmodArchive extends JarArchive {
|
||||
return EntryType.NATIVE_CMD;
|
||||
case CONFIG:
|
||||
return EntryType.CONFIG;
|
||||
case MODULE_NAME:
|
||||
return EntryType.MODULE_NAME;
|
||||
default:
|
||||
throw new InternalError("unexpected entry: " + section);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getSection(String entryName) {
|
||||
int i = entryName.indexOf('/');
|
||||
// Unnamed section.
|
||||
String section = "";
|
||||
if (i > 0) {
|
||||
section = entryName.substring(0, entryName.indexOf('/'));
|
||||
}
|
||||
return section;
|
||||
}
|
||||
private Entry toEntry(JmodFile.Entry entry) {
|
||||
EntryType type = toEntryType(entry.section());
|
||||
String name = entry.name();
|
||||
String path = entry.section().jmodDir() + "/" + name;
|
||||
|
||||
@Override
|
||||
String getFileName(String entryName) {
|
||||
entryName = entryName.replace('\\', '/');
|
||||
return entryName.substring(entryName.indexOf('/') + 1);
|
||||
// Entry.path() contains the kind of file native, conf, bin, ...
|
||||
// Keep it to avoid naming conflict (eg: native/jvm.cfg and config/jvm.cfg
|
||||
String resourceName = name;
|
||||
if (type != EntryType.CLASS_OR_RESOURCE) {
|
||||
resourceName = path;
|
||||
}
|
||||
|
||||
return new JmodEntry(path, resourceName, type, entry);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ package jdk.tools.jlink.internal;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import jdk.tools.jlink.internal.Archive.Entry.EntryType;
|
||||
|
||||
/**
|
||||
@ -35,6 +37,7 @@ import jdk.tools.jlink.internal.Archive.Entry.EntryType;
|
||||
public class ModularJarArchive extends JarArchive {
|
||||
|
||||
private static final String JAR_EXT = ".jar";
|
||||
private static final String MODULE_INFO = "module-info.class";
|
||||
|
||||
public ModularJarArchive(String mn, Path jmod) {
|
||||
super(mn, jmod);
|
||||
@ -49,6 +52,17 @@ public class ModularJarArchive extends JarArchive {
|
||||
return EntryType.CLASS_OR_RESOURCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
Entry toEntry(ZipEntry ze) {
|
||||
if (ze.isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String name = ze.getName();
|
||||
EntryType type = toEntryType(name);
|
||||
return new JarEntry(ze.getName(), getFileName(name), type, zipFile, ze);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getFileName(String entryName) {
|
||||
return entryName;
|
||||
|
@ -27,15 +27,12 @@ package jdk.tools.jlink.internal;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.jimage.decompressor.CompressedResourceHeader;
|
||||
import jdk.tools.jlink.plugin.ResourcePool;
|
||||
@ -44,7 +41,6 @@ import jdk.tools.jlink.plugin.ResourcePoolEntry;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolModule;
|
||||
import jdk.tools.jlink.plugin.ResourcePoolModuleView;
|
||||
import jdk.tools.jlink.plugin.PluginException;
|
||||
import jdk.tools.jlink.internal.plugins.FileCopierPlugin;
|
||||
|
||||
/**
|
||||
* A manager for pool of resources.
|
||||
@ -100,17 +96,17 @@ public class ResourcePoolManager {
|
||||
@Override
|
||||
public Set<String> packages() {
|
||||
Set<String> pkgs = new HashSet<>();
|
||||
moduleContent.values().stream().filter(m -> m.type().
|
||||
equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE)).forEach(res -> {
|
||||
// Module metadata only contains packages with .class files
|
||||
if (ImageFileCreator.isClassPackage(res.path())) {
|
||||
String[] split = ImageFileCreator.splitPath(res.path());
|
||||
String pkg = split[1];
|
||||
if (pkg != null && !pkg.isEmpty()) {
|
||||
pkgs.add(pkg);
|
||||
moduleContent.values().stream()
|
||||
.filter(m -> m.type() == ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
|
||||
.forEach(res -> {
|
||||
String name = ImageFileCreator.resourceName(res.path());
|
||||
if (name.endsWith(".class") && !name.endsWith("module-info.class")) {
|
||||
String pkg = ImageFileCreator.toPackage(name);
|
||||
if (!pkg.isEmpty()) {
|
||||
pkgs.add(pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return pkgs;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.tools.jmod;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static jdk.internal.jmod.JmodFile.*;
|
||||
|
||||
/**
|
||||
* Output stream to write to JMOD file
|
||||
*/
|
||||
class JmodOutputStream extends OutputStream implements AutoCloseable {
|
||||
/**
|
||||
* This method creates (or overrides, if exists) the JMOD file,
|
||||
* returning the the output stream to write to the JMOD file.
|
||||
*/
|
||||
static JmodOutputStream newOutputStream(Path file) throws IOException {
|
||||
OutputStream out = Files.newOutputStream(file);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(out);
|
||||
return new JmodOutputStream(bos);
|
||||
}
|
||||
|
||||
private final ZipOutputStream zos;
|
||||
private JmodOutputStream(OutputStream out) {
|
||||
this.zos = new ZipOutputStream(out);
|
||||
try {
|
||||
out.write(JMOD_MAGIC_NUMBER);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the input stream to the named entry of the given section.
|
||||
*/
|
||||
public void writeEntry(InputStream in, Section section, String name)
|
||||
throws IOException
|
||||
{
|
||||
ZipEntry ze = newEntry(section, name);
|
||||
zos.putNextEntry(ze);
|
||||
in.transferTo(zos);
|
||||
zos.closeEntry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given bytes to the named entry of the given section.
|
||||
*/
|
||||
public void writeEntry(byte[] bytes, Section section, String path)
|
||||
throws IOException
|
||||
{
|
||||
ZipEntry ze = newEntry(section, path);
|
||||
zos.putNextEntry(ze);
|
||||
zos.write(bytes);
|
||||
zos.closeEntry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given entry to the given input stream.
|
||||
*/
|
||||
public void writeEntry(InputStream in, Entry e) throws IOException {
|
||||
zos.putNextEntry(e.zipEntry());
|
||||
zos.write(in.readAllBytes());
|
||||
zos.closeEntry();
|
||||
}
|
||||
|
||||
private ZipEntry newEntry(Section section, String path) {
|
||||
String prefix = section.jmodDir();
|
||||
String name = Paths.get(prefix, path).toString()
|
||||
.replace(File.separatorChar, '/');
|
||||
return new ZipEntry(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
zos.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
zos.close();
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,6 @@
|
||||
|
||||
package jdk.tools.jmod;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@ -60,7 +58,6 @@ import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@ -80,15 +77,16 @@ import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import jdk.internal.jmod.JmodFile;
|
||||
import jdk.internal.jmod.JmodFile.Section;
|
||||
import jdk.internal.joptsimple.BuiltinHelpFormatter;
|
||||
import jdk.internal.joptsimple.NonOptionArgumentSpec;
|
||||
import jdk.internal.joptsimple.OptionDescriptor;
|
||||
@ -250,23 +248,14 @@ public class JmodTask {
|
||||
}
|
||||
|
||||
private boolean describe() throws IOException {
|
||||
ZipFile zip = null;
|
||||
try {
|
||||
try {
|
||||
zip = new ZipFile(options.jmodFile.toFile());
|
||||
} catch (IOException x) {
|
||||
throw new IOException("error opening jmod file", x);
|
||||
try (JmodFile jf = new JmodFile(options.jmodFile)) {
|
||||
try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
|
||||
ModuleDescriptor md = ModuleDescriptor.read(in);
|
||||
printModuleDescriptor(md);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
throw new CommandException("err.module.descriptor.not.found");
|
||||
}
|
||||
|
||||
try (InputStream in = Files.newInputStream(options.jmodFile)) {
|
||||
boolean found = printModuleDescriptor(in);
|
||||
if (!found)
|
||||
throw new CommandException("err.module.descriptor.not.found");
|
||||
return found;
|
||||
}
|
||||
} finally {
|
||||
if (zip != null)
|
||||
zip.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,65 +267,52 @@ public class JmodTask {
|
||||
|
||||
private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
|
||||
|
||||
private boolean printModuleDescriptor(InputStream in)
|
||||
private void printModuleDescriptor(ModuleDescriptor md)
|
||||
throws IOException
|
||||
{
|
||||
final String mi = Section.CLASSES.jmodDir() + "/" + MODULE_INFO;
|
||||
try (BufferedInputStream bis = new BufferedInputStream(in);
|
||||
ZipInputStream zis = new ZipInputStream(bis)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\n").append(md.toNameAndVersion());
|
||||
|
||||
ZipEntry e;
|
||||
while ((e = zis.getNextEntry()) != null) {
|
||||
if (e.getName().equals(mi)) {
|
||||
ModuleDescriptor md = ModuleDescriptor.read(zis);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\n").append(md.toNameAndVersion());
|
||||
md.requires().stream()
|
||||
.sorted(Comparator.comparing(Requires::name))
|
||||
.forEach(r -> {
|
||||
sb.append("\n requires ");
|
||||
if (!r.modifiers().isEmpty())
|
||||
sb.append(toString(r.modifiers())).append(" ");
|
||||
sb.append(r.name());
|
||||
});
|
||||
|
||||
md.requires().stream()
|
||||
.sorted(Comparator.comparing(Requires::name))
|
||||
.forEach(r -> {
|
||||
sb.append("\n requires ");
|
||||
if (!r.modifiers().isEmpty())
|
||||
sb.append(toString(r.modifiers())).append(" ");
|
||||
sb.append(r.name());
|
||||
});
|
||||
md.uses().stream().sorted()
|
||||
.forEach(s -> sb.append("\n uses ").append(s));
|
||||
|
||||
md.uses().stream().sorted()
|
||||
.forEach(s -> sb.append("\n uses ").append(s));
|
||||
md.exports().stream()
|
||||
.sorted(Comparator.comparing(Exports::source))
|
||||
.forEach(p -> sb.append("\n exports ").append(p));
|
||||
|
||||
md.exports().stream()
|
||||
.sorted(Comparator.comparing(Exports::source))
|
||||
.forEach(p -> sb.append("\n exports ").append(p));
|
||||
md.conceals().stream().sorted()
|
||||
.forEach(p -> sb.append("\n conceals ").append(p));
|
||||
|
||||
md.conceals().stream().sorted()
|
||||
.forEach(p -> sb.append("\n conceals ").append(p));
|
||||
md.provides().values().stream()
|
||||
.sorted(Comparator.comparing(Provides::service))
|
||||
.forEach(p -> sb.append("\n provides ").append(p.service())
|
||||
.append(" with ")
|
||||
.append(toString(p.providers())));
|
||||
|
||||
md.provides().values().stream()
|
||||
.sorted(Comparator.comparing(Provides::service))
|
||||
.forEach(p -> sb.append("\n provides ").append(p.service())
|
||||
.append(" with ")
|
||||
.append(toString(p.providers())));
|
||||
md.mainClass().ifPresent(v -> sb.append("\n main-class " + v));
|
||||
|
||||
md.mainClass().ifPresent(v -> sb.append("\n main-class " + v));
|
||||
md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v));
|
||||
|
||||
md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v));
|
||||
md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v));
|
||||
|
||||
md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v));
|
||||
md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v));
|
||||
|
||||
md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v));
|
||||
JLMA.hashes(md).ifPresent(
|
||||
hashes -> hashes.names().stream().sorted().forEach(
|
||||
mod -> sb.append("\n hashes ").append(mod).append(" ")
|
||||
.append(hashes.algorithm()).append(" ")
|
||||
.append(hashes.hashFor(mod))));
|
||||
|
||||
JLMA.hashes(md).ifPresent(
|
||||
hashes -> hashes.names().stream().sorted().forEach(
|
||||
mod -> sb.append("\n hashes ").append(mod).append(" ")
|
||||
.append(hashes.algorithm()).append(" ")
|
||||
.append(hashes.hashFor(mod))));
|
||||
|
||||
out.println(sb.toString());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
out.println(sb.toString());
|
||||
}
|
||||
|
||||
private boolean create() throws IOException {
|
||||
@ -347,9 +323,8 @@ public class JmodTask {
|
||||
Path target = options.jmodFile;
|
||||
Path tempTarget = target.resolveSibling(target.getFileName() + ".tmp");
|
||||
try {
|
||||
try (OutputStream out = Files.newOutputStream(tempTarget);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(out)) {
|
||||
jmod.write(bos);
|
||||
try (JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget)) {
|
||||
jmod.write(jos);
|
||||
}
|
||||
Files.move(tempTarget, target);
|
||||
} catch (Exception e) {
|
||||
@ -383,19 +358,16 @@ public class JmodTask {
|
||||
/**
|
||||
* Writes the jmod to the given output stream.
|
||||
*/
|
||||
void write(OutputStream out) throws IOException {
|
||||
try (ZipOutputStream zos = new ZipOutputStream(out)) {
|
||||
void write(JmodOutputStream out) throws IOException {
|
||||
// module-info.class
|
||||
writeModuleInfo(out, findPackages(classpath));
|
||||
|
||||
// module-info.class
|
||||
writeModuleInfo(zos, findPackages(classpath));
|
||||
// classes
|
||||
processClasses(out, classpath);
|
||||
|
||||
// classes
|
||||
processClasses(zos, classpath);
|
||||
|
||||
processSection(zos, Section.NATIVE_CMDS, cmds);
|
||||
processSection(zos, Section.NATIVE_LIBS, libs);
|
||||
processSection(zos, Section.CONFIG, configs);
|
||||
}
|
||||
processSection(out, Section.NATIVE_CMDS, cmds);
|
||||
processSection(out, Section.NATIVE_LIBS, libs);
|
||||
processSection(out, Section.CONFIG, configs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -441,7 +413,7 @@ public class JmodTask {
|
||||
* then the corresponding class file attributes are added to the
|
||||
* module-info here.
|
||||
*/
|
||||
void writeModuleInfo(ZipOutputStream zos, Set<String> packages)
|
||||
void writeModuleInfo(JmodOutputStream out, Set<String> packages)
|
||||
throws IOException
|
||||
{
|
||||
Supplier<InputStream> miSupplier = newModuleInfoSupplier();
|
||||
@ -492,11 +464,7 @@ public class JmodTask {
|
||||
}
|
||||
|
||||
// write the (possibly extended or modified) module-info.class
|
||||
String e = Section.CLASSES.jmodDir() + "/" + MODULE_INFO;
|
||||
ZipEntry ze = new ZipEntry(e);
|
||||
zos.putNextEntry(ze);
|
||||
extender.write(zos);
|
||||
zos.closeEntry();
|
||||
out.writeEntry(extender.toByteArray(), Section.CLASSES, MODULE_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,7 +595,7 @@ public class JmodTask {
|
||||
return "";
|
||||
}
|
||||
|
||||
void processClasses(ZipOutputStream zos, List<Path> classpaths)
|
||||
void processClasses(JmodOutputStream zos, List<Path> classpaths)
|
||||
throws IOException
|
||||
{
|
||||
if (classpaths == null)
|
||||
@ -645,7 +613,7 @@ public class JmodTask {
|
||||
}
|
||||
}
|
||||
|
||||
void processSection(ZipOutputStream zos, Section section, List<Path> paths)
|
||||
void processSection(JmodOutputStream zos, Section section, List<Path> paths)
|
||||
throws IOException
|
||||
{
|
||||
if (paths == null)
|
||||
@ -655,11 +623,9 @@ public class JmodTask {
|
||||
processSection(zos, section, p);
|
||||
}
|
||||
|
||||
void processSection(ZipOutputStream zos, Section section, Path top)
|
||||
void processSection(JmodOutputStream out, Section section, Path top)
|
||||
throws IOException
|
||||
{
|
||||
final String prefix = section.jmodDir();
|
||||
|
||||
Files.walkFileTree(top, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||
@ -667,13 +633,19 @@ public class JmodTask {
|
||||
{
|
||||
Path relPath = top.relativize(file);
|
||||
if (relPath.toString().equals(MODULE_INFO)
|
||||
&& !Section.CLASSES.equals(section))
|
||||
&& !Section.CLASSES.equals(section))
|
||||
warning("warn.ignore.entry", MODULE_INFO, section);
|
||||
|
||||
if (!relPath.toString().equals(MODULE_INFO)
|
||||
&& !matches(relPath, excludes)) {
|
||||
&& !matches(relPath, excludes)) {
|
||||
try (InputStream in = Files.newInputStream(file)) {
|
||||
writeZipEntry(zos, in, prefix, relPath.toString());
|
||||
out.writeEntry(in, section, relPath.toString());
|
||||
} catch (IOException x) {
|
||||
if (x.getMessage().contains("duplicate entry")) {
|
||||
warning("warn.ignore.duplicate.entry", relPath.toString(), section);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
@ -691,36 +663,17 @@ public class JmodTask {
|
||||
return false;
|
||||
}
|
||||
|
||||
void writeZipEntry(ZipOutputStream zos, InputStream in, String prefix, String other)
|
||||
throws IOException
|
||||
{
|
||||
String name = Paths.get(prefix, other).toString()
|
||||
.replace(File.separatorChar, '/');
|
||||
ZipEntry ze = new ZipEntry(name);
|
||||
try {
|
||||
zos.putNextEntry(ze);
|
||||
in.transferTo(zos);
|
||||
zos.closeEntry();
|
||||
} catch (ZipException x) {
|
||||
if (x.getMessage().contains("duplicate entry")) {
|
||||
warning("warn.ignore.duplicate.entry", name, prefix);
|
||||
return;
|
||||
}
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
|
||||
class JarEntryConsumer implements Consumer<JarEntry>, Predicate<JarEntry> {
|
||||
final ZipOutputStream zos;
|
||||
final JmodOutputStream out;
|
||||
final JarFile jarfile;
|
||||
JarEntryConsumer(ZipOutputStream zos, JarFile jarfile) {
|
||||
this.zos = zos;
|
||||
JarEntryConsumer(JmodOutputStream out, JarFile jarfile) {
|
||||
this.out = out;
|
||||
this.jarfile = jarfile;
|
||||
}
|
||||
@Override
|
||||
public void accept(JarEntry je) {
|
||||
try (InputStream in = jarfile.getInputStream(je)) {
|
||||
writeZipEntry(zos, in, Section.CLASSES.jmodDir(), je.getName());
|
||||
out.writeEntry(in, Section.CLASSES, je.getName());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
@ -947,29 +900,11 @@ public class JmodTask {
|
||||
{
|
||||
Path target = moduleNameToPath.get(name);
|
||||
Path tempTarget = target.resolveSibling(target.getFileName() + ".tmp");
|
||||
ZipFile zip = new ZipFile(target.toFile());
|
||||
try {
|
||||
try (OutputStream out = Files.newOutputStream(tempTarget);
|
||||
ZipOutputStream zos = new ZipOutputStream(out)) {
|
||||
zip.stream().forEach(e -> {
|
||||
try {
|
||||
InputStream in = zip.getInputStream(e);
|
||||
if (e.getName().equals(MODULE_INFO) ||
|
||||
e.getName().equals(Section.CLASSES.jmodDir() + "/" + MODULE_INFO)) {
|
||||
ZipEntry ze = new ZipEntry(e.getName());
|
||||
ze.setTime(System.currentTimeMillis());
|
||||
zos.putNextEntry(ze);
|
||||
recordHashes(in, zos, moduleHashes);
|
||||
zos.closeEntry();
|
||||
} else {
|
||||
zos.putNextEntry(e);
|
||||
zos.write(in.readAllBytes());
|
||||
zos.closeEntry();
|
||||
}
|
||||
} catch (IOException x) {
|
||||
throw new UncheckedIOException(x);
|
||||
}
|
||||
});
|
||||
if (target.getFileName().toString().endsWith(".jmod")) {
|
||||
updateJmodFile(target, tempTarget, moduleHashes);
|
||||
} else {
|
||||
updateModularJar(target, tempTarget, moduleHashes);
|
||||
}
|
||||
} catch (IOException|RuntimeException e) {
|
||||
if (Files.exists(tempTarget)) {
|
||||
@ -980,13 +915,67 @@ public class JmodTask {
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
} finally {
|
||||
zip.close();
|
||||
}
|
||||
|
||||
out.println(getMessage("module.hashes.recorded", name));
|
||||
Files.move(tempTarget, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
private void updateModularJar(Path target, Path tempTarget,
|
||||
ModuleHashes moduleHashes)
|
||||
throws IOException
|
||||
{
|
||||
try (JarFile jf = new JarFile(target.toFile());
|
||||
OutputStream out = Files.newOutputStream(tempTarget);
|
||||
JarOutputStream jos = new JarOutputStream(out))
|
||||
{
|
||||
jf.stream().forEach(e -> {
|
||||
try (InputStream in = jf.getInputStream(e)) {
|
||||
if (e.getName().equals(MODULE_INFO)) {
|
||||
// what about module-info.class in versioned entries?
|
||||
ZipEntry ze = new ZipEntry(e.getName());
|
||||
ze.setTime(System.currentTimeMillis());
|
||||
jos.putNextEntry(ze);
|
||||
recordHashes(in, jos, moduleHashes);
|
||||
jos.closeEntry();
|
||||
} else {
|
||||
jos.putNextEntry(e);
|
||||
jos.write(in.readAllBytes());
|
||||
jos.closeEntry();
|
||||
}
|
||||
} catch (IOException x) {
|
||||
throw new UncheckedIOException(x);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateJmodFile(Path target, Path tempTarget,
|
||||
ModuleHashes moduleHashes)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
try (JmodFile jf = new JmodFile(target);
|
||||
JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget))
|
||||
{
|
||||
jf.stream().forEach(e -> {
|
||||
try (InputStream in = jf.getInputStream(e.section(), e.name())) {
|
||||
if (e.name().equals(MODULE_INFO)) {
|
||||
// replace module-info.class
|
||||
ModuleInfoExtender extender =
|
||||
ModuleInfoExtender.newExtender(in);
|
||||
extender.hashes(moduleHashes);
|
||||
jos.writeEntry(extender.toByteArray(), e.section(), e.name());
|
||||
} else {
|
||||
jos.writeEntry(in, e);
|
||||
}
|
||||
} catch (IOException x) {
|
||||
throw new UncheckedIOException(x);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private Path moduleToPath(String name) {
|
||||
ModuleReference mref = moduleFinder.find(name).orElseThrow(
|
||||
() -> new InternalError("Selected module " + name + " not on module path"));
|
||||
@ -1001,22 +990,6 @@ public class JmodTask {
|
||||
}
|
||||
}
|
||||
|
||||
enum Section {
|
||||
NATIVE_LIBS("native"),
|
||||
NATIVE_CMDS("bin"),
|
||||
CLASSES("classes"),
|
||||
CONFIG("conf"),
|
||||
UNKNOWN("unknown");
|
||||
|
||||
private final String jmodDir;
|
||||
|
||||
Section(String jmodDir) {
|
||||
this.jmodDir = jmodDir;
|
||||
}
|
||||
|
||||
String jmodDir() { return jmodDir; }
|
||||
}
|
||||
|
||||
static class ClassPathConverter implements ValueConverter<Path> {
|
||||
static final ValueConverter<Path> INSTANCE = new ClassPathConverter();
|
||||
|
||||
|
@ -191,7 +191,7 @@ public class JLinkNegativeTest {
|
||||
.output(imageFile)
|
||||
.addMods("not_zip")
|
||||
.modulePath(helper.defaultModulePath())
|
||||
.call().assertFailure("Error: java.util.zip.ZipException: zip file is empty");
|
||||
.call().assertFailure("Error: java.io.IOException: Invalid jmod file");
|
||||
} finally {
|
||||
deleteDirectory(jmod);
|
||||
}
|
||||
@ -236,13 +236,10 @@ public class JLinkNegativeTest {
|
||||
JImageGenerator.addFiles(module, new InMemoryFile("unknown/A.class", new byte[0]));
|
||||
try {
|
||||
Result result = helper.generateDefaultImage(moduleName);
|
||||
if (result.getExitCode() != 4) {
|
||||
System.err.println(result.getMessage());
|
||||
if (result.getExitCode() == 0) {
|
||||
throw new AssertionError("Crash expected");
|
||||
}
|
||||
if (!result.getMessage().contains("java.lang.InternalError: unexpected entry: unknown")) {
|
||||
System.err.println(result.getMessage());
|
||||
throw new AssertionError("InternalError expected");
|
||||
}
|
||||
} finally {
|
||||
deleteDirectory(module);
|
||||
}
|
||||
@ -250,7 +247,7 @@ public class JLinkNegativeTest {
|
||||
|
||||
@Test(enabled = true)
|
||||
public void testSectionsAreFiles() throws IOException {
|
||||
String moduleName = "module";
|
||||
String moduleName = "hacked4";
|
||||
Path jmod = helper.generateDefaultJModule(moduleName).assertSuccess();
|
||||
JImageGenerator.addFiles(jmod,
|
||||
new InMemoryFile("/native", new byte[0]),
|
||||
@ -258,13 +255,10 @@ public class JLinkNegativeTest {
|
||||
new InMemoryFile("/bin", new byte[0]));
|
||||
try {
|
||||
Result result = helper.generateDefaultImage(moduleName);
|
||||
if (result.getExitCode() != 4) {
|
||||
System.err.println(result.getMessage());
|
||||
if (result.getExitCode() == 0) {
|
||||
throw new AssertionError("Crash expected");
|
||||
}
|
||||
if (!result.getMessage().contains("java.lang.InternalError: unexpected entry: ")) {
|
||||
System.err.println(result.getMessage());
|
||||
throw new AssertionError("InternalError expected");
|
||||
}
|
||||
} finally {
|
||||
deleteDirectory(jmod);
|
||||
}
|
||||
|
@ -121,15 +121,6 @@ public class JLinkTest {
|
||||
.call().assertFailure("Error: no value given for --module-path");
|
||||
}
|
||||
|
||||
{
|
||||
String moduleName = "filter";
|
||||
Path jmod = helper.generateDefaultJModule(moduleName).assertSuccess();
|
||||
String className = "_A.class";
|
||||
JImageGenerator.addFiles(jmod, new InMemoryFile(className, new byte[0]));
|
||||
Path image = helper.generateDefaultImage(moduleName).assertSuccess();
|
||||
helper.checkImage(image, moduleName, new String[] {"/" + moduleName + "/" + className}, null);
|
||||
}
|
||||
|
||||
{
|
||||
String moduleName = "m"; // 8163382
|
||||
Path jmod = helper.generateDefaultJModule(moduleName).assertSuccess();
|
||||
|
Loading…
Reference in New Issue
Block a user