1 /* 2 * Copyright (c) 2011, 2014, 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.ant; 27 28 import java.io.File; 29 import java.util.LinkedList; 30 import java.util.List; 31 import com.sun.javafx.tools.ant.Platform.Jvmarg; 32 import com.sun.javafx.tools.ant.Platform.Property; 33 import com.sun.javafx.tools.packager.DeployParams; 34 import com.sun.javafx.tools.packager.Log; 35 import com.sun.javafx.tools.packager.PackagerException; 36 import com.sun.javafx.tools.packager.PackagerLib; 37 import com.sun.javafx.tools.packager.bundlers.Bundler.BundleType; 38 import org.apache.tools.ant.BuildException; 39 import org.apache.tools.ant.DynamicAttribute; 40 import org.apache.tools.ant.Task; 41 import org.apache.tools.ant.types.DataType; 42 43 /** 44 * Generates package for Web deployment and redistribution of application. 45 * Package includes of set of jar files, JNLP file and HTML file. 46 * 47 * Minimal example: 48 * <pre> 49 * <fx:deploy width="600" height="400" 50 * outdir="web-dist" outfile="Fish"> 51 * <info title="Sample application"/> 52 * <fx:application refid="myapp"/> 53 * <fx:resources refid="myresources"/> 54 * </fx:deploy> 55 * </pre> 56 * Above example will generate HTML/JNLP files into the web-dist directory 57 * and use "Fish" as prefix for generated files. Details about application and 58 * its resources are defined elsewhere in the application and resource elements. 59 * <p> 60 * Minimal complete example: 61 * <pre> 62 * <fx:deploy width="600" height="400" 63 * outdir="web-dist" outfile="Fish"> 64 * <info title="Sample application"/> 65 * <fx:application name="SampleApp" 66 * mainClass="testapp.MainApp" 67 * preloaderClass="testpreloader.Preloader"/> 68 * <fx:resources> 69 * <fx:fileset requiredFor="preloader" dir="dist"> 70 * <include name="preloader.jar"/> 71 * </fx:fileset> 72 * <fx:fileset dir="dist"> 73 * <include name="helloworld.jar"/> 74 * </fx:fileset> 75 * </fx:resources> 76 * </fx:deploy> 77 * </pre> 78 * Same as above but here application and resource details are defined in place. 79 * Note that using references helps with reducing code duplication as fx:jar need 80 * to be used for double clickable jars. 81 * 82 * @ant.task name="deploy" category="javafx" 83 */ 84 public class DeployFXTask extends Task implements DynamicAttribute { 85 private String width = null; 86 private String height = null; 87 private String embeddedWidth = null; 88 private String embeddedHeight = null; 89 private String outfile = null; 90 private String outdir = null; 91 private boolean embedJNLP; 92 private boolean isExtension = false; 93 94 //Before FCS default is to include DT files with app 95 // to ensure tests are using latest and compatible. 96 //After FCS default is to use shared copy. 97 private boolean includeDT = false; 98 99 private String updateMode="background"; 100 private Info appInfo = null; 101 private Application app = null; 102 private Resources resources = null; 103 private Preferences prefs = null; 104 private String codebase = null; 105 106 //container to embed application into 107 //could be either string id or js code. If it is string id then it needs to 108 //be escaped 109 private String placeholder; 110 111 private PackagerLib packager; 112 private DeployParams deployParams; 113 114 private Callbacks callbacks; 115 116 boolean offlineAllowed = true; 117 118 //default native bundle settings 119 // use NONE to avoid large disk space and build time overhead 120 BundleType nativeBundles = BundleType.NONE; 121 String bundleFormat = null; 122 123 private boolean verbose = false; 124 public void setVerbose(boolean v) { 125 verbose = v; 126 } 127 128 public void setCodebase(String str) { 129 codebase = str; 130 } 131 132 public DeployFXTask() { 133 packager = new PackagerLib(); 134 deployParams = new DeployParams(); 135 } 136 137 @Override 138 public void execute() { 139 deployParams.setOutfile(outfile); 140 deployParams.setOutdir(new File(outdir)); 141 deployParams.setOfflineAllowed(offlineAllowed); 142 deployParams.setVerbose(verbose); 143 deployParams.setCodebase(codebase); 144 145 if (width != null) { 146 deployParams.setWidth(Integer.valueOf(width)); 147 } 148 149 if (height != null) { 150 deployParams.setHeight(Integer.valueOf(height)); 151 } 152 153 if (embeddedWidth != null && embeddedHeight != null) { 154 deployParams.setEmbeddedDimensions(embeddedWidth, embeddedHeight); 155 } 156 157 deployParams.setEmbedJNLP(embedJNLP); 158 if (perms != null) { 159 deployParams.setEmbedCertifcates(perms.embed); 160 deployParams.setAllPermissions(perms.elevated); 161 } 162 163 if (app != null) { 164 deployParams.setApplicationClass(app.get().mainClass); 165 deployParams.setPreloader(app.get().preloaderClass); 166 deployParams.setAppId(app.get().id); 167 deployParams.setAppName(app.get().name); 168 deployParams.setParams(app.get().parameters); 169 deployParams.setArguments(app.get().getArguments()); 170 deployParams.setHtmlParams(app.get().htmlParameters); 171 deployParams.setFallback(app.get().fallbackApp); 172 deployParams.setSwingAppWithEmbeddedJavaFX( 173 app.get().embeddedIntoSwing); 174 deployParams.setVersion(app.get().version); 175 deployParams.setId(app.get().id); 176 deployParams.setServiceHint(app.get().daemon); 177 } 178 179 if (appInfo != null) { 180 deployParams.setTitle(appInfo.title); 181 deployParams.setVendor(appInfo.vendor); 182 deployParams.setDescription(appInfo.appDescription); 183 deployParams.setCategory(appInfo.category); 184 deployParams.setLicenseType(appInfo.licenseType); 185 deployParams.setCopyright(appInfo.copyright); 186 deployParams.setEmail(appInfo.email); 187 188 for (Info.Icon i: appInfo.icons) { 189 if (i instanceof Info.Splash) { 190 deployParams.addIcon(i.href, i.kind, i.width, i.height, i.depth, 191 ((Info.Splash) i).mode); 192 } else { 193 deployParams.addIcon(i.href, i.kind, i.width, i.height, i.depth, 194 DeployParams.RunMode.WEBSTART); 195 } 196 } 197 } 198 199 deployParams.setUpdateMode(updateMode); 200 deployParams.setExtension(isExtension); 201 deployParams.setIncludeDT(includeDT); 202 203 if (platform != null) { 204 Platform pl = platform.get(); 205 if (pl.j2se != null) { 206 deployParams.setJRE(pl.j2se); 207 } 208 if (pl.javafx != null) { 209 deployParams.setJavafx(pl.javafx); 210 } 211 212 //only pass it further if it was explicitly set 213 // as we do not want to override default 214 if (pl.javaRoot != null) { 215 if (Platform.USE_SYSTEM_JRE.equals(pl.javaRoot)) { 216 deployParams.setJavaRuntimeSource(null); 217 } else { 218 deployParams.setJavaRuntimeSource(new File(pl.javaRoot)); 219 } 220 } 221 for (Property p: pl.properties) { 222 deployParams.addJvmProperty(p.name, p.value); 223 } 224 for (Jvmarg a: pl.jvmargs) { 225 deployParams.addJvmArg(a.value); 226 } 227 for (Property a: pl.jvmUserArgs) { 228 deployParams.addJvmUserArg(a.name, a.value); 229 } 230 } 231 232 if (callbacks != null) { 233 deployParams.setCallbacks(callbacks.callbacks); 234 } 235 236 if (prefs != null) { 237 deployParams.setNeedShortcut(prefs.getShortcut()); 238 deployParams.setNeedInstall(prefs.getInstall()); 239 deployParams.setNeedMenu(prefs.getMenu()); 240 deployParams.setSystemWide(prefs.getSystemInstall()); 241 } 242 243 for (Template t: templateList) { 244 deployParams.addTemplate(t.infile, t.outfile); 245 } 246 247 for (BundleArgument ba : bundleArgumentList) { 248 deployParams.addBundleArgument(ba.arg, ba.value); 249 } 250 251 deployParams.setPlaceholder(placeholder); 252 253 if (resources != null) { 254 for (FileSet fs: resources.getResources()) { 255 Utils.addResources(deployParams, fs); 256 } 257 } 258 259 deployParams.setBundleType(nativeBundles); 260 deployParams.setTargetFormat(bundleFormat); 261 262 Log.setLogger(new AntLog(this.getProject())); 263 264 try { 265 packager.generateDeploymentPackages(deployParams); 266 } catch (PackagerException pe) { 267 if (pe.getCause() != null) { 268 throw new BuildException(pe.getCause().getMessage(), 269 pe.getCause()); 270 } else { 271 throw new BuildException(pe.getMessage(), 272 pe); 273 } 274 } catch (Exception e) { 275 throw new BuildException(e.getMessage(), e); 276 } finally { 277 Log.setLogger(null); 278 } 279 } 280 281 /** 282 * Set to true if we are generating an 'extension' JNLP. 283 * 284 * @ant.not-required Default is false. 285 */ 286 public void setExtension(boolean v) { 287 isExtension = v; 288 } 289 290 public void setNativeBundles(String v) { 291 if ("false".equals(v) || "none".equals(v)) { 292 nativeBundles = BundleType.NONE; 293 } else if ("all".equals(v) || "true".equals(v)) { 294 nativeBundles = BundleType.ALL; 295 } else if ("image".equals(v)) { 296 nativeBundles = BundleType.IMAGE; 297 } else if ("installer".equals(v)) { 298 nativeBundles = BundleType.INSTALLER; 299 } else { 300 //assume it is request to build only specific format (like exe or msi) 301 nativeBundles = BundleType.INSTALLER; 302 bundleFormat = (v != null) ? v.toLowerCase() : null; 303 } 304 } 305 306 /** 307 * Indicates the preferences for when checks for application updates 308 * are performed. Supported modes are always, timeout and background. 309 * 310 * @ant.not-required Default is background. 311 */ 312 public void setUpdateMode(String v) { 313 String l = v.toLowerCase(); 314 if ("eager".equals(l)) { 315 //workaround for doc bug in 2.0 316 l="always"; 317 } 318 if (!"always".equals(l) && !"background".equals(l) 319 && !"timeout".equals(l)) { 320 throw new BuildException("Unknown update mode: ["+l+"]." + 321 "Supported modes are: 'always', 'timeout' and 'background'"); 322 } 323 updateMode = l; 324 } 325 326 /** 327 * Indicates if the application can be launched offline. 328 * 329 * If application is already downloaded and update mode is eager then 330 * the check will timeout after a few seconds, in which case the cached 331 * application will be launched instead. 332 * 333 * Given a reasonably fast server connection, 334 * the latest version of the application will usually be run, 335 * but it is not guaranteed. The application, however, can be run offline. 336 * 337 * @ant.not-required Default is true. 338 */ 339 public void setOfflineAllowed(boolean v) { 340 offlineAllowed = v; 341 } 342 343 /** 344 * Application width for embedding application into Web page 345 * 346 * @ant.optional 347 */ 348 public void setEmbeddedWidth(String w) { 349 embeddedWidth = w; 350 } 351 352 /** 353 * Application width. Used for webstart and embedded applications 354 * unless emdeddedWidth is specified 355 * 356 * @ant.required 357 */ 358 public void setWidth(String v) { 359 width = v; 360 } 361 362 /** 363 * Application width for embedding application into Web page 364 * 365 * @ant.optional 366 */ 367 public void setEmbeddedHeight(String w) { 368 embeddedHeight = w; 369 } 370 371 /** 372 * Application height. Used for webstart and embedded applications 373 * unless emdeddedHeight is specified 374 * 375 * @ant.required 376 */ 377 public void setHeight(String v) { 378 height = v; 379 } 380 381 /** 382 * Enable embedding JNLP descriptor into Web page. 383 * Reduces number of network connections to be made on startup and 384 * help to improve startup time. 385 * 386 * @ant.not-required Default is false. 387 */ 388 public void setEmbedJNLP(boolean v) { 389 embedJNLP = v; 390 } 391 392 /** 393 * Directory where application package will be saved. 394 * 395 * @ant.required 396 */ 397 public void setOutdir(String v) { 398 outdir = v; 399 } 400 401 /** 402 * Prefix to be used for new generated files. 403 * 404 * @ant.required 405 */ 406 public void setOutfile(String v) { 407 outfile = v; 408 } 409 410 /** 411 * If true then web deployment is done using javascript files 412 * on java.com. Otherwise copy of javascript file is included into 413 * application package. 414 * 415 * @ant.not-required Before FCS default is false. For FCS default is true. 416 */ 417 public void setIncludeDT(Boolean v) { 418 includeDT = v; 419 } 420 421 /** 422 * Placeholder in the web page where application will be embedded. 423 * This is expected to be Javascript DOM object. 424 * 425 * @ant.required Either reference or id of placeholder is required. 426 */ 427 public void setPlaceholderRef(String p) { 428 this.placeholder = p; 429 } 430 431 /** 432 * Id of the placeholder in the web page where application will be embedded. 433 * Javascript's document.getElementById() is expected to be able to resolve it. 434 * 435 * @ant.required Either reference or id of placeholder is required. 436 */ 437 public void setPlaceholderId(String id) { 438 //raw id of the placeholder, need to escape it 439 this.placeholder = "'"+id+"'"; 440 } 441 442 public Info createInfo() { 443 appInfo = new Info(); 444 return appInfo; 445 } 446 447 public Application createApplication() { 448 app = new Application(); 449 return app; 450 } 451 452 public Preferences createPreferences() { 453 prefs = new Preferences(); 454 return prefs; 455 } 456 457 public Callbacks createCallbacks() { 458 if (callbacks != null) { 459 throw new BuildException("Only one callbacks element is supported."); 460 } 461 callbacks = new Callbacks(); 462 return callbacks; 463 } 464 465 public Resources createResources() { 466 if (resources != null) { 467 throw new BuildException("Only one resources element is supported."); 468 } 469 resources = new Resources(); 470 return resources; 471 } 472 473 List<Template> templateList = new LinkedList<>(); 474 475 public Template createTemplate() { 476 Template t = new Template(); 477 templateList.add(t); 478 return t; 479 } 480 481 Platform platform; 482 483 public Platform createPlatform() { 484 platform = new Platform(); 485 return platform; 486 } 487 488 private Permissions perms = null; 489 490 public Permissions createPermissions() { 491 perms = new Permissions(); 492 return perms; 493 } 494 495 List<BundleArgument> bundleArgumentList = new LinkedList<>(); 496 497 public BundleArgument createBundleArgument() { 498 BundleArgument ba = new BundleArgument(); 499 bundleArgumentList.add(ba); 500 return ba; 501 } 502 503 @Override 504 public void setDynamicAttribute(String name, String value) throws BuildException { 505 //Use qName and value - can't really validate anything until we know which bundlers we have, so this has 506 //to done (way) downstream 507 bundleArgumentList.add(new BundleArgument(name, value)); 508 } 509 510 511 /** 512 * Definition of security permissions needed by application. 513 * By default it is assumed that application may run in sandbox. 514 * Requesting elevated permissions assumes that application jar 515 * files are signed. 516 * 517 * @ant.type name="Permissions" category="javafx" 518 */ 519 public static class Permissions extends DataType { 520 boolean embed = false; 521 boolean elevated = true; 522 523 /** 524 * If set to false then application can run in sandbox. 525 * 526 * @ant.not-required Default is true. 527 */ 528 public void setElevated(boolean v) { 529 elevated = v; 530 } 531 532 /** 533 * If true then certificate used to sign jar files will be cached 534 * in the JNLP file. This allows to ask user to accept elevating 535 * permissions earlier and improves startup time. 536 * <p> 537 * This has no effect if application is run in the sandbox. 538 * 539 * @ant.not-required By default is false. 540 */ 541 public void setCacheCertificates(boolean v) { 542 embed = v; 543 } 544 } 545 546 /** 547 * Template to preprocess. 548 * <p> 549 * Template is the HTML file containing markers to be replaced with 550 * javascript or HTML snippets needed to deploy JavaFX application on the 551 * Web page. This allows to deploy application into "real" Web pages 552 * and simplify development process if application is tightly 553 * integrated with the page (e.g. uses javascript to communicate to it). 554 * <p> 555 * Marker has the form of #XXX# or #XXX(id)#. Where id is identifier 556 * of an application and XXX is one of following: 557 * <ul> 558 * <li>DT.SCRIPT.URL - location of dtjava.js 559 * <li>DT.SCRIPT.CODE - script element to include dtjava.js 560 * <li>DT.EMBED.CODE.DYNAMIC - code to embed application into given placeholder 561 * It is expected it will be wrapped into function() 562 * <li>DT.EMBED.CODE.ONLOAD - all code needed to embed application into Web page 563 * using onload hook (except inclusion of dtjava.js) 564 * <li>DT.LAUNCH.CODE - code need to launch application. 565 * Expected to be wrappend into function(). 566 * </ul> 567 * 568 * Page with multiple different applications can be processed multiple times 569 * - one per application. To avoid confusion markers need to use 570 * application ids (alphanumeric string no spaces). 571 * <p> 572 * If input and output files are the same then template is processed in place. 573 * <p> 574 * Example: 575 * <pre> 576 * <template file="App_template.html" tofile="App.html"/> 577 * </pre> 578 * 579 * @ant.type name="Template" category="javafx" 580 */ 581 public static class Template extends DataType { 582 File infile = null; 583 File outfile = null; 584 585 /** 586 * Input file. 587 * 588 * @ant.required 589 */ 590 public void setFile(File f) { 591 infile = f; 592 } 593 594 /** 595 * Output file (after preprocessing). 596 * 597 * @ant.not-required Default is the same as input file. 598 */ 599 public void setTofile(File f) { 600 outfile = f; 601 } 602 } 603 604 /** 605 * An argument to be passed off to the bundlers. 606 * 607 * Each bundler uses a set of arguments that may be shared across 608 * the different bundlers or it may be specific to each bundler. 609 * 610 * Some bundlers declare argument types that are not known to the JDK 611 * and may be specific to the particular bundler (such as Mac App Store 612 * categories). These arguments allow you to set and adjust these a 613 * rguments. 614 * 615 * @ant.type name="BundleArgument" category="javafx" 616 */ 617 public static class BundleArgument extends DataType { 618 String arg = null; 619 String value = null; 620 621 BundleArgument() { 622 623 } 624 625 BundleArgument(String arg, String value) { 626 this.arg = arg; 627 this.value = value; 628 } 629 630 /** 631 * Name of the bundle argument. 632 * 633 * @ant.required 634 */ 635 public void setArg(String arg) { 636 this.arg = arg; 637 } 638 639 /** 640 * Value for the bundle argument. 641 * 642 * @ant.not-required Default is a literal null 643 */ 644 public void setValue(String value) { 645 this.value = value; 646 } 647 } 648 }