8158456: ModuleDescriptor.read does not verify dependence on java.base in module-info.class

8159248: ModuleFinder.of not clear that FindException thrown if module descriptor cannot be derived for automatic module

Reviewed-by: chegar, mchung
This commit is contained in:
Alan Bateman 2016-06-17 08:41:39 +01:00
parent 0b83896bd2
commit 8547893efe
7 changed files with 338 additions and 133 deletions

View File

@ -1095,7 +1095,7 @@ public class ModuleDescriptor
public static final class Builder {
final String name;
final boolean automatic;
boolean automatic;
boolean synthetic;
final Map<String, Requires> requires = new HashMap<>();
final Set<String> uses = new HashSet<>();
@ -1120,12 +1120,19 @@ public class ModuleDescriptor
* identifier
*/
public Builder(String name) {
this(name, false);
this.name = requireModuleName(name);
}
/* package */ Builder(String name, boolean automatic) {
this.name = requireModuleName(name);
this.automatic = automatic;
/**
* Updates the builder so that it builds an automatic module.
*
* @return This builder
*
* @see ModuleDescriptor#isAutomatic()
*/
/* package */ Builder automatic() {
this.automatic = true;
return this;
}
/**

View File

@ -42,6 +42,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import sun.security.action.GetPropertyAction;
/**
@ -267,6 +268,13 @@ public interface ModuleFinder {
*
* </ul>
*
* <p> If a {@code ModuleDescriptor} cannot be created (by means of the
* {@link ModuleDescriptor.Builder ModuleDescriptor.Builder} API) for an
* automatic module then {@code FindException} is thrown. This can arise,
* for example, when a legal Java identifier name cannot be derived from
* the file name of the JAR file or where a package name derived from an
* entry ending with {@code .class} is not a legal Java identifier. </p>
*
* <p> In addition to JAR files, an implementation may also support modules
* that are packaged in other implementation specific module formats. When
* a file is encountered that is not recognized as a packaged module then

View File

@ -276,10 +276,7 @@ final class ModuleInfo {
throws IOException
{
int requires_count = in.readUnsignedShort();
if (requires_count == 0 && !mn.equals("java.base")) {
throw invalidModuleDescriptor("The requires table must have"
+ " at least one entry");
}
boolean requiresJavaBase = false;
for (int i=0; i<requires_count; i++) {
int index = in.readUnsignedShort();
int flags = in.readUnsignedShort();
@ -297,6 +294,17 @@ final class ModuleInfo {
mods.add(Modifier.MANDATED);
}
builder.requires(mods, dn);
if (dn.equals("java.base"))
requiresJavaBase = true;
}
if (mn.equals("java.base")) {
if (requires_count > 0) {
throw invalidModuleDescriptor("The requires table for java.base"
+ " must be 0 length");
}
} else if (!requiresJavaBase) {
throw invalidModuleDescriptor("The requires table must have"
+ " an entry for java.base");
}
int exports_count = in.readUnsignedShort();

View File

@ -55,7 +55,6 @@ import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import jdk.internal.module.Checks;
import jdk.internal.module.ConfigurableModuleFinder;
import jdk.internal.perf.PerfCounter;
@ -343,8 +342,7 @@ class ModulePath implements ConfigurableModuleFinder {
String prefix = cf.substring(0, index);
if (prefix.equals(SERVICES_PREFIX)) {
String sn = cf.substring(index);
if (Checks.isJavaIdentifier(sn))
return Optional.of(sn);
return Optional.of(sn);
}
}
return Optional.empty();
@ -378,9 +376,6 @@ class ModulePath implements ConfigurableModuleFinder {
* to "provides" declarations
* 5. The Main-Class attribute in the main attributes of the JAR manifest
* is mapped to the module descriptor mainClass
*
* @apiNote This needs to move to somewhere where it can be used by tools,
* maybe even a standard API if automatic modules are a Java SE feature.
*/
private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
throws IOException
@ -397,7 +392,7 @@ class ModulePath implements ConfigurableModuleFinder {
String vs = null;
// find first occurrence of -${NUMBER}. or -${NUMBER}$
Matcher matcher = Pattern.compile("-(\\d+(\\.|$))").matcher(mn);
Matcher matcher = Patterns.DASH_VERSION.matcher(mn);
if (matcher.find()) {
int start = matcher.start();
@ -412,16 +407,13 @@ class ModulePath implements ConfigurableModuleFinder {
}
// finally clean up the module name
mn = mn.replaceAll("[^A-Za-z0-9]", ".") // replace non-alphanumeric
.replaceAll("(\\.)(\\1)+", ".") // collapse repeating dots
.replaceAll("^\\.", "") // drop leading dots
.replaceAll("\\.$", ""); // drop trailing dots
mn = cleanModuleName(mn);
// Builder throws IAE if module name is empty or invalid
ModuleDescriptor.Builder builder
= new ModuleDescriptor.Builder(mn, true)
.requires(Requires.Modifier.MANDATED, "java.base");
= new ModuleDescriptor.Builder(mn)
.automatic()
.requires(Set.of(Requires.Modifier.MANDATED), "java.base");
if (vs != null)
builder.version(vs);
@ -457,7 +449,7 @@ class ModulePath implements ConfigurableModuleFinder {
= new BufferedReader(new InputStreamReader(in, "UTF-8"));
String cn;
while ((cn = nextLine(reader)) != null) {
if (Checks.isJavaIdentifier(cn)) {
if (cn.length() > 0) {
providerClasses.add(cn);
}
}
@ -478,6 +470,39 @@ class ModulePath implements ConfigurableModuleFinder {
return builder.build();
}
/**
* Patterns used to derive the module name from a JAR file name.
*/
private static class Patterns {
static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))");
static final Pattern NON_ALPHANUM = Pattern.compile("[^A-Za-z0-9]");
static final Pattern REPEATING_DOTS = Pattern.compile("(\\.)(\\1)+");
static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
static final Pattern TRAILING_DOTS = Pattern.compile("\\.$");
}
/**
* Clean up candidate module name derived from a JAR file name.
*/
private static String cleanModuleName(String mn) {
// replace non-alphanumeric
mn = Patterns.NON_ALPHANUM.matcher(mn).replaceAll(".");
// collapse repeating dots
mn = Patterns.REPEATING_DOTS.matcher(mn).replaceAll(".");
// drop leading dots
if (mn.length() > 0 && mn.charAt(0) == '.')
mn = Patterns.LEADING_DOTS.matcher(mn).replaceAll("");
// drop trailing dots
int len = mn.length();
if (len > 0 && mn.charAt(len-1) == '.')
mn = Patterns.TRAILING_DOTS.matcher(mn).replaceAll("");
return mn;
}
private Set<String> jarPackages(JarFile jf) {
return jf.stream()
.filter(e -> e.getName().endsWith(".class"))

View File

@ -36,7 +36,6 @@ import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@ -454,17 +453,13 @@ final class Resolver {
/**
* Computes and sets the readability graph for the modules in the given
* Resolution object.
* Computes the readability graph for the modules in the given Configuration.
*
* The readability graph is created by propagating "requires" through the
* "public requires" edges of the module dependence graph. So if the module
* dependence graph has m1 requires m2 && m2 requires public m3 then the
* resulting readability graph will contain m1 reads m2, m1
* reads m3, and m2 reads m3.
*
* TODO: Use a more efficient algorithm, maybe cache the requires public
* in parent configurations.
* resulting readability graph will contain m1 reads m2, m1 reads m3, and
* m2 reads m3.
*/
private Map<ResolvedModule, Set<ResolvedModule>> makeGraph(Configuration cf) {
@ -483,16 +478,15 @@ final class Resolver {
Configuration p = parent;
while (p != null) {
for (ModuleDescriptor descriptor : p.descriptors()) {
ResolvedModule x = p.findModule(descriptor.name()).orElse(null);
if (x == null)
throw new InternalError();
String name = descriptor.name();
ResolvedModule m1 = p.findModule(name)
.orElseThrow(() -> new InternalError(name + " not found"));
for (ModuleDescriptor.Requires requires : descriptor.requires()) {
if (requires.modifiers().contains(Modifier.PUBLIC)) {
String dn = requires.name();
ResolvedModule y = p.findModule(dn).orElse(null);
if (y == null)
throw new InternalError(dn + " not found");
g2.computeIfAbsent(x, k -> new HashSet<>()).add(y);
ResolvedModule m2 = p.findModule(dn)
.orElseThrow(() -> new InternalError(dn + " not found"));
g2.computeIfAbsent(m1, k -> new HashSet<>()).add(m2);
}
}
}
@ -501,53 +495,60 @@ final class Resolver {
}
// populate g1 and g2 with the dependences from the selected modules
Map<String, ResolvedModule> nameToResolved = new HashMap<>();
for (ModuleReference mref : nameToReference.values()) {
ModuleDescriptor descriptor = mref.descriptor();
ResolvedModule x = new ResolvedModule(cf, mref);
String name = descriptor.name();
ResolvedModule m1 = computeIfAbsent(nameToResolved, name, cf, mref);
Set<ResolvedModule> reads = new HashSet<>();
g1.put(x, reads);
Set<ResolvedModule> requiresPublic = new HashSet<>();
g2.put(x, requiresPublic);
for (ModuleDescriptor.Requires requires : descriptor.requires()) {
String dn = requires.name();
ResolvedModule y;
ModuleReference other = nameToReference.get(dn);
if (other != null) {
y = new ResolvedModule(cf, other); // cache?
ResolvedModule m2;
ModuleReference mref2 = nameToReference.get(dn);
if (mref2 != null) {
// same configuration
m2 = computeIfAbsent(nameToResolved, dn, cf, mref2);
} else {
y = parent.findModule(dn).orElse(null);
if (y == null)
throw new InternalError("unable to find " + dn);
// parent configuration
m2 = parent.findModule(dn).orElse(null);
if (m2 == null) {
continue;
}
}
// m requires other => m reads other
reads.add(y);
// m1 requires m2 => m1 reads m2
reads.add(m2);
// m requires public other
// m1 requires public m2
if (requires.modifiers().contains(Modifier.PUBLIC)) {
requiresPublic.add(y);
requiresPublic.add(m2);
}
}
// automatic modules reads all selected modules and all modules
// automatic modules read all selected modules and all modules
// in parent configurations
if (descriptor.isAutomatic()) {
String name = descriptor.name();
// reads all selected modules
// requires public` all selected automatic modules
// `requires public` all selected automatic modules
for (ModuleReference mref2 : nameToReference.values()) {
ModuleDescriptor descriptor2 = mref2.descriptor();
if (!name.equals(descriptor2.name())) {
ResolvedModule m = new ResolvedModule(cf, mref2);
reads.add(m);
String name2 = descriptor2.name();
if (!name.equals(name2)) {
ResolvedModule m2
= computeIfAbsent(nameToResolved, name2, cf, mref2);
reads.add(m2);
if (descriptor2.isAutomatic())
requiresPublic.add(m);
requiresPublic.add(m2);
}
}
@ -565,54 +566,58 @@ final class Resolver {
}
g1.put(m1, reads);
g2.put(m1, requiresPublic);
}
// Iteratively update g1 until there are no more requires public to propagate
boolean changed;
Map<ResolvedModule, Set<ResolvedModule>> changes = new HashMap<>();
Set<ResolvedModule> toAdd = new HashSet<>();
do {
changed = false;
for (Entry<ResolvedModule, Set<ResolvedModule>> entry : g1.entrySet()) {
ResolvedModule m1 = entry.getKey();
Set<ResolvedModule> m1Reads = entry.getValue();
for (Set<ResolvedModule> m1Reads : g1.values()) {
for (ResolvedModule m2 : m1Reads) {
Set<ResolvedModule> m2RequiresPublic = g2.get(m2);
if (m2RequiresPublic != null) {
for (ResolvedModule m3 : m2RequiresPublic) {
if (!m1Reads.contains(m3)) {
// computeIfAbsent
Set<ResolvedModule> s = changes.get(m1);
if (s == null) {
s = new HashSet<>();
changes.put(m1, s);
}
s.add(m3);
changed = true;
// m1 reads m2, m2 requires public m3
// => need to add m1 reads m3
toAdd.add(m3);
}
}
}
}
}
if (changed) {
for (Map.Entry<ResolvedModule, Set<ResolvedModule>> e :
changes.entrySet()) {
ResolvedModule m = e.getKey();
g1.get(m).addAll(e.getValue());
if (!toAdd.isEmpty()) {
m1Reads.addAll(toAdd);
toAdd.clear();
changed = true;
}
changes.clear();
}
} while (changed);
return g1;
}
/**
* Equivalent to
* <pre>{@code
* map.computeIfAbsent(name, k -> new ResolvedModule(cf, mref))
* </pre>}
*/
private ResolvedModule computeIfAbsent(Map<String, ResolvedModule> map,
String name,
Configuration cf,
ModuleReference mref)
{
ResolvedModule m = map.get(name);
if (m == null) {
m = new ResolvedModule(cf, mref);
map.put(name, m);
}
return m;
}
/**
* Checks the readability graph to ensure that no two modules export the

View File

@ -24,14 +24,14 @@
/**
* @test
* @library /lib/testlibrary
* @build AutomaticModulesTest ModuleUtils
* @build AutomaticModulesTest ModuleUtils JarUtils
* @run testng AutomaticModulesTest
* @summary Basic tests for automatic modules
*/
import java.io.IOException;
import java.io.OutputStream;
import java.lang.module.Configuration;
import java.lang.module.FindException;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Requires.Modifier;
@ -46,11 +46,9 @@ import java.nio.file.Paths;
import java.util.Optional;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@ -62,10 +60,8 @@ public class AutomaticModulesTest {
private static final Path USER_DIR
= Paths.get(System.getProperty("user.dir"));
@DataProvider(name = "names")
public Object[][] createNames() {
return new Object[][] {
// JAR file name module-name[/version]
@ -98,12 +94,22 @@ public class AutomaticModulesTest {
};
}
// JAR file names that do not map to a legal module name
@DataProvider(name = "badnames")
public Object[][] createBadNames() {
return new Object[][]{
{ ".jar", null },
{ "_.jar", null }
};
}
/**
* Test mapping of JAR file names to module names
*/
@Test(dataProvider = "names")
public void testNames(String fn, String mid) throws IOException {
String[] s = mid.split("/");
String mn = s[0];
String vs = (s.length == 2) ? s[1] : null;
@ -112,7 +118,7 @@ public class AutomaticModulesTest {
Path jf = dir.resolve(fn);
// create empty JAR file
createJarFile(jf);
createDummyJarFile(jf);
// create a ModuleFinder to find modules in the directory
ModuleFinder finder = ModuleFinder.of(dir);
@ -128,17 +134,32 @@ public class AutomaticModulesTest {
} else {
assertEquals(descriptor.version().get().toString(), vs);
}
}
/**
* Test impossible mapping of JAR files to modules names
*/
@Test(dataProvider = "badnames", expectedExceptions = FindException.class)
public void testBadNames(String fn, String ignore) throws IOException {
Path dir = Files.createTempDirectory(USER_DIR, "mods");
Path jf = dir.resolve(fn);
// create empty JAR file
createDummyJarFile(jf);
// should throw FindException
ModuleFinder.of(dir).findAll();
}
/**
* Test all packages are exported
*/
public void testExports() throws IOException {
public void testPackages() throws IOException {
Path dir = Files.createTempDirectory(USER_DIR, "mods");
createJarFile(dir.resolve("m1.jar"),
"p/C1.class", "p/C2.class", "q/C1.class");
createDummyJarFile(dir.resolve("m1.jar"),
"p/C1.class", "p/C2.class", "q/C1.class");
ModuleFinder finder = ModuleFinder.of(dir);
@ -157,6 +178,84 @@ public class AutomaticModulesTest {
}
/**
* Test class file in JAR file where the entry does not correspond to a
* legal package name.
*/
@Test(expectedExceptions = FindException.class)
public void testBadPackage() throws IOException {
Path dir = Files.createTempDirectory(USER_DIR, "mods");
createDummyJarFile(dir.resolve("m1.jar"), "p-/T.class");
// should throw FindException
ModuleFinder.of(dir).findAll();
}
/**
* Test JAR file with META-INF/services configuration file
*/
public void testServicesConfiguration() throws IOException {
String service = "p.S";
String provider = "p.S1";
Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp");
Path services = tmpdir.resolve("META-INF").resolve("services");
Files.createDirectories(services);
Files.write(services.resolve(service), Set.of(provider));
Path dir = Files.createTempDirectory(USER_DIR, "mods");
JarUtils.createJarFile(dir.resolve("m1.jar"), tmpdir);
ModuleFinder finder = ModuleFinder.of(dir);
Optional<ModuleReference> mref = finder.find("m1");
assertTrue(mref.isPresent(), "m1 not found");
ModuleDescriptor descriptor = mref.get().descriptor();
assertTrue(descriptor.provides().size() == 1);
assertTrue(descriptor.provides().containsKey(service));
ModuleDescriptor.Provides provides = descriptor.provides().get(service);
assertTrue(provides.providers().size() == 1);
assertTrue(provides.providers().contains((provider)));
}
// META-INF/services configuration file/entries that are not legal
@DataProvider(name = "badproviders")
public Object[][] createProviders() {
return new Object[][] {
// service type provider type
{ "p.S", "-" },
{ "p.S", ".S1" },
{ "p.S", "S1." },
{ "-", "p.S1" },
{ ".S", "p.S1" },
};
}
/**
* Test JAR file with META-INF/services configuration file with bad
* values or names.
*/
@Test(dataProvider = "badproviders", expectedExceptions = FindException.class)
public void testBadServicesConfiguration(String service, String provider)
throws IOException
{
Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp");
Path services = tmpdir.resolve("META-INF").resolve("services");
Files.createDirectories(services);
Files.write(services.resolve(service), Set.of(provider));
Path dir = Files.createTempDirectory(USER_DIR, "mods");
JarUtils.createJarFile(dir.resolve("m1.jar"), tmpdir);
// should throw FindException
ModuleFinder.of(dir).findAll();
}
/**
* Test that a JAR file with a Main-Class attribute results
* in a module with a main class.
@ -170,7 +269,7 @@ public class AutomaticModulesTest {
attrs.put(Attributes.Name.MAIN_CLASS, mainClass);
Path dir = Files.createTempDirectory(USER_DIR, "mods");
createJarFile(dir.resolve("m1.jar"), man);
createDummyJarFile(dir.resolve("m1.jar"), man);
ModuleFinder finder = ModuleFinder.of(dir);
@ -184,6 +283,36 @@ public class AutomaticModulesTest {
}
// Main-Class files that do not map to a legal Java identifier
@DataProvider(name = "badmainclass")
public Object[][] createBadMainClass() {
return new Object[][]{
{ "p-.Main", null },
{ ".Main", null }
};
}
/**
* Test that a JAR file with a Main-Class attribute that is not a valid
* Java identifier
*/
@Test(dataProvider = "badmainclass", expectedExceptions = FindException.class)
public void testBadMainClass(String mainClass, String ignore) throws IOException {
Manifest man = new Manifest();
Attributes attrs = man.getMainAttributes();
attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
attrs.put(Attributes.Name.MAIN_CLASS, mainClass);
Path dir = Files.createTempDirectory(USER_DIR, "mods");
createDummyJarFile(dir.resolve("m1.jar"), man);
// should throw FindException
ModuleFinder.of(dir).findAll();
}
/**
* Basic test of a configuration created with automatic modules.
* m1 requires m2*
@ -201,8 +330,8 @@ public class AutomaticModulesTest {
// m2 and m3 are automatic modules
Path dir = Files.createTempDirectory(USER_DIR, "mods");
createJarFile(dir.resolve("m2.jar"), "p/T.class");
createJarFile(dir.resolve("m3.jar"), "q/T.class");
createDummyJarFile(dir.resolve("m2.jar"), "p/T.class");
createDummyJarFile(dir.resolve("m3.jar"), "q/T.class");
// module finder locates m1 and the modules in the directory
ModuleFinder finder
@ -252,7 +381,6 @@ public class AutomaticModulesTest {
* m4*
*/
public void testInConfiguration2() throws IOException {
ModuleDescriptor descriptor1
= new ModuleDescriptor.Builder("m1")
.requires("m2")
@ -267,8 +395,8 @@ public class AutomaticModulesTest {
// m3 and m4 are automatic modules
Path dir = Files.createTempDirectory(USER_DIR, "mods");
createJarFile(dir.resolve("m3.jar"), "p/T.class");
createJarFile(dir.resolve("m4.jar"), "q/T.class");
createDummyJarFile(dir.resolve("m3.jar"), "p/T.class");
createDummyJarFile(dir.resolve("m4.jar"), "q/T.class");
// module finder locates m1 and the modules in the directory
ModuleFinder finder
@ -315,7 +443,6 @@ public class AutomaticModulesTest {
assertTrue(m4.reads().contains(m2));
assertTrue(m4.reads().contains(m3));
testReadAllBootModules(cf, "m4"); // m4 reads all modules in boot layer
}
@ -327,7 +454,6 @@ public class AutomaticModulesTest {
* m4*
*/
public void testInConfiguration3() throws IOException {
ModuleDescriptor descriptor1
= new ModuleDescriptor.Builder("m1")
.requires("m2")
@ -342,8 +468,8 @@ public class AutomaticModulesTest {
// m3 and m4 are automatic modules
Path dir = Files.createTempDirectory(USER_DIR, "mods");
createJarFile(dir.resolve("m3.jar"), "p/T.class");
createJarFile(dir.resolve("m4.jar"), "q/T.class");
createDummyJarFile(dir.resolve("m3.jar"), "p/T.class");
createDummyJarFile(dir.resolve("m4.jar"), "q/T.class");
// module finder locates m1 and the modules in the directory
ModuleFinder finder
@ -396,7 +522,6 @@ public class AutomaticModulesTest {
assertTrue(m4.reads().contains(m2));
assertTrue(m4.reads().contains(m3));
testReadAllBootModules(cf, "m4"); // m4 reads all modules in boot layer
}
@ -412,8 +537,8 @@ public class AutomaticModulesTest {
// m2 and m3 are simple JAR files
Path dir = Files.createTempDirectory(USER_DIR, "mods");
createJarFile(dir.resolve("m2.jar"), "p/T.class");
createJarFile(dir.resolve("m3.jar"), "q/T2.class");
createDummyJarFile(dir.resolve("m2.jar"), "p/T.class");
createDummyJarFile(dir.resolve("m3.jar"), "q/T2.class");
// module finder locates m1 and the modules in the directory
ModuleFinder finder
@ -447,7 +572,7 @@ public class AutomaticModulesTest {
*/
public void testMisc() throws IOException {
Path dir = Files.createTempDirectory(USER_DIR, "mods");
Path m1_jar = createJarFile(dir.resolve("m1.jar"), "p/T.class");
Path m1_jar = createDummyJarFile(dir.resolve("m1.jar"), "p/T.class");
ModuleFinder finder = ModuleFinder.of(m1_jar);
@ -535,38 +660,32 @@ public class AutomaticModulesTest {
* Creates a JAR file, optionally with a manifest, and with the given
* entries. The entries will be empty in the resulting JAR file.
*/
static Path createJarFile(Path file, Manifest man, String... entries)
static Path createDummyJarFile(Path jarfile, Manifest man, String... entries)
throws IOException
{
try (OutputStream out = Files.newOutputStream(file)) {
try (JarOutputStream jos = new JarOutputStream(out)) {
Path dir = Files.createTempDirectory(USER_DIR, "tmp");
if (man != null) {
JarEntry je = new JarEntry(JarFile.MANIFEST_NAME);
jos.putNextEntry(je);
man.write(jos);
jos.closeEntry();
}
for (String entry : entries) {
JarEntry je = new JarEntry(entry);
jos.putNextEntry(je);
jos.closeEntry();
}
}
for (String entry : entries) {
Path file = dir.resolve(entry);
Path parent = file.getParent();
if (parent != null)
Files.createDirectories(parent);
Files.createFile(file);
}
return file;
Path[] paths = Stream.of(entries).map(Paths::get).toArray(Path[]::new);
JarUtils.createJarFile(jarfile, man, dir, paths);
return jarfile;
}
/**
* Creates a JAR file and with the given entries. The entries will be empty
* in the resulting JAR file.
*/
static Path createJarFile(Path file, String... entries)
static Path createDummyJarFile(Path jarfile, String... entries)
throws IOException
{
return createJarFile(file, null, entries);
return createDummyJarFile(jarfile, null, entries);
}
}

View File

@ -23,6 +23,7 @@
/**
* @test
* @modules java.base/jdk.internal.module
* @run testng ModuleDescriptorTest
* @summary Basic test for java.lang.module.ModuleDescriptor and its builder
*/
@ -47,6 +48,7 @@ import java.util.Set;
import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
import jdk.internal.module.ModuleInfoWriter;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
@ -668,6 +670,37 @@ public class ModuleDescriptorTest {
ModuleDescriptor.read(bb);
}
// The requires table for java.base must be 0 length
@Test(expectedExceptions = InvalidModuleDescriptorException.class)
public void testReadOfJavaBaseWithRequires() {
ModuleDescriptor descriptor
= new ModuleDescriptor.Builder("java.base")
.requires("other")
.build();
ByteBuffer bb = ModuleInfoWriter.toByteBuffer(descriptor);
ModuleDescriptor.read(bb);
}
// The requires table must have an entry for java.base
@Test(expectedExceptions = InvalidModuleDescriptorException.class)
public void testReadWithEmptyRequires() {
ModuleDescriptor descriptor = new ModuleDescriptor.Builder("m1").build();
ByteBuffer bb = ModuleInfoWriter.toByteBuffer(descriptor);
ModuleDescriptor.read(bb);
}
// The requires table must have an entry for java.base
@Test(expectedExceptions = InvalidModuleDescriptorException.class)
public void testReadWithNoRequiresBase() {
ModuleDescriptor descriptor
= new ModuleDescriptor.Builder("m1")
.requires("m2")
.build();
ByteBuffer bb = ModuleInfoWriter.toByteBuffer(descriptor);
ModuleDescriptor.read(bb);
}
public void testReadWithNull() throws Exception {
Module base = Object.class.getModule();