# HG changeset patch # User alanb # Date 1529603795 -3600 # Node ID 97e9c4f58986aa478bb49bded1769592e5e5957b # Parent cc7fc46cc8c19b2ef6ec459c5c7e33cfed783a33 8194937: Inconsistent behavior of --validate-modules when combined with -m and other options Reviewed-by: mchung diff -r cc7fc46cc8c1 -r 97e9c4f58986 src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java --- 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 allSystem = ModuleFinder.ofSystem().findAll() - .stream() - .map(ModuleReference::descriptor) - .map(ModuleDescriptor::name) - .collect(Collectors.toSet()); - - Configuration cf = SharedSecrets.getJavaLangModuleAccess() - .resolveAndBind(ModuleFinder.ofSystem(), - allSystem, - null); - - Function 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, diff -r cc7fc46cc8c1 -r 97e9c4f58986 src/java.base/share/classes/jdk/internal/module/ModulePathValidator.java --- /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 nameToModule; + private final Map 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 stream = Files.newDirectoryStream(dir)) { + Map 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 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")); + } +} diff -r cc7fc46cc8c1 -r 97e9c4f58986 src/java.base/share/classes/sun/launcher/LauncherHelper.java --- 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 nameToModule = new HashMap<>(); - private Map 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 stream = Files.newDirectoryStream(dir)) { - Map 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 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(); - } - } - } } diff -r cc7fc46cc8c1 -r 97e9c4f58986 src/java.base/share/native/libjli/java.c --- 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 */ diff -r cc7fc46cc8c1 -r 97e9c4f58986 test/hotspot/jtreg/compiler/jvmci/TestValidateModules.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") diff -r cc7fc46cc8c1 -r 97e9c4f58986 test/jdk/tools/launcher/modules/validate/ValidateModulesTest.java --- 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; } /** diff -r cc7fc46cc8c1 -r 97e9c4f58986 test/jdk/tools/launcher/modules/validate/src/hello/module-info.java --- /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 { } diff -r cc7fc46cc8c1 -r 97e9c4f58986 test/jdk/tools/launcher/modules/validate/src/hello/p/Main.java --- /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"); + } +}