8194937: Inconsistent behavior of --validate-modules when combined with -m and other options
Reviewed-by: mchung
--- a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Thu Jun 21 10:54:07 2018 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Thu Jun 21 18:56:35 2018 +0100
@@ -136,7 +136,7 @@
/**
* Initialize the module system, returning the boot layer.
*
- * @see java.lang.System#initPhase2()
+ * @see java.lang.System#initPhase2(boolean, boolean)
*/
public static ModuleLayer boot() throws Exception {
@@ -213,11 +213,13 @@
Counters.add("jdk.module.boot.2.defineBaseTime", t2);
- // Step 2a: If --validate-modules is specified then the VM needs to
- // start with only system modules, all other options are ignored.
+ // Step 2a: Scan all modules when --validate-modules specified
if (getAndRemoveProperty("jdk.module.validation") != null) {
- return createBootLayerForValidation();
+ int errors = ModulePathValidator.scanAllModules(System.out);
+ if (errors > 0) {
+ fail("Validation of module path failed");
+ }
}
@@ -420,26 +422,6 @@
}
/**
- * Create a boot module layer for validation that resolves all
- * system modules.
- */
- private static ModuleLayer createBootLayerForValidation() {
- Set<String> allSystem = ModuleFinder.ofSystem().findAll()
- .stream()
- .map(ModuleReference::descriptor)
- .map(ModuleDescriptor::name)
- .collect(Collectors.toSet());
-
- Configuration cf = SharedSecrets.getJavaLangModuleAccess()
- .resolveAndBind(ModuleFinder.ofSystem(),
- allSystem,
- null);
-
- Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
- return ModuleLayer.empty().defineModules(cf, clf);
- }
-
- /**
* Load/register the modules to the built-in class loaders.
*/
private static void loadModules(Configuration cf,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ModulePathValidator.java Thu Jun 21 18:56:35 2018 +0100
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2018, 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.module;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.module.FindException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+import java.net.URI;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+/**
+ * A validator to check for errors and conflicts between modules.
+ */
+
+class ModulePathValidator {
+ private static final String MODULE_INFO = "module-info.class";
+ private static final String INDENT = " ";
+
+ private final Map<String, ModuleReference> nameToModule;
+ private final Map<String, ModuleReference> packageToModule;
+ private final PrintStream out;
+
+ private int errorCount;
+
+ private ModulePathValidator(PrintStream out) {
+ this.nameToModule = new HashMap<>();
+ this.packageToModule = new HashMap<>();
+ this.out = out;
+ }
+
+ /**
+ * Scans and the validates all modules on the module path. The module path
+ * comprises the upgrade module path, system modules, and the application
+ * module path.
+ *
+ * @param out the print stream for output messages
+ * @return the number of errors found
+ */
+ static int scanAllModules(PrintStream out) {
+ ModulePathValidator validator = new ModulePathValidator(out);
+
+ // upgrade module path
+ String value = System.getProperty("jdk.module.upgrade.path");
+ if (value != null) {
+ Stream.of(value.split(File.pathSeparator))
+ .map(Path::of)
+ .forEach(validator::scan);
+ }
+
+ // system modules
+ ModuleFinder.ofSystem().findAll().stream()
+ .sorted(Comparator.comparing(ModuleReference::descriptor))
+ .forEach(validator::process);
+
+ // application module path
+ value = System.getProperty("jdk.module.path");
+ if (value != null) {
+ Stream.of(value.split(File.pathSeparator))
+ .map(Path::of)
+ .forEach(validator::scan);
+ }
+
+ return validator.errorCount;
+ }
+
+ /**
+ * Prints the module location and name.
+ */
+ private void printModule(ModuleReference mref) {
+ mref.location()
+ .filter(uri -> !isJrt(uri))
+ .ifPresent(uri -> out.print(uri + " "));
+ ModuleDescriptor descriptor = mref.descriptor();
+ out.print(descriptor.name());
+ if (descriptor.isAutomatic())
+ out.print(" automatic");
+ out.println();
+ }
+
+ /**
+ * Prints the module location and name, checks if the module is
+ * shadowed by a previously seen module, and finally checks for
+ * package conflicts with previously seen modules.
+ */
+ private void process(ModuleReference mref) {
+ String name = mref.descriptor().name();
+ ModuleReference previous = nameToModule.putIfAbsent(name, mref);
+ if (previous != null) {
+ printModule(mref);
+ out.print(INDENT + "shadowed by ");
+ printModule(previous);
+ } else {
+ boolean first = true;
+
+ // check for package conflicts when not shadowed
+ for (String pkg : mref.descriptor().packages()) {
+ previous = packageToModule.putIfAbsent(pkg, mref);
+ if (previous != null) {
+ if (first) {
+ printModule(mref);
+ first = false;
+ errorCount++;
+ }
+ String mn = previous.descriptor().name();
+ out.println(INDENT + "contains " + pkg
+ + " conflicts with module " + mn);
+ }
+ }
+ }
+ }
+
+ /**
+ * Scan an element on a module path. The element is a directory
+ * of modules, an exploded module, or a JAR file.
+ */
+ private void scan(Path entry) {
+ BasicFileAttributes attrs;
+ try {
+ attrs = Files.readAttributes(entry, BasicFileAttributes.class);
+ } catch (NoSuchFileException ignore) {
+ return;
+ } catch (IOException ioe) {
+ out.println(entry + " " + ioe);
+ errorCount++;
+ return;
+ }
+
+ String fn = entry.getFileName().toString();
+ if (attrs.isRegularFile() && fn.endsWith(".jar")) {
+ // JAR file, explicit or automatic module
+ scanModule(entry).ifPresent(this::process);
+ } else if (attrs.isDirectory()) {
+ Path mi = entry.resolve(MODULE_INFO);
+ if (Files.exists(mi)) {
+ // exploded module
+ scanModule(entry).ifPresent(this::process);
+ } else {
+ // directory of modules
+ scanDirectory(entry);
+ }
+ }
+ }
+
+ /**
+ * Scan the JAR files and exploded modules in a directory.
+ */
+ private void scanDirectory(Path dir) {
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
+ Map<String, Path> moduleToEntry = new HashMap<>();
+
+ for (Path entry : stream) {
+ BasicFileAttributes attrs;
+ try {
+ attrs = Files.readAttributes(entry, BasicFileAttributes.class);
+ } catch (IOException ioe) {
+ out.println(entry + " " + ioe);
+ errorCount++;
+ continue;
+ }
+
+ ModuleReference mref = null;
+
+ String fn = entry.getFileName().toString();
+ if (attrs.isRegularFile() && fn.endsWith(".jar")) {
+ mref = scanModule(entry).orElse(null);
+ } else if (attrs.isDirectory()) {
+ Path mi = entry.resolve(MODULE_INFO);
+ if (Files.exists(mi)) {
+ mref = scanModule(entry).orElse(null);
+ }
+ }
+
+ if (mref != null) {
+ String name = mref.descriptor().name();
+ Path previous = moduleToEntry.putIfAbsent(name, entry);
+ if (previous != null) {
+ // same name as other module in the directory
+ printModule(mref);
+ out.println(INDENT + "contains same module as "
+ + previous.getFileName());
+ errorCount++;
+ } else {
+ process(mref);
+ }
+ }
+ }
+ } catch (IOException ioe) {
+ out.println(dir + " " + ioe);
+ errorCount++;
+ }
+ }
+
+ /**
+ * Scan a JAR file or exploded module.
+ */
+ private Optional<ModuleReference> scanModule(Path entry) {
+ ModuleFinder finder = ModuleFinder.of(entry);
+ try {
+ return finder.findAll().stream().findFirst();
+ } catch (FindException e) {
+ out.println(entry);
+ out.println(INDENT + e.getMessage());
+ Throwable cause = e.getCause();
+ if (cause != null) {
+ out.println(INDENT + cause);
+ }
+ errorCount++;
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * Returns true if the given URI is a jrt URI
+ */
+ private static boolean isJrt(URI uri) {
+ return (uri != null && uri.getScheme().equalsIgnoreCase("jrt"));
+ }
+}
--- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java Thu Jun 21 10:54:07 2018 -0700
+++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java Thu Jun 21 18:56:35 2018 +0100
@@ -44,7 +44,6 @@
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.lang.module.Configuration;
-import java.lang.module.FindException;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Exports;
@@ -62,21 +61,16 @@
import java.nio.charset.Charset;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.attribute.BasicFileAttributes;
import java.text.Normalizer;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Locale.Category;
-import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.ResourceBundle;
@@ -1212,197 +1206,4 @@
return (uri != null && uri.getScheme().equalsIgnoreCase("jrt"));
}
- /**
- * Called by the launcher to validate the modules on the upgrade and
- * application module paths.
- *
- * @return {@code true} if no errors are found
- */
- private static boolean validateModules() {
- initOutput(System.out);
-
- ModuleValidator validator = new ModuleValidator();
-
- // upgrade module path
- String value = System.getProperty("jdk.module.upgrade.path");
- if (value != null) {
- Stream.of(value.split(File.pathSeparator))
- .map(Paths::get)
- .forEach(validator::scan);
- }
-
- // system modules
- ModuleFinder.ofSystem().findAll().stream()
- .sorted(Comparator.comparing(ModuleReference::descriptor))
- .forEach(validator::process);
-
- // application module path
- value = System.getProperty("jdk.module.path");
- if (value != null) {
- Stream.of(value.split(File.pathSeparator))
- .map(Paths::get)
- .forEach(validator::scan);
- }
-
- return !validator.foundErrors();
- }
-
- /**
- * A simple validator to check for errors and conflicts between modules.
- */
- static class ModuleValidator {
- private static final String MODULE_INFO = "module-info.class";
-
- private Map<String, ModuleReference> nameToModule = new HashMap<>();
- private Map<String, ModuleReference> packageToModule = new HashMap<>();
- private boolean errorFound;
-
- /**
- * Returns true if at least one error was found
- */
- boolean foundErrors() {
- return errorFound;
- }
-
- /**
- * Prints the module location and name.
- */
- private void printModule(ModuleReference mref) {
- mref.location()
- .filter(uri -> !isJrt(uri))
- .ifPresent(uri -> ostream.print(uri + " "));
- ModuleDescriptor descriptor = mref.descriptor();
- ostream.print(descriptor.name());
- if (descriptor.isAutomatic())
- ostream.print(" automatic");
- ostream.println();
- }
-
- /**
- * Prints the module location and name, checks if the module is
- * shadowed by a previously seen module, and finally checks for
- * package conflicts with previously seen modules.
- */
- void process(ModuleReference mref) {
- printModule(mref);
-
- String name = mref.descriptor().name();
- ModuleReference previous = nameToModule.putIfAbsent(name, mref);
- if (previous != null) {
- ostream.print(INDENT + "shadowed by ");
- printModule(previous);
- } else {
- // check for package conflicts when not shadowed
- for (String pkg : mref.descriptor().packages()) {
- previous = packageToModule.putIfAbsent(pkg, mref);
- if (previous != null) {
- String mn = previous.descriptor().name();
- ostream.println(INDENT + "contains " + pkg
- + " conflicts with module " + mn);
- errorFound = true;
- }
- }
- }
- }
-
- /**
- * Scan an element on a module path. The element is a directory
- * of modules, an exploded module, or a JAR file.
- */
- void scan(Path entry) {
- BasicFileAttributes attrs;
- try {
- attrs = Files.readAttributes(entry, BasicFileAttributes.class);
- } catch (NoSuchFileException ignore) {
- return;
- } catch (IOException ioe) {
- ostream.println(entry + " " + ioe);
- errorFound = true;
- return;
- }
-
- String fn = entry.getFileName().toString();
- if (attrs.isRegularFile() && fn.endsWith(".jar")) {
- // JAR file, explicit or automatic module
- scanModule(entry).ifPresent(this::process);
- } else if (attrs.isDirectory()) {
- Path mi = entry.resolve(MODULE_INFO);
- if (Files.exists(mi)) {
- // exploded module
- scanModule(entry).ifPresent(this::process);
- } else {
- // directory of modules
- scanDirectory(entry);
- }
- }
- }
-
- /**
- * Scan the JAR files and exploded modules in a directory.
- */
- private void scanDirectory(Path dir) {
- try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
- Map<String, Path> moduleToEntry = new HashMap<>();
-
- for (Path entry : stream) {
- BasicFileAttributes attrs;
- try {
- attrs = Files.readAttributes(entry, BasicFileAttributes.class);
- } catch (IOException ioe) {
- ostream.println(entry + " " + ioe);
- errorFound = true;
- continue;
- }
-
- ModuleReference mref = null;
-
- String fn = entry.getFileName().toString();
- if (attrs.isRegularFile() && fn.endsWith(".jar")) {
- mref = scanModule(entry).orElse(null);
- } else if (attrs.isDirectory()) {
- Path mi = entry.resolve(MODULE_INFO);
- if (Files.exists(mi)) {
- mref = scanModule(entry).orElse(null);
- }
- }
-
- if (mref != null) {
- String name = mref.descriptor().name();
- Path previous = moduleToEntry.putIfAbsent(name, entry);
- if (previous != null) {
- // same name as other module in the directory
- printModule(mref);
- ostream.println(INDENT + "contains same module as "
- + previous.getFileName());
- errorFound = true;
- } else {
- process(mref);
- }
- }
- }
- } catch (IOException ioe) {
- ostream.println(dir + " " + ioe);
- errorFound = true;
- }
- }
-
- /**
- * Scan a JAR file or exploded module.
- */
- private Optional<ModuleReference> scanModule(Path entry) {
- ModuleFinder finder = ModuleFinder.of(entry);
- try {
- return finder.findAll().stream().findFirst();
- } catch (FindException e) {
- ostream.println(entry);
- ostream.println(INDENT + e.getMessage());
- Throwable cause = e.getCause();
- if (cause != null) {
- ostream.println(INDENT + cause);
- }
- errorFound = true;
- return Optional.empty();
- }
- }
- }
}
--- a/src/java.base/share/native/libjli/java.c Thu Jun 21 10:54:07 2018 -0700
+++ b/src/java.base/share/native/libjli/java.c Thu Jun 21 18:56:35 2018 +0100
@@ -441,14 +441,6 @@
LEAVE();
}
- // validate modules on the module path, then exit
- if (validateModules) {
- jboolean okay = ValidateModules(env);
- CHECK_EXCEPTION_LEAVE(1);
- if (!okay) ret = 1;
- LEAVE();
- }
-
if (printVersion || showVersion) {
PrintJavaVersion(env, showVersion);
CHECK_EXCEPTION_LEAVE(0);
@@ -457,6 +449,11 @@
}
}
+ // modules have been validated at startup so exit
+ if (validateModules) {
+ LEAVE();
+ }
+
/* If the user specified neither a class name nor a JAR file */
if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) {
PrintUsage(env, printXUsage);
@@ -1955,20 +1952,6 @@
(*env)->CallStaticVoidMethod(env, cls, describeModuleID, joptString);
}
-/**
- * Validate modules
- */
-static jboolean
-ValidateModules(JNIEnv *env)
-{
- jmethodID validateModulesID;
- jclass cls = GetLauncherHelperClass(env);
- NULL_CHECK_RETURN_VALUE(cls, JNI_FALSE);
- validateModulesID = (*env)->GetStaticMethodID(env, cls, "validateModules", "()Z");
- NULL_CHECK_RETURN_VALUE(cls, JNI_FALSE);
- return (*env)->CallStaticBooleanMethod(env, cls, validateModulesID);
-}
-
/*
* Prints default usage or the Xusage message, see sun.launcher.LauncherHelper.java
*/
--- a/test/hotspot/jtreg/compiler/jvmci/TestValidateModules.java Thu Jun 21 10:54:07 2018 -0700
+++ b/test/hotspot/jtreg/compiler/jvmci/TestValidateModules.java Thu Jun 21 18:56:35 2018 +0100
@@ -37,7 +37,8 @@
public static void main(String... args) throws Exception {
ProcessTools.executeTestJava("-XX:+UnlockExperimentalVMOptions",
"-XX:+EnableJVMCI",
- "--validate-modules")
+ "--validate-modules",
+ "--list-modules")
.outputTo(System.out)
.errorTo(System.out)
.stdoutShouldContain("java.base")
--- a/test/jdk/tools/launcher/modules/validate/ValidateModulesTest.java Thu Jun 21 10:54:07 2018 -0700
+++ b/test/jdk/tools/launcher/modules/validate/ValidateModulesTest.java Thu Jun 21 18:56:35 2018 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -23,9 +23,10 @@
/**
* @test
+ * @bug 8178380 8194937
* @modules java.xml
- * @library /lib/testlibrary
- * @build ValidateModulesTest JarUtils jdk.testlibrary.*
+ * @library src /lib/testlibrary
+ * @build ValidateModulesTest hello/* JarUtils jdk.testlibrary.*
* @run testng ValidateModulesTest
* @summary Basic test for java --validate-modules
*/
@@ -45,13 +46,40 @@
public class ValidateModulesTest {
/**
- * Test that the system modules validate.
+ * Basic test --validate-modules when there are no errors.
*/
- public void testSystemModules() throws Exception {
- run("--validate-modules")
- .stdoutShouldContain("java.base")
- .stdoutShouldContain("java.xml")
- .shouldHaveExitValue(0);
+ public void testNoErrors() throws Exception {
+ String modulePath = System.getProperty("test.module.path");
+
+ test("--validate-modules");
+
+ test("--validate-modules", "-version")
+ .shouldContain("Runtime Environment");
+
+ test("--validate-modules", "--list-modules")
+ .shouldContain("java.base");
+
+ test("--validate-modules", "-d", "java.base")
+ .shouldContain("exports java.lang");
+
+ test("-p", modulePath, "-m", "hello/p.Main")
+ .shouldContain("Hello world");
+
+ test("-p", modulePath, "--validate-modules", "-m", "hello/p.Main")
+ .shouldNotContain("Hello world");
+
+ test("-p", modulePath, "--validate-modules", "--list-modules")
+ .shouldContain("hello");
+
+ test("-p", modulePath, "--validate-modules", "-d", "hello")
+ .shouldContain("hello")
+ .shouldContain("contains p");
+
+ testExpectingError("--validate-modules", "--add-modules", "BAD")
+ .shouldContain("Module BAD not found");
+
+ testExpectingError("--validate-modules", "-m", "BAD")
+ .shouldContain("Module BAD not found");
}
/**
@@ -68,12 +96,9 @@
Path lib = Files.createDirectory(tmpdir.resolve("lib"));
JarUtils.createJarFile(lib.resolve("xml.jar"), classes);
- int exitValue = run("-p", lib.toString(), "--validate-modules")
+ testExpectingError("-p", lib.toString(), "--validate-modules")
.shouldContain("xml automatic")
- .shouldContain("conflicts with module java.xml")
- .getExitValue();
- assertTrue(exitValue != 0);
-
+ .shouldContain("conflicts with module java.xml");
}
/**
@@ -89,10 +114,8 @@
JarUtils.createJarFile(lib.resolve("foo-1.0.jar"), classes);
JarUtils.createJarFile(lib.resolve("foo-2.0.jar"), classes);
- int exitValue = run("-p", lib.toString(), "--validate-modules")
- .shouldContain("contains same module")
- .getExitValue();
- assertTrue(exitValue != 0);
+ testExpectingError("-p", lib.toString(), "--validate-modules")
+ .shouldContain("contains same module");
}
/**
@@ -110,18 +133,30 @@
Path lib2 = Files.createDirectory(tmpdir.resolve("lib2"));
JarUtils.createJarFile(lib2.resolve("foo-2.0.jar"), classes);
- run("-p", lib1 + File.pathSeparator + lib2, "--validate-modules")
- .shouldContain("shadowed by")
- .shouldHaveExitValue(0);
+ test("-p", lib1 + File.pathSeparator + lib2, "--validate-modules")
+ .shouldContain("shadowed by");
}
/**
- * Runs the java launcher with the given arguments.
+ * Runs the java launcher with the given arguments, expecting a 0 exit code
*/
- private OutputAnalyzer run(String... args) throws Exception {
- return ProcessTools.executeTestJava(args)
+ private OutputAnalyzer test(String... args) throws Exception {
+ OutputAnalyzer analyzer = ProcessTools.executeTestJava(args)
.outputTo(System.out)
.errorTo(System.out);
+ assertTrue(analyzer.getExitValue() == 0);
+ return analyzer;
+ }
+
+ /**
+ * Runs the java launcher with the given arguments, expecting a non-0 exit code
+ */
+ private OutputAnalyzer testExpectingError(String... args) throws Exception {
+ OutputAnalyzer analyzer = ProcessTools.executeTestJava(args)
+ .outputTo(System.out)
+ .errorTo(System.out);
+ assertTrue(analyzer.getExitValue() != 0);
+ return analyzer;
}
/**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/launcher/modules/validate/src/hello/module-info.java Thu Jun 21 18:56:35 2018 +0100
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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.
+ */
+module hello { }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/launcher/modules/validate/src/hello/p/Main.java Thu Jun 21 18:56:35 2018 +0100
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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 p;
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello world");
+ }
+}