1 /* 2 * Copyright (c) 2011, 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 26 package com.sun.javafx.tools.packager; 27 28 import com.oracle.tools.packager.*; 29 import com.oracle.tools.packager.RelativeFileSet; 30 import com.oracle.tools.packager.jnlp.JNLPBundler; 31 import com.sun.javafx.tools.ant.Callback; 32 import com.sun.javafx.tools.packager.bundlers.*; 33 import com.sun.javafx.tools.packager.bundlers.Bundler.BundleType; 34 import java.io.File; 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 import java.util.Collection; 38 import java.util.LinkedHashMap; 39 import java.util.LinkedHashSet; 40 import java.util.LinkedList; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Set; 44 import java.util.TreeMap; 45 import java.util.TreeSet; 46 47 import static com.oracle.tools.packager.jnlp.JNLPBundler.*; 48 49 public class DeployParams extends CommonParams { 50 public enum RunMode { 51 WEBSTART, EMBEDDED, STANDALONE, ALL 52 } 53 54 final List<RelativeFileSet> resources = new ArrayList<>(); 55 56 String id; 57 String title; 58 String vendor; 59 String email; 60 String description; 61 String category; 62 String licenseType; 63 String copyright; 64 String version; 65 Boolean systemWide; 66 Boolean serviceHint; 67 Boolean signBundle; 68 Boolean installdirChooser; 69 70 String applicationClass; 71 String preloader; 72 73 List<Param> params; 74 List<HtmlParam> htmlParams; 75 List<String> arguments; //unnamed arguments 76 77 // Java 9 modules support 78 Set<String> addModules = new LinkedHashSet<>(); 79 Set<String> limitModules = new LinkedHashSet<>(); 80 boolean detectModules; 81 String linkModulePath; 82 String appModulePath; 83 84 int width; 85 int height; 86 String embeddedWidth = null; 87 String embeddedHeight = null; 88 89 String appName; 90 String codebase; 91 92 boolean embedJNLP = true; 93 @Deprecated final boolean embedCertificates = false; 94 boolean allPermissions = false; 95 String updateMode = "background"; 96 boolean isExtension = false; 97 boolean isSwingApp = false; 98 99 Boolean needShortcut = null; 100 Boolean needMenu = null; 101 Boolean needInstall = null; 102 103 String outfile; 104 //if true then we cobundle js and image files needed 105 // for web deployment with the application 106 boolean includeDT; 107 108 String placeholder = "'javafx-app-placeholder'"; 109 String appId = null; 110 111 // didn't have a setter... 112 boolean offlineAllowed = true; 113 114 List<JSCallback> callbacks; 115 116 //list of HTML templates to process 117 List<Template> templates = new LinkedList<>(); 118 119 String jrePlatform = "1.8+"; 120 String fxPlatform = PackagerLib.JAVAFX_VERSION+"+"; 121 File javaRuntimeToUse = null; 122 boolean javaRuntimeWasSet = false; 123 124 //list of jvm args (in theory string can contain spaces and need to be escaped 125 List<String> jvmargs = new LinkedList<>(); 126 Map<String, String> jvmUserArgs = new LinkedHashMap<>(); 127 128 //list of jvm properties (can also be passed as VM args 129 // but keeping them separate make it a bit more convinient for JNLP generation) 130 Map<String, String> properties = new LinkedHashMap<>(); 131 132 // raw arguments to the bundler 133 Map<String, ? super Object> bundlerArguments = new LinkedHashMap<>(); 134 135 String fallbackApp = null; 136 137 public void setJavaRuntimeSource(File src) { 138 javaRuntimeToUse = src; 139 javaRuntimeWasSet = true; 140 } 141 142 public void setCodebase(String codebase) { 143 this.codebase = codebase; 144 } 145 146 public void setId(String id) { 147 this.id = id; 148 } 149 150 public void setCategory(String category) { 151 this.category = category; 152 } 153 154 public void setLicenseType(String licenseType) { 155 this.licenseType = licenseType; 156 } 157 158 public void setCopyright(String copyright) { 159 this.copyright = copyright; 160 } 161 162 public void setVersion(String version) { 163 this.version = version; 164 } 165 166 public void setSystemWide(Boolean systemWide) { 167 this.systemWide = systemWide; 168 } 169 170 public void setServiceHint(Boolean serviceHint) { 171 this.serviceHint = serviceHint; 172 } 173 174 public void setInstalldirChooser(Boolean installdirChooser) { 175 this.installdirChooser = installdirChooser; 176 } 177 178 public void setSignBundle(Boolean signBundle) { 179 this.signBundle = signBundle; 180 } 181 182 public void setJRE(String v) { 183 jrePlatform = v; 184 } 185 186 public void setSwingAppWithEmbeddedJavaFX(boolean v) { 187 isSwingApp = v; 188 } 189 190 public void setNeedInstall(boolean b) { 191 needInstall = b; 192 } 193 194 public void setOfflineAllowed(boolean b) { 195 offlineAllowed = b; 196 } 197 198 public void setNeedShortcut(Boolean b) { 199 needShortcut = b; 200 } 201 202 public void setNeedMenu(Boolean b) { 203 needMenu = b; 204 } 205 206 public void setEmbeddedDimensions(String w, String h) { 207 embeddedWidth = w; 208 embeddedHeight = h; 209 } 210 211 public void setFallback(String v) { 212 if (v == null) { 213 return; 214 } 215 216 if ("none".equals(v) || "null".equals(v)) { 217 fallbackApp = null; 218 } else { 219 fallbackApp = v; 220 } 221 } 222 223 public void setJavafx(String v) { 224 fxPlatform = v; 225 } 226 227 public void addJvmArg(String v) { 228 jvmargs.add(v); 229 } 230 231 public void addJvmUserArg(String n, String v) { 232 jvmUserArgs.put(n, v); 233 } 234 235 public void addJvmProperty(String n, String v) { 236 properties.put(n, v); 237 } 238 239 public void setAllPermissions(boolean allPermissions) { 240 this.allPermissions = allPermissions; 241 } 242 243 public void setAppName(String appName) { 244 this.appName = appName; 245 } 246 247 public void setArguments(List<String> args) { 248 this.arguments = args; 249 } 250 251 public void addAddModule(String module) { 252 addModules.add(module); 253 } 254 255 public void addLimitModule(String module) { 256 limitModules.add(module); 257 } 258 259 public void setLinkModulePath(String linkModulePath) { 260 this.linkModulePath = linkModulePath; 261 } 262 263 public void setAppModulePath(String appModulePath) { 264 this.appModulePath = appModulePath; 265 } 266 267 public void setDetectModules(boolean detectModules) { 268 this.detectModules = detectModules; 269 } 270 271 public void setDescription(String description) { 272 this.description = description; 273 } 274 275 public void setEmbedJNLP(boolean embedJNLP) { 276 this.embedJNLP = embedJNLP; 277 } 278 279 @Deprecated 280 public void setEmbedCertifcates(boolean v) { 281 if (v) { 282 System.out.println("JavaFX Packager no longer supports embedding certificates in JNLP files. Setting will be ignored."); 283 } 284 } 285 286 public void setPlaceholder(String p) { 287 placeholder = p; 288 } 289 290 public void setAppId(String id) { 291 appId = id; 292 } 293 294 public void setHeight(int height) { 295 this.height = height; 296 } 297 298 public void setHtmlParams(List<HtmlParam> htmlParams) { 299 this.htmlParams = htmlParams; 300 } 301 302 public void setOutfile(String outfile) { 303 this.outfile = outfile; 304 } 305 306 public void setParams(List<Param> params) { 307 this.params = params; 308 } 309 310 public void setPreloader(String preloader) { 311 this.preloader = preloader; 312 } 313 314 public void setTitle(String title) { 315 this.title = title; 316 } 317 318 public void setUpdateMode(String updateMode) { 319 this.updateMode = updateMode; 320 } 321 322 public void setVendor(String vendor) { 323 this.vendor = vendor; 324 } 325 326 public void setEmail(String email) { 327 this.email = email; 328 } 329 330 public void setWidth(int width) { 331 this.width = width; 332 } 333 334 public void setExtension(boolean isExtension) { 335 this.isExtension = isExtension; 336 } 337 338 public void setApplicationClass(String applicationClass) { 339 this.applicationClass = applicationClass; 340 } 341 342 public void setIncludeDT(boolean doEmbed) { 343 includeDT = doEmbed; 344 } 345 346 public void setJSCallbacks(List<JSCallback> list) { 347 callbacks = list; 348 } 349 350 public void setCallbacks(List<Callback> list) { 351 List<JSCallback> jslist = new ArrayList<>(list.size()); 352 for (Callback cb: list) { 353 jslist.add(new JSCallback(cb.getName(), cb.getCmd())); 354 } 355 callbacks = jslist; 356 } 357 358 static class Template { 359 File in; 360 File out; 361 362 Template(File in, File out) { 363 this.in = in; 364 this.out = out; 365 } 366 } 367 368 public void addTemplate(File in, File out) { 369 templates.add(new Template(in, out)); 370 } 371 372 //we need to expand as in some cases 373 // (most notably javapackager) 374 //we may get "." as filename and assumption is we include 375 // everything in the given folder 376 // (IOUtils.copyfiles() have recursive behavior) 377 List<File> expandFileset(File root) { 378 List<File> files = new LinkedList<>(); 379 if (com.oracle.tools.packager.IOUtils.isNotSymbolicLink(root)) { 380 if (root.isDirectory()) { 381 File[] children = root.listFiles(); 382 if (children != null) { 383 for (File f : children) { 384 files.addAll(expandFileset(f)); 385 } 386 } 387 } else { 388 files.add(root); 389 } 390 } 391 return files; 392 } 393 394 @Override 395 public void addResource(File baseDir, String path) { 396 File file = new File(baseDir, path); 397 //normalize top level dir 398 // to strip things like "." in the path 399 // or it can confuse symlink detection logic 400 file = file.getAbsoluteFile(); 401 402 if (baseDir == null) { 403 baseDir = file.getParentFile(); 404 } 405 resources.add(new RelativeFileSet(baseDir, new LinkedHashSet<>(expandFileset(file)))); 406 } 407 408 @Override 409 public void addResource(File baseDir, File file) { 410 //normalize initial file 411 // to strip things like "." in the path 412 // or it can confuse symlink detection logic 413 file = file.getAbsoluteFile(); 414 415 if (baseDir == null) { 416 baseDir = file.getParentFile(); 417 } 418 resources.add(new RelativeFileSet(baseDir, new LinkedHashSet<>(expandFileset(file)))); 419 } 420 421 public void addResource(File baseDir, String path, String type) { 422 addResource(baseDir, createFile(baseDir, path), type); 423 } 424 425 public void addResource(File baseDir, File file, String type) { 426 addResource(baseDir, file, "eager", type, null, null); 427 } 428 429 public void addResource(File baseDir, File file, String mode, String type, String os, String arch) { 430 Set<File> singleFile = new LinkedHashSet<>(); 431 singleFile.add(file); 432 if (baseDir == null) { 433 baseDir = file.getParentFile(); 434 } 435 RelativeFileSet rfs = new RelativeFileSet(baseDir, singleFile); 436 rfs.setArch(arch); 437 rfs.setMode(mode); 438 rfs.setOs(os); 439 rfs.setType(parseTypeFromString(type, file)); 440 resources.add(rfs); 441 } 442 443 private RelativeFileSet.Type parseTypeFromString(String type, File file) { 444 if (type == null) { 445 if (file.getName().endsWith(".jar")) { 446 return RelativeFileSet.Type.jar; 447 } else if (file.getName().endsWith(".jnlp")) { 448 return RelativeFileSet.Type.jnlp; 449 } else { 450 return RelativeFileSet.Type.UNKNOWN; 451 } 452 } else { 453 return RelativeFileSet.Type.valueOf(type); 454 } 455 } 456 457 private static File createFile(final File baseDir, final String path) { 458 final File testFile = new File(path); 459 return testFile.isAbsolute() 460 ? testFile 461 : new File(baseDir == null 462 ? null 463 : baseDir.getAbsolutePath(), 464 path); 465 } 466 467 468 @Override 469 public void validate() throws PackagerException { 470 if (outdir == null) { 471 throw new PackagerException("ERR_MissingArgument", "-outdir"); 472 } 473 if (outfile == null) { 474 throw new PackagerException("ERR_MissingArgument", "-outfile"); 475 } 476 if (resources.isEmpty()) { 477 throw new PackagerException("ERR_MissingAppResources"); 478 } 479 if (applicationClass == null) { 480 throw new PackagerException("ERR_MissingArgument", "-appclass"); 481 } 482 } 483 484 //could be icon or splash 485 static class Icon { 486 final static int UNDEFINED = -1; 487 488 String href; 489 String kind; 490 int width = UNDEFINED; 491 int height = UNDEFINED; 492 int depth = UNDEFINED; 493 RunMode mode = RunMode.WEBSTART; 494 495 Icon(String href, String kind, int w, int h, int d, RunMode m) { 496 mode = m; 497 this.href = href; 498 this.kind = kind; 499 if (w > 0) { 500 width = w; 501 } 502 if (h > 0) { 503 height = h; 504 } 505 if (d > 0) { 506 depth = d; 507 } 508 } 509 } 510 511 List<Icon> icons = new LinkedList<>(); 512 513 public void addIcon(String href, String kind, int w, int h, int d, RunMode m) { 514 icons.add(new Icon(href, kind, w, h, d, m)); 515 } 516 517 BundleType bundleType = BundleType.NONE; 518 String targetFormat = null; //means any 519 520 public void setBundleType(BundleType type) { 521 bundleType = type; 522 } 523 524 public BundleType getBundleType() { 525 return bundleType; 526 } 527 528 public void setTargetFormat(String t) { 529 targetFormat = t; 530 } 531 532 public String getTargetFormat() { 533 return targetFormat; 534 } 535 536 private String getArch() { 537 String arch = System.getProperty("os.arch").toLowerCase(); 538 539 if ("x86".equals(arch) || "i386".equals(arch) || "i486".equals(arch) 540 || "i586".equals(arch) || "i686".equals(arch)) { 541 arch = "x86"; 542 } else if ("x86_64".equals(arch) || "amd64".equals("arch")) { 543 arch = "x86_64"; 544 } 545 546 return arch; 547 } 548 549 static final Set<String> multi_args = new TreeSet<>(Arrays.asList( 550 StandardBundlerParam.JVM_PROPERTIES.getID(), 551 StandardBundlerParam.JVM_OPTIONS.getID(), 552 StandardBundlerParam.USER_JVM_OPTIONS.getID(), 553 StandardBundlerParam.ARGUMENTS.getID(), 554 JLinkBundlerHelper.LINK_MODULE_PATH.getID(), 555 JLinkBundlerHelper.APP_MODULE_PATH.getID(), 556 JLinkBundlerHelper.ADD_MODULES.getID(), 557 JLinkBundlerHelper.LIMIT_MODULES.getID() 558 )); 559 560 @SuppressWarnings("unchecked") 561 public void addBundleArgument(String key, Object value) { 562 // special hack for multi-line arguments 563 if (multi_args.contains(key) && value instanceof String) { 564 Object existingValue = bundlerArguments.get(key); 565 if (existingValue instanceof String) { 566 bundlerArguments.put(key, existingValue + "\n\n" + value); 567 } else if (existingValue instanceof List) { 568 ((List)existingValue).add(value); 569 } else if (existingValue instanceof Map && ((String)value).contains("=")) { 570 String[] mapValues = ((String)value).split("=", 2); 571 ((Map)existingValue).put(mapValues[0], mapValues[1]); 572 } else { 573 bundlerArguments.put(key, value); 574 } 575 } else { 576 bundlerArguments.put(key, value); 577 } 578 } 579 580 public BundleParams getBundleParams() { 581 BundleParams bundleParams = new BundleParams(); 582 583 //construct app resources 584 // relative to output folder! 585 String currentOS = System.getProperty("os.name").toLowerCase(); 586 String currentArch = getArch(); 587 588 for (RelativeFileSet rfs : resources) { 589 String os = rfs.getOs(); 590 String arch = rfs.getArch(); 591 //skip resources for other OS 592 // and nativelib jars (we are including raw libraries) 593 if ((os == null || currentOS.contains(os.toLowerCase())) && 594 (arch == null || currentArch.startsWith(arch.toLowerCase())) 595 && rfs.getType() != RelativeFileSet.Type.nativelib) { 596 if (rfs.getType() == RelativeFileSet.Type.license) { 597 for (String s : rfs.getIncludedFiles()) { 598 bundleParams.addLicenseFile(s); 599 } 600 } 601 } 602 } 603 604 bundleParams.setAppResourcesList(resources); 605 606 bundleParams.setIdentifier(id); 607 608 if (javaRuntimeWasSet) { 609 bundleParams.setRuntime(javaRuntimeToUse); 610 } 611 bundleParams.setApplicationClass(applicationClass); 612 bundleParams.setPrelaoderClass(preloader); 613 bundleParams.setName(this.appName); 614 bundleParams.setAppVersion(version); 615 bundleParams.setType(bundleType); 616 bundleParams.setBundleFormat(targetFormat); 617 bundleParams.setVendor(vendor); 618 bundleParams.setEmail(email); 619 bundleParams.setShortcutHint(needShortcut); 620 bundleParams.setMenuHint(needMenu); 621 putUnlessNull(INSTALL_HINT.getID(), needInstall); 622 bundleParams.setSystemWide(systemWide); 623 bundleParams.setServiceHint(serviceHint); 624 bundleParams.setInstalldirChooser(installdirChooser); 625 bundleParams.setSignBundle(signBundle); 626 bundleParams.setCopyright(copyright); 627 bundleParams.setApplicationCategory(category); 628 bundleParams.setLicenseType(licenseType); 629 bundleParams.setDescription(description); 630 bundleParams.setTitle(title); 631 if (verbose) bundleParams.setVerbose(true); 632 633 bundleParams.setJvmProperties(properties); 634 bundleParams.setJvmargs(jvmargs); 635 bundleParams.setJvmUserArgs(jvmUserArgs); 636 bundleParams.setArguments(arguments); 637 638 bundleParams.setAddModules(addModules); 639 bundleParams.setLimitModules(limitModules); 640 bundleParams.setDetectModules(detectModules); 641 bundleParams.setAppModulePath(appModulePath); 642 bundleParams.setLinkModulePath(linkModulePath); 643 644 File appIcon = null; 645 List<Map<String, ? super Object>> bundlerIcons = new ArrayList<>(); 646 for (Icon ic: icons) { 647 //NB: in theory we should be paying attention to RunMode but 648 // currently everything is marked as webstart internally and runmode 649 // is not publicly documented property 650 if (/* (ic.mode == RunMode.ALL || ic.mode == RunMode.STANDALONE) && */ 651 (ic.kind == null || ic.kind.equals("default"))) 652 { 653 //could be full path or something relative to the output folder 654 appIcon = new File(ic.href); 655 if (!appIcon.exists()) { 656 com.oracle.tools.packager.Log.debug("Icon [" + ic.href + "] is not valid absolute path. " + 657 "Assume it is relative to the output dir."); 658 appIcon = new File(outdir, ic.href); 659 } 660 } 661 662 Map<String, ? super Object> iconInfo = new TreeMap<>(); 663 if (ic.href != null) iconInfo.put(ICONS_HREF.getID(), ic.href); 664 if (ic.kind != null) iconInfo.put(ICONS_KIND.getID(), ic.kind); 665 if (ic.width > 0) iconInfo.put(ICONS_WIDTH.getID(), Integer.toString(ic.width)); 666 if (ic.height > 0) iconInfo.put(ICONS_HEIGHT.getID(), Integer.toString(ic.height)); 667 if (ic.depth > 0) iconInfo.put(ICONS_DEPTH.getID(), Integer.toString(ic.depth)); 668 669 if (!iconInfo.isEmpty()) bundlerIcons.add(iconInfo); 670 } 671 putUnlessNullOrEmpty(ICONS.getID(), bundlerIcons); 672 673 bundleParams.setIcon(appIcon); 674 675 Map<String, String> paramsMap = new TreeMap<>(); 676 if (params != null) { 677 for (Param p : params) { 678 paramsMap.put(p.name, p.value); 679 } 680 } 681 putUnlessNullOrEmpty(JNLPBundler.APP_PARAMS.getID(), paramsMap); 682 683 Map<String, String> unescapedHtmlParams = new TreeMap<>(); 684 Map<String, String> escapedHtmlParams = new TreeMap<>(); 685 if (htmlParams != null) { 686 for (HtmlParam hp : htmlParams) { 687 if (hp.needEscape) { 688 escapedHtmlParams.put(hp.name, hp.value); 689 } else { 690 unescapedHtmlParams.put(hp.name, hp.value); 691 } 692 } 693 } 694 putUnlessNullOrEmpty(JNLPBundler.APPLET_PARAMS.getID(), unescapedHtmlParams); 695 putUnlessNullOrEmpty(ESCAPED_APPLET_PARAMS.getID(), escapedHtmlParams); 696 697 698 putUnlessNull(WIDTH.getID(), width); 699 putUnlessNull(HEIGHT.getID(), height); 700 putUnlessNull(EMBEDDED_WIDTH.getID(), embeddedWidth); 701 putUnlessNull(EMBEDDED_HEIGHT.getID(), embeddedHeight); 702 703 putUnlessNull(CODEBASE.getID(), codebase); 704 putUnlessNull(EMBED_JNLP.getID(), embedJNLP); 705 // embedCertificates 706 putUnlessNull(ALL_PERMISSIONS.getID(), allPermissions); 707 putUnlessNull(UPDATE_MODE.getID(), updateMode); 708 putUnlessNull(EXTENSION.getID(), isExtension); 709 putUnlessNull(SWING_APP.getID(), isSwingApp); 710 711 putUnlessNull(OUT_FILE.getID(), outfile); 712 putUnlessNull(INCLUDE_DT.getID(), includeDT); 713 putUnlessNull(PLACEHOLDER.getID(), placeholder); 714 putUnlessNull(OFFLINE_ALLOWED.getID(), offlineAllowed); 715 716 Map<String, String> callbacksMap = new TreeMap<>(); 717 if (callbacks != null) { 718 for (JSCallback callback : callbacks) { 719 callbacksMap.put(callback.getName(), callback.getCmd()); 720 } 721 } 722 putUnlessNull(JS_CALLBACKS.getID(), callbacksMap); 723 724 Map<File, File> templatesMap = new TreeMap<>(); 725 if (templates != null) { 726 for (Template template : templates) { 727 templatesMap.put(template.in, template.out); 728 } 729 } 730 putUnlessNull(TEMPLATES.getID(), templatesMap); 731 732 putUnlessNull(FX_PLATFORM.getID(), fxPlatform); 733 putUnlessNull(JRE_PLATFORM.getID(), jrePlatform); 734 735 putUnlessNull(FALLBACK_APP.getID(), fallbackApp); 736 737 // check for collisions 738 TreeSet<String> keys = new TreeSet<>(bundlerArguments.keySet()); 739 keys.retainAll(bundleParams.getBundleParamsAsMap().keySet()); 740 741 if (!keys.isEmpty()) { 742 throw new RuntimeException("Deploy Params and Bundler Arguments overlap in the following values:" + keys.toString()); 743 } 744 745 bundleParams.addAllBundleParams(bundlerArguments); 746 747 return bundleParams; 748 } 749 750 public void putUnlessNull(String param, Object value) { 751 if (value != null) { 752 bundlerArguments.put(param, value); 753 } 754 } 755 756 public void putUnlessNullOrEmpty(String param, Map<?, ?> value) { 757 if (value != null && !value.isEmpty()) { 758 bundlerArguments.put(param, value); 759 } 760 } 761 762 public void putUnlessNullOrEmpty(String param, Collection<?> value) { 763 if (value != null && !value.isEmpty()) { 764 bundlerArguments.put(param, value); 765 } 766 } 767 }