1 /* 2 * Copyright (c) 2015, 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; 26 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.FileNotFoundException; 30 import java.io.IOException; 31 import java.io.PrintWriter; 32 import java.lang.module.Configuration; 33 import java.lang.module.ModuleFinder; 34 import java.lang.reflect.Layer; 35 import java.nio.file.Path; 36 import java.nio.file.Paths; 37 import java.text.MessageFormat; 38 import java.util.ArrayList; 39 import java.util.HashMap; 40 import java.util.List; 41 import java.util.Locale; 42 import java.util.Map; 43 import java.util.Map.Entry; 44 import java.util.MissingResourceException; 45 import java.util.Properties; 46 import java.util.ResourceBundle; 47 48 import jdk.internal.module.ConfigurableModuleFinder; 49 import jdk.internal.module.ConfigurableModuleFinder.Phase; 50 import jdk.tools.jlink.internal.ImagePluginProviderRepository; 51 import jdk.tools.jlink.internal.ImagePluginConfiguration; 52 import jdk.tools.jlink.plugins.CmdPluginProvider; 53 import jdk.tools.jlink.plugins.CmdResourcePluginProvider; 54 import jdk.tools.jlink.plugins.ImageBuilderProvider; 55 import jdk.tools.jlink.plugins.OnOffImageFilePluginProvider; 56 import jdk.tools.jlink.plugins.OnOffResourcePluginProvider; 57 import jdk.tools.jlink.plugins.PluginProvider; 58 59 /** 60 * 61 * JLink and JImage tools shared helper. 62 */ 63 public final class TaskHelper { 64 65 public static final String JLINK_BUNDLE = "jdk.tools.jlink.resources.jlink"; 66 public static final String JIMAGE_BUNDLE = "jdk.tools.jimage.resources.jimage"; 67 68 private static final String DEFAULTS_PROPERTY = "jdk.jlink.defaults"; 69 private static final String CONFIGURATION = "configuration"; 70 71 public final class BadArgs extends Exception { 72 73 static final long serialVersionUID = 8765093759964640721L; 74 75 private BadArgs(String key, Object... args) { 76 super(bundleHelper.getMessage(key, args)); 77 this.key = key; 78 this.args = args; 79 } 80 81 public BadArgs showUsage(boolean b) { 82 showUsage = b; 83 return this; 84 } 85 public final String key; 86 public final Object[] args; 87 public boolean showUsage; 88 } 89 90 public static class Option<T> { 91 92 public interface Processing<T> { 93 94 void process(T task, String opt, String arg) throws BadArgs; 95 } 96 97 final boolean hasArg; 98 final String[] aliases; 99 final Processing<T> processing; 100 101 public Option(boolean hasArg, Processing<T> processing, String... aliases) { 102 this.hasArg = hasArg; 103 this.processing = processing; 104 this.aliases = aliases; 105 } 106 107 public boolean isHidden() { 108 return false; 109 } 110 111 public boolean matches(String opt) { 112 for (String a : aliases) { 113 if (a.equals(opt)) { 114 return true; 115 } else if (opt.startsWith("--") 116 && (hasArg && opt.startsWith(a + "="))) { 117 return true; 118 } 119 } 120 return false; 121 } 122 123 public boolean ignoreRest() { 124 return false; 125 } 126 127 void process(T task, String opt, String arg) throws BadArgs { 128 processing.process(task, opt, arg); 129 } 130 } 131 132 private static class PluginOption extends Option<PluginsOptions> { 133 134 public PluginOption(boolean hasArg, 135 Processing<PluginsOptions> processing, String... aliases) { 136 super(hasArg, processing, aliases); 137 } 138 } 139 140 private static class HiddenPluginOption extends PluginOption { 141 142 public HiddenPluginOption(boolean hasArg, 143 Processing<PluginsOptions> processing, String... aliases) { 144 super(hasArg, processing, aliases); 145 } 146 147 @Override 148 public boolean isHidden() { 149 return true; 150 } 151 } 152 153 private final class PluginsOptions { 154 155 private static final String PLUGINS_PATH = "--plugins-modulepath"; 156 157 private Layer pluginsLayer = Layer.boot(); 158 private String pluginsProperties; 159 private boolean listPlugins; 160 private final Map<PluginProvider, Map<String, String>> plugins = new HashMap<>(); 161 private final Map<ImageBuilderProvider, Map<String, String>> builders = new HashMap<>(); 162 private final List<PluginOption> pluginsOptions = new ArrayList<>(); 163 164 private PluginsOptions(String pp) throws BadArgs { 165 166 if (pp != null) { 167 String[] dirs = pp.split(File.pathSeparator); 168 Path[] paths = new Path[dirs.length]; 169 int i = 0; 170 for (String dir : dirs) { 171 paths[i++] = Paths.get(dir); 172 } 173 174 pluginsLayer = createPluginsLayer(paths); 175 } 176 177 Map<String, List<String>> seen = new HashMap<>(); 178 for (PluginProvider prov : ImagePluginProviderRepository.getPluginProviders(pluginsLayer)) { 179 if (prov instanceof CmdPluginProvider) { 180 CmdPluginProvider provider = (CmdPluginProvider) prov; 181 if (provider.getToolOption() != null) { 182 for (Entry<String, List<String>> entry : seen.entrySet()) { 183 if (entry.getKey().equals(provider.getToolOption()) 184 || entry.getValue().contains(provider.getToolOption())) { 185 throw new BadArgs("err.plugin.mutiple.options", 186 provider.getToolOption()); 187 } 188 } 189 List<String> optional = new ArrayList<>(); 190 seen.put(provider.getToolOption(), optional); 191 PluginOption option 192 = new PluginOption(provider.getToolArgument() != null, 193 (task, opt, arg) -> { 194 Map<String, String> m = plugins.get(provider); 195 if (m == null) { 196 m = new HashMap<>(); 197 plugins.put(provider, m); 198 } 199 m.put(CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, arg); 200 }, 201 "--" + provider.getToolOption()); 202 pluginsOptions.add(option); 203 if (provider.getAdditionalOptions() != null) { 204 for (String other : provider.getAdditionalOptions().keySet()) { 205 optional.add(other); 206 PluginOption otherOption = new PluginOption(true, 207 (task, opt, arg) -> { 208 Map<String, String> m = plugins.get(provider); 209 if (m == null) { 210 m = new HashMap<>(); 211 plugins.put(provider, m); 212 } 213 m.put(other, arg); 214 }, 215 "--" + other); 216 pluginsOptions.add(otherOption); 217 } 218 } 219 // On/Off enabled by default plugin 220 // Command line option can override it 221 boolean edefault = false; 222 if (provider instanceof OnOffResourcePluginProvider) { 223 edefault = ((OnOffResourcePluginProvider) provider).isEnabledByDefault(); 224 } else { 225 if (provider instanceof OnOffImageFilePluginProvider) { 226 edefault = ((OnOffImageFilePluginProvider) provider).isEnabledByDefault(); 227 } 228 } 229 if (edefault) { 230 Map<String, String> m = new HashMap<>(); 231 m.put(CmdPluginProvider.TOOL_ARGUMENT_PROPERTY, 232 ImagePluginConfiguration.ON_ARGUMENT); 233 plugins.put(provider, m); 234 } 235 } 236 } 237 } 238 pluginsOptions.add(new HiddenPluginOption(true, 239 (task, opt, arg) -> { 240 pluginsProperties = arg; 241 }, 242 "--plugins-configuration")); 243 pluginsOptions.add(new HiddenPluginOption(false, 244 (task, opt, arg) -> { 245 listPlugins = true; 246 }, 247 "--list-plugins")); 248 249 //Image Builder options 250 for (ImageBuilderProvider provider 251 : ImagePluginProviderRepository.getImageBuilderProviders(pluginsLayer)) { 252 Map<String, String> options = provider.getOptions(); 253 if (options != null && !options.isEmpty()) { 254 for (Entry<String, String> o : options.entrySet()) { 255 PluginOption option 256 = new PluginOption(provider.hasArgument(o.getKey()), 257 (task, opt, arg) -> { 258 Map<String, String> m = builders.get(provider); 259 if (m == null) { 260 m = new HashMap<>(); 261 builders.put(provider, m); 262 } 263 m.put(o.getKey(), arg); 264 }, 265 "--" + o.getKey()); 266 pluginsOptions.add(option); 267 } 268 } 269 } 270 } 271 272 private PluginOption getOption(String name) throws BadArgs { 273 for (PluginOption o : pluginsOptions) { 274 if (o.matches(name)) { 275 return o; 276 } 277 } 278 return null; 279 } 280 281 private Properties getPluginsProperties() throws IOException { 282 Properties props = new Properties(); 283 if (pluginsProperties != null) { 284 try (FileInputStream stream 285 = new FileInputStream(pluginsProperties);) { 286 props.load(stream); 287 } catch (FileNotFoundException ex) { 288 throw new IOException(bundleHelper. 289 getMessage("err.path.not.valid") 290 + " " + pluginsProperties); 291 } 292 } 293 for (Entry<PluginProvider, Map<String, String>> entry : plugins.entrySet()) { 294 PluginProvider provider = entry.getKey(); 295 ImagePluginConfiguration.addPluginProperty(props, provider); 296 if (entry.getValue() != null) { 297 for (Entry<String, String> opts : entry.getValue().entrySet()) { 298 if (opts.getValue() != null) { 299 props.setProperty(provider.getName() + "." 300 + opts.getKey(), opts.getValue()); 301 } 302 } 303 } 304 } 305 for (Entry<ImageBuilderProvider, Map<String, String>> provs : builders.entrySet()) { 306 ImageBuilderProvider provider = provs.getKey(); 307 for (Entry<String, String> entry : provs.getValue().entrySet()) { 308 props.setProperty(provider.getName() + "." 309 + entry.getKey(), entry.getValue() == null ? "" 310 : entry.getValue()); 311 } 312 } 313 return props; 314 } 315 } 316 317 public static class HiddenOption<T> extends Option<T> { 318 319 public HiddenOption(boolean hasArg, Processing<T> processing, 320 String... aliases) { 321 super(hasArg, processing, aliases); 322 } 323 324 @Override 325 public boolean isHidden() { 326 return true; 327 } 328 } 329 330 private static final class ResourceBundleHelper { 331 332 private final ResourceBundle bundle; 333 private final ResourceBundle pluginBundle; 334 335 ResourceBundleHelper(String path) { 336 Locale locale = Locale.getDefault(); 337 try { 338 bundle = ResourceBundle.getBundle(path, locale); 339 pluginBundle = ResourceBundle.getBundle("jdk.tools.jlink.resources.plugins", locale); 340 } catch (MissingResourceException e) { 341 throw new InternalError("Cannot find jlink resource bundle for locale " + locale); 342 } 343 } 344 345 String getMessage(String key, Object... args) { 346 String val; 347 try { 348 val = bundle.getString(key); 349 } catch (MissingResourceException e) { 350 // XXX OK, check in plugin bundle 351 val = pluginBundle.getString(key); 352 } 353 return MessageFormat.format(val, args); 354 } 355 356 } 357 358 public final class OptionsHelper<T> { 359 360 private final List<Option<T>> options; 361 private String[] expandedCommand; 362 private String[] command; 363 private String defaults; 364 365 OptionsHelper(List<Option<T>> options) { 366 this.options = options; 367 } 368 369 private boolean hasArgument(String optionName) throws BadArgs { 370 Option<?> opt = getOption(optionName); 371 if (opt == null) { 372 opt = pluginOptions.getOption(optionName); 373 if (opt == null) { 374 throw new BadArgs("err.unknown.option", optionName). 375 showUsage(true); 376 } 377 } 378 return opt.hasArg; 379 } 380 381 private String[] handleDefaults(String[] args) throws BadArgs { 382 String[] ret = args; 383 List<String> defArgs = null; 384 385 List<String> override = new ArrayList<>(); 386 boolean expanded = false; 387 for (int i = 0; i < args.length; i++) { 388 if (args[i].equals("--" + CONFIGURATION)) { 389 i++; 390 String path = args[i]; 391 Properties p = new Properties(); 392 try (FileInputStream fs = new FileInputStream(path)) { 393 p.load(fs); 394 } catch (IOException ex) { 395 throw new RuntimeException(ex); 396 } 397 defaults = p.getProperty(DEFAULTS_PROPERTY); 398 if (defaults == null) { 399 throw new BadArgs("err.config.defaults", 400 DEFAULTS_PROPERTY); 401 } 402 try { 403 defArgs = parseDefaults(defaults); 404 } catch (Exception ex) { 405 throw new BadArgs("err.config.defaults", defaults); 406 } 407 expanded = true; 408 } else { 409 override.add(args[i]); 410 } 411 } 412 if (defArgs != null) { 413 List<String> output = new ArrayList<>(); 414 for (int i = 0; i < defArgs.size(); i++) { 415 String arg = defArgs.get(i); 416 if (arg.charAt(0) == '-') { 417 output.add(arg); 418 boolean hasArgument = hasArgument(arg); 419 String a = null; 420 if (hasArgument) { 421 i++; 422 a = defArgs.get(i); 423 } 424 int overIndex = override.indexOf(arg); 425 if (overIndex >= 0) { 426 if (hasArgument) { 427 a = override.get(overIndex + 1); 428 override.remove(a); 429 } 430 override.remove(arg); 431 } 432 if (hasArgument) { 433 if (a == null) { 434 throw newBadArgs("err.unknown.option", arg). 435 showUsage(true); 436 } 437 output.add(a); 438 } 439 } else { 440 throw newBadArgs("err.unknown.option", arg). 441 showUsage(true); 442 } 443 } 444 //Add remaining 445 output.addAll(override); 446 ret = new String[output.size()]; 447 output.toArray(ret); 448 } 449 if (expanded) { 450 expandedCommand = ret; 451 } 452 return ret; 453 } 454 455 public List<String> handleOptions(T task, String[] args) throws BadArgs { 456 command = args; 457 // Handle defaults. 458 args = handleDefaults(args); 459 460 // First extract plugins path if any 461 String pp = null; 462 List<String> filteredArgs = new ArrayList<>(); 463 for (int i = 0; i < args.length; i++) { 464 if (args[i].equals(PluginsOptions.PLUGINS_PATH)) { 465 if (i == args.length - 1) { 466 throw new BadArgs("err.no.plugins.path").showUsage(true); 467 } else { 468 warning("warn.thirdparty.plugins.enabled"); 469 log.println(bundleHelper.getMessage("warn.thirdparty.plugins")); 470 i += 1; 471 String arg = args[i]; 472 if (!arg.isEmpty() && arg.charAt(0) == '-') { 473 throw new BadArgs("err.no.plugins.path").showUsage(true); 474 } 475 pp = args[i]; 476 } 477 } else { 478 filteredArgs.add(args[i]); 479 } 480 } 481 String[] arr = new String[filteredArgs.size()]; 482 args = filteredArgs.toArray(arr); 483 484 // Unit tests can call Task multiple time in same JVM. 485 pluginOptions = new PluginsOptions(pp); 486 487 List<String> rest = new ArrayList<>(); 488 // process options 489 for (int i = 0; i < args.length; i++) { 490 if (!args[i].isEmpty() && args[i].charAt(0) == '-') { 491 String name = args[i]; 492 PluginOption pluginOption = null; 493 Option<T> option = getOption(name); 494 if (option == null) { 495 pluginOption = pluginOptions.getOption(name); 496 if (pluginOption == null) { 497 498 throw new BadArgs("err.unknown.option", name). 499 showUsage(true); 500 } 501 } 502 Option<?> opt = pluginOption == null ? option : pluginOption; 503 String param = null; 504 if (opt.hasArg) { 505 if (name.startsWith("--") && name.indexOf('=') > 0) { 506 param = name.substring(name.indexOf('=') + 1, 507 name.length()); 508 } else if (i + 1 < args.length) { 509 param = args[++i]; 510 } 511 if (param == null || param.isEmpty() 512 || param.charAt(0) == '-') { 513 throw new BadArgs("err.missing.arg", name). 514 showUsage(true); 515 } 516 } 517 if (pluginOption != null) { 518 pluginOption.process(pluginOptions, name, param); 519 } else { 520 option.process(task, name, param); 521 } 522 if (opt.ignoreRest()) { 523 i = args.length; 524 } 525 } else { 526 rest.add(args[i]); 527 } 528 } 529 return rest; 530 } 531 532 private Option<T> getOption(String name) { 533 for (Option<T> o : options) { 534 if (o.matches(name)) { 535 return o; 536 } 537 } 538 return null; 539 } 540 541 public void showHelp(String progName, String pluginsHeader, 542 boolean showsImageBuilder) { 543 log.println(bundleHelper.getMessage("main.usage", progName)); 544 // First configuration 545 log.println(bundleHelper.getMessage("main.opt." + CONFIGURATION)); 546 for (Option<?> o : options) { 547 String name = o.aliases[0].substring(1); // there must always be at least one name 548 name = name.charAt(0) == '-' ? name.substring(1) : name; 549 if (o.isHidden() || name.equals("h")) { 550 continue; 551 } 552 log.println(bundleHelper.getMessage("main.opt." + name)); 553 } 554 log.println(bundleHelper.getMessage("main.plugins-modulepath") 555 + ". " + bundleHelper.getMessage("warn.prefix") + " " 556 + bundleHelper.getMessage("warn.thirdparty.plugins")); 557 log.println(bundleHelper.getMessage("main.command.files")); 558 559 log.println("\n" + pluginsHeader); 560 for (PluginProvider prov : ImagePluginProviderRepository. 561 getPluginProviders(pluginOptions.pluginsLayer)) { 562 if (showsPlugin(prov, showsImageBuilder)) { 563 CmdPluginProvider provider = (CmdPluginProvider) prov; 564 565 if (provider.getToolOption() != null) { 566 StringBuilder line = new StringBuilder(); 567 line.append(" --").append(provider.getToolOption()); 568 if (provider.getToolArgument() != null) { 569 line.append(" ").append(provider.getToolArgument()); 570 } 571 line.append("\n ").append(provider.getDescription()); 572 if (provider.getAdditionalOptions() != null) { 573 line.append("\n").append(bundleHelper. 574 getMessage("main.plugin.additional.options")). 575 append(": "); 576 for (Entry<String, String> entry : provider.getAdditionalOptions().entrySet()) { 577 line.append(" --").append(entry.getKey()).append(" "). 578 append(entry.getValue()); 579 } 580 } 581 log.println(line.toString() + "\n"); 582 } 583 } 584 } 585 if (showsImageBuilder) { 586 log.println(bundleHelper.getMessage("main.image.builders")); 587 for (ImageBuilderProvider prov 588 : ImagePluginProviderRepository.getImageBuilderProviders(getPluginsLayer())) { 589 log.println("\n" + bundleHelper.getMessage("main.image.builder.name") 590 + ": " + prov.getName()); 591 logBuilderOptions(prov.getOptions()); 592 } 593 } 594 } 595 596 public void showPlugins(PrintWriter log, boolean showsImageBuilder) { 597 for (PluginProvider prov : ImagePluginProviderRepository.getPluginProviders(getPluginsLayer())) { 598 if (showsPlugin(prov, showsImageBuilder)) { 599 CmdPluginProvider fact = (CmdPluginProvider) prov; 600 log.println("\n" + bundleHelper.getMessage("main.plugin.name") 601 + ": " + fact.getName()); 602 Integer[] range = ImagePluginConfiguration.getRange(fact); 603 String cat = range == null ? fact.getCategory() : fact.getCategory() 604 + ". " + bundleHelper.getMessage("main.plugin.range.from") 605 + " " + range[0] + " " + bundleHelper. 606 getMessage("main.plugin.range.to") + " " 607 + range[1] + "."; 608 log.println(bundleHelper.getMessage("main.plugin.category") 609 + ": " + cat); 610 log.println(bundleHelper.getMessage("main.plugin.description") 611 + ": " + fact.getDescription()); 612 log.println(bundleHelper.getMessage("main.plugin.argument") 613 + ": " + (fact.getToolArgument() == null 614 ? bundleHelper.getMessage("main.plugin.no.value") 615 : fact.getToolArgument())); 616 String additionalOptions = bundleHelper.getMessage("main.plugin.no.value"); 617 if (fact.getAdditionalOptions() != null) { 618 StringBuilder builder = new StringBuilder(); 619 for (Entry<String, String> entry : fact.getAdditionalOptions().entrySet()) { 620 builder.append("--" + entry.getKey()).append(" "). 621 append(entry.getValue()); 622 } 623 additionalOptions = builder.toString(); 624 } 625 log.println(bundleHelper.getMessage("main.plugin.additional.options") 626 + ": " + additionalOptions); 627 String option = fact.getToolOption(); 628 if (option != null) { 629 log.println(bundleHelper.getMessage("main.plugin.option") 630 + ": --" + option); 631 } 632 } 633 } 634 if (showsImageBuilder) { 635 for (ImageBuilderProvider prov 636 : ImagePluginProviderRepository.getImageBuilderProviders(getPluginsLayer())) { 637 log.println("\n" + bundleHelper.getMessage("main.image.builder.name") 638 + ": " + prov.getName()); 639 log.println(bundleHelper.getMessage("main.image.builder.description") 640 + ": " + prov.getDescription()); 641 logBuilderOptions(prov.getOptions()); 642 } 643 } 644 } 645 646 private void logBuilderOptions(Map<String, String> options) { 647 if (options != null && !options.isEmpty()) { 648 for (Entry<String, String> opt : options.entrySet()) { 649 log.println(" --" + opt.getKey() + " " + opt.getValue() + "\n"); 650 } 651 } 652 } 653 654 public boolean listPlugins() { 655 return pluginOptions.listPlugins; 656 } 657 658 String[] getExpandedCommand() { 659 return expandedCommand; 660 } 661 662 String[] getInputCommand() { 663 return command; 664 } 665 666 String getDefaults() { 667 return defaults; 668 } 669 670 String getPluginsConfig() throws IOException { 671 String ret = null; 672 if (pluginOptions.pluginsProperties != null) { 673 Properties props = new Properties(); 674 try (FileInputStream fis 675 = new FileInputStream(pluginOptions.pluginsProperties)) { 676 props.load(fis); 677 } catch (FileNotFoundException ex) { 678 throw new IOException(bundleHelper. 679 getMessage("err.path.not.valid") 680 + " " + pluginOptions.pluginsProperties); 681 } 682 StringBuilder sb = new StringBuilder(); 683 for (String str : props.stringPropertyNames()) { 684 sb.append(str).append(" = ").append(props.getProperty(str)). 685 append("\n"); 686 } 687 ret = sb.toString(); 688 } 689 return ret; 690 } 691 692 Layer getPluginsLayer() { 693 return pluginOptions.pluginsLayer; 694 } 695 } 696 697 private PluginsOptions pluginOptions; 698 private PrintWriter log; 699 private final ResourceBundleHelper bundleHelper; 700 701 public TaskHelper(String path) { 702 if (!JLINK_BUNDLE.equals(path) && !JIMAGE_BUNDLE.equals(path)) { 703 throw new IllegalArgumentException("Invalid Bundle"); 704 } 705 this.bundleHelper = new ResourceBundleHelper(path); 706 } 707 708 public <T> OptionsHelper<T> newOptionsHelper(Class<T> clazz, 709 Option<?>[] options) { 710 List<Option<T>> optionsList = new ArrayList<>(); 711 for (Option<?> o : options) { 712 @SuppressWarnings("unchecked") 713 Option<T> opt = (Option<T>) o; 714 optionsList.add(opt); 715 } 716 return new OptionsHelper<>(optionsList); 717 } 718 719 public BadArgs newBadArgs(String key, Object... args) { 720 return new BadArgs(key, args); 721 } 722 723 public String getMessage(String key, Object... args) { 724 return bundleHelper.getMessage(key, args); 725 } 726 727 public void setLog(PrintWriter log) { 728 this.log = log; 729 } 730 731 public void reportError(String key, Object... args) { 732 log.println(bundleHelper.getMessage("error.prefix") + " " 733 + bundleHelper.getMessage(key, args)); 734 } 735 736 public void reportUnknownError(String message) { 737 log.println(bundleHelper.getMessage("error.prefix") + " " + message); 738 } 739 740 public void warning(String key, Object... args) { 741 log.println(bundleHelper.getMessage("warn.prefix") + " " 742 + bundleHelper.getMessage(key, args)); 743 } 744 745 public Properties getPluginsProperties() throws IOException { 746 return pluginOptions.getPluginsProperties(); 747 } 748 749 public void showVersion(boolean full) { 750 log.println(version(full ? "full" : "release")); 751 } 752 753 public String version(String key) { 754 return System.getProperty("java.version"); 755 } 756 757 // public for testing purpose 758 public static List<String> parseDefaults(String defaults) throws Exception { 759 List<String> arguments = new ArrayList<>(); 760 while (!defaults.isEmpty()) { 761 int start = defaults.indexOf("--"); 762 if (start < 0) { 763 throw new Exception("Invalid defaults " + defaults); 764 } 765 defaults = defaults.substring(start); 766 start = 0; 767 int end = defaults.indexOf(" "); 768 String remaining; 769 770 if (end < 0) { 771 arguments.add(defaults); 772 remaining = ""; 773 } else { 774 String option = defaults.substring(start, end); 775 arguments.add(option); 776 defaults = defaults.substring(end); 777 int nextOption = defaults.indexOf("--"); 778 int argEnd = nextOption < 0 ? defaults.length() : nextOption; 779 String arg = defaults.substring(0, argEnd); 780 arg = arg.replaceAll(" ", ""); 781 if (!arg.isEmpty()) { 782 arguments.add(arg); 783 } 784 remaining = defaults.substring(argEnd); 785 } 786 787 defaults = remaining; 788 } 789 return arguments; 790 } 791 792 static Layer createPluginsLayer(Path[] paths) { 793 ModuleFinder finder = ModuleFinder.of(paths); 794 795 // jmods are located at link-time 796 if (finder instanceof ConfigurableModuleFinder) 797 ((ConfigurableModuleFinder)finder).configurePhase(Phase.LINK_TIME); 798 799 Configuration cf 800 = Configuration.resolve(ModuleFinder.empty(), Layer.boot(), finder); 801 cf = cf.bind(); 802 // The creation of this classloader is done outside privileged block in purpose 803 // If a security manager is set, then permission must be granted to jlink 804 // codebase to create a classloader. This is the expected behavior. 805 ClassLoader cl = new ModuleClassLoader(cf); 806 return Layer.create(cf, mn -> cl); 807 } 808 809 // Display all plugins or resource only. 810 private static boolean showsPlugin(PluginProvider prov, boolean showsImageBuilder) { 811 return (prov instanceof CmdPluginProvider && showsImageBuilder) 812 || (prov instanceof CmdResourcePluginProvider); 813 } 814 }