1 /* 2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package jdk.tools.jlink.internal; 26 27 import java.io.File; 28 import java.io.IOException; 29 import java.io.PrintWriter; 30 import java.lang.module.Configuration; 31 import java.lang.module.ModuleFinder; 32 import java.lang.reflect.Layer; 33 import java.nio.file.Files; 34 import java.nio.file.Path; 35 import java.nio.file.Paths; 36 import java.text.MessageFormat; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Collections; 40 import java.util.HashMap; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Locale; 44 import java.util.Map; 45 import java.util.Map.Entry; 46 import java.util.MissingResourceException; 47 import java.util.ResourceBundle; 48 import java.util.Set; 49 50 import jdk.internal.module.ConfigurableModuleFinder; 51 import jdk.internal.module.ConfigurableModuleFinder.Phase; 52 import jdk.tools.jlink.Jlink; 53 import jdk.tools.jlink.Jlink.PluginsConfiguration; 54 import jdk.tools.jlink.internal.plugins.ExcludeFilesPlugin; 55 import jdk.tools.jlink.internal.plugins.ExcludeJmodSectionPlugin; 56 import jdk.tools.jlink.plugin.Plugin; 57 import jdk.tools.jlink.plugin.Plugin.Category; 58 import jdk.tools.jlink.builder.DefaultImageBuilder; 59 import jdk.tools.jlink.builder.ImageBuilder; 60 import jdk.tools.jlink.plugin.PluginException; 61 import jdk.tools.jlink.internal.plugins.PluginsResourceBundle; 62 import jdk.tools.jlink.internal.plugins.DefaultCompressPlugin; 63 import jdk.tools.jlink.internal.plugins.StripDebugPlugin; 64 65 /** 66 * 67 * JLink and JImage tools shared helper. 68 */ 69 public final class TaskHelper { 70 71 public static final String JLINK_BUNDLE = "jdk.tools.jlink.resources.jlink"; 72 public static final String JIMAGE_BUNDLE = "jdk.tools.jimage.resources.jimage"; 73 74 private static final String DEFAULTS_PROPERTY = "jdk.jlink.defaults"; 75 76 public final class BadArgs extends Exception { 77 78 static final long serialVersionUID = 8765093759964640721L; 79 80 private BadArgs(String key, Object... args) { 81 super(bundleHelper.getMessage(key, args)); 82 this.key = key; 83 this.args = args; 84 } 85 86 public BadArgs showUsage(boolean b) { 87 showUsage = b; 88 return this; 89 } 90 public final String key; 91 public final Object[] args; 92 public boolean showUsage; 93 } 94 95 public static class Option<T> { 96 97 public interface Processing<T> { 98 99 void process(T task, String opt, String arg) throws BadArgs; 100 } 101 102 final boolean hasArg; 103 final Processing<T> processing; 104 final boolean hidden; 105 final String[] aliases; 106 107 public Option(boolean hasArg, Processing<T> processing, boolean hidden, String... aliases) { 108 this.hasArg = hasArg; 109 this.processing = processing; 110 this.aliases = aliases; 111 this.hidden = hidden; 112 } 113 114 public Option(boolean hasArg, Processing<T> processing, String... aliases) { 115 this(hasArg, processing, false, aliases); 116 } 117 118 public boolean isHidden() { 119 return hidden; 120 } 121 122 public boolean matches(String opt) { 123 for (String a : aliases) { 124 if (a.equals(opt)) { 125 return true; 126 } else if (opt.startsWith("--") 127 && (hasArg && opt.startsWith(a + "="))) { 128 return true; 129 } 130 } 131 return false; 132 } 133 134 public boolean ignoreRest() { 135 return false; 136 } 137 138 void process(T task, String opt, String arg) throws BadArgs { 139 processing.process(task, opt, arg); 140 } 141 142 public String[] aliases() { 143 return aliases; 144 } 145 } 146 147 private static class PlugOption extends Option<PluginsOptions> { 148 149 public PlugOption(boolean hasArg, 150 Processing<PluginsOptions> processing, boolean hidden, String... aliases) { 151 super(hasArg, processing, hidden, aliases); 152 } 153 154 public PlugOption(boolean hasArg, 155 Processing<PluginsOptions> processing, String... aliases) { 156 super(hasArg, processing, aliases); 157 } 158 } 159 160 private final class PluginsOptions { 161 162 private static final String PLUGINS_PATH = "--plugin-module-path"; 163 private static final String POST_PROCESS = "--post-process-path"; 164 165 private Layer pluginsLayer = Layer.boot(); 166 private String lastSorter; 167 private boolean listPlugins; 168 private Path existingImage; 169 170 // plugin to args maps. Each plugin may be used more than once in command line. 171 // Each such occurrence results in a Map of arguments. So, there could be multiple 172 // args maps per plugin instance. 173 private final Map<Plugin, List<Map<String, String>>> pluginToMaps = new HashMap<>(); 174 private final List<PlugOption> pluginsOptions = new ArrayList<>(); 175 private final List<PlugOption> mainOptions = new ArrayList<>(); 176 177 private PluginsOptions(String pp) throws BadArgs { 178 179 if (pp != null) { 180 String[] dirs = pp.split(File.pathSeparator); 181 List<Path> paths = new ArrayList<>(dirs.length); 182 for (String dir : dirs) { 183 paths.add(Paths.get(dir)); 184 } 185 186 pluginsLayer = createPluginsLayer(paths); 187 } 188 189 Set<String> optionsSeen = new HashSet<>(); 190 for (Plugin plugin : PluginRepository. 191 getPlugins(pluginsLayer)) { 192 if (!Utils.isDisabled(plugin)) { 193 addOrderedPluginOptions(plugin, optionsSeen); 194 } 195 } 196 mainOptions.add(new PlugOption(false, 197 (task, opt, arg) -> { 198 // This option is handled prior 199 // to have the options parsed. 200 }, 201 "--plugin-module-path")); 202 mainOptions.add(new PlugOption(true, (task, opt, arg) -> { 203 Path path = Paths.get(arg); 204 if (!Files.exists(path) || !Files.isDirectory(path)) { 205 throw newBadArgs("err.existing.image.must.exist"); 206 } 207 existingImage = path.toAbsolutePath(); 208 }, true, POST_PROCESS)); 209 mainOptions.add(new PlugOption(true, 210 (task, opt, arg) -> { 211 lastSorter = arg; 212 }, 213 true, "--resources-last-sorter")); 214 mainOptions.add(new PlugOption(false, 215 (task, opt, arg) -> { 216 listPlugins = true; 217 }, 218 "--list-plugins")); 219 } 220 221 private List<Map<String, String>> argListFor(Plugin plugin) { 222 List<Map<String, String>> mapList = pluginToMaps.get(plugin); 223 if (mapList == null) { 224 mapList = new ArrayList<>(); 225 pluginToMaps.put(plugin, mapList); 226 } 227 return mapList; 228 } 229 230 private void addEmptyArgumentMap(Plugin plugin) { 231 argListFor(plugin).add(Collections.emptyMap()); 232 } 233 234 private Map<String, String> addArgumentMap(Plugin plugin) { 235 Map<String, String> map = new HashMap<>(); 236 argListFor(plugin).add(map); 237 return map; 238 } 239 240 private void addOrderedPluginOptions(Plugin plugin, 241 Set<String> optionsSeen) throws BadArgs { 242 String option = plugin.getOption(); 243 if (option == null) { 244 return; 245 } 246 247 // make sure that more than one plugin does not use the same option! 248 if (optionsSeen.contains(option)) { 249 throw new BadArgs("err.plugin.mutiple.options", 250 option); 251 } 252 optionsSeen.add(option); 253 254 PlugOption plugOption 255 = new PlugOption(plugin.hasArguments(), 256 (task, opt, arg) -> { 257 if (!Utils.isFunctional(plugin)) { 258 throw newBadArgs("err.provider.not.functional", 259 option); 260 } 261 262 if (! plugin.hasArguments()) { 263 addEmptyArgumentMap(plugin); 264 return; 265 } 266 267 Map<String, String> m = addArgumentMap(plugin); 268 // handle one or more arguments 269 if (arg.indexOf(':') == -1) { 270 // single argument case 271 m.put(option, arg); 272 } else { 273 // This option can accept more than one arguments 274 // like --option_name=arg_value:arg2=value2:arg3=value3 275 276 // ":" followed by word char condition takes care of args that 277 // like Windows absolute paths "C:\foo", "C:/foo" [cygwin] etc. 278 // This enforces that key names start with a word character. 279 String[] args = arg.split(":(?=\\w)", -1); 280 String firstArg = args[0]; 281 if (firstArg.isEmpty()) { 282 throw newBadArgs("err.provider.additional.arg.error", 283 option, arg); 284 } 285 m.put(option, firstArg); 286 // process the additional arguments 287 for (int i = 1; i < args.length; i++) { 288 String addArg = args[i]; 289 int eqIdx = addArg.indexOf('='); 290 if (eqIdx == -1) { 291 throw newBadArgs("err.provider.additional.arg.error", 292 option, arg); 293 } 294 295 String addArgName = addArg.substring(0, eqIdx); 296 String addArgValue = addArg.substring(eqIdx+1); 297 if (addArgName.isEmpty() || addArgValue.isEmpty()) { 298 throw newBadArgs("err.provider.additional.arg.error", 299 option, arg); 300 } 301 m.put(addArgName, addArgValue); 302 } 303 } 304 }, 305 "--" + option); 306 pluginsOptions.add(plugOption); 307 308 if (Utils.isFunctional(plugin)) { 309 if (Utils.isAutoEnabled(plugin)) { 310 addEmptyArgumentMap(plugin); 311 } 312 313 if (plugin instanceof DefaultCompressPlugin) { 314 plugOption 315 = new PlugOption(false, 316 (task, opt, arg) -> { 317 Map<String, String> m = addArgumentMap(plugin); 318 m.put(DefaultCompressPlugin.NAME, DefaultCompressPlugin.LEVEL_2); 319 }, "-c"); 320 mainOptions.add(plugOption); 321 } else if (plugin instanceof StripDebugPlugin) { 322 plugOption 323 = new PlugOption(false, 324 (task, opt, arg) -> { 325 addArgumentMap(plugin); 326 }, "-G"); 327 mainOptions.add(plugOption); 328 } else if (plugin instanceof ExcludeJmodSectionPlugin) { 329 plugOption = new PlugOption(false, (task, opt, arg) -> { 330 Map<String, String> m = addArgumentMap(plugin); 331 m.put(ExcludeJmodSectionPlugin.NAME, 332 ExcludeJmodSectionPlugin.MAN_PAGES); 333 }, "--no-man-pages"); 334 mainOptions.add(plugOption); 335 336 plugOption = new PlugOption(false, (task, opt, arg) -> { 337 Map<String, String> m = addArgumentMap(plugin); 338 m.put(ExcludeJmodSectionPlugin.NAME, 339 ExcludeJmodSectionPlugin.INCLUDE_HEADER_FILES); 340 }, "--no-header-files"); 341 mainOptions.add(plugOption); 342 } 343 } 344 } 345 346 private PlugOption getOption(String name) throws BadArgs { 347 for (PlugOption o : pluginsOptions) { 348 if (o.matches(name)) { 349 return o; 350 } 351 } 352 for (PlugOption o : mainOptions) { 353 if (o.matches(name)) { 354 return o; 355 } 356 } 357 return null; 358 } 359 360 private PluginsConfiguration getPluginsConfig(Path output 361 ) throws IOException, BadArgs { 362 if (output != null) { 363 if (Files.exists(output)) { 364 throw new PluginException(PluginsResourceBundle. 365 getMessage("err.dir.already.exits", output)); 366 } 367 } 368 369 List<Plugin> pluginsList = new ArrayList<>(); 370 for (Entry<Plugin, List<Map<String, String>>> entry : pluginToMaps.entrySet()) { 371 Plugin plugin = entry.getKey(); 372 List<Map<String, String>> argsMaps = entry.getValue(); 373 374 // same plugin option may be used multiple times in command line. 375 // we call configure once for each occurrence. It is upto the plugin 376 // to 'merge' and/or 'override' arguments. 377 for (Map<String, String> map : argsMaps) { 378 plugin.configure(Collections.unmodifiableMap(map)); 379 } 380 381 if (!Utils.isDisabled(plugin)) { 382 pluginsList.add(plugin); 383 } 384 } 385 386 // recreate or postprocessing don't require an output directory. 387 ImageBuilder builder = null; 388 if (output != null) { 389 builder = new DefaultImageBuilder(output); 390 391 } 392 return new Jlink.PluginsConfiguration(pluginsList, 393 builder, lastSorter); 394 } 395 } 396 397 private static final class ResourceBundleHelper { 398 399 private final ResourceBundle bundle; 400 private final ResourceBundle pluginBundle; 401 402 ResourceBundleHelper(String path) { 403 Locale locale = Locale.getDefault(); 404 try { 405 bundle = ResourceBundle.getBundle(path, locale); 406 pluginBundle = ResourceBundle.getBundle("jdk.tools.jlink.resources.plugins", locale); 407 } catch (MissingResourceException e) { 408 throw new InternalError("Cannot find jlink resource bundle for locale " + locale); 409 } 410 } 411 412 String getMessage(String key, Object... args) { 413 String val; 414 try { 415 val = bundle.getString(key); 416 } catch (MissingResourceException e) { 417 // XXX OK, check in plugin bundle 418 val = pluginBundle.getString(key); 419 } 420 return MessageFormat.format(val, args); 421 } 422 423 } 424 425 public final class OptionsHelper<T> { 426 427 private final List<Option<T>> options; 428 private String[] command; 429 private String defaults; 430 431 OptionsHelper(List<Option<T>> options) { 432 this.options = options; 433 } 434 435 private boolean hasArgument(String optionName) throws BadArgs { 436 Option<?> opt = getOption(optionName); 437 if (opt == null) { 438 opt = pluginOptions.getOption(optionName); 439 if (opt == null) { 440 throw new BadArgs("err.unknown.option", optionName). 441 showUsage(true); 442 } 443 } 444 return opt.hasArg; 445 } 446 447 public boolean shouldListPlugins() { 448 return pluginOptions.listPlugins; 449 } 450 451 private String getPluginsPath(String[] args) throws BadArgs { 452 String pp = null; 453 for (int i = 0; i < args.length; i++) { 454 if (args[i].equals(PluginsOptions.PLUGINS_PATH)) { 455 if (i == args.length - 1) { 456 throw new BadArgs("err.no.plugins.path").showUsage(true); 457 } else { 458 i += 1; 459 pp = args[i]; 460 if (!pp.isEmpty() && pp.charAt(0) == '-') { 461 throw new BadArgs("err.no.plugins.path").showUsage(true); 462 } 463 break; 464 } 465 } 466 } 467 return pp; 468 } 469 470 public List<String> handleOptions(T task, String[] args) throws BadArgs { 471 // findbugs warning, copy instead of keeping a reference. 472 command = Arrays.copyOf(args, args.length); 473 474 // Must extract it prior to do any option analysis. 475 // Required to interpret custom plugin options. 476 // Unit tests can call Task multiple time in same JVM. 477 pluginOptions = new PluginsOptions(getPluginsPath(args)); 478 479 // First extract plugins path if any 480 String pp = null; 481 List<String> filteredArgs = new ArrayList<>(); 482 for (int i = 0; i < args.length; i++) { 483 if (args[i].equals(PluginsOptions.PLUGINS_PATH)) { 484 if (i == args.length - 1) { 485 throw new BadArgs("err.no.plugins.path").showUsage(true); 486 } else { 487 warning("warn.thirdparty.plugins.enabled"); 488 log.println(bundleHelper.getMessage("warn.thirdparty.plugins")); 489 i += 1; 490 String arg = args[i]; 491 if (!arg.isEmpty() && arg.charAt(0) == '-') { 492 throw new BadArgs("err.no.plugins.path").showUsage(true); 493 } 494 pp = args[i]; 495 } 496 } else { 497 filteredArgs.add(args[i]); 498 } 499 } 500 String[] arr = new String[filteredArgs.size()]; 501 args = filteredArgs.toArray(arr); 502 503 List<String> rest = new ArrayList<>(); 504 // process options 505 for (int i = 0; i < args.length; i++) { 506 if (!args[i].isEmpty() && args[i].charAt(0) == '-') { 507 String name = args[i]; 508 PlugOption pluginOption = null; 509 Option<T> option = getOption(name); 510 if (option == null) { 511 pluginOption = pluginOptions.getOption(name); 512 if (pluginOption == null) { 513 514 throw new BadArgs("err.unknown.option", name). 515 showUsage(true); 516 } 517 } 518 Option<?> opt = pluginOption == null ? option : pluginOption; 519 String param = null; 520 if (opt.hasArg) { 521 if (name.startsWith("--") && name.indexOf('=') > 0) { 522 param = name.substring(name.indexOf('=') + 1, 523 name.length()); 524 } else if (i + 1 < args.length) { 525 param = args[++i]; 526 } 527 if (param == null || param.isEmpty() 528 || (param.length() >= 2 && param.charAt(0) == '-' 529 && param.charAt(1) == '-')) { 530 throw new BadArgs("err.missing.arg", name). 531 showUsage(true); 532 } 533 } 534 if (pluginOption != null) { 535 pluginOption.process(pluginOptions, name, param); 536 } else { 537 option.process(task, name, param); 538 } 539 if (opt.ignoreRest()) { 540 i = args.length; 541 } 542 } else { 543 rest.add(args[i]); 544 } 545 } 546 return rest; 547 } 548 549 private Option<T> getOption(String name) { 550 for (Option<T> o : options) { 551 if (o.matches(name)) { 552 return o; 553 } 554 } 555 return null; 556 } 557 558 public void showHelp(String progName) { 559 showHelp(progName, true); 560 } 561 562 private void showHelp(String progName, boolean showsImageBuilder) { 563 log.println(bundleHelper.getMessage("main.usage", progName)); 564 for (Option<?> o : options) { 565 String name = o.aliases[0].substring(1); // there must always be at least one name 566 name = name.charAt(0) == '-' ? name.substring(1) : name; 567 if (o.isHidden() || name.equals("h")) { 568 continue; 569 } 570 log.println(bundleHelper.getMessage("main.opt." + name)); 571 } 572 573 for (Option<?> o : pluginOptions.mainOptions) { 574 if (o.aliases[0].equals(PluginsOptions.POST_PROCESS) 575 && !showsImageBuilder) { 576 continue; 577 } 578 String name = o.aliases[0].substring(1); // there must always be at least one name 579 name = name.charAt(0) == '-' ? name.substring(1) : name; 580 if (o.isHidden()) { 581 continue; 582 } 583 log.println(bundleHelper.getMessage("plugin.opt." + name)); 584 } 585 586 log.println(bundleHelper.getMessage("main.command.files")); 587 } 588 589 public void listPlugins() { 590 log.println("\n" + bundleHelper.getMessage("main.extended.help")); 591 List<Plugin> pluginList = PluginRepository. 592 getPlugins(pluginOptions.pluginsLayer); 593 for (Plugin plugin : Utils.getSortedPlugins(pluginList)) { 594 showPlugin(plugin, log); 595 } 596 597 log.println("\n" + bundleHelper.getMessage("main.extended.help.footer")); 598 } 599 600 private void showPlugin(Plugin plugin, PrintWriter log) { 601 if (showsPlugin(plugin)) { 602 log.println("\n" + bundleHelper.getMessage("main.plugin.name") 603 + ": " + plugin.getName()); 604 605 // print verbose details for non-builtin plugins 606 if (!Utils.isBuiltin(plugin)) { 607 log.println(bundleHelper.getMessage("main.plugin.class") 608 + ": " + plugin.getClass().getName()); 609 log.println(bundleHelper.getMessage("main.plugin.module") 610 + ": " + plugin.getClass().getModule().getName()); 611 Category category = plugin.getType(); 612 log.println(bundleHelper.getMessage("main.plugin.category") 613 + ": " + category.getName()); 614 log.println(bundleHelper.getMessage("main.plugin.state") 615 + ": " + plugin.getStateDescription()); 616 } 617 618 String option = plugin.getOption(); 619 if (option != null) { 620 log.println(bundleHelper.getMessage("main.plugin.option") 621 + ": --" + plugin.getOption() 622 + (plugin.hasArguments()? ("=" + plugin.getArgumentsDescription()) : "")); 623 } 624 625 // description can be long spanning more than one line and so 626 // print a newline after description label. 627 log.println(bundleHelper.getMessage("main.plugin.description") 628 + ": " + plugin.getDescription()); 629 } 630 } 631 632 String[] getInputCommand() { 633 return command; 634 } 635 636 String getDefaults() { 637 return defaults; 638 } 639 640 public Layer getPluginsLayer() { 641 return pluginOptions.pluginsLayer; 642 } 643 } 644 645 private PluginsOptions pluginOptions; 646 private PrintWriter log; 647 private final ResourceBundleHelper bundleHelper; 648 649 public TaskHelper(String path) { 650 if (!JLINK_BUNDLE.equals(path) && !JIMAGE_BUNDLE.equals(path)) { 651 throw new IllegalArgumentException("Invalid Bundle"); 652 } 653 this.bundleHelper = new ResourceBundleHelper(path); 654 } 655 656 public <T> OptionsHelper<T> newOptionsHelper(Class<T> clazz, 657 Option<?>[] options) { 658 List<Option<T>> optionsList = new ArrayList<>(); 659 for (Option<?> o : options) { 660 @SuppressWarnings("unchecked") 661 Option<T> opt = (Option<T>) o; 662 optionsList.add(opt); 663 } 664 return new OptionsHelper<>(optionsList); 665 } 666 667 public BadArgs newBadArgs(String key, Object... args) { 668 return new BadArgs(key, args); 669 } 670 671 public String getMessage(String key, Object... args) { 672 return bundleHelper.getMessage(key, args); 673 } 674 675 public void setLog(PrintWriter log) { 676 this.log = log; 677 } 678 679 public void reportError(String key, Object... args) { 680 log.println(bundleHelper.getMessage("error.prefix") + " " 681 + bundleHelper.getMessage(key, args)); 682 } 683 684 public void reportUnknownError(String message) { 685 log.println(bundleHelper.getMessage("error.prefix") + " " + message); 686 } 687 688 public void warning(String key, Object... args) { 689 log.println(bundleHelper.getMessage("warn.prefix") + " " 690 + bundleHelper.getMessage(key, args)); 691 } 692 693 public PluginsConfiguration getPluginsConfig(Path output) 694 throws IOException, BadArgs { 695 return pluginOptions.getPluginsConfig(output); 696 } 697 698 public Path getExistingImage() { 699 return pluginOptions.existingImage; 700 } 701 702 public void showVersion(boolean full) { 703 log.println(version(full ? "full" : "release")); 704 } 705 706 public String version(String key) { 707 return System.getProperty("java.version"); 708 } 709 710 static Layer createPluginsLayer(List<Path> paths) { 711 Path[] arr = new Path[paths.size()]; 712 paths.toArray(arr); 713 ModuleFinder finder = ModuleFinder.of(arr); 714 715 // jmods are located at link-time 716 if (finder instanceof ConfigurableModuleFinder) { 717 ((ConfigurableModuleFinder) finder).configurePhase(Phase.LINK_TIME); 718 } 719 720 Configuration bootConfiguration = Layer.boot().configuration(); 721 try { 722 Configuration cf = bootConfiguration 723 .resolveRequiresAndUses(ModuleFinder.of(), 724 finder, 725 Collections.emptySet()); 726 ClassLoader scl = ClassLoader.getSystemClassLoader(); 727 return Layer.boot().defineModulesWithOneLoader(cf, scl); 728 } catch (Exception ex) { 729 // Malformed plugin modules (e.g.: same package in multiple modules). 730 throw new PluginException("Invalid modules in the plugins path: " + ex); 731 } 732 } 733 734 // Display all plugins 735 private static boolean showsPlugin(Plugin plugin) { 736 return (!Utils.isDisabled(plugin) && plugin.getOption() != null); 737 } 738 }