/* * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.javafx.tools.ant; import java.io.File; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import com.oracle.tools.packager.StandardBundlerParam; import com.sun.javafx.tools.ant.Platform.Jvmarg; import com.sun.javafx.tools.ant.Platform.Property; import com.sun.javafx.tools.packager.DeployParams; import com.oracle.tools.packager.Log; import com.sun.javafx.tools.packager.PackagerException; import com.sun.javafx.tools.packager.PackagerLib; import com.sun.javafx.tools.packager.bundlers.Bundler; import com.sun.javafx.tools.packager.bundlers.Bundler.Bundle; import com.sun.javafx.tools.packager.bundlers.Bundler.BundleType; import java.util.ResourceBundle; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DynamicAttribute; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.DataType; /** * Generates package for Web deployment and redistribution of application. * Package includes of set of jar files, JNLP file and HTML file. * * Minimal example: *
 *   <fx:deploy width="600" height="400"
 *                 outdir="web-dist" outfile="Fish">
 *       <info title="Sample application"/>
 *       <fx:application refid="myapp"/>
 *       <fx:resources refid="myresources"/>
 *   </fx:deploy>
 * 
* Above example will generate HTML/JNLP files into the web-dist directory * and use "Fish" as prefix for generated files. Details about application and * its resources are defined elsewhere in the application and resource elements. *

* Minimal complete example: *

 *   <fx:deploy width="600" height="400"
 *                 outdir="web-dist" outfile="Fish">
 *       <info title="Sample application"/>
 *       <fx:application name="SampleApp"
 *              mainClass="testapp.MainApp"
 *              preloaderClass="testpreloader.Preloader"/>
 *       <fx:resources>
 *              <fx:fileset requiredFor="preloader" dir="dist">
 *                <include name="preloader.jar"/>
 *             </fx:fileset>
 *              <fx:fileset dir="dist">
 *                <include name="helloworld.jar"/>
 *             </fx:fileset>
 *       </fx:resources>
 *   </fx:deploy>
 * 
* Same as above but here application and resource details are defined in place. * Note that using references helps with reducing code duplication as fx:jar need * to be used for double clickable jars. * * @ant.task name="deploy" category="javafx" */ public class DeployFXTask extends Task implements DynamicAttribute { private static final ResourceBundle I18N = ResourceBundle.getBundle(DeployFXTask.class.getName()); private String width = null; private String height = null; private String embeddedWidth = null; private String embeddedHeight = null; private String outfile = null; private String outdir = null; private boolean embedJNLP; private boolean isExtension = false; private Boolean signBundle; //Before FCS default is to include DT files with app // to ensure tests are using latest and compatible. //After FCS default is to use shared copy. private boolean includeDT = false; private String updateMode="background"; private Info appInfo = null; private Application app = null; private Resources resources = null; private Preferences prefs = null; private String codebase = null; //container to embed application into //could be either string id or js code. If it is string id then it needs to //be escaped private String placeholder; private PackagerLib packager; private DeployParams deployParams; private Callbacks callbacks; boolean offlineAllowed = true; //default native bundle settings // use NONE to avoid large disk space and build time overhead BundleType nativeBundles = BundleType.NONE; String bundleFormat = null; boolean versionCheck = true; private boolean verbose = false; public void setVerbose(boolean v) { verbose = v; } public void setCodebase(String str) { codebase = str; } public DeployFXTask() { packager = new PackagerLib(); deployParams = new DeployParams(); } @Override public void execute() { boolean isModular = (app.getModule() != null) && !app.getModule().isEmpty(); deployParams.setOutfile(outfile); deployParams.setOutdir(new File(outdir)); if (versionCheck) { if (!com.sun.javafx.tools.ant.VersionCheck.isSameVersion()) { throw new BuildException(I18N.getString("message.java.version.mismatch")); } } if (!isModular && (nativeBundles == BundleType.JNLP || nativeBundles == BundleType.ALL || nativeBundles == BundleType.NONE)) { deployParams.setOfflineAllowed(offlineAllowed); deployParams.setVerbose(verbose); deployParams.setCodebase(codebase); deployParams.setSignBundle(signBundle); if (app.getModule() == null) { deployParams.setApplicationClass(app.get().mainClass); } if (width != null) { deployParams.setWidth(Integer.valueOf(width)); } if (height != null) { deployParams.setHeight(Integer.valueOf(height)); } if (embeddedWidth != null && embeddedHeight != null) { deployParams.setEmbeddedDimensions(embeddedWidth, embeddedHeight); } deployParams.setEmbedJNLP(embedJNLP); if (perms != null) { deployParams.setAllPermissions(perms.getElevated()); } deployParams.setUpdateMode(updateMode); deployParams.setExtension(isExtension); deployParams.setIncludeDT(includeDT); if (callbacks != null) { for (Callback cb: callbacks.callbacks) { deployParams.addCallback(cb.getName(), cb.getCmd()); } } setPlatform(); setPreferences(); for (Template t: templateList) { deployParams.addTemplate(t.infile, t.outfile); } } if (isModular && (nativeBundles == BundleType.NATIVE || nativeBundles == BundleType.IMAGE || nativeBundles == BundleType.INSTALLER || nativeBundles == BundleType.ALL)) { if (app != null) { if (app.getModule() == null) { deployParams.setApplicationClass(app.get().mainClass); } else { int index = app.getModule().indexOf("/"); if (index > 0) { deployParams.setModule(app.getModule()); } else { deployParams.setModule(app.getModule() + "/" + app.get().mainClass); } } deployParams.setPreloader(app.get().preloaderClass); deployParams.setAppId(app.get().id); deployParams.setAppName(app.get().name); deployParams.setParams(app.get().parameters); deployParams.setArguments(app.get().getArguments()); deployParams.setHtmlParams(app.get().htmlParameters); deployParams.setFallback(app.get().fallbackApp); deployParams.setSwingAppWithEmbeddedJavaFX(app.get().embeddedIntoSwing); deployParams.setVersion(app.get().version); deployParams.setId(app.get().id); deployParams.setServiceHint(app.get().daemon); setRuntime(); } if (appInfo != null) { deployParams.setTitle(appInfo.title); deployParams.setVendor(appInfo.vendor); deployParams.setDescription(appInfo.appDescription); deployParams.setCategory(appInfo.category); deployParams.setLicenseType(appInfo.licenseType); deployParams.setCopyright(appInfo.copyright); deployParams.setEmail(appInfo.email); for (Info.Icon i: appInfo.icons) { if (i instanceof Info.Splash) { deployParams.addIcon(i.href, i.kind, i.width, i.height, i.depth, ((Info.Splash) i).mode); } else { deployParams.addIcon(i.href, i.kind, i.width, i.height, i.depth, DeployParams.RunMode.WEBSTART); } } deployParams.addBundleArgument(StandardBundlerParam.FILE_ASSOCIATIONS.getID(), appInfo.fileAssociations.stream() .map(FileAssociation::createLauncherMap) .collect(Collectors.toList())); } setPlatform(); setPreferences(); } if (!isModular && (nativeBundles == BundleType.NATIVE || nativeBundles == BundleType.IMAGE || nativeBundles == BundleType.INSTALLER || nativeBundles == BundleType.ALL)) { if (app != null) { deployParams.setApplicationClass(app.get().mainClass); deployParams.setPreloader(app.get().preloaderClass); deployParams.setAppId(app.get().id); deployParams.setAppName(app.get().name); deployParams.setParams(app.get().parameters); deployParams.setArguments(app.get().getArguments()); deployParams.setHtmlParams(app.get().htmlParameters); deployParams.setFallback(app.get().fallbackApp); deployParams.setSwingAppWithEmbeddedJavaFX(app.get().embeddedIntoSwing); deployParams.setVersion(app.get().version); deployParams.setId(app.get().id); deployParams.setServiceHint(app.get().daemon); setRuntime(); } if (appInfo != null) { deployParams.setTitle(appInfo.title); deployParams.setVendor(appInfo.vendor); deployParams.setDescription(appInfo.appDescription); deployParams.setCategory(appInfo.category); deployParams.setLicenseType(appInfo.licenseType); deployParams.setCopyright(appInfo.copyright); deployParams.setEmail(appInfo.email); for (Info.Icon i: appInfo.icons) { if (i instanceof Info.Splash) { deployParams.addIcon(i.href, i.kind, i.width, i.height, i.depth, ((Info.Splash) i).mode); } else { deployParams.addIcon(i.href, i.kind, i.width, i.height, i.depth, DeployParams.RunMode.WEBSTART); } } deployParams.addBundleArgument(StandardBundlerParam.FILE_ASSOCIATIONS.getID(), appInfo.fileAssociations.stream() .map(FileAssociation::createLauncherMap) .collect(Collectors.toList())); } setPlatform(); setPreferences(); } for (BundleArgument ba : bundleArgumentList) { deployParams.addBundleArgument(ba.arg, ba.value); } deployParams.setPlaceholder(placeholder); if (resources != null) { for (FileSet fs: resources.getResources()) { Utils.addResources(deployParams, fs); } } List> launchersAsMap = new ArrayList<>(); for (SecondaryLauncher sl : secondaryLaunchers) { launchersAsMap.add(sl.createLauncherMap()); } deployParams.addBundleArgument( StandardBundlerParam.SECONDARY_LAUNCHERS.getID(), launchersAsMap); deployParams.setBundleType(nativeBundles); deployParams.setTargetFormat(bundleFormat); Log.setLogger(new AntLog(this.getProject())); try { packager.generateDeploymentPackages(deployParams); } catch (PackagerException pe) { if (pe.getCause() != null) { throw new BuildException(pe.getCause().getMessage(), pe.getCause()); } else { throw new BuildException(pe.getMessage(), pe); } } catch (Exception e) { throw new BuildException(e.getMessage(), e); } finally { Log.setLogger(null); } } private void setRuntime() { if (runtime != null) { for (String s : runtime.getAddModules()) { deployParams.addAddModule(s); } for (String s : runtime.getLimitModules()) { deployParams.addLimitModule(s); } deployParams.setModulePath(runtime.getModulePath()); Boolean stripNativeCommands = runtime.getStripNativeCommands(); if (stripNativeCommands != null) { deployParams.setStripNativeCommands(stripNativeCommands); } Boolean detectModules = runtime.getDetectModules(); if (detectModules != null) { deployParams.setDetectModules(detectModules); } } } /** * Set to true if we are generating an 'extension' JNLP. * * @ant.not-required Default is false. */ public void setExtension(boolean v) { isExtension = v; } public void setNativeBundles(String v) { Bundle bundle = Bundler.stringToBundle(v); this.nativeBundles = bundle.type; this.bundleFormat = bundle.format; } public void setVersionCheck(String value) { this.versionCheck = Boolean.valueOf(value); } /** * Indicates the preferences for when checks for application updates * are performed. Supported modes are always, timeout and background. * * @ant.not-required Default is background. */ public void setUpdateMode(String v) { String l = v.toLowerCase(); if ("eager".equals(l)) { //workaround for doc bug in 2.0 l="always"; } if (!"always".equals(l) && !"background".equals(l) && !"timeout".equals(l)) { throw new BuildException("Unknown update mode: ["+l+"]." + "Supported modes are: 'always', 'timeout' and 'background'"); } updateMode = l; } /** * Indicates if the application can be launched offline. * * If application is already downloaded and update mode is eager then * the check will timeout after a few seconds, in which case the cached * application will be launched instead. * * Given a reasonably fast server connection, * the latest version of the application will usually be run, * but it is not guaranteed. The application, however, can be run offline. * * @ant.not-required Default is true. */ public void setOfflineAllowed(boolean v) { offlineAllowed = v; } /** * Application width for embedding application into Web page * * @ant.optional */ public void setEmbeddedWidth(String w) { embeddedWidth = w; } /** * Application width. Used for webstart and embedded applications * unless emdeddedWidth is specified * * @ant.required */ public void setWidth(String v) { width = v; } /** * Application width for embedding application into Web page * * @ant.optional */ public void setEmbeddedHeight(String w) { embeddedHeight = w; } /** * Application height. Used for webstart and embedded applications * unless emdeddedHeight is specified * * @ant.required */ public void setHeight(String v) { height = v; } /** * Enable embedding JNLP descriptor into Web page. * Reduces number of network connections to be made on startup and * help to improve startup time. * * @ant.not-required Default is false. */ public void setEmbedJNLP(boolean v) { embedJNLP = v; } /** * Directory where application package will be saved. * * @ant.required */ public void setOutdir(String v) { outdir = v; } /** * Prefix to be used for new generated files. * * @ant.required */ public void setOutfile(String v) { outfile = v; } /** * If true then web deployment is done using javascript files * on java.com. Otherwise copy of javascript file is included into * application package. * * @ant.not-required Before FCS default is false. For FCS default is true. */ public void setIncludeDT(Boolean v) { includeDT = v; } /** * Placeholder in the web page where application will be embedded. * This is expected to be Javascript DOM object. * * @ant.required Either reference or id of placeholder is required. */ public void setPlaceholderRef(String p) { this.placeholder = p; } /** * Id of the placeholder in the web page where application will be embedded. * Javascript's document.getElementById() is expected to be able to resolve it. * * @ant.required Either reference or id of placeholder is required. */ public void setPlaceholderId(String id) { //raw id of the placeholder, need to escape it this.placeholder = "'"+id+"'"; } public void setSignBundle(boolean signBundle) { this.signBundle = signBundle; } public Info createInfo() { appInfo = new Info(); return appInfo; } public Application createApplication() { app = new Application(); return app; } public Preferences createPreferences() { prefs = new Preferences(); return prefs; } public Callbacks createCallbacks() { if (callbacks != null) { throw new BuildException("Only one callbacks element is supported."); } callbacks = new Callbacks(); return callbacks; } public Resources createResources() { if (resources != null) { throw new BuildException("Only one resources element is supported."); } resources = new Resources(); return resources; } List