1 /* 2 * Copyright (c) 2018, 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.packager.internal; 26 27 import jdk.packager.internal.bundlers.BundlerType; 28 import jdk.packager.internal.bundlers.BundleParams; 29 30 import java.io.File; 31 import java.io.FileInputStream; 32 import java.io.IOException; 33 import java.nio.file.Files; 34 import java.nio.file.Path; 35 import java.text.MessageFormat; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.Collection; 39 import java.util.EnumSet; 40 import java.util.HashMap; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Set; 45 import java.util.Properties; 46 import java.util.ResourceBundle; 47 import java.util.jar.Attributes; 48 import java.util.jar.JarFile; 49 import java.util.jar.Manifest; 50 import java.util.stream.Stream; 51 import java.util.regex.Matcher; 52 import java.util.regex.Pattern; 53 54 /** 55 * Arguments 56 * 57 * This class encapsulates and processes the command line arguments, 58 * in effect, implementing all the work of jpackager tool. 59 * 60 * The primary entry point, processArguments(): 61 * Processes and validates command line arguments, constructing DeployParams. 62 * Validates the DeployParams, and generate the BundleParams. 63 * Generates List of Bundlers from BundleParams valid for this platform. 64 * Executes each Bundler in the list. 65 */ 66 public class Arguments { 67 private static final ResourceBundle I18N = ResourceBundle.getBundle( 68 "jdk.packager.internal.resources.Arguments"); 69 70 private static final String IMAGE_MODE = "image"; 71 private static final String INSTALLER_MODE = "installer"; 72 private static final String JRE_INSTALLER_MODE = "jre-installer"; 73 74 private static final String FA_EXTENSIONS = "extension"; 75 private static final String FA_CONTENT_TYPE = "mime-type"; 76 private static final String FA_DESCRIPTION = "description"; 77 private static final String FA_ICON = "icon"; 78 79 public static final BundlerParamInfo<Boolean> CREATE_IMAGE = 80 new StandardBundlerParam<>( 81 I18N.getString("param.create-image.name"), 82 I18N.getString("param.create-image.description"), 83 IMAGE_MODE, 84 Boolean.class, 85 p -> Boolean.FALSE, 86 (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? 87 true : Boolean.valueOf(s)); 88 89 public static final BundlerParamInfo<Boolean> CREATE_INSTALLER = 90 new StandardBundlerParam<>( 91 I18N.getString("param.create-installer.name"), 92 I18N.getString("param.create-installer.description"), 93 INSTALLER_MODE, 94 Boolean.class, 95 p -> Boolean.FALSE, 96 (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? 97 true : Boolean.valueOf(s)); 98 99 public static final BundlerParamInfo<Boolean> CREATE_JRE_INSTALLER = 100 new StandardBundlerParam<>( 101 I18N.getString("param.create-jre-installer.name"), 102 I18N.getString("param.create-jre-installer.description"), 103 JRE_INSTALLER_MODE, 104 Boolean.class, 105 p -> Boolean.FALSE, 106 (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? 107 true : Boolean.valueOf(s)); 108 109 // regexp for parsing args (for example, for secondary launchers) 110 private static Pattern pattern = Pattern.compile( 111 "(?:(?:([\"'])(?:\\\\\\1|.)*?(?:\\1|$))|(?:\\\\[\"'\\s]|[^\\s]))++"); 112 113 private DeployParams deployParams = null; 114 private BundlerType bundleType = null; 115 116 private int pos = 0; 117 private List<String> argList = null; 118 119 private List<CLIOptions> allOptions = null; 120 121 private ArrayList<String> files = null; 122 123 private String input = null; 124 private String output = null; 125 126 private boolean hasMainJar = false; 127 private boolean hasMainClass = false; 128 private boolean hasMainModule = false; 129 private boolean hasTargetFormat = false; 130 private boolean hasAppImage = false; 131 132 private String mainJarPath = null; 133 134 private static boolean jreInstaller = false; 135 136 private List<jdk.packager.internal.Bundler> platformBundlers = null; 137 138 private List<SecondaryLauncherArguments> secondaryLaunchers = null; 139 140 private static Map<String, CLIOptions> argIds = new HashMap<>(); 141 private static Map<String, CLIOptions> argShortIds = new HashMap<>(); 142 143 { 144 // init maps for parsing arguments 145 EnumSet<CLIOptions> options = EnumSet.allOf(CLIOptions.class); 146 147 options.forEach(option -> { 148 argIds.put(option.getIdWithPrefix(), option); 149 if (option.getShortIdWithPrefix() != null) { 150 argShortIds.put(option.getShortIdWithPrefix(), option); 151 } 152 }); 153 } 154 155 public Arguments(String[] args) { 156 initArgumentList(args); 157 } 158 159 public enum CLIOptions { 160 CREATE_IMAGE(IMAGE_MODE, OptionCategories.MODE, () -> { 161 context().bundleType = BundlerType.IMAGE; 162 context().deployParams.setTargetFormat("image"); 163 setOptionValue(IMAGE_MODE, true); 164 }), 165 166 CREATE_INSTALLER(INSTALLER_MODE, OptionCategories.MODE, () -> { 167 setOptionValue(INSTALLER_MODE, true); 168 context().bundleType = BundlerType.INSTALLER; 169 String format = "installer"; 170 if (hasNextArg()) { 171 String arg = popArg(); 172 if (!arg.startsWith("-")) { 173 format = arg.toLowerCase(); 174 context().hasTargetFormat = true; 175 } else { 176 prevArg(); 177 } 178 } 179 context().deployParams.setTargetFormat(format); 180 }), 181 182 CREATE_JRE_INSTALLER(JRE_INSTALLER_MODE, OptionCategories.MODE, () -> { 183 setOptionValue(JRE_INSTALLER_MODE, true); 184 context().bundleType = BundlerType.INSTALLER; 185 String format = "installer"; 186 if (hasNextArg()) { 187 String arg = popArg(); 188 if (!arg.startsWith("-")) { 189 format = arg.toLowerCase(); 190 context().hasTargetFormat = true; 191 } else { 192 prevArg(); 193 } 194 } 195 jreInstaller = true; 196 context().deployParams.setTargetFormat(format); 197 context().deployParams.setJreInstaller(true); 198 }), 199 200 INPUT ("input", "i", OptionCategories.PROPERTY, () -> { 201 context().input = popArg(); 202 setOptionValue("input", context().input); 203 }), 204 205 OUTPUT ("output", "o", OptionCategories.PROPERTY, () -> { 206 context().output = popArg(); 207 context().deployParams.setOutput(new File(context().output)); 208 }), 209 210 DESCRIPTION ("description", "d", OptionCategories.PROPERTY), 211 212 VENDOR ("vendor", OptionCategories.PROPERTY), 213 214 APPCLASS ("class", "c", OptionCategories.PROPERTY, () -> { 215 context().hasMainClass = true; 216 setOptionValue("class", popArg()); 217 }), 218 219 SINGLETON ("singleton", OptionCategories.PROPERTY, () -> { 220 setOptionValue("singleton", true); 221 }), 222 223 NAME ("name", "n", OptionCategories.PROPERTY), 224 225 IDENTIFIER ("identifier", OptionCategories.PROPERTY), 226 227 VERBOSE ("verbose", OptionCategories.PROPERTY, () -> { 228 setOptionValue("verbose", true); 229 Log.setVerbose(true); 230 }), 231 232 FILES ("files", "f", OptionCategories.PROPERTY, () -> { 233 context().files = new ArrayList<>(); 234 String files = popArg(); 235 context().files.addAll( 236 Arrays.asList(files.split(File.pathSeparator))); 237 }), 238 239 ARGUMENTS ("arguments", "a", OptionCategories.PROPERTY, () -> { 240 List<String> arguments = getArgumentList(popArg()); 241 setOptionValue("arguments", arguments); 242 }), 243 244 STRIP_NATIVE_COMMANDS ("strip-native-commands", 245 OptionCategories.PROPERTY, () -> { 246 setOptionValue("strip-native-commands", true); 247 }), 248 249 ICON ("icon", OptionCategories.PROPERTY), 250 CATEGORY ("category", OptionCategories.PROPERTY), 251 COPYRIGHT ("copyright", OptionCategories.PROPERTY), 252 253 LICENSE_FILE ("license-file", OptionCategories.PROPERTY), 254 255 VERSION ("version", "v", OptionCategories.PROPERTY), 256 257 JVM_ARGS ("jvm-args", OptionCategories.PROPERTY, () -> { 258 List<String> args = getArgumentList(popArg()); 259 args.forEach(a -> setOptionValue("jvm-args", a)); 260 }), 261 262 FILE_ASSOCIATIONS ("file-associations", 263 OptionCategories.PROPERTY, () -> { 264 Map<String, ? super Object> args = new HashMap<>(); 265 266 // load .properties file 267 Map<String, String> initialMap = getPropertiesFromFile(popArg()); 268 269 String ext = initialMap.get(FA_EXTENSIONS); 270 if (ext != null) { 271 args.put(StandardBundlerParam.FA_EXTENSIONS.getID(), ext); 272 } 273 274 String type = initialMap.get(FA_CONTENT_TYPE); 275 if (type != null) { 276 args.put(StandardBundlerParam.FA_CONTENT_TYPE.getID(), type); 277 } 278 279 String desc = initialMap.get(FA_DESCRIPTION); 280 if (desc != null) { 281 args.put(StandardBundlerParam.FA_DESCRIPTION.getID(), desc); 282 } 283 284 String icon = initialMap.get(FA_ICON); 285 if (icon != null) { 286 args.put(StandardBundlerParam.FA_ICON.getID(), icon); 287 } 288 289 ArrayList<Map<String, ? super Object>> associationList = 290 new ArrayList<Map<String, ? super Object>>(); 291 292 associationList.add(args); 293 294 // check that we really add _another_ value to the list 295 setOptionValue("file-associations", associationList); 296 297 }), 298 299 SECONDARY_LAUNCHER ("secondary-launcher", 300 OptionCategories.PROPERTY, () -> { 301 context().secondaryLaunchers.add( 302 new SecondaryLauncherArguments(popArg())); 303 }), 304 305 BUILD_ROOT ("build-root", OptionCategories.PROPERTY), 306 307 INSTALL_DIR ("install-dir", OptionCategories.PROPERTY), 308 309 PREDEFINED_APP_IMAGE ("app-image", OptionCategories.PROPERTY, ()-> { 310 setOptionValue("app-image", popArg()); 311 context().hasAppImage = true; 312 }), 313 314 PREDEFINED_RUNTIME_IMAGE ("runtime-image", OptionCategories.PROPERTY), 315 316 MAIN_JAR ("main-jar", "j", OptionCategories.PROPERTY, () -> { 317 context().mainJarPath = popArg(); 318 context().hasMainJar = true; 319 setOptionValue("main-jar", context().mainJarPath); 320 }), 321 322 MODULE ("module", "m", OptionCategories.MODULAR, () -> { 323 context().hasMainModule = true; 324 setOptionValue("module", popArg()); 325 }), 326 327 ADD_MODULES ("add-modules", OptionCategories.MODULAR), 328 329 MODULE_PATH ("module-path", "p", OptionCategories.MODULAR), 330 331 LIMIT_MODULES ("limit-modules", OptionCategories.MODULAR), 332 333 MAC_SIGN ("mac-sign", "s", OptionCategories.PLATFORM_MAC, () -> { 334 setOptionValue("mac-sign", true); 335 }), 336 337 MAC_BUNDLE_NAME ("mac-bundle-name", OptionCategories.PLATFORM_MAC), 338 339 MAC_BUNDLE_IDENTIFIER("mac-bundle-identifier", 340 OptionCategories.PLATFORM_MAC), 341 342 MAC_APP_STORE_CATEGORY ("mac-app-store-category", 343 OptionCategories.PLATFORM_MAC), 344 345 MAC_BUNDLE_SIGNING_PREFIX ("mac-bundle-signing-prefix", 346 OptionCategories.PLATFORM_MAC), 347 348 MAC_SIGNING_KEY_NAME ("mac-signing-key-user-name", 349 OptionCategories.PLATFORM_MAC), 350 351 MAC_SIGNING_KEYCHAIN ("mac-signing-keychain", 352 OptionCategories.PLATFORM_MAC), 353 354 MAC_APP_STORE_ENTITLEMENTS ("mac-app-store-entitlements", 355 OptionCategories.PLATFORM_MAC), 356 357 WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, () -> { 358 setOptionValue("win-menu", true); 359 }), 360 361 WIN_MENU_GROUP ("win-menu-group", OptionCategories.PLATFORM_WIN), 362 363 WIN_SHORTCUT_HINT ("win-shortcut", 364 OptionCategories.PLATFORM_WIN, () -> { 365 setOptionValue("win-shortcut", true); 366 }), 367 368 WIN_PER_USER_INSTALLATION ("win-per-user-install", 369 OptionCategories.PLATFORM_WIN, () -> { 370 setOptionValue("win-per-user-install", false); 371 }), 372 373 WIN_DIR_CHOOSER ("win-dir-chooser", 374 OptionCategories.PLATFORM_WIN, () -> { 375 setOptionValue("win-dir-chooser", true); 376 }), 377 378 WIN_REGISTRY_NAME ("win-registry-name", OptionCategories.PLATFORM_WIN), 379 380 WIN_MSI_UPGRADE_UUID ("win-upgrade-uuid", 381 OptionCategories.PLATFORM_WIN), 382 383 WIN_CONSOLE_HINT ("win-console", OptionCategories.PLATFORM_WIN, () -> { 384 setOptionValue("win-console", true); 385 }), 386 387 LINUX_BUNDLE_NAME ("linux-bundle-name", 388 OptionCategories.PLATFORM_LINUX), 389 390 LINUX_DEB_MAINTAINER ("linux-deb-maintainer", 391 OptionCategories.PLATFORM_LINUX), 392 393 LINUX_RPM_LICENSE_TYPE ("linux-rpm-license-type", 394 OptionCategories.PLATFORM_LINUX), 395 396 LINUX_PACKAGE_DEPENDENCIES ("linux-package-deps", 397 OptionCategories.PLATFORM_LINUX); 398 399 private final String id; 400 private final String shortId; 401 private final OptionCategories category; 402 private final ArgAction action; 403 private static Arguments argContext; 404 405 private CLIOptions(String id, OptionCategories category) { 406 this(id, null, category, null); 407 } 408 409 private CLIOptions(String id, String shortId, 410 OptionCategories category) { 411 this(id, shortId, category, null); 412 } 413 414 private CLIOptions(String id, 415 OptionCategories category, ArgAction action) { 416 this(id, null, category, action); 417 } 418 419 private CLIOptions(String id, String shortId, 420 OptionCategories category, ArgAction action) { 421 this.id = id; 422 this.shortId = shortId; 423 this.action = action; 424 this.category = category; 425 } 426 427 public static void setContext(Arguments context) { 428 argContext = context; 429 } 430 431 private static Arguments context() { 432 if (argContext != null) { 433 return argContext; 434 } else { 435 throw new RuntimeException("Argument context is not set."); 436 } 437 } 438 439 public String getId() { 440 return this.id; 441 } 442 443 public String getIdWithPrefix() { 444 String prefix = isMode() ? "create-" : "--"; 445 return prefix + this.id; 446 } 447 448 public String getShortIdWithPrefix() { 449 return this.shortId == null ? null : "-" + this.shortId; 450 } 451 452 public void execute() { 453 if (action != null) { 454 action.execute(); 455 } else { 456 defaultAction(); 457 } 458 } 459 460 public boolean isMode() { 461 return category == OptionCategories.MODE; 462 } 463 464 public OptionCategories getCategory() { 465 return category; 466 } 467 468 private void defaultAction() { 469 context().deployParams.addBundleArgument(id, popArg()); 470 } 471 472 private static void setOptionValue(String option, Object value) { 473 context().deployParams.addBundleArgument(option, value); 474 } 475 476 private static String popArg() { 477 nextArg(); 478 return (context().pos >= context().argList.size()) ? 479 "" : context().argList.get(context().pos); 480 } 481 482 private static String getArg() { 483 return (context().pos >= context().argList.size()) ? 484 "" : context().argList.get(context().pos); 485 } 486 487 private static void nextArg() { 488 context().pos++; 489 } 490 491 private static void prevArg() { 492 context().pos--; 493 } 494 495 private static boolean hasNextArg() { 496 return context().pos < context().argList.size(); 497 } 498 } 499 500 public enum OptionCategories { 501 MODE, 502 MODULAR, 503 PROPERTY, 504 PLATFORM_MAC, 505 PLATFORM_WIN, 506 PLATFORM_LINUX; 507 } 508 509 private void initArgumentList(String[] args) { 510 argList = new ArrayList<>(Arrays.asList(args)); 511 pos = 0; 512 513 deployParams = new DeployParams(); 514 bundleType = BundlerType.NONE; 515 516 allOptions = new ArrayList<>(); 517 518 secondaryLaunchers = new ArrayList<>(); 519 } 520 521 public boolean processArguments() throws Exception { 522 try { 523 524 // init context of arguments 525 CLIOptions.setContext(this); 526 527 // parse cmd line 528 String arg; 529 CLIOptions option; 530 for (; CLIOptions.hasNextArg(); CLIOptions.nextArg()) { 531 arg = CLIOptions.getArg(); 532 // check if it's a CLI option 533 if ((option = toCLIOption(arg)) != null) { 534 allOptions.add(option); 535 option.execute(); 536 } else { 537 Log.info("Illegal argument ["+arg+"]"); 538 } 539 } 540 541 if (allOptions.isEmpty() || !allOptions.get(0).isMode()) { 542 // first argument should always be a mode. 543 Log.info("ERROR: Mode is not specified"); 544 return false; 545 } 546 547 if (!hasAppImage && !hasMainJar && !hasMainModule && 548 !hasMainClass && !jreInstaller) { 549 Log.info("ERROR: Main jar, main class, main module, " 550 + "or app-image must be specified."); 551 } else if (!hasMainModule && !hasMainClass) { 552 // try to get main-class from manifest 553 String mainClass = getMainClassFromManifest(); 554 if (mainClass != null) { 555 CLIOptions.setOptionValue( 556 CLIOptions.APPCLASS.getId(), mainClass); 557 } 558 } 559 560 // display warning for arguments that are not supported 561 // for current configuration. 562 563 validateArguments(); 564 565 addResources(deployParams, input, files); 566 567 deployParams.setBundleType(bundleType); 568 569 List<Map<String, ? super Object>> launchersAsMap = 570 new ArrayList<>(); 571 572 for (SecondaryLauncherArguments sl : secondaryLaunchers) { 573 launchersAsMap.add(sl.getLauncherMap()); 574 } 575 576 deployParams.addBundleArgument( 577 StandardBundlerParam.SECONDARY_LAUNCHERS.getID(), 578 launchersAsMap); 579 580 // at this point deployParams should be already configured 581 582 deployParams.validate(); 583 584 BundleParams bp = deployParams.getBundleParams(); 585 586 // validate name(s) 587 ArrayList<String> usedNames = new ArrayList<String>(); 588 usedNames.add(bp.getName()); // add main app name 589 590 for (SecondaryLauncherArguments sl : secondaryLaunchers) { 591 Map<String, ? super Object> slMap = sl.getLauncherMap(); 592 String slName = 593 (String) slMap.get(Arguments.CLIOptions.NAME.getId()); 594 if (slName == null) { 595 throw new PackagerException("ERR_NoSecondaryLauncherName"); 596 } else { 597 for (String usedName : usedNames) { 598 if (slName.equals(usedName)) { 599 throw new PackagerException("ERR_NoUniqueName"); 600 } 601 } 602 } 603 usedNames.add(slName); 604 } 605 if (jreInstaller && bp.getName() == null) { 606 throw new PackagerException("ERR_NoJreInstallerName"); 607 } 608 609 generateBundle(bp.getBundleParamsAsMap()); 610 } catch (Exception e) { 611 if (Log.isVerbose()) { 612 throw e; 613 } else { 614 System.err.println(e.getMessage()); 615 if (e.getCause() != null && e.getCause() != e) { 616 System.err.println(e.getCause().getMessage()); 617 } 618 System.exit(-1); 619 } 620 } 621 return true; 622 } 623 624 private void validateArguments() { 625 CLIOptions mode = allOptions.get(0); 626 for (CLIOptions option : allOptions) { 627 if(!ValidOptions.checkIfSupported(mode, option)) { 628 System.out.println("WARNING: argument [" 629 + option.getId() + "] is not " 630 + "supported for current configuration."); 631 } 632 } 633 } 634 635 private List<jdk.packager.internal.Bundler> getPlatformBundlers() { 636 637 if (platformBundlers != null) { 638 return platformBundlers; 639 } 640 641 platformBundlers = new ArrayList<>(); 642 for (jdk.packager.internal.Bundler bundler : 643 Bundlers.createBundlersInstance().getBundlers( 644 bundleType.toString())) { 645 if (hasTargetFormat && deployParams.getTargetFormat() != null && 646 !deployParams.getTargetFormat().equalsIgnoreCase( 647 bundler.getID())) { 648 continue; 649 } 650 if (bundler.supported()) { 651 platformBundlers.add(bundler); 652 } 653 } 654 655 return platformBundlers; 656 } 657 658 private void generateBundle(Map<String,? super Object> params) 659 throws PackagerException { 660 for (jdk.packager.internal.Bundler bundler : getPlatformBundlers()) { 661 Map<String, ? super Object> localParams = new HashMap<>(params); 662 try { 663 if (bundler.validate(localParams)) { 664 File result = 665 bundler.execute(localParams, deployParams.outdir); 666 bundler.cleanup(localParams); 667 if (result == null) { 668 throw new PackagerException("MSG_BundlerFailed", 669 bundler.getID(), bundler.getName()); 670 } 671 } 672 } catch (UnsupportedPlatformException e) { 673 Log.debug(MessageFormat.format( 674 I18N.getString("MSG_BundlerPlatformException"), 675 bundler.getName())); 676 } catch (ConfigException e) { 677 Log.debug(e); 678 if (e.getAdvice() != null) { 679 Log.info(MessageFormat.format( 680 I18N.getString("MSG_BundlerConfigException"), 681 bundler.getName(), e.getMessage(), e.getAdvice())); 682 } else { 683 Log.info(MessageFormat.format(I18N.getString( 684 "MSG_BundlerConfigExceptionNoAdvice"), 685 bundler.getName(), e.getMessage())); 686 } 687 } catch (RuntimeException re) { 688 Log.info(MessageFormat.format( 689 I18N.getString("MSG_BundlerRuntimeException"), 690 bundler.getName(), re.toString())); 691 Log.debug(re); 692 } 693 } 694 } 695 696 private void addResources(DeployParams deployParams, 697 String inputdir, List<String> inputfiles) { 698 699 if (inputdir == null || inputdir.isEmpty()) { 700 return; 701 } 702 703 File baseDir = new File(inputdir); 704 705 if (!baseDir.isDirectory()) { 706 Log.info( 707 "Unable to add resources: \"-srcdir\" is not a directory."); 708 return; 709 } 710 711 List<String> fileNames; 712 if (inputfiles != null) { 713 fileNames = inputfiles; 714 } else { 715 // "-files" is omitted, all files in input cdir (which 716 // is a mandatory argument in this case) will be packaged. 717 fileNames = new ArrayList<>(); 718 try (Stream<Path> files = Files.list(baseDir.toPath())) { 719 files.forEach(file -> fileNames.add( 720 file.getFileName().toString())); 721 } catch (IOException e) { 722 Log.info("Unable to add resources: " + e.getMessage()); 723 } 724 } 725 fileNames.forEach(file -> deployParams.addResource(baseDir, file)); 726 setClasspath(fileNames); 727 } 728 729 private void setClasspath(List<String> inputfiles) { 730 String classpath = ""; 731 for (String file : inputfiles) { 732 if (file.endsWith(".jar")) { 733 classpath += file; 734 classpath += File.pathSeparator; 735 } 736 } 737 deployParams.addBundleArgument( 738 StandardBundlerParam.CLASSPATH.getID(), classpath); 739 } 740 741 public static boolean isCLIOption(String arg) { 742 return toCLIOption(arg) != null; 743 } 744 745 public static CLIOptions toCLIOption(String arg) { 746 CLIOptions option; 747 if ((option = argIds.get(arg)) == null) { 748 option = argShortIds.get(arg); 749 } 750 return option; 751 } 752 753 static Map<String, String> getArgumentMap(String inputString) { 754 Map<String, String> map = new HashMap<>(); 755 List<String> list = getArgumentList(inputString); 756 for (String pair : list) { 757 int equals = pair.indexOf("="); 758 if (equals != -1) { 759 String key = pair.substring(0, equals); 760 String value = pair.substring(equals+1, pair.length()); 761 map.put(key, value); 762 } 763 } 764 return map; 765 } 766 767 static Map<String, String> getPropertiesFromFile(String filename) { 768 Map<String, String> map = new HashMap<>(); 769 // load properties file 770 File file = new File(filename); 771 Properties properties = new Properties(); 772 try (FileInputStream in = new FileInputStream(file)) { 773 properties.load(in); 774 in.close(); 775 } catch (IOException e) { 776 Log.info("Exception: " + e.getMessage()); 777 } 778 779 for (final String name: properties.stringPropertyNames()) { 780 map.put(name, properties.getProperty(name)); 781 } 782 783 return map; 784 } 785 786 static List<String> getArgumentList(String inputString) { 787 List<String> list = new ArrayList<>(); 788 if (inputString == null || inputString.isEmpty()) { 789 return list; 790 } 791 792 // The "pattern" regexp attempts to abide to the rule that 793 // strings are delimited by whitespace unless surrounded by 794 // quotes, then it is anything (including spaces) in the quotes. 795 Matcher m = pattern.matcher(inputString); 796 while (m.find()) { 797 String s = inputString.substring(m.start(), m.end()).trim(); 798 // Ensure we do not have an empty string. trim() will take care of 799 // whitespace only strings. The regex preserves quotes and escaped 800 // chars so we need to clean them before adding to the List 801 if (!s.isEmpty()) { 802 list.add(unquoteIfNeeded(s)); 803 } 804 } 805 return list; 806 } 807 808 private static String unquoteIfNeeded(String in) { 809 if (in == null) { 810 return null; 811 } 812 813 if (in.isEmpty()) { 814 return ""; 815 } 816 817 // Use code points to preserve non-ASCII chars 818 StringBuilder sb = new StringBuilder(); 819 int codeLen = in.codePointCount(0, in.length()); 820 int quoteChar = -1; 821 for (int i = 0; i < codeLen; i++) { 822 int code = in.codePointAt(i); 823 if (code == '"' || code == '\'') { 824 // If quote is escaped make sure to copy it 825 if (i > 0 && in.codePointAt(i - 1) == '\\') { 826 sb.deleteCharAt(sb.length() - 1); 827 sb.appendCodePoint(code); 828 continue; 829 } 830 if (quoteChar != -1) { 831 if (code == quoteChar) { 832 // close quote, skip char 833 quoteChar = -1; 834 } else { 835 sb.appendCodePoint(code); 836 } 837 } else { 838 // opening quote, skip char 839 quoteChar = code; 840 } 841 } else { 842 sb.appendCodePoint(code); 843 } 844 } 845 return sb.toString(); 846 } 847 848 private String getMainClassFromManifest() { 849 if (mainJarPath == null || 850 input == null ) { 851 return null; 852 } 853 854 JarFile jf; 855 try { 856 File file = new File(input, mainJarPath); 857 if (!file.exists()) { 858 return null; 859 } 860 jf = new JarFile(file); 861 Manifest m = jf.getManifest(); 862 Attributes attrs = (m != null) ? m.getMainAttributes() : null; 863 if (attrs != null) { 864 return attrs.getValue(Attributes.Name.MAIN_CLASS); 865 } 866 } catch (IOException ignore) {} 867 return null; 868 } 869 870 public static boolean isJreInstaller() { 871 return jreInstaller; 872 } 873 }