| /* |
| * 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 |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package jdk.tools.jlink.internal; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.lang.module.Configuration; |
| import java.lang.module.ModuleFinder; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| 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; |
| import jdk.tools.jlink.plugin.Plugin; |
| import jdk.tools.jlink.plugin.Plugin.Category; |
| import jdk.tools.jlink.builder.DefaultImageBuilder; |
| import jdk.tools.jlink.builder.ImageBuilder; |
| import jdk.tools.jlink.plugin.PluginException; |
| import jdk.tools.jlink.internal.Jlink.PluginsConfiguration; |
| import jdk.tools.jlink.internal.plugins.PluginsResourceBundle; |
| import jdk.tools.jlink.internal.plugins.DefaultCompressPlugin; |
| import jdk.tools.jlink.internal.plugins.StripDebugPlugin; |
| import jdk.internal.module.ModulePath; |
| |
| /** |
| * |
| * JLink and JImage tools shared helper. |
| */ |
| public final class TaskHelper { |
| |
| public static final String JLINK_BUNDLE = "jdk.tools.jlink.resources.jlink"; |
| public static final String JIMAGE_BUNDLE = "jdk.tools.jimage.resources.jimage"; |
| |
| private static final String DEFAULTS_PROPERTY = "jdk.jlink.defaults"; |
| |
| public final class BadArgs extends Exception { |
| |
| static final long serialVersionUID = 8765093759964640721L; |
| |
| private BadArgs(String key, Object... args) { |
| super(bundleHelper.getMessage(key, args)); |
| this.key = key; |
| this.args = args; |
| } |
| |
| public BadArgs showUsage(boolean b) { |
| showUsage = b; |
| return this; |
| } |
| public final String key; |
| public final Object[] args; |
| public boolean showUsage; |
| } |
| |
| public static class Option<T> implements Comparable<T> { |
| public interface Processing<T> { |
| |
| void process(T task, String opt, String arg) throws BadArgs; |
| } |
| |
| final boolean hasArg; |
| final Processing<T> processing; |
| final boolean hidden; |
| final String name; |
| final String shortname; |
| final boolean terminalOption; |
| |
| public Option(boolean hasArg, |
| Processing<T> processing, |
| boolean hidden, |
| String name, |
| String shortname, |
| boolean isTerminal) |
| { |
| if (!name.startsWith("--")) { |
| throw new RuntimeException("option name missing --, " + name); |
| } |
| if (!shortname.isEmpty() && !shortname.startsWith("-")) { |
| throw new RuntimeException("short name missing -, " + shortname); |
| } |
| |
| this.hasArg = hasArg; |
| this.processing = processing; |
| this.hidden = hidden; |
| this.name = name; |
| this.shortname = shortname; |
| this.terminalOption = isTerminal; |
| } |
| |
| public Option(boolean hasArg, Processing<T> processing, String name, String shortname, boolean isTerminal) { |
| this(hasArg, processing, false, name, shortname, isTerminal); |
| } |
| |
| public Option(boolean hasArg, Processing<T> processing, String name, String shortname) { |
| this(hasArg, processing, false, name, shortname, false); |
| } |
| |
| public Option(boolean hasArg, Processing<T> processing, boolean hidden, String name) { |
| this(hasArg, processing, hidden, name, "", false); |
| } |
| |
| public Option(boolean hasArg, Processing<T> processing, String 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) || |
| hasArg && opt.startsWith("--") && opt.startsWith(name + "="); |
| } |
| |
| public boolean ignoreRest() { |
| return false; |
| } |
| |
| void process(T task, String opt, String arg) throws BadArgs { |
| processing.process(task, opt, arg); |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public String resourceName() { |
| return resourcePrefix() + name.substring(2); |
| } |
| |
| public String getShortname() { |
| return shortname; |
| } |
| |
| public String resourcePrefix() { |
| return "main.opt."; |
| } |
| |
| @Override |
| public int compareTo(Object object) { |
| if (!(object instanceof Option<?>)) { |
| throw new RuntimeException("comparing non-Option"); |
| } |
| |
| Option<?> option = (Option<?>)object; |
| |
| return name.compareTo(option.name); |
| } |
| |
| } |
| |
| private static class PluginOption extends Option<PluginsHelper> { |
| public PluginOption(boolean hasArg, |
| Processing<PluginsHelper> processing, boolean hidden, String name, String shortname) { |
| super(hasArg, processing, hidden, name, shortname, false); |
| } |
| |
| public PluginOption(boolean hasArg, |
| Processing<PluginsHelper> processing, boolean hidden, String name) { |
| super(hasArg, processing, hidden, name, "", false); |
| } |
| |
| public String resourcePrefix() { |
| return "plugin.opt."; |
| } |
| } |
| |
| private final class PluginsHelper { |
| |
| private ModuleLayer pluginsLayer = ModuleLayer.boot(); |
| private final List<Plugin> plugins; |
| private String lastSorter; |
| private boolean listPlugins; |
| private Path existingImage; |
| |
| // plugin to args maps. Each plugin may be used more than once in command line. |
| // Each such occurrence results in a Map of arguments. So, there could be multiple |
| // args maps per plugin instance. |
| private final Map<Plugin, List<Map<String, String>>> pluginToMaps = new HashMap<>(); |
| private final List<PluginOption> pluginsOptions = new ArrayList<>(); |
| private final List<PluginOption> mainOptions = new ArrayList<>(); |
| |
| private PluginsHelper(String pp) throws BadArgs { |
| |
| if (pp != null) { |
| String[] dirs = pp.split(File.pathSeparator); |
| List<Path> paths = new ArrayList<>(dirs.length); |
| for (String dir : dirs) { |
| paths.add(Paths.get(dir)); |
| } |
| |
| pluginsLayer = createPluginsLayer(paths); |
| } |
| |
| plugins = PluginRepository.getPlugins(pluginsLayer); |
| |
| Set<String> optionsSeen = new HashSet<>(); |
| for (Plugin plugin : plugins) { |
| if (!Utils.isDisabled(plugin)) { |
| addOrderedPluginOptions(plugin, optionsSeen); |
| } |
| } |
| mainOptions.add(new PluginOption(true, (task, opt, arg) -> { |
| for (Plugin plugin : plugins) { |
| if (plugin.getName().equals(arg)) { |
| pluginToMaps.remove(plugin); |
| return; |
| } |
| } |
| throw newBadArgs("err.no.such.plugin", arg); |
| }, |
| false, "--disable-plugin")); |
| mainOptions.add(new PluginOption(true, (task, opt, arg) -> { |
| Path path = Paths.get(arg); |
| if (!Files.exists(path) || !Files.isDirectory(path)) { |
| throw newBadArgs("err.image.must.exist", path); |
| } |
| existingImage = path.toAbsolutePath(); |
| }, true, "--post-process-path")); |
| mainOptions.add(new PluginOption(true, |
| (task, opt, arg) -> { |
| lastSorter = arg; |
| }, |
| true, "--resources-last-sorter")); |
| mainOptions.add(new PluginOption(false, |
| (task, opt, arg) -> { |
| listPlugins = true; |
| }, |
| false, "--list-plugins")); |
| } |
| |
| private List<Map<String, String>> argListFor(Plugin plugin) { |
| List<Map<String, String>> mapList = pluginToMaps.get(plugin); |
| if (mapList == null) { |
| mapList = new ArrayList<>(); |
| pluginToMaps.put(plugin, mapList); |
| } |
| return mapList; |
| } |
| |
| private void addEmptyArgumentMap(Plugin plugin) { |
| argListFor(plugin).add(Collections.emptyMap()); |
| } |
| |
| private Map<String, String> addArgumentMap(Plugin plugin) { |
| Map<String, String> map = new HashMap<>(); |
| argListFor(plugin).add(map); |
| return map; |
| } |
| |
| private void addOrderedPluginOptions(Plugin plugin, |
| Set<String> optionsSeen) throws BadArgs { |
| String option = plugin.getOption(); |
| if (option == null) { |
| return; |
| } |
| |
| // make sure that more than one plugin does not use the same option! |
| if (optionsSeen.contains(option)) { |
| throw new BadArgs("err.plugin.mutiple.options", |
| option); |
| } |
| optionsSeen.add(option); |
| |
| PluginOption plugOption |
| = new PluginOption(plugin.hasArguments(), |
| (task, opt, arg) -> { |
| if (!Utils.isFunctional(plugin)) { |
| throw newBadArgs("err.provider.not.functional", |
| option); |
| } |
| |
| if (! plugin.hasArguments()) { |
| addEmptyArgumentMap(plugin); |
| return; |
| } |
| |
| Map<String, String> m = addArgumentMap(plugin); |
| // handle one or more arguments |
| if (arg.indexOf(':') == -1) { |
| // single argument case |
| m.put(option, arg); |
| } else { |
| // This option can accept more than one arguments |
| // like --option_name=arg_value:arg2=value2:arg3=value3 |
| |
| // ":" followed by word char condition takes care of args that |
| // like Windows absolute paths "C:\foo", "C:/foo" [cygwin] etc. |
| // This enforces that key names start with a word character. |
| String[] args = arg.split(":(?=\\w)", -1); |
| String firstArg = args[0]; |
| if (firstArg.isEmpty()) { |
| throw newBadArgs("err.provider.additional.arg.error", |
| option, arg); |
| } |
| m.put(option, firstArg); |
| // process the additional arguments |
| for (int i = 1; i < args.length; i++) { |
| String addArg = args[i]; |
| int eqIdx = addArg.indexOf('='); |
| if (eqIdx == -1) { |
| throw newBadArgs("err.provider.additional.arg.error", |
| option, arg); |
| } |
| |
| String addArgName = addArg.substring(0, eqIdx); |
| String addArgValue = addArg.substring(eqIdx+1); |
| if (addArgName.isEmpty() || addArgValue.isEmpty()) { |
| throw newBadArgs("err.provider.additional.arg.error", |
| option, arg); |
| } |
| m.put(addArgName, addArgValue); |
| } |
| } |
| }, |
| false, "--" + option); |
| pluginsOptions.add(plugOption); |
| |
| if (Utils.isFunctional(plugin)) { |
| if (Utils.isAutoEnabled(plugin)) { |
| addEmptyArgumentMap(plugin); |
| } |
| |
| if (plugin instanceof DefaultCompressPlugin) { |
| plugOption |
| = new PluginOption(false, |
| (task, opt, arg) -> { |
| Map<String, String> m = addArgumentMap(plugin); |
| m.put(DefaultCompressPlugin.NAME, DefaultCompressPlugin.LEVEL_2); |
| }, false, "--compress", "-c"); |
| mainOptions.add(plugOption); |
| } else if (plugin instanceof StripDebugPlugin) { |
| plugOption |
| = new PluginOption(false, |
| (task, opt, arg) -> { |
| addArgumentMap(plugin); |
| }, false, "--strip-debug", "-G"); |
| mainOptions.add(plugOption); |
| } else if (plugin instanceof ExcludeJmodSectionPlugin) { |
| plugOption = new PluginOption(false, (task, opt, arg) -> { |
| Map<String, String> m = addArgumentMap(plugin); |
| m.put(ExcludeJmodSectionPlugin.NAME, |
| ExcludeJmodSectionPlugin.MAN_PAGES); |
| }, false, "--no-man-pages"); |
| mainOptions.add(plugOption); |
| |
| plugOption = new PluginOption(false, (task, opt, arg) -> { |
| Map<String, String> m = addArgumentMap(plugin); |
| m.put(ExcludeJmodSectionPlugin.NAME, |
| ExcludeJmodSectionPlugin.INCLUDE_HEADER_FILES); |
| }, false, "--no-header-files"); |
| mainOptions.add(plugOption); |
| } |
| } |
| } |
| |
| private PluginOption getOption(String name) throws BadArgs { |
| for (PluginOption o : pluginsOptions) { |
| if (o.matches(name)) { |
| return o; |
| } |
| } |
| for (PluginOption o : mainOptions) { |
| if (o.matches(name)) { |
| return o; |
| } |
| } |
| return null; |
| } |
| |
| private PluginsConfiguration getPluginsConfig(Path output, Map<String, String> launchers |
| ) throws IOException, BadArgs { |
| if (output != null) { |
| if (Files.exists(output)) { |
| throw new PluginException(PluginsResourceBundle. |
| getMessage("err.dir.already.exits", output)); |
| } |
| } |
| |
| List<Plugin> pluginsList = new ArrayList<>(); |
| for (Entry<Plugin, List<Map<String, String>>> entry : pluginToMaps.entrySet()) { |
| Plugin plugin = entry.getKey(); |
| List<Map<String, String>> argsMaps = entry.getValue(); |
| |
| // same plugin option may be used multiple times in command line. |
| // we call configure once for each occurrence. It is upto the plugin |
| // to 'merge' and/or 'override' arguments. |
| for (Map<String, String> map : argsMaps) { |
| try { |
| plugin.configure(Collections.unmodifiableMap(map)); |
| } catch (IllegalArgumentException e) { |
| if (JlinkTask.DEBUG) { |
| System.err.println("Plugin " + plugin.getName() + " threw exception with config: " + map); |
| e.printStackTrace(); |
| } |
| throw e; |
| } |
| } |
| |
| if (!Utils.isDisabled(plugin)) { |
| pluginsList.add(plugin); |
| } |
| } |
| |
| // recreate or postprocessing don't require an output directory. |
| ImageBuilder builder = null; |
| if (output != null) { |
| builder = new DefaultImageBuilder(output, launchers); |
| } |
| |
| return new Jlink.PluginsConfiguration(pluginsList, |
| builder, lastSorter); |
| } |
| } |
| |
| private static final class ResourceBundleHelper { |
| |
| private final ResourceBundle bundle; |
| private final ResourceBundle pluginBundle; |
| |
| ResourceBundleHelper(String path) { |
| Locale locale = Locale.getDefault(); |
| try { |
| bundle = ResourceBundle.getBundle(path, locale); |
| pluginBundle = ResourceBundle.getBundle("jdk.tools.jlink.resources.plugins", locale); |
| } catch (MissingResourceException e) { |
| throw new InternalError("Cannot find jlink resource bundle for locale " + locale); |
| } |
| } |
| |
| String getMessage(String key, Object... args) { |
| String val; |
| try { |
| val = bundle.getString(key); |
| } catch (MissingResourceException e) { |
| // XXX OK, check in plugin bundle |
| val = pluginBundle.getString(key); |
| } |
| return MessageFormat.format(val, args); |
| } |
| |
| } |
| |
| public final class OptionsHelper<T> { |
| |
| private final List<Option<T>> options; |
| private String[] command; |
| private String defaults; |
| |
| OptionsHelper(List<Option<T>> options) { |
| this.options = options; |
| } |
| |
| private boolean hasArgument(String optionName) throws BadArgs { |
| Option<?> opt = getOption(optionName); |
| if (opt == null) { |
| opt = pluginOptions.getOption(optionName); |
| if (opt == null) { |
| throw new BadArgs("err.unknown.option", optionName). |
| showUsage(true); |
| } |
| } |
| return opt.hasArg; |
| } |
| |
| public boolean shouldListPlugins() { |
| return pluginOptions.listPlugins; |
| } |
| |
| private String getPluginsPath(String[] args) throws BadArgs { |
| return null; |
| } |
| |
| /** |
| * 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<String> handleOptions(T task, String[] args) throws BadArgs { |
| // findbugs warning, copy instead of keeping a reference. |
| command = Arrays.copyOf(args, args.length); |
| |
| // Must extract it prior to do any option analysis. |
| // Required to interpret custom plugin options. |
| // Unit tests can call Task multiple time in same JVM. |
| pluginOptions = new PluginsHelper(null); |
| |
| // process options |
| for (int i = 0; i < args.length; i++) { |
| if (args[i].startsWith("-")) { |
| String name = args[i]; |
| PluginOption pluginOption = null; |
| Option<T> option = getOption(name); |
| if (option == null) { |
| pluginOption = pluginOptions.getOption(name); |
| if (pluginOption == null) { |
| throw new BadArgs("err.unknown.option", name). |
| showUsage(true); |
| } |
| } |
| Option<?> opt = pluginOption == null ? option : pluginOption; |
| String param = null; |
| if (opt.hasArg) { |
| if (name.startsWith("--") && name.indexOf('=') > 0) { |
| param = name.substring(name.indexOf('=') + 1, |
| name.length()); |
| } else if (i + 1 < args.length) { |
| param = args[++i]; |
| } |
| if (param == null || param.isEmpty() |
| || (param.length() >= 2 && param.charAt(0) == '-' |
| && param.charAt(1) == '-')) { |
| throw new BadArgs("err.missing.arg", name). |
| showUsage(true); |
| } |
| } |
| if (pluginOption != null) { |
| 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 { |
| return Stream.of(Arrays.copyOfRange(args, i, args.length)) |
| .collect(Collectors.toList()); |
| } |
| } |
| return Collections.emptyList(); |
| } |
| |
| private Option<T> getOption(String name) { |
| for (Option<T> o : options) { |
| if (o.matches(name)) { |
| return o; |
| } |
| } |
| return null; |
| } |
| |
| public void showHelp(String progName) { |
| log.println(bundleHelper.getMessage("main.usage", progName)); |
| Stream.concat(options.stream(), pluginOptions.mainOptions.stream()) |
| .filter(option -> !option.isHidden()) |
| .sorted() |
| .forEach(option -> { |
| log.println(bundleHelper.getMessage(option.resourceName())); |
| }); |
| |
| log.println(bundleHelper.getMessage("main.command.files")); |
| } |
| |
| public void listPlugins() { |
| log.println("\n" + bundleHelper.getMessage("main.extended.help")); |
| List<Plugin> pluginList = PluginRepository. |
| getPlugins(pluginOptions.pluginsLayer); |
| |
| for (Plugin plugin : Utils.getSortedPlugins(pluginList)) { |
| showPlugin(plugin, log); |
| } |
| |
| log.println("\n" + bundleHelper.getMessage("main.extended.help.footer")); |
| } |
| |
| private void showPlugin(Plugin plugin, PrintWriter log) { |
| if (showsPlugin(plugin)) { |
| log.println("\n" + bundleHelper.getMessage("main.plugin.name") |
| + ": " + plugin.getName()); |
| |
| // print verbose details for non-builtin plugins |
| if (!Utils.isBuiltin(plugin)) { |
| log.println(bundleHelper.getMessage("main.plugin.class") |
| + ": " + plugin.getClass().getName()); |
| log.println(bundleHelper.getMessage("main.plugin.module") |
| + ": " + plugin.getClass().getModule().getName()); |
| Category category = plugin.getType(); |
| log.println(bundleHelper.getMessage("main.plugin.category") |
| + ": " + category.getName()); |
| log.println(bundleHelper.getMessage("main.plugin.state") |
| + ": " + plugin.getStateDescription()); |
| } |
| |
| String option = plugin.getOption(); |
| if (option != null) { |
| log.println(bundleHelper.getMessage("main.plugin.option") |
| + ": --" + plugin.getOption() |
| + (plugin.hasArguments()? ("=" + plugin.getArgumentsDescription()) : "")); |
| } |
| |
| // description can be long spanning more than one line and so |
| // print a newline after description label. |
| log.println(bundleHelper.getMessage("main.plugin.description") |
| + ": " + plugin.getDescription()); |
| } |
| } |
| |
| String[] getInputCommand() { |
| return command; |
| } |
| |
| String getDefaults() { |
| return defaults; |
| } |
| |
| public ModuleLayer getPluginsLayer() { |
| return pluginOptions.pluginsLayer; |
| } |
| } |
| |
| private PluginsHelper pluginOptions; |
| private PrintWriter log; |
| private final ResourceBundleHelper bundleHelper; |
| |
| public TaskHelper(String path) { |
| if (!JLINK_BUNDLE.equals(path) && !JIMAGE_BUNDLE.equals(path)) { |
| throw new IllegalArgumentException("Invalid Bundle"); |
| } |
| this.bundleHelper = new ResourceBundleHelper(path); |
| } |
| |
| public <T> OptionsHelper<T> newOptionsHelper(Class<T> clazz, |
| Option<?>[] options) { |
| List<Option<T>> optionsList = new ArrayList<>(); |
| for (Option<?> o : options) { |
| @SuppressWarnings("unchecked") |
| Option<T> opt = (Option<T>) o; |
| optionsList.add(opt); |
| } |
| return new OptionsHelper<>(optionsList); |
| } |
| |
| public BadArgs newBadArgs(String key, Object... args) { |
| return new BadArgs(key, args); |
| } |
| |
| public String getMessage(String key, Object... args) { |
| return bundleHelper.getMessage(key, args); |
| } |
| |
| public void setLog(PrintWriter log) { |
| this.log = log; |
| } |
| |
| public void reportError(String key, Object... args) { |
| log.println(bundleHelper.getMessage("error.prefix") + " " |
| + bundleHelper.getMessage(key, args)); |
| } |
| |
| public void reportUnknownError(String message) { |
| log.println(bundleHelper.getMessage("error.prefix") + " " + message); |
| } |
| |
| public void warning(String key, Object... args) { |
| log.println(bundleHelper.getMessage("warn.prefix") + " " |
| + bundleHelper.getMessage(key, args)); |
| } |
| |
| public PluginsConfiguration getPluginsConfig(Path output, Map<String, String> launchers) |
| throws IOException, BadArgs { |
| return pluginOptions.getPluginsConfig(output, launchers); |
| } |
| |
| public Path getExistingImage() { |
| return pluginOptions.existingImage; |
| } |
| |
| public void showVersion(boolean full) { |
| log.println(version(full ? "full" : "release")); |
| } |
| |
| public String version(String key) { |
| return System.getProperty("java.version"); |
| } |
| |
| static ModuleLayer createPluginsLayer(List<Path> paths) { |
| |
| Path[] dirs = paths.toArray(new Path[0]); |
| ModuleFinder finder = ModulePath.of(Runtime.version(), true, dirs); |
| Configuration bootConfiguration = ModuleLayer.boot().configuration(); |
| try { |
| Configuration cf = bootConfiguration |
| .resolveAndBind(ModuleFinder.of(), |
| finder, |
| Collections.emptySet()); |
| ClassLoader scl = ClassLoader.getSystemClassLoader(); |
| return ModuleLayer.boot().defineModulesWithOneLoader(cf, scl); |
| } catch (Exception ex) { |
| // Malformed plugin modules (e.g.: same package in multiple modules). |
| throw new PluginException("Invalid modules in the plugins path: " + ex); |
| } |
| } |
| |
| // Display all plugins |
| private static boolean showsPlugin(Plugin plugin) { |
| return (!Utils.isDisabled(plugin) && plugin.getOption() != null); |
| } |
| } |