8147794: Jlink's ModuleEntry.stream can't be consumed more than once and ModuleEntry content should be read only if needed

Reviewed-by: jlaskey, psandoz
This commit is contained in:
Athijegannathan Sundararajan 2016-06-24 19:56:50 +05:30
parent 2146bab54f
commit 72d2e8594f
22 changed files with 479 additions and 277 deletions

@ -25,9 +25,7 @@
package jdk.tools.jlink.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.Objects;
import jdk.tools.jlink.plugin.ModuleEntry;
@ -44,103 +42,39 @@ import jdk.tools.jlink.plugin.ModuleEntry;
* {@literal bin|conf|native}/{dir1}>/.../{dirN}/{file name}</li>
* </ul>
*/
public class ModuleEntryImpl implements ModuleEntry {
private final Type type;
abstract class AbstractModuleEntry implements ModuleEntry {
private final String path;
private final String module;
private final long length;
private final InputStream stream;
private byte[] buffer;
private final Type type;
/**
* Create a new LinkModuleEntry.
* Create a new AbstractModuleEntry.
*
* @param module The module name.
* @param path The data path identifier.
* @param type The data type.
* @param stream The data content stream.
* @param length The stream length.
*/
public ModuleEntryImpl(String module, String path, Type type, InputStream stream, long length) {
Objects.requireNonNull(module);
Objects.requireNonNull(path);
Objects.requireNonNull(type);
Objects.requireNonNull(stream);
this.path = path;
this.type = type;
this.module = module;
this.stream = stream;
this.length = length;
AbstractModuleEntry(String module, String path, Type type) {
this.module = Objects.requireNonNull(module);
this.path = Objects.requireNonNull(path);
this.type = Objects.requireNonNull(type);
}
/**
* The LinkModuleEntry module name.
*
* @return The module name.
*/
@Override
public final String getModule() {
return module;
}
/**
* The LinkModuleEntry path.
*
* @return The module path.
*/
@Override
public final String getPath() {
return path;
}
/**
* The LinkModuleEntry's type.
*
* @return The data type.
*/
@Override
public final Type getType() {
return type;
}
/**
* The LinkModuleEntry content as an array of byte.
*
* @return An Array of bytes.
*/
@Override
public byte[] getBytes() {
if (buffer == null) {
try (InputStream is = stream) {
buffer = is.readAllBytes();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
return buffer;
}
/**
* The LinkModuleEntry content length.
*
* @return The length.
*/
@Override
public long getLength() {
return length;
}
/**
* The LinkModuleEntry stream.
*
* @return The module data stream.
*/
@Override
public InputStream stream() {
return stream;
}
@Override
public int hashCode() {
int hash = 7;
@ -150,10 +84,10 @@ public class ModuleEntryImpl implements ModuleEntry {
@Override
public boolean equals(Object other) {
if (!(other instanceof ModuleEntryImpl)) {
if (!(other instanceof AbstractModuleEntry)) {
return false;
}
ModuleEntryImpl f = (ModuleEntryImpl) other;
AbstractModuleEntry f = (AbstractModuleEntry) other;
return f.path.equals(path);
}
@ -161,4 +95,4 @@ public class ModuleEntryImpl implements ModuleEntry {
public String toString() {
return getPath();
}
}
}

@ -0,0 +1,82 @@
/*
* 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.jlink.internal;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.Objects;
import jdk.tools.jlink.plugin.ModuleEntry;
/**
* A ModuleEntry backed by a given Archive Entry.
*/
final class ArchiveEntryModuleEntry extends AbstractModuleEntry {
private final Archive.Entry entry;
/**
* Create a new ArchiveModuleEntry.
*
* @param module The module name.
* @param path The data path identifier.
* @param entry The archive Entry.
*/
ArchiveEntryModuleEntry(String module, String path, Archive.Entry entry) {
super(module, path, getImageFileType(entry));
this.entry = entry;
}
@Override
public InputStream stream() {
try {
return entry.stream();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
@Override
public long getLength() {
return entry.size();
}
private static ModuleEntry.Type getImageFileType(Archive.Entry entry) {
Objects.requireNonNull(entry);
switch(entry.type()) {
case CLASS_OR_RESOURCE:
return ModuleEntry.Type.CLASS_OR_RESOURCE;
case CONFIG:
return ModuleEntry.Type.CONFIG;
case NATIVE_CMD:
return ModuleEntry.Type.NATIVE_CMD;
case NATIVE_LIB:
return ModuleEntry.Type.NATIVE_LIB;
default:
return ModuleEntry.Type.OTHER;
}
}
}

@ -0,0 +1,78 @@
/*
* 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.jlink.internal;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.util.Objects;
import jdk.tools.jlink.plugin.ModuleEntry;
/**
* A ModuleEntry backed by a given byte[].
*/
class ByteArrayModuleEntry extends AbstractModuleEntry {
private final byte[] buffer;
/**
* Create a new ByteArrayModuleEntry.
*
* @param module The module name.
* @param path The data path identifier.
* @param type The data type.
* @param buf The byte buffer.
*/
ByteArrayModuleEntry(String module, String path, Type type, byte[] buffer) {
super(module, path, type);
this.buffer = Objects.requireNonNull(buffer);
}
@Override
public byte[] getBytes() {
return buffer.clone();
}
@Override
public InputStream stream() {
return new ByteArrayInputStream(buffer);
}
@Override
public void write(OutputStream out) {
try {
out.write(buffer);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
@Override
public long getLength() {
return buffer.length;
}
}

@ -233,8 +233,7 @@ public final class ImageFileCreator {
// write module content
for (ModuleEntry res : content) {
byte[] buf = res.getBytes();
out.write(buf, 0, buf.length);
res.write(out);
}
tree.addContent(out);
@ -244,21 +243,6 @@ public final class ImageFileCreator {
return resultResources;
}
private static ModuleEntry.Type mapImageFileType(EntryType type) {
switch(type) {
case CONFIG: {
return ModuleEntry.Type.CONFIG;
}
case NATIVE_CMD: {
return ModuleEntry.Type.NATIVE_CMD;
}
case NATIVE_LIB: {
return ModuleEntry.Type.NATIVE_LIB;
}
}
return null;
}
private static ModulePoolImpl createPools(Set<Archive> archives,
Map<String, List<Entry>> entriesForModule,
ByteOrder byteOrder,
@ -278,34 +262,22 @@ public final class ImageFileCreator {
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.
String path = entry.name();
try (InputStream stream = entry.stream()) {
byte[] bytes = readAllBytes(stream);
if (path.endsWith("module-info.class")) {
path = "/" + path;
} else {
path = "/" + mn + "/" + path;
}
try {
resources.add(ModuleEntry.create(path, bytes));
} catch (Exception ex) {
throw new IOException(ex);
}
path = entry.name();
if (path.endsWith("module-info.class")) {
path = "/" + path;
} else {
path = "/" + mn + "/" + path;
}
} else {
try {
// Entry.path() contains the kind of file native, conf, bin, ...
// Keep it to avoid naming conflict (eg: native/jvm.cfg and config/jvm.cfg
resources.add(ModuleEntry.create(mn,
"/" + mn + "/" + entry.path(), mapImageFileType(entry.type()),
entry.stream(), entry.size()));
} catch (Exception ex) {
throw new IOException(ex);
}
// 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 ArchiveEntryModuleEntry(mn, path, entry));
}
}
return resources;

@ -443,9 +443,7 @@ public final class ImagePluginStack {
byte[] bytes = decompressor.decompressResource(getByteOrder(),
(int offset) -> pool.getStringTable().getString(offset),
res.getBytes());
res = ModuleEntry.create(res.getPath(),
new ByteArrayInputStream(bytes),
bytes.length);
res = res.create(bytes);
} catch (IOException ex) {
throw new PluginException(ex);
}

@ -0,0 +1,67 @@
/*
* 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.jlink.internal;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Objects;
import jdk.tools.jlink.plugin.ModuleEntry;
public final class ModuleEntryFactory {
private ModuleEntryFactory() {}
public static ModuleEntry create(String path,
ModuleEntry.Type type, byte[] content) {
return new ByteArrayModuleEntry(moduleFrom(path), path, type, content);
}
public static ModuleEntry create(String path,
ModuleEntry.Type type, Path file) {
return new PathModuleEntry(moduleFrom(path), path, type, file);
}
public static ModuleEntry create(ModuleEntry original, byte[] content) {
return new ByteArrayModuleEntry(original.getModule(),
original.getPath(), original.getType(), content);
}
public static ModuleEntry create(ModuleEntry original, Path file) {
return new PathModuleEntry(original.getModule(),
original.getPath(), original.getType(), file);
}
private static String moduleFrom(String path) {
Objects.requireNonNull(path);
if (path.isEmpty() || path.charAt(0) != '/') {
throw new IllegalArgumentException(path + " must start with /");
}
String noRoot = path.substring(1);
int idx = noRoot.indexOf('/');
if (idx == -1) {
throw new IllegalArgumentException("/ missing after module: " + path);
}
return noRoot.substring(0, idx);
}
}

@ -95,7 +95,7 @@ public class ModulePoolImpl implements ModulePool {
@Override
public void add(ModuleEntry data) {
if (isReadOnly()) {
throw new PluginException("LinkConfiguration is readonly");
throw new PluginException("ModulePool is readonly");
}
Objects.requireNonNull(data);
if (!data.getModule().equals(name)) {
@ -180,7 +180,7 @@ public class ModulePoolImpl implements ModulePool {
@Override
public void add(ModuleEntry data) {
if (isReadOnly()) {
throw new PluginException("LinkConfiguration is readonly");
throw new PluginException("ModulePool is readonly");
}
Objects.requireNonNull(data);
if (resources.get(data.getPath()) != null) {
@ -215,7 +215,7 @@ public class ModulePoolImpl implements ModulePool {
}
/**
* The stream of modules contained in this LinkConfiguration.
* The stream of modules contained in this ModulePool.
*
* @return The stream of modules.
*/
@ -225,7 +225,7 @@ public class ModulePoolImpl implements ModulePool {
}
/**
* Return the number of LinkModule count in this LinkConfiguration.
* Return the number of LinkModule count in this ModulePool.
*
* @return the module count.
*/
@ -235,7 +235,7 @@ public class ModulePoolImpl implements ModulePool {
}
/**
* Get all ModuleEntry contained in this LinkConfiguration instance.
* Get all ModuleEntry contained in this ModulePool instance.
*
* @return The stream of LinkModuleEntries.
*/
@ -245,7 +245,7 @@ public class ModulePoolImpl implements ModulePool {
}
/**
* Return the number of ModuleEntry count in this LinkConfiguration.
* Return the number of ModuleEntry count in this ModulePool.
*
* @return the entry count.
*/
@ -267,7 +267,7 @@ public class ModulePoolImpl implements ModulePool {
}
/**
* Check if the LinkConfiguration contains the given ModuleEntry.
* Check if the ModulePool contains the given ModuleEntry.
*
* @param data The module data to check existence for.
* @return The module data or null if not found.
@ -279,7 +279,7 @@ public class ModulePoolImpl implements ModulePool {
}
/**
* Check if the LinkConfiguration contains some content at all.
* Check if the ModulePool contains some content at all.
*
* @return True, no content, false otherwise.
*/
@ -289,16 +289,16 @@ public class ModulePoolImpl implements ModulePool {
}
/**
* Visit each ModuleEntry in this LinkConfiguration to transform it and
* copy the transformed ModuleEntry to the output LinkConfiguration.
* Visit each ModuleEntry in this ModulePool to transform it and
* copy the transformed ModuleEntry to the output ModulePool.
*
* @param transform The function called for each ModuleEntry found in
* the LinkConfiguration. The transform function should return a
* the ModulePool. The transform function should return a
* ModuleEntry instance which will be added to the output or it should
* return null if the passed ModuleEntry is to be ignored for the
* output.
*
* @param output The LinkConfiguration to be filled with Visitor returned
* @param output The ModulePool to be filled with Visitor returned
* ModuleEntry.
*/
@Override
@ -351,14 +351,13 @@ public class ModulePoolImpl implements ModulePool {
/**
* A resource that has been compressed.
*/
public static final class CompressedModuleData extends ModuleEntryImpl {
public static final class CompressedModuleData extends ByteArrayModuleEntry {
final long uncompressed_size;
private CompressedModuleData(String module, String path,
InputStream stream, long size,
long uncompressed_size) {
super(module, path, ModuleEntry.Type.CLASS_OR_RESOURCE, stream, size);
byte[] content, long uncompressed_size) {
super(module, path, ModuleEntry.Type.CLASS_OR_RESOURCE, content);
this.uncompressed_size = uncompressed_size;
}
@ -413,8 +412,7 @@ public class ModulePoolImpl implements ModulePool {
CompressedModuleData compressedResource
= new CompressedModuleData(original.getModule(), original.getPath(),
new ByteArrayInputStream(contentWithHeader),
contentWithHeader.length, uncompressed_size);
contentWithHeader, uncompressed_size);
return compressedResource;
}

@ -0,0 +1,75 @@
/*
* 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 "Classfile" 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.jlink.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import jdk.tools.jlink.plugin.ModuleEntry;
/**
* A ModuleEntry backed by a given nio Path.
*/
public class PathModuleEntry extends AbstractModuleEntry {
private final Path file;
/**
* Create a new PathModuleEntry.
*
* @param module The module name.
* @param file The data file identifier.
* @param type The data type.
* @param file The Path for the resource content.
*/
public PathModuleEntry(String module, String path, Type type, Path file) {
super(module, path, type);
this.file = Objects.requireNonNull(file);
if (!Files.isRegularFile(file)) {
throw new IllegalArgumentException(file + " not a file");
}
}
@Override
public final InputStream stream() {
try {
return Files.newInputStream(file);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
@Override
public final long getLength() {
try {
return Files.size(file);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
}

@ -244,10 +244,7 @@ public final class ExcludeVMPlugin implements TransformerPlugin {
byte[] content = builder.toString().getBytes(StandardCharsets.UTF_8);
return ModuleEntry.create(orig.getModule(),
orig.getPath(),
orig.getType(),
new ByteArrayInputStream(content), content.length);
return orig.create(content);
}
private static String jvmlib() {

@ -39,7 +39,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import jdk.tools.jlink.internal.ModuleEntryImpl;
import jdk.tools.jlink.internal.PathModuleEntry;
import jdk.tools.jlink.plugin.PluginException;
import jdk.tools.jlink.plugin.ModuleEntry;
import jdk.tools.jlink.plugin.ModulePool;
@ -66,13 +66,13 @@ public class FileCopierPlugin implements TransformerPlugin {
/**
* Symbolic link to another path.
*/
public static abstract class SymImageFile extends ModuleEntryImpl {
public static abstract class SymImageFile extends PathModuleEntry {
private final String targetPath;
public SymImageFile(String targetPath, String module, String path,
ModuleEntry.Type type, InputStream stream, long size) {
super(module, path, type, stream, size);
ModuleEntry.Type type, Path file) {
super(module, path, type, file);
this.targetPath = targetPath;
}
@ -85,23 +85,7 @@ public class FileCopierPlugin implements TransformerPlugin {
public SymImageFileImpl(String targetPath, Path file, String module,
String path, ModuleEntry.Type type) {
super(targetPath, module, path, type, newStream(file), length(file));
}
}
private static long length(Path file) {
try {
return Files.size(file);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private static InputStream newStream(Path file) {
try {
return Files.newInputStream(file);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
super(targetPath, module, path, type, file);
}
}
@ -175,9 +159,9 @@ public class FileCopierPlugin implements TransformerPlugin {
Objects.requireNonNull(pool);
Objects.requireNonNull(file);
Objects.requireNonNull(path);
ModuleEntry impl = ModuleEntry.create(FAKE_MODULE,
ModuleEntry impl = ModuleEntry.create(
"/" + FAKE_MODULE + "/other/" + path,
ModuleEntry.Type.OTHER, newStream(file), length(file));
ModuleEntry.Type.OTHER, file);
try {
pool.add(impl);
} catch (Exception ex) {

@ -171,10 +171,9 @@ public final class GenerateJLIClassesPlugin implements TransformerPlugin {
byte[] bytes = result.getValue();
// Add class to pool
ModuleEntry ndata = ModuleEntry.create(data.getModule(),
ModuleEntry ndata = ModuleEntry.create(
"/java.base/" + className + ".class",
ModuleEntry.Type.CLASS_OR_RESOURCE,
new ByteArrayInputStream(bytes), bytes.length);
bytes);
if (!out.contains(ndata)) {
out.add(ndata);
}

@ -123,9 +123,7 @@ public final class IncludeLocalesPlugin implements TransformerPlugin, ResourcePr
if (Arrays.stream(cr.getInterfaces())
.anyMatch(i -> i.contains(METAINFONAME)) &&
stripUnsupportedLocales(bytes, cr)) {
resource = ModuleEntry.create(MODULENAME, path,
resource.getType(),
new ByteArrayInputStream(bytes), bytes.length);
resource = resource.create(bytes);
}
}
}

@ -75,7 +75,7 @@ public final class StripDebugPlugin implements TransformerPlugin {
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
reader.accept(writer, ClassReader.SKIP_DEBUG);
byte[] content = writer.toByteArray();
res = ModuleEntry.create(path, new ByteArrayInputStream(content), content.length);
res = resource.create(content);
}
}
} else if (predicate.test(res.getPath())) {

@ -139,11 +139,7 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
ModuleInfoRewriter minfoWriter =
new ModuleInfoRewriter(bain, mbuilder.conceals());
// replace with the overridden version
data = ModuleEntry.create(data.getModule(),
data.getPath(),
data.getType(),
minfoWriter.stream(),
minfoWriter.size());
data = data.create(minfoWriter.getBytes());
}
out.add(data);
} catch (IOException e) {
@ -158,12 +154,7 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
return;
if (builder.isOverriddenClass(data.getPath())) {
byte[] bytes = cwriter.toByteArray();
ModuleEntry ndata =
ModuleEntry.create(data.getModule(),
data.getPath(),
data.getType(),
new ByteArrayInputStream(bytes),
bytes.length);
ModuleEntry ndata = data.create(bytes);
out.add(ndata);
} else {
out.add(data);
@ -183,8 +174,8 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
this.extender.write(this);
}
InputStream stream() {
return new ByteArrayInputStream(buf);
byte[] getBytes() {
return buf;
}
}

@ -93,8 +93,7 @@ final class AsmPoolImpl implements AsmModulePool {
}
byte[] content = writer.toByteArray();
ModuleEntry res = ModuleEntry.create(path,
new ByteArrayInputStream(content), content.length);
ModuleEntry res = ModuleEntry.create(path, content);
transformedClasses.put(className, res);
}

@ -26,16 +26,18 @@ package jdk.tools.jlink.plugin;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Objects;
import jdk.tools.jlink.internal.ImageFileCreator;
import jdk.tools.jlink.internal.ModuleEntryImpl;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import jdk.tools.jlink.internal.ModuleEntryFactory;
/**
* A LinkModuleEntry is the elementary unit of data inside an image. It is
* generally a file. e.g.: a java class file, a resource file, a shared library,
* ...
* A ModuleEntry is the elementary unit of data inside an image. It is
* generally a file. e.g.: a java class file, a resource file, a shared library.
* <br>
* A LinkModuleEntry is identified by a path of the form:
* A ModuleEntry is identified by a path of the form:
* <ul>
* <li>For jimage content: /{module name}/{package1}/.../{packageN}/{file
* name}</li>
@ -63,93 +65,133 @@ public interface ModuleEntry {
OTHER
}
/**
* The LinkModuleEntry module name.
* The ModuleEntry module name.
*
* @return The module name.
*/
public String getModule();
/**
* The LinkModuleEntry path.
* The ModuleEntry path.
*
* @return The module path.
*/
public String getPath();
/**
* The LinkModuleEntry's type.
* The ModuleEntry's type.
*
* @return The data type.
*/
public Type getType();
/**
* The LinkModuleEntry content as an array of byte.
* The ModuleEntry content as an array of bytes.
*
* @return An Array of bytes.
*/
public byte[] getBytes();
public default byte[] getBytes() {
try (InputStream is = stream()) {
return is.readAllBytes();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
/**
* The LinkModuleEntry content length.
* The ModuleEntry content length.
*
* @return The length.
*/
public long getLength();
/**
* The LinkModuleEntry stream.
* The ModuleEntry stream.
*
* @return The module data stream.
*/
public InputStream stream();
/**
* Create a LinkModuleEntry located inside a jimage file. Such
* LinkModuleEntry has a Type being equals to CLASS_OR_RESOURCE.
* Write the content of this ModuleEntry to stream.
*
* @param path The complete resource path (contains the module radical).
* @param content The resource content.
* @param size The content size.
* @return A new LinkModuleEntry.
* @param out the output stream
*/
public static ModuleEntry create(String path, InputStream content, long size) {
Objects.requireNonNull(path);
Objects.requireNonNull(content);
String[] split = ImageFileCreator.splitPath(path);
String module = split[0];
return new ModuleEntryImpl(module, path, Type.CLASS_OR_RESOURCE, content, size);
public default void write(OutputStream out) {
try {
out.write(getBytes());
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
/**
* Create a LinkModuleEntry for a file that will be located inside a jimage
* file.
* Create a ModuleEntry with new content but other information
* copied from this ModuleEntry.
*
* @param content The new resource content.
* @return A new ModuleEntry.
*/
public default ModuleEntry create(byte[] content) {
return ModuleEntryFactory.create(this, content);
}
/**
* Create a ModuleEntry with new content but other information
* copied from this ModuleEntry.
*
* @param file The new resource content.
* @return A new ModuleEntry.
*/
public default ModuleEntry create(Path file) {
return ModuleEntryFactory.create(this, file);
}
/**
* Create a ModuleEntry for a resource of the given type.
*
* @param path The resource path.
* @param type The ModuleEntry type.
* @param content The resource content.
* @return A new ModuleEntry.
*/
public static ModuleEntry create(String path,
ModuleEntry.Type type, byte[] content) {
return ModuleEntryFactory.create(path, type, content);
}
/**
* Create a ModuleEntry for a resource of type {@link Type#CLASS_OR_RESOURCE}.
*
* @param path The resource path.
* @param content The resource content.
* @return A new LinkModuleEntry.
* @return A new ModuleEntry.
*/
public static ModuleEntry create(String path, byte[] content) {
return create(path, new ByteArrayInputStream(content),
content.length);
return create(path, Type.CLASS_OR_RESOURCE, content);
}
/**
* Create a LinkModuleEntry for a file that will be located outside a jimage
* file.
* Create a ModuleEntry for a resource of the given type.
*
* @param module The module in which this files is located.
* @param path The file path locator (doesn't contain the module name).
* @param type The LinkModuleEntry type.
* @param content The file content.
* @param size The content size.
* @return A new LinkModuleEntry.
* @param path The resource path.
* @param type The ModuleEntry type.
* @param file The resource file.
* @return A new ModuleEntry.
*/
public static ModuleEntry create(String module, String path, ModuleEntry.Type type,
InputStream content, long size) {
Objects.requireNonNull(path);
Objects.requireNonNull(content);
return new ModuleEntryImpl(module, path, type, content, size);
public static ModuleEntry create(String path,
ModuleEntry.Type type, Path file) {
return ModuleEntryFactory.create(path, type, file);
}
/**
* Create a ModuleEntry for a resource of type {@link Type#CLASS_OR_RESOURCE}.
*
* @param path The resource path.
* @param file The resource file.
* @return A new ModuleEntry.
*/
public static ModuleEntry create(String path, Path file) {
return create(path, Type.CLASS_OR_RESOURCE, file);
}
}

@ -33,7 +33,7 @@
import java.io.ByteArrayInputStream;
import java.util.Optional;
import java.util.function.Function;
import jdk.tools.jlink.internal.ModuleEntryImpl;
import jdk.tools.jlink.internal.ModuleEntryFactory;
import jdk.tools.jlink.internal.ModulePoolImpl;
import jdk.tools.jlink.plugin.ModuleEntry;
import jdk.tools.jlink.plugin.ModulePool;
@ -54,7 +54,7 @@ public class ImageFilePoolTest {
ModulePool input = new ModulePoolImpl();
for (int i = 0; i < 1000; ++i) {
String module = "module" + (i / 100);
input.add(new InMemoryImageFile(module, "/" + module + "/java/class" + i,
input.add(newInMemoryImageFile("/" + module + "/java/class" + i,
ModuleEntry.Type.CONFIG, "class" + i));
}
if (input.getEntryCount() != 1000) {
@ -94,11 +94,11 @@ public class ImageFilePoolTest {
switch (index) {
case 0:
++amountAfter;
return new InMemoryImageFile(file.getModule(), file.getPath() + SUFFIX,
return newInMemoryImageFile(file.getPath() + SUFFIX,
file.getType(), file.getPath());
case 1:
++amountAfter;
return new InMemoryImageFile(file.getModule(), file.getPath(),
return newInMemoryImageFile(file.getPath(),
file.getType(), file.getPath());
}
return null;
@ -130,28 +130,27 @@ public class ImageFilePoolTest {
if (input.findEntry("unknown").isPresent()) {
throw new AssertionError("ImageFileModulePool does not return null for unknown file");
}
if (input.contains(new InMemoryImageFile("", "unknown", ModuleEntry.Type.CONFIG, "unknown"))) {
throw new AssertionError("'contain' returns true for unknown file");
if (input.contains(newInMemoryImageFile("/unknown/foo", ModuleEntry.Type.CONFIG, "unknown"))) {
throw new AssertionError("'contain' returns true for /unknown/foo file");
}
input.add(new InMemoryImageFile("", "/aaa/bbb", ModuleEntry.Type.CONFIG, ""));
input.add(newInMemoryImageFile("/aaa/bbb", ModuleEntry.Type.CONFIG, ""));
try {
input.add(new InMemoryImageFile("", "/aaa/bbb", ModuleEntry.Type.CONFIG, ""));
input.add(newInMemoryImageFile("/aaa/bbb", ModuleEntry.Type.CONFIG, ""));
throw new AssertionError("Exception expected");
} catch (Exception e) {
// expected
}
input.setReadOnly();
try {
input.add(new InMemoryImageFile("", "/aaa/ccc", ModuleEntry.Type.CONFIG, ""));
input.add(newInMemoryImageFile("/aaa/ccc", ModuleEntry.Type.CONFIG, ""));
throw new AssertionError("Exception expected");
} catch (Exception e) {
// expected
}
}
private static class InMemoryImageFile extends ModuleEntryImpl {
public InMemoryImageFile(String module, String path, ModuleEntry.Type type, String content) {
super(module, path, type, new ByteArrayInputStream(content.getBytes()), content.getBytes().length);
}
private static ModuleEntry newInMemoryImageFile(String path,
ModuleEntry.Type type, String content) {
return ModuleEntryFactory.create(path, type, content.getBytes());
}
}

@ -66,9 +66,7 @@ public class ResourcePoolTest {
String module = "/module" + (i / 10);
String resourcePath = module + "/java/package" + i;
byte[] bytes = resourcePath.getBytes();
input.add(ModuleEntry.create(module, resourcePath,
ModuleEntry.Type.CLASS_OR_RESOURCE,
new ByteArrayInputStream(bytes), bytes.length));
input.add(ModuleEntry.create(resourcePath, bytes));
}
ModulePool output = new ModulePoolImpl();
ResourceVisitor visitor = new ResourceVisitor();
@ -103,12 +101,11 @@ public class ResourcePoolTest {
switch (index) {
case 0:
++amountAfter;
return ModuleEntry.create(resource.getModule(), resource.getPath() + SUFFIX,
resource.getType(), resource.stream(), resource.getLength());
return ModuleEntry.create(resource.getPath() + SUFFIX,
resource.getType(), resource.getBytes());
case 1:
++amountAfter;
return ModuleEntry.create(resource.getModule(), resource.getPath(),
resource.getType(), resource.stream(), resource.getLength());
return resource.create(resource.getBytes());
}
return null;
}
@ -132,9 +129,7 @@ public class ResourcePoolTest {
samples.add("javax/management/ObjectName");
test(samples, (resources, module, path) -> {
try {
resources.add(ModuleEntry.create(module, path,
ModuleEntry.Type.CLASS_OR_RESOURCE,
new ByteArrayInputStream(new byte[0]), 0));
resources.add(ModuleEntry.create(path, new byte[0]));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
@ -142,9 +137,7 @@ public class ResourcePoolTest {
test(samples, (resources, module, path) -> {
try {
resources.add(ModulePoolImpl.
newCompressedResource(ModuleEntry.create(module, path,
ModuleEntry.Type.CLASS_OR_RESOURCE,
new ByteArrayInputStream(new byte[0]), 0),
newCompressedResource(ModuleEntry.create(path, new byte[0]),
ByteBuffer.allocate(99), "bitcruncher", null,
((ModulePoolImpl)resources).getStringTable(), ByteOrder.nativeOrder()));
} catch (Exception ex) {
@ -203,20 +196,14 @@ public class ResourcePoolTest {
private void checkResourcesAfterCompression() throws Exception {
ModulePoolImpl resources1 = new ModulePoolImpl();
ModuleEntry res1 = ModuleEntry.create("module1", "/module1/toto1",
ModuleEntry.Type.CLASS_OR_RESOURCE,
new ByteArrayInputStream(new byte[0]), 0);
ModuleEntry res2 = ModuleEntry.create("module2", "/module2/toto1",
ModuleEntry.Type.CLASS_OR_RESOURCE,
new ByteArrayInputStream(new byte[0]), 0);
ModuleEntry res1 = ModuleEntry.create("/module1/toto1", new byte[0]);
ModuleEntry res2 = ModuleEntry.create("/module2/toto1", new byte[0]);
resources1.add(res1);
resources1.add(res2);
checkResources(resources1, res1, res2);
ModulePool resources2 = new ModulePoolImpl();
ModuleEntry res3 = ModuleEntry.create("module2", "/module2/toto1",
ModuleEntry.Type.CLASS_OR_RESOURCE,
new ByteArrayInputStream(new byte[7]), 7);
ModuleEntry res3 = ModuleEntry.create("/module2/toto1", new byte[7]);
resources2.add(res3);
resources2.add(ModulePoolImpl.newCompressedResource(res1,
ByteBuffer.allocate(7), "zip", null, resources1.getStringTable(),
@ -260,8 +247,7 @@ public class ResourcePoolTest {
((ModulePoolImpl) resources).setReadOnly();
try {
resources.add(ModuleEntry.create("module2", "/module2/toto1",
ModuleEntry.Type.CLASS_OR_RESOURCE, new ByteArrayInputStream(new byte[0]), 0));
resources.add(ModuleEntry.create("/module2/toto1", new byte[0]));
throw new AssertionError("ModulePool is read-only, but an exception is not thrown");
} catch (Exception ex) {
// Expected

@ -74,8 +74,8 @@ public class ExcludeFilesPluginTest {
fplug.configure(prop);
ModulePoolImpl files = new ModulePoolImpl();
ModulePoolImpl fresult = new ModulePoolImpl();
ModuleEntry f = ModuleEntry.create(module, "/" + module + "/" + sample,
ModuleEntry.Type.CONFIG, new ByteArrayInputStream(new byte[0]), 0);
ModuleEntry f = ModuleEntry.create("/" + module + "/" + sample,
ModuleEntry.Type.CONFIG, new byte[0]);
files.add(f);
fplug.visit(files, fresult);

@ -48,16 +48,16 @@ public class ExcludePluginTest {
public void test() throws Exception {
check("**.jcov", "/num/toto.jcov", true);
check("**.jcov", "//toto.jcov", true);
check("**.jcov", "/toto.jcov/", true);
check("**.jcov", "/toto.jcov/tutu/tata", false);
check("/java.base/*.jcov", "/java.base/toto.jcov", true);
check("/java.base/toto.jcov", "t/java.base/iti.jcov", false);
check("/java.base/toto.jcov", "/tjava.base/iti.jcov", false);
check("/java.base/*/toto.jcov", "/java.base/toto.jcov", false);
check("/java.base/*/toto.jcov", "/java.base/tutu/toto.jcov", true);
check("**/java.base/*/toto.jcov", "/tutu/java.base/tutu/toto.jcov", true);
check("/META-INF/**", "/META-INF/services/ MyProvider ", true);
check("/META-INF/**", "/META-INF/services/MyProvider", true);
check("**/META-INF", " /META-INF/services/MyProvider", false);
check("**/META-INF", "/ META-INF/services/MyProvider", false);
check("**/META-INF/**", "/java.base//META-INF/services/MyProvider", true);
check("/java.base/*/Toto$Titi.class", "/java.base/tutu/Toto$Titi.class", true);
check("/**$**.class", "/java.base/tutu/Toto$Titi.class", true);

@ -165,11 +165,11 @@ public class ExcludeVMPluginTest {
// Create a pool with jvm.cfg and the input paths.
byte[] jvmcfgContent = jvmcfg.getBytes();
ModulePool pool = new ModulePoolImpl();
pool.add(ModuleEntry.create("java.base", "/java.base/native/jvm.cfg",
ModuleEntry.Type.NATIVE_LIB, new ByteArrayInputStream(jvmcfgContent), jvmcfgContent.length));
pool.add(ModuleEntry.create("/java.base/native/jvm.cfg",
ModuleEntry.Type.NATIVE_LIB, jvmcfgContent));
for (String in : input) {
pool.add(ModuleEntry.create("java.base", in,
ModuleEntry.Type.NATIVE_LIB, new ByteArrayInputStream(new byte[0]), 0));
pool.add(ModuleEntry.create(in,
ModuleEntry.Type.NATIVE_LIB, new byte[0]));
}
ModulePool out = new ModulePoolImpl();

@ -106,6 +106,9 @@ public class StringSharingPluginTest {
byte[] content = Files.readAllBytes(p);
String path = p.toString().replace('\\', '/');
path = path.substring("/modules".length());
if (path.charAt(0) != '/') {
path = "/" + path;
}
ModuleEntry res = ModuleEntry.create(path, content);
resources.add(res);
} catch (Exception ex) {