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