# HG changeset patch # User mchung # Date 1490652721 25200 # Node ID a81c9c6619fb1502fe10c96d973699b6e0b92009 # Parent 18a7c084f50a4b4aa8e3e547f5b270e7b05f3d06 8174826: jlink support for linking in service provider modules Reviewed-by: alanb, anazarov diff -r 18a7c084f50a -r a81c9c6619fb jdk/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java Fri Mar 24 15:02:19 2017 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java Mon Mar 27 15:12:01 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 @@ -35,8 +35,12 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.MissingResourceException; import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import jdk.internal.jimage.BasicImageReader; import jdk.internal.jimage.ImageHeader; import jdk.internal.jimage.ImageLocation; @@ -99,7 +103,7 @@ } static class OptionsValues { - Task task = Task.LIST; + Task task = null; String directory = "."; String include = ""; boolean fullVersion; @@ -172,24 +176,31 @@ } try { - List unhandled = OPTION_HELPER.handleOptions(this, args); + String command; + String[] remaining = args; + try { + command = args[0]; + options.task = Enum.valueOf(Task.class, args[0].toUpperCase(Locale.ENGLISH)); + remaining = args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) + : new String[0]; + } catch (IllegalArgumentException ex) { + command = null; + options.task = null; + } - if(!unhandled.isEmpty()) { - try { - options.task = Enum.valueOf(Task.class, unhandled.get(0).toUpperCase()); - } catch (IllegalArgumentException ex) { - throw TASK_HELPER.newBadArgs("err.not.a.task", unhandled.get(0)); - } + // process arguments + List unhandled = OPTION_HELPER.handleOptions(this, remaining); + for (String f : unhandled) { + options.jimages.add(new File(f)); + } - for(int i = 1; i < unhandled.size(); i++) { - options.jimages.add(new File(unhandled.get(i))); - } - } else if (!options.help && !options.version && !options.fullVersion) { - throw TASK_HELPER.newBadArgs("err.invalid.task", ""); + if (options.task == null && !options.help && !options.version && !options.fullVersion) { + throw TASK_HELPER.newBadArgs("err.not.a.task", + command != null ? command : ""); } if (options.help) { - if (unhandled.isEmpty()) { + if (options.task == null) { log.println(TASK_HELPER.getMessage("main.usage", PROGNAME)); Arrays.asList(RECOGNIZED_OPTIONS).stream() .filter(option -> !option.isHidden()) @@ -203,15 +214,19 @@ log.println(TASK_HELPER.getMessage("main.usage." + options.task.toString().toLowerCase())); } catch (MissingResourceException ex) { - throw TASK_HELPER.newBadArgs("err.not.a.task", unhandled.get(0)); + throw TASK_HELPER.newBadArgs("err.not.a.task", command); } } return EXIT_OK; } if (options.version || options.fullVersion) { + if (options.task == null && !unhandled.isEmpty()) { + throw TASK_HELPER.newBadArgs("err.not.a.task", + Stream.of(args).collect(Collectors.joining(" "))); + } + TASK_HELPER.showVersion(options.fullVersion); - if (unhandled.isEmpty()) { return EXIT_OK; } @@ -435,7 +450,7 @@ iterate(this::listTitle, null, this::verify); break; default: - throw TASK_HELPER.newBadArgs("err.invalid.task", + throw TASK_HELPER.newBadArgs("err.not.a.task", options.task.name()).showUsage(true); } return true; diff -r 18a7c084f50a -r a81c9c6619fb jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Jlink.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Jlink.java Fri Mar 24 15:02:19 2017 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Jlink.java Mon Mar 27 15:12:01 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -24,6 +24,8 @@ */ package jdk.tools.jlink.internal; +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; import java.lang.reflect.Layer; import java.nio.ByteOrder; import java.nio.file.Path; @@ -33,6 +35,8 @@ import java.util.Map; import java.util.Objects; import java.util.Set; + +import jdk.internal.module.ModulePath; import jdk.tools.jlink.plugin.Plugin; import jdk.tools.jlink.plugin.PluginException; import jdk.tools.jlink.builder.ImageBuilder; @@ -147,8 +151,8 @@ private final Path output; private final Set modules; private final Set limitmods; - private final ByteOrder endian; + private final ModuleFinder finder; /** * jlink configuration, @@ -160,31 +164,23 @@ * @param endian Jimage byte order. Native order by default */ public JlinkConfiguration(Path output, - List modulepaths, - Set modules, - Set limitmods, - ByteOrder endian) { - this.output = output; - this.modulepaths = modulepaths == null ? Collections.emptyList() : modulepaths; - this.modules = modules == null ? Collections.emptySet() : modules; - this.limitmods = limitmods == null ? Collections.emptySet() : limitmods; - this.endian = endian == null ? ByteOrder.nativeOrder() : endian; - } + List modulepaths, + Set modules, + Set limitmods, + ByteOrder endian) { + if (Objects.requireNonNull(modulepaths).isEmpty()) { + throw new IllegalArgumentException("Empty module path"); + } + if (Objects.requireNonNull(modules).isEmpty()) { + throw new IllegalArgumentException("Empty modules"); + } - /** - * jlink configuration, - * - * @param output Output directory, must not exist. - * @param modulepaths Modules paths - * @param modules Root modules to resolve - * @param limitmods Limit the universe of observable modules - */ - public JlinkConfiguration(Path output, - List modulepaths, - Set modules, - Set limitmods) { - this(output, modulepaths, modules, limitmods, - ByteOrder.nativeOrder()); + this.output = output; + this.modulepaths = modulepaths; + this.modules = modules; + this.limitmods = Objects.requireNonNull(limitmods); + this.endian = Objects.requireNonNull(endian); + this.finder = moduleFinder(); } /** @@ -222,6 +218,45 @@ return limitmods; } + /** + * Returns {@link ModuleFinder} that finds all observable modules + * for this jlink configuration. + */ + public ModuleFinder finder() { + return finder; + } + + /** + * Returns a {@link Configuration} of the given module path, + * root modules with full service binding. + */ + public Configuration resolveAndBind() + { + return Configuration.empty().resolveAndBind(finder, + ModuleFinder.of(), + modules); + } + + /** + * Returns a {@link Configuration} of the given module path, + * root modules with no service binding. + */ + public Configuration resolve() + { + return Configuration.empty().resolve(finder, + ModuleFinder.of(), + modules); + } + + private ModuleFinder moduleFinder() { + Path[] entries = modulepaths.toArray(new Path[0]); + ModuleFinder finder = ModulePath.of(Runtime.version(), true, entries); + if (!limitmods.isEmpty()) { + finder = JlinkTask.limitFinder(finder, limitmods, modules); + } + return finder; + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); diff -r 18a7c084f50a -r a81c9c6619fb jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java Fri Mar 24 15:02:19 2017 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java Mon Mar 27 15:12:01 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -110,6 +110,12 @@ Path path = Paths.get(arg); task.options.output = path; }, "--output"), + new Option(false, (task, opt, arg) -> { + task.options.bindServices = true; + }, "--bind-services"), + new Option(false, (task, opt, arg) -> { + task.options.suggestProviders = true; + }, "--suggest-providers", "", true), new Option(true, (task, opt, arg) -> { String[] values = arg.split("="); // check values @@ -141,6 +147,9 @@ } }, "--endian"), new Option(false, (task, opt, arg) -> { + task.options.verbose = true; + }, "--verbose", "-v"), + new Option(false, (task, opt, arg) -> { task.options.version = true; }, "--version"), new Option(true, (task, opt, arg) -> { @@ -185,6 +194,7 @@ static class OptionsValues { boolean help; String saveoptsfile; + boolean verbose; boolean version; boolean fullVersion; final List modulePath = new ArrayList<>(); @@ -195,6 +205,8 @@ Path packagedModulesPath; ByteOrder endian = ByteOrder.nativeOrder(); boolean ignoreSigning = false; + boolean bindServices = false; + boolean suggestProviders = false; } int run(String[] args) { @@ -203,7 +215,11 @@ new PrintWriter(System.err, true)); } try { - optionsHelper.handleOptionsNoUnhandled(this, args); + List remaining = optionsHelper.handleOptions(this, args); + if (remaining.size() > 0 && !options.suggestProviders) { + throw taskHelper.newBadArgs("err.orphan.arguments", toString(remaining)) + .showUsage(true); + } if (options.help) { optionsHelper.showHelp(PROGNAME); return EXIT_OK; @@ -217,17 +233,24 @@ return EXIT_OK; } - if (taskHelper.getExistingImage() == null) { - if (options.modulePath.isEmpty()) { - throw taskHelper.newBadArgs("err.modulepath.must.be.specified").showUsage(true); - } - createImage(); - } else { + if (taskHelper.getExistingImage() != null) { postProcessOnly(taskHelper.getExistingImage()); + return EXIT_OK; } - if (options.saveoptsfile != null) { - Files.write(Paths.get(options.saveoptsfile), getSaveOpts().getBytes()); + if (options.modulePath.isEmpty()) { + throw taskHelper.newBadArgs("err.modulepath.must.be.specified") + .showUsage(true); + } + + JlinkConfiguration config = initJlinkConfig(); + if (options.suggestProviders) { + suggestProviders(config, remaining); + } else { + createImage(config); + if (options.saveoptsfile != null) { + Files.write(Paths.get(options.saveoptsfile), getSaveOpts().getBytes()); + } } return EXIT_OK; @@ -266,25 +289,13 @@ Objects.requireNonNull(config.getOutput()); plugins = plugins == null ? new PluginsConfiguration() : plugins; - if (config.getModulepaths().isEmpty()) { - throw new IllegalArgumentException("Empty module paths"); - } - - ModuleFinder finder = newModuleFinder(config.getModulepaths(), - config.getLimitmods(), - config.getModules()); - - if (config.getModules().isEmpty()) { - throw new IllegalArgumentException("No modules to add"); - } - // First create the image provider ImageProvider imageProvider = - createImageProvider(finder, - config.getModules(), - config.getByteOrder(), + createImageProvider(config, null, IGNORE_SIGNING_DEFAULT, + false, + false, null); // Then create the Plugin Stack @@ -319,20 +330,24 @@ // the token for "all modules on the module path" private static final String ALL_MODULE_PATH = "ALL-MODULE-PATH"; - private void createImage() throws Exception { - if (options.output == null) { - throw taskHelper.newBadArgs("err.output.must.be.specified").showUsage(true); - } - + private JlinkConfiguration initJlinkConfig() throws BadArgs { if (options.addMods.isEmpty()) { throw taskHelper.newBadArgs("err.mods.must.be.specified", "--add-modules") - .showUsage(true); + .showUsage(true); } Set roots = new HashSet<>(); for (String mod : options.addMods) { if (mod.equals(ALL_MODULE_PATH)) { - ModuleFinder finder = modulePathFinder(); + Path[] entries = options.modulePath.toArray(new Path[0]); + ModuleFinder finder = ModulePath.of(Runtime.version(), true, entries); + if (!options.limitMods.isEmpty()) { + // finder for the observable modules specified in + // the --module-path and --limit-modules options + finder = limitFinder(finder, options.limitMods, Collections.emptySet()); + } + + // all observable modules are roots finder.findAll() .stream() .map(ModuleReference::descriptor) @@ -343,40 +358,34 @@ } } - ModuleFinder finder = newModuleFinder(options.modulePath, - options.limitMods, - roots); + return new JlinkConfiguration(options.output, + options.modulePath, + roots, + options.limitMods, + options.endian); + } + private void createImage(JlinkConfiguration config) throws Exception { + if (options.output == null) { + throw taskHelper.newBadArgs("err.output.must.be.specified").showUsage(true); + } // First create the image provider - ImageProvider imageProvider = createImageProvider(finder, - roots, - options.endian, + ImageProvider imageProvider = createImageProvider(config, options.packagedModulesPath, options.ignoreSigning, + options.bindServices, + options.verbose, log); // Then create the Plugin Stack - ImagePluginStack stack = ImagePluginConfiguration. - parseConfiguration(taskHelper.getPluginsConfig(options.output, options.launchers)); + ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration( + taskHelper.getPluginsConfig(options.output, options.launchers)); //Ask the stack to proceed stack.operate(imageProvider); } - /** - * Returns a module finder to find the observable modules specified in - * the --module-path and --limit-modules options - */ - private ModuleFinder modulePathFinder() { - Path[] entries = options.modulePath.toArray(new Path[0]); - ModuleFinder finder = ModulePath.of(Runtime.version(), true, entries); - if (!options.limitMods.isEmpty()) { - finder = limitFinder(finder, options.limitMods, Collections.emptySet()); - } - return finder; - } - /* * Returns a module finder of the given module path that limits * the observable modules to those in the transitive closure of @@ -405,22 +414,32 @@ return Paths.get(uri); } - private static ImageProvider createImageProvider(ModuleFinder finder, - Set roots, - ByteOrder order, + + private static ImageProvider createImageProvider(JlinkConfiguration config, Path retainModulesPath, boolean ignoreSigning, + boolean bindService, + boolean verbose, PrintWriter log) throws IOException { - if (roots.isEmpty()) { - throw new IllegalArgumentException("empty modules and limitmods"); - } + Configuration cf = bindService ? config.resolveAndBind() + : config.resolve(); - Configuration cf = Configuration.empty() - .resolve(finder, - ModuleFinder.of(), - roots); + if (verbose && log != null) { + // print modules to be linked in + cf.modules().stream() + .sorted(Comparator.comparing(ResolvedModule::name)) + .forEach(rm -> log.format("module %s (%s)%n", + rm.name(), rm.reference().location().get())); + + // print provider info + Set references = cf.modules().stream() + .map(ResolvedModule::reference).collect(Collectors.toSet()); + + String msg = String.format("%n%s:", taskHelper.getMessage("providers.header")); + printProviders(log, msg, references); + } // emit a warning for any incubating modules in the configuration if (log != null) { @@ -438,16 +457,16 @@ Map mods = cf.modules().stream() .collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation)); - return new ImageHelper(cf, mods, order, retainModulesPath, ignoreSigning); + return new ImageHelper(cf, mods, config.getByteOrder(), retainModulesPath, ignoreSigning); } /* * Returns a ModuleFinder that limits observability to the given root * modules, their transitive dependences, plus a set of other modules. */ - private static ModuleFinder limitFinder(ModuleFinder finder, - Set roots, - Set otherMods) { + public static ModuleFinder limitFinder(ModuleFinder finder, + Set roots, + Set otherMods) { // resolve all root modules Configuration cf = Configuration.empty() @@ -484,6 +503,147 @@ }; } + /* + * Returns a map of each service type to the modules that use it + */ + private static Map> uses(Set modules) { + // collects the services used by the modules and print uses + Map> uses = new HashMap<>(); + modules.stream() + .map(ModuleReference::descriptor) + .forEach(md -> md.uses().forEach(s -> + uses.computeIfAbsent(s, _k -> new HashSet<>()).add(md.name())) + ); + return uses; + } + + private static void printProviders(PrintWriter log, + String header, + Set modules) { + printProviders(log, header, modules, uses(modules)); + } + + /* + * Prints the providers that are used by the services specified in + * the given modules. + * + * The specified uses maps a service type name to the modules + * using the service type and that may or may not be present + * the given modules. + */ + private static void printProviders(PrintWriter log, + String header, + Set modules, + Map> uses) { + if (modules.isEmpty()) + return; + + // Build a map of a service type to the provider modules + Map> providers = new HashMap<>(); + modules.stream() + .map(ModuleReference::descriptor) + .forEach(md -> { + md.provides().stream() + .filter(p -> uses.containsKey(p.service())) + .forEach(p -> providers.computeIfAbsent(p.service(), _k -> new HashSet<>()) + .add(md)); + }); + + if (!providers.isEmpty()) { + log.println(header); + } + + // print the providers of the service types used by the specified modules + // sorted by the service type name and then provider's module name + providers.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .forEach(e -> { + String service = e.getKey(); + e.getValue().stream() + .sorted(Comparator.comparing(ModuleDescriptor::name)) + .forEach(md -> + md.provides().stream() + .filter(p -> p.service().equals(service)) + .forEach(p -> log.format(" module %s provides %s, used by %s%n", + md.name(), p.service(), + uses.get(p.service()).stream() + .sorted() + .collect(Collectors.joining(",")))) + ); + }); + } + + private void suggestProviders(JlinkConfiguration config, List args) + throws BadArgs + { + if (args.size() > 1) { + throw taskHelper.newBadArgs("err.orphan.argument", + toString(args.subList(1, args.size()))) + .showUsage(true); + } + + if (options.bindServices) { + log.println(taskHelper.getMessage("no.suggested.providers")); + return; + } + + ModuleFinder finder = config.finder(); + if (args.isEmpty()) { + // print providers used by the modules resolved without service binding + Configuration cf = config.resolve(); + Set mrefs = cf.modules().stream() + .map(ResolvedModule::reference) + .collect(Collectors.toSet()); + + // print uses of the modules that would be linked into the image + mrefs.stream() + .sorted(Comparator.comparing(mref -> mref.descriptor().name())) + .forEach(mref -> { + ModuleDescriptor md = mref.descriptor(); + log.format("module %s located (%s)%n", md.name(), + mref.location().get()); + md.uses().stream().sorted() + .forEach(s -> log.format(" uses %s%n", s)); + }); + + String msg = String.format("%n%s:", taskHelper.getMessage("suggested.providers.header")); + printProviders(log, msg, finder.findAll(), uses(mrefs)); + + } else { + // comma-separated service types, if specified + Set names = Stream.of(args.get(0).split(",")) + .collect(Collectors.toSet()); + // find the modules that provide the specified service + Set mrefs = finder.findAll().stream() + .filter(mref -> mref.descriptor().provides().stream() + .map(ModuleDescriptor.Provides::service) + .anyMatch(names::contains)) + .collect(Collectors.toSet()); + + // the specified services may or may not be in the modules that + // would be linked in. So find uses declared in all observable modules + Map> uses = uses(finder.findAll()); + + // check if any name given on the command line are unused service + mrefs.stream() + .flatMap(mref -> mref.descriptor().provides().stream() + .map(ModuleDescriptor.Provides::service)) + .forEach(names::remove); + if (!names.isEmpty()) { + log.println(taskHelper.getMessage("warn.unused.services", + toString(names))); + } + + String msg = String.format("%n%s:", taskHelper.getMessage("suggested.providers.header")); + printProviders(log, msg, mrefs, uses); + } + } + + private static String toString(Collection collection) { + return collection.stream().sorted() + .collect(Collectors.joining(",")); + } + private String getSaveOpts() { StringBuilder sb = new StringBuilder(); sb.append('#').append(new Date()).append("\n"); diff -r 18a7c084f50a -r a81c9c6619fb jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java Fri Mar 24 15:02:19 2017 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java Mon Mar 27 15:12:01 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -46,6 +46,7 @@ import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.tools.jlink.internal.plugins.ExcludeJmodSectionPlugin; @@ -101,8 +102,15 @@ final boolean hidden; final String name; final String shortname; + final boolean terminalOption; - public Option(boolean hasArg, Processing processing, boolean hidden, String name, String shortname) { + public Option(boolean hasArg, + Processing processing, + boolean hidden, + String name, + String shortname, + boolean isTerminal) + { if (!name.startsWith("--")) { throw new RuntimeException("option name missing --, " + name); } @@ -115,24 +123,33 @@ this.hidden = hidden; this.name = name; this.shortname = shortname; + this.terminalOption = isTerminal; + } + + public Option(boolean hasArg, Processing processing, String name, String shortname, boolean isTerminal) { + this(hasArg, processing, false, name, shortname, isTerminal); } public Option(boolean hasArg, Processing processing, String name, String shortname) { - this(hasArg, processing, false, name, shortname); + this(hasArg, processing, false, name, shortname, false); } public Option(boolean hasArg, Processing processing, boolean hidden, String name) { - this(hasArg, processing, hidden, name, ""); + this(hasArg, processing, hidden, name, "", false); } public Option(boolean hasArg, Processing processing, String name) { - this(hasArg, processing, false, name, ""); + this(hasArg, processing, false, name, "", false); } public boolean isHidden() { return hidden; } + public boolean isTerminal() { + return terminalOption; + } + public boolean matches(String opt) { return opt.equals(name) || opt.equals(shortname) || @@ -179,12 +196,12 @@ private static class PluginOption extends Option { public PluginOption(boolean hasArg, Processing processing, boolean hidden, String name, String shortname) { - super(hasArg, processing, hidden, name, shortname); + super(hasArg, processing, hidden, name, shortname, false); } public PluginOption(boolean hasArg, Processing processing, boolean hidden, String name) { - super(hasArg, processing, hidden, name, ""); + super(hasArg, processing, hidden, name, "", false); } public String resourcePrefix() { @@ -498,21 +515,13 @@ return null; } - // used by jimage. Return unhandled arguments like "create", "describe". + /** + * Handles all options. This method stops processing the argument + * at the first non-option argument i.e. not starts with `-`, or + * at the first terminal option and returns the remaining arguments, + * if any. + */ public List handleOptions(T task, String[] args) throws BadArgs { - return handleOptions(task, args, true); - } - - // used by jlink. No unhandled arguments like "create", "describe". - void handleOptionsNoUnhandled(T task, String[] args) throws BadArgs { - handleOptions(task, args, false); - } - - // shared code that handles options for both jlink and jimage. jimage uses arguments like - // "create", "describe" etc. as "task names". Those arguments are unhandled here and returned - // as "unhandled arguments list". jlink does not want such arguments. "collectUnhandled" flag - // tells whether to allow for unhandled arguments or not. - private List handleOptions(T task, String[] args, boolean collectUnhandled) throws BadArgs { // findbugs warning, copy instead of keeping a reference. command = Arrays.copyOf(args, args.length); @@ -521,7 +530,6 @@ // Unit tests can call Task multiple time in same JVM. pluginOptions = new PluginsHelper(null); - List rest = collectUnhandled? new ArrayList<>() : null; // process options for (int i = 0; i < args.length; i++) { if (args[i].startsWith("-")) { @@ -531,7 +539,6 @@ if (option == null) { pluginOption = pluginOptions.getOption(name); if (pluginOption == null) { - throw new BadArgs("err.unknown.option", name). showUsage(true); } @@ -556,20 +563,23 @@ pluginOption.process(pluginOptions, name, param); } else { option.process(task, name, param); + if (option.isTerminal()) { + return ++i < args.length + ? Stream.of(Arrays.copyOfRange(args, i, args.length)) + .collect(Collectors.toList()) + : Collections.emptyList(); + + } } if (opt.ignoreRest()) { i = args.length; } } else { - if (collectUnhandled) { - rest.add(args[i]); - } else { - throw new BadArgs("err.orphan.argument", args[i]). - showUsage(true); - } + return Stream.of(Arrays.copyOfRange(args, i, args.length)) + .collect(Collectors.toList()); } } - return rest; + return Collections.emptyList(); } private Option getOption(String name) { diff -r 18a7c084f50a -r a81c9c6619fb jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java Fri Mar 24 15:02:19 2017 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java Mon Mar 27 15:12:01 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -34,6 +34,7 @@ import java.io.File; import java.io.IOException; import java.lang.module.ModuleFinder; +import java.nio.ByteOrder; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; @@ -93,9 +94,12 @@ public void build() throws IOException { // jlink main arguments - Jlink.JlinkConfiguration jlinkConfig = new Jlink.JlinkConfiguration( - new File("").toPath(), // Unused - modulePath, addModules, limitModules); + Jlink.JlinkConfiguration jlinkConfig = + new Jlink.JlinkConfiguration(new File("").toPath(), // Unused + modulePath, + addModules, + limitModules, + ByteOrder.nativeOrder()); // plugin configuration List plugins = new ArrayList(); diff -r 18a7c084f50a -r a81c9c6619fb jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties Fri Mar 24 15:02:19 2017 -0700 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties Mon Mar 27 15:12:01 2017 -0700 @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2017, 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 @@ -24,15 +24,12 @@ # main.usage.summary=\ -Usage: {0} --module-path --add-modules --output\n\ -\ use --help for a list of possible options +Usage: {0} --module-path --add-modules [,...]\n\ +\Use --help for a list of possible options main.usage=\ -Usage: {0} --module-path --add-modules --output\n\ -\ Possible options include: - -error.prefix=Error: -warn.prefix=Warning: +Usage: {0} --module-path --add-modules [,...]\n\ +\Possible options include: main.opt.help=\ \ -h, --help Print this help message @@ -54,9 +51,18 @@ \ --output Location of output path main.opt.launcher=\ -\ --launcher = Launcher command name for the module\n\ -\ --launcher =/
\n\ -\ Launcher command name for the module and the main class +\ --launcher =[/]\n\ +\ Add a launcher command of the given\n\ +\ name for the module and the main class\n\ +\ if specified + +main.opt.bind-services=\ +\ --bind-services Do full service binding + +main.opt.suggest-providers=\ +\ --suggest-providers [,...] Suggest providers of services used by\n\ +\ the modules that would be linked, or\n\ +\ of the given service types main.command.files=\ \ @ Read options from file @@ -75,6 +81,9 @@ \ signed modular JARs are not copied to\n\ \ the runtime image. +main.opt.verbose=\ +\ -v, --verbose Enable verbose tracing + main.msg.bug=\ An exception has occurred in jlink. \ Please file a bug at the Java Bug Database (http://bugreport.java.com/bugreport/) \ @@ -95,6 +104,9 @@ \n\ +error.prefix=Error: +warn.prefix=Warning: + err.unknown.byte.order:unknown byte order {0} err.launcher.main.class.empty:launcher main class name cannot be empty: {0} err.launcher.module.name.empty:launcher module name cannot be empty: {0} @@ -111,12 +123,12 @@ err.dir.exists={0} already exists err.badpattern=bad pattern {0} err.unknown.option=unknown option: {0} -err.orphan.argument=orphan argument: {0} err.missing.arg=no value given for {0} err.internal.error=internal error: {0} {1} {2} err.invalid.arg.for.option=invalid argument for option: {0} err.option.after.class=option must be specified before classes: {0} err.option.unsupported={0} not supported: {1} +err.orphan.arguments=invalid argument: {0} err.config.defaults=property {0} is missing from configuration err.config.defaults.value=wrong value in defaults property: {0} err.bom.generation=bom file generation failed: {0} @@ -126,3 +138,7 @@ warn.signing=WARNING: signed modular JAR {0} is currently not supported warn.invalid.arg=invalid classname or pathname not exist: {0} warn.split.package=package {0} defined in {1} {2} +warn.unused.services=Services specified in --suggest-providers not used: {0} +no.suggested.providers=--bind-services option is specified. No additional providers suggested. +suggested.providers.header=Suggested providers +providers.header=Providers diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/IntegrationTest.java --- a/jdk/test/tools/jlink/IntegrationTest.java Fri Mar 24 15:02:19 2017 -0700 +++ b/jdk/test/tools/jlink/IntegrationTest.java Mon Mar 27 15:12:01 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -25,6 +25,7 @@ import java.io.FileReader; import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.ByteOrder; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -135,11 +136,6 @@ } System.out.println(jl); - JlinkConfiguration config - = new JlinkConfiguration(null, null, null, null); - - System.out.println(config); - Plugin p = Jlink.newPlugin("toto", Collections.emptyMap(), null); if (p != null) { throw new Exception("Plugin should be null"); @@ -163,7 +159,7 @@ Set limits = new HashSet<>(); limits.add("java.management"); JlinkConfiguration config = new Jlink.JlinkConfiguration(output, - modulePaths, mods, limits, null); + modulePaths, mods, limits, ByteOrder.nativeOrder()); List lst = new ArrayList<>(); diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/JLinkTest.java --- a/jdk/test/tools/jlink/JLinkTest.java Fri Mar 24 15:02:19 2017 -0700 +++ b/jdk/test/tools/jlink/JLinkTest.java Mon Mar 27 15:12:01 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -274,7 +274,7 @@ String[] userOptions = {"--compress", "2", "foo" }; String moduleName = "orphanarg1"; helper.generateDefaultJModule(moduleName, "composite2"); - helper.generateDefaultImage(userOptions, moduleName).assertFailure("Error: orphan argument: foo"); + helper.generateDefaultImage(userOptions, moduleName).assertFailure("Error: invalid argument: foo"); } // orphan argument - JDK-8166810 @@ -282,7 +282,7 @@ String[] userOptions = {"--output", "foo", "bar" }; String moduleName = "orphanarg2"; helper.generateDefaultJModule(moduleName, "composite2"); - helper.generateDefaultImage(userOptions, moduleName).assertFailure("Error: orphan argument: bar"); + helper.generateDefaultImage(userOptions, moduleName).assertFailure("Error: invalid argument: bar"); } // basic check for --help - JDK-8173717 diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/bindservices/BindServices.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/bindservices/BindServices.java Mon Mar 27 15:12:01 2017 -0700 @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.spi.ToolProvider; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static jdk.testlibrary.Asserts.assertTrue; +import static jdk.testlibrary.ProcessTools.*; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +/** + * @test + * @bug 8174826 + * @library /lib/testlibrary + * @modules jdk.compiler jdk.jlink + * @build BindServices CompilerUtils jdk.testlibrary.ProcessTools + * @run testng BindServices + */ + +public class BindServices { + private static final String JAVA_HOME = System.getProperty("java.home"); + private static final String TEST_SRC = System.getProperty("test.src"); + + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + private static final Path MODS_DIR = Paths.get("mods"); + + private static final String MODULE_PATH = + Paths.get(JAVA_HOME, "jmods").toString() + + File.pathSeparator + MODS_DIR.toString(); + + // the names of the modules in this test + private static String[] modules = new String[] {"m1", "m2", "m3"}; + + + private static boolean hasJmods() { + if (!Files.exists(Paths.get(JAVA_HOME, "jmods"))) { + System.err.println("Test skipped. NO jmods directory"); + return false; + } + return true; + } + + /* + * Compiles all modules used by the test + */ + @BeforeTest + public void compileAll() throws Throwable { + if (!hasJmods()) return; + + for (String mn : modules) { + Path msrc = SRC_DIR.resolve(mn); + assertTrue(CompilerUtils.compile(msrc, MODS_DIR, + "--module-source-path", SRC_DIR.toString())); + } + } + + @Test + public void noServiceBinding() throws Throwable { + if (!hasJmods()) return; + + Path dir = Paths.get("noServiceBinding"); + + // no service binding and does not link m2,m3 providers. + JLink.run("--output", dir.toString(), + "--module-path", MODULE_PATH, + "--add-modules", "m1").output(); + + testImage(dir, "m1"); + } + + @Test + public void fullServiceBinding() throws Throwable { + if (!hasJmods()) return; + + Path dir = Paths.get("fullServiceBinding"); + + // full service binding + // m2 is a provider used by m1. During service binding, when m2 is + // resolved, m2 uses p2.T that causes m3 to be linked as it is a + // provider to p2.T + JLink.run("--output", dir.toString(), + "--module-path", MODULE_PATH, + "--add-modules", "m1", + "--bind-services", + "--limit-modules", "m1,m2,m3,java.base"); + + testImage(dir, "m1", "m2", "m3"); + } + + @Test + public void testVerbose() throws Throwable { + if (!hasJmods()) return; + + Path dir = Paths.get("verbose"); + + List output = + JLink.run("--output", dir.toString(), + "--module-path", MODULE_PATH, + "--add-modules", "m1", + "--bind-services", + "--verbose", + "--limit-modules", "m1,m2,m3,java.base").output(); + + List expected = List.of( + "module m1 (" + MODS_DIR.resolve("m1").toUri().toString() + ")", + "module m2 (" + MODS_DIR.resolve("m2").toUri().toString() + ")", + "module m3 (" + MODS_DIR.resolve("m3").toUri().toString() + ")", + "module m1 provides p1.S, used by m1", + "module m2 provides p1.S, used by m1", + "module m2 provides p2.T, used by m2", + "module m3 provides p2.T, used by m2" + ); + + assertTrue(output.containsAll(expected)); + + testImage(dir, "m1", "m2", "m3"); + } + + /* + * Tests the given ${java.home} to only contain the specified modules + */ + private void testImage(Path javaHome, String... modules) throws Throwable { + Path java = javaHome.resolve("bin").resolve("java"); + String[] cmd = Stream.concat( + Stream.of(java.toString(), "-m", "m1/p1.Main"), + Stream.of(modules)).toArray(String[]::new); + + assertTrue(executeProcess(cmd).outputTo(System.out) + .errorTo(System.out) + .getExitValue() == 0); + } + + static class JLink { + static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink") + .orElseThrow(() -> + new RuntimeException("jlink tool not found") + ); + + static JLink run(String... options) { + JLink jlink = new JLink(); + assertTrue(jlink.execute(options) == 0); + return jlink; + } + + final List output = new ArrayList<>(); + private int execute(String... options) { + System.out.println("jlink " + + Stream.of(options).collect(Collectors.joining(" "))); + + StringWriter writer = new StringWriter(); + PrintWriter pw = new PrintWriter(writer); + int rc = JLINK_TOOL.run(pw, pw, options); + System.out.println(writer.toString()); + Stream.of(writer.toString().split("\\v")) + .map(String::trim) + .forEach(output::add); + return rc; + } + + boolean contains(String s) { + return output.contains(s); + } + + List output() { + return output; + } + } +} diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/bindservices/SuggestProviders.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/bindservices/SuggestProviders.java Mon Mar 27 15:12:01 2017 -0700 @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.spi.ToolProvider; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static jdk.testlibrary.Asserts.assertTrue; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +/** + * @test + * @bug 8174826 + * @library /lib/testlibrary + * @modules jdk.charsets jdk.compiler jdk.jlink + * @build SuggestProviders CompilerUtils + * @run testng SuggestProviders + */ + +public class SuggestProviders { + private static final String JAVA_HOME = System.getProperty("java.home"); + private static final String TEST_SRC = System.getProperty("test.src"); + + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + private static final Path MODS_DIR = Paths.get("mods"); + + private static final String MODULE_PATH = + Paths.get(JAVA_HOME, "jmods").toString() + + File.pathSeparator + MODS_DIR.toString(); + + // the names of the modules in this test + private static String[] modules = new String[] {"m1", "m2", "m3"}; + + + private static boolean hasJmods() { + if (!Files.exists(Paths.get(JAVA_HOME, "jmods"))) { + System.err.println("Test skipped. NO jmods directory"); + return false; + } + return true; + } + + /* + * Compiles all modules used by the test + */ + @BeforeTest + public void compileAll() throws Throwable { + if (!hasJmods()) return; + + for (String mn : modules) { + Path msrc = SRC_DIR.resolve(mn); + assertTrue(CompilerUtils.compile(msrc, MODS_DIR, + "--module-source-path", SRC_DIR.toString())); + } + } + + @Test + public void suggestProviders() throws Throwable { + if (!hasJmods()) return; + + List output = JLink.run("--module-path", MODULE_PATH, + "--add-modules", "m1", + "--suggest-providers").output(); + // check a subset of services used by java.base + List expected = List.of( + "uses java.lang.System$LoggerFinder", + "uses java.net.ContentHandlerFactory", + "uses java.net.spi.URLStreamHandlerProvider", + "uses java.nio.channels.spi.AsynchronousChannelProvider", + "uses java.nio.channels.spi.SelectorProvider", + "uses java.nio.charset.spi.CharsetProvider", + "uses java.nio.file.spi.FileSystemProvider", + "uses java.nio.file.spi.FileTypeDetector", + "uses java.security.Provider", + "uses java.util.spi.ToolProvider", + "uses p1.S", + "module jdk.charsets provides java.nio.charset.spi.CharsetProvider, used by java.base", + "module jdk.compiler provides java.util.spi.ToolProvider, used by java.base", + "module jdk.jlink provides java.util.spi.ToolProvider, used by java.base", + "module m1 provides p1.S, used by m1", + "module m2 provides p1.S, used by m1" + ); + + assertTrue(output.containsAll(expected)); + } + + @Test + public void providersForServices() throws Throwable { + if (!hasJmods()) return; + + List output = + JLink.run("--module-path", MODULE_PATH, + "--add-modules", "m1", + "--suggest-providers", + "java.nio.charset.spi.CharsetProvider,p1.S,p2.T").output(); + + System.out.println(output); + List expected = List.of( + "module jdk.charsets provides java.nio.charset.spi.CharsetProvider, used by java.base", + "module m1 provides p1.S, used by m1", + "module m2 provides p1.S, used by m1", + "module m2 provides p2.T, used by m2", + "module m3 provides p2.T, used by m2" + ); + + assertTrue(output.containsAll(expected)); + } + + @Test + public void unusedService() throws Throwable { + if (!hasJmods()) return; + + List output = + JLink.run("--module-path", MODULE_PATH, + "--add-modules", "m1", + "--suggest-providers", + "nonExistentType").output(); + + System.out.println(output); + List expected = List.of( + "Services specified in --suggest-providers not used: nonExistentType" + ); + + assertTrue(output.containsAll(expected)); + } + + @Test + public void noSuggestProviders() throws Throwable { + if (!hasJmods()) return; + + List output = + JLink.run("--module-path", MODULE_PATH, + "--add-modules", "m1", + "--bind-services", + "--limit-modules", "m1,m2,m3,java.base", + "--suggest-providers").output(); + + String expected = "--bind-services option is specified. No additional providers suggested."; + assertTrue(output.contains(expected)); + + } + + static class JLink { + static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink") + .orElseThrow(() -> + new RuntimeException("jlink tool not found") + ); + + static JLink run(String... options) { + JLink jlink = new JLink(); + assertTrue(jlink.execute(options) == 0); + return jlink; + } + + final List output = new ArrayList<>(); + private int execute(String... options) { + System.out.println("jlink " + + Stream.of(options).collect(Collectors.joining(" "))); + + StringWriter writer = new StringWriter(); + PrintWriter pw = new PrintWriter(writer); + int rc = JLINK_TOOL.run(pw, pw, options); + System.out.println(writer.toString()); + Stream.of(writer.toString().split("\\v")) + .map(String::trim) + .forEach(output::add); + return rc; + } + + boolean contains(String s) { + return output.contains(s); + } + + List output() { + return output; + } + } +} diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/bindservices/src/m1/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/bindservices/src/m1/module-info.java Mon Mar 27 15:12:01 2017 -0700 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017, 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 m1 { + exports p1; + uses p1.S; + provides p1.S with p1.Impl; +} diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/bindservices/src/m1/p1/Impl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/bindservices/src/m1/p1/Impl.java Mon Mar 27 15:12:01 2017 -0700 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017, 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 p1; + +public class Impl implements S { + public String name() { + return this.getClass().getName(); + } +} diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/bindservices/src/m1/p1/Main.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/bindservices/src/m1/p1/Main.java Mon Mar 27 15:12:01 2017 -0700 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017, 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 p1; + +import java.lang.module.ModuleFinder; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * This tests if JAVA_HOME is linked only with the specified modules. + */ +public class Main { + public static void main(String... args) { + Set modules = ModuleFinder.ofSystem().findAll().stream() + .map(mref -> mref.descriptor().name()) + .filter(mn -> !mn.equals("java.base")) + .collect(Collectors.toSet()); + + Set notLinked = Stream.of(args).filter(mn -> !modules.contains(mn)) + .collect(Collectors.toSet()); + if (!notLinked.isEmpty()) { + throw new RuntimeException("Expected modules not linked in the image: " + + notLinked); + } + Stream.of(args).forEach(modules::remove); + + if (!modules.isEmpty()) { + throw new RuntimeException("Unexpected modules linked in the image: " + + modules); + } + } +} diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/bindservices/src/m1/p1/S.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/bindservices/src/m1/p1/S.java Mon Mar 27 15:12:01 2017 -0700 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017, 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 p1; + +public interface S { + String name(); +} diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/bindservices/src/m2/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/bindservices/src/m2/module-info.java Mon Mar 27 15:12:01 2017 -0700 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017, 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 m2 { + requires m1; + exports p2; + uses p2.T; + provides p1.S with p2.Impl; + provides p2.T with p2.Impl; +} diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/bindservices/src/m2/p2/Impl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/bindservices/src/m2/p2/Impl.java Mon Mar 27 15:12:01 2017 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, 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 p2; + +public class Impl implements p1.S, T { + public String name() { + return this.getClass().getName(); + } + + public void run() { + } +} diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/bindservices/src/m2/p2/T.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/bindservices/src/m2/p2/T.java Mon Mar 27 15:12:01 2017 -0700 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017, 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 p2; + +public interface T { + void run(); +} diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/bindservices/src/m3/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/bindservices/src/m3/module-info.java Mon Mar 27 15:12:01 2017 -0700 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017, 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 m3 { + requires m2; + provides p2.T with p3.Impl; +} diff -r 18a7c084f50a -r a81c9c6619fb jdk/test/tools/jlink/bindservices/src/m3/p3/Impl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/bindservices/src/m3/p3/Impl.java Mon Mar 27 15:12:01 2017 -0700 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017, 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 p3; + +public class Impl implements p2.T { + public void run() { + } +}