modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/WinAppBundler.java

Print this page




  24  */
  25 
  26 package com.sun.javafx.tools.packager.bundlers;
  27 
  28 import com.oracle.bundlers.AbstractBundler;
  29 import com.oracle.bundlers.BundlerParamInfo;
  30 import com.oracle.bundlers.StandardBundlerParam;
  31 import com.oracle.bundlers.windows.WindowsBundlerParam;
  32 import com.sun.javafx.tools.packager.Log;
  33 import com.sun.javafx.tools.resource.windows.WinResources;
  34 
  35 import java.io.File;
  36 import java.io.FileNotFoundException;
  37 import java.io.IOException;
  38 import java.io.PrintStream;
  39 import java.net.MalformedURLException;
  40 import java.net.URL;
  41 import java.text.MessageFormat;
  42 import java.util.*;
  43 

  44 import static com.oracle.bundlers.StandardBundlerParam.*;
  45 import static com.oracle.bundlers.windows.WindowsBundlerParam.BIT_ARCH_64;
  46 import static com.oracle.bundlers.windows.WindowsBundlerParam.BIT_ARCH_64_RUNTIME;
  47 
  48 public class WinAppBundler extends AbstractBundler {
  49 
  50     private static final ResourceBundle I18N = 
  51             ResourceBundle.getBundle("com.oracle.bundlers.windows.WinAppBundler");
  52 
  53     public static final BundlerParamInfo<File> CONFIG_ROOT = new WindowsBundlerParam<>(
  54             I18N.getString("param.config-root.name"),
  55             I18N.getString("param.config-root.description"), 
  56             "configRoot", //KEY 
  57             File.class, null, params -> {
  58                 File imagesRoot = new File(StandardBundlerParam.BUILD_ROOT.fetchFrom(params), "windows");
  59                 imagesRoot.mkdirs();
  60                 return imagesRoot;
  61             }, false, s -> null);


















































  62 
  63     private final static String EXECUTABLE_NAME = "WinLauncher.exe";


  64     private static final String TOOL_ICON_SWAP="IconSwap.exe";
  65 
  66     public static final BundlerParamInfo<URL> RAW_EXECUTABLE_URL = new WindowsBundlerParam<>(
  67             I18N.getString("param.raw-executable-url.name"),
  68             I18N.getString("param.raw-executable-url.description"),
  69             "win.launcher.url", //KEY
  70             URL.class, null, params -> WinResources.class.getResource(EXECUTABLE_NAME), 
  71             false, s -> {
  72                 try {
  73                     return new URL(s);
  74                 } catch (MalformedURLException e) {
  75                     Log.info(e.toString());
  76                     return null;
  77                 }
  78             });
  79 
  80     public static final BundlerParamInfo<Boolean> REBRAND_EXECUTABLE = new WindowsBundlerParam<>(
  81             I18N.getString("param.rebrand-executable.name"),
  82             I18N.getString("param.rebrand-executable.description"),
  83             "win.launcher.rebrand", //KEY
  84             Boolean.class, null, params -> Boolean.TRUE, 
  85             false, Boolean::valueOf);
  86 
  87     public WinAppBundler() {
  88         super();
  89         baseResourceLoader = WinResources.class;
  90     }
  91 
  92     public final static String WIN_BUNDLER_PREFIX =
  93             BUNDLER_PREFIX + "windows/";
  94 
  95     File getConfigRoot(Map<String, ? super Object> params) {
  96         return CONFIG_ROOT.fetchFrom(params);
  97     }
  98 
  99     @Override
 100     public boolean validate(Map<String, ? super Object> params) throws UnsupportedPlatformException, ConfigException {

 101         if (params == null) throw new ConfigException(
 102                 I18N.getString("error.parameters-null"),
 103                 I18N.getString("error.parameters-null.advice"));
 104 
 105         return doValidate(params);



 106     }
 107 
 108     //to be used by chained bundlers, e.g. by EXE bundler to avoid
 109     // skipping validation if p.type does not include "image"
 110     boolean doValidate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
 111         if (!System.getProperty("os.name").toLowerCase().startsWith("win")) {
 112             throw new UnsupportedPlatformException();
 113         }
 114 
 115         if (WinResources.class.getResource(TOOL_ICON_SWAP) == null) {
 116             throw new ConfigException(
 117                     I18N.getString("error.no-windows-resources"),
 118                     I18N.getString("error.no-windows-resources.advice"));
 119         }
 120 
 121         if (StandardBundlerParam.MAIN_JAR.fetchFrom(p) == null) {






 122             throw new ConfigException(
 123                     I18N.getString("error.no-application-jar"),
 124                     I18N.getString("error.no-application-jar.advice"));
 125         }
 126 
 127         //validate required inputs
 128         if (USE_FX_PACKAGING.fetchFrom(p)) {
 129             testRuntime(p, new String[] {"lib/ext/jfxrt.jar", "lib/jfxrt.jar"});
 130         }
 131 
 132         //validate runtime bit-architectire
 133         testRuntimeBitArchitecture(p);
 134 
 135         return true;
 136     }
 137 
 138     private static void testRuntimeBitArchitecture(Map<String, ? super Object> params) throws ConfigException {
 139         if ("true".equalsIgnoreCase(System.getProperty("fxpackager.disableBitArchitectureMismatchCheck"))) {
 140             Log.debug(I18N.getString("message.disable-bit-architecture-check"));
 141             return;
 142         }
 143 
 144         if (BIT_ARCH_64.fetchFrom(params) != BIT_ARCH_64_RUNTIME.fetchFrom(params)) {
 145             throw new ConfigException(
 146                     I18N.getString("error.bit-architecture-mismatch"),
 147                     I18N.getString("error.bit-architecture-mismatch.advice"));
 148         }
 149     }
 150 
 151     static String getAppName(Map<String, ? super Object>  p) {
 152         return StandardBundlerParam.APP_NAME.fetchFrom(p);




 153     }
 154 
 155     //it is static for the sake of sharing with "Exe" bundles
 156     // that may skip calls to validate/bundle in this class!
 157     private static File getRootDir(File outDir, Map<String, ? super Object> p) {
 158         return new File(outDir, getAppName(p));
 159     }
 160 
 161     public static File getLauncher(File outDir, Map<String, ? super Object> p) {
 162         return new File(getRootDir(outDir, p), getAppName(p)+".exe");
 163     }
 164 




 165     private File getConfig_AppIcon(Map<String, ? super Object> params) {
 166         return new File(getConfigRoot(params), getAppName(params) + ".ico");
 167     }
 168 
 169     private final static String TEMPLATE_APP_ICON ="javalogo_white_48.ico";
 170 
 171     //remove
 172     protected void cleanupConfigFiles(Map<String, ? super Object> params) {
 173         if (getConfig_AppIcon(params) != null) {
 174             getConfig_AppIcon(params).delete();
 175         }
 176     }
 177 
 178     private void prepareConfigFiles(Map<String, ? super Object> params) throws IOException {
 179         File iconTarget = getConfig_AppIcon(params);
 180 
 181         File icon = ICON.fetchFrom(params);
 182         if (icon != null && icon.exists()) {
 183             fetchResource(WIN_BUNDLER_PREFIX + iconTarget.getName(),
 184                     I18N.getString("resource.application-icon"),
 185                     icon,
 186                     iconTarget);

 187         } else {
 188             fetchResource(WIN_BUNDLER_PREFIX + iconTarget.getName(),
 189                     I18N.getString("resource.application-icon"),
 190                     WinAppBundler.TEMPLATE_APP_ICON,
 191                     iconTarget);

 192         }
 193     }
 194 
 195     public boolean bundle(Map<String, ? super Object> p, File outputDirectory) {
 196         return doBundle(p, outputDirectory, false) != null;
 197     }
 198 
 199     File doBundle(Map<String, ? super Object> p, File outputDirectory, boolean dependentTask) {
 200         try {
 201             outputDirectory.mkdirs();
 202 
 203             if (!dependentTask) {
 204                 Log.info(MessageFormat.format(I18N.getString("message.creating-app-bundle"), getAppName(p), outputDirectory.getAbsolutePath()));
 205             }
 206 
 207             prepareConfigFiles(p);
 208 
 209             // Create directory structure
 210             File rootDirectory = getRootDir(outputDirectory, p);
 211             IOUtils.deleteRecursive(rootDirectory);
 212             rootDirectory.mkdirs();
 213 
 214             File appDirectory = new File(rootDirectory, "app");
 215             appDirectory.mkdirs();
 216             copyApplication(p, appDirectory);
 217 
 218             // Generate PkgInfo
 219             File pkgInfoFile = new File(appDirectory, "package.cfg");
 220             pkgInfoFile.createNewFile();
 221             writePkgInfo(p, pkgInfoFile);
 222 
 223             // Copy executable root folder
 224             File executableFile = getLauncher(outputDirectory, p);
 225             IOUtils.copyFromURL(
 226                     RAW_EXECUTABLE_URL.fetchFrom(p),
 227                     executableFile);
 228             executableFile.setExecutable(true, false);
 229 









 230             //Update branding of exe file
 231             if (REBRAND_EXECUTABLE.fetchFrom(p) && getConfig_AppIcon(p).exists()) {
 232                 //extract helper tool
 233                 File iconSwapTool = File.createTempFile("iconswap", ".exe");
 234                 iconSwapTool.delete();
 235                 IOUtils.copyFromURL(
 236                         WinResources.class.getResource(TOOL_ICON_SWAP),
 237                         iconSwapTool);
 238                 iconSwapTool.setExecutable(true, false);
 239                 iconSwapTool.deleteOnExit();
 240 
 241                 //run it on launcher file
 242                 executableFile.setWritable(true);
 243                 ProcessBuilder pb = new ProcessBuilder(
 244                         iconSwapTool.getAbsolutePath(),
 245                         getConfig_AppIcon(p).getAbsolutePath(),
 246                         executableFile.getAbsolutePath());
 247                 IOUtils.exec(pb, verbose);
 248                 executableFile.setReadOnly();
 249                 iconSwapTool.delete();
 250             }
 251 
 252             // Copy runtime to PlugIns folder
 253             File runtimeDirectory = new File(rootDirectory, "runtime");
 254             copyRuntime(p, runtimeDirectory);
 255 
 256             IOUtils.copyFile(getConfig_AppIcon(p),
 257                     new File(getRootDir(outputDirectory, p), getAppName(p) + ".ico"));
 258 
 259             if (!dependentTask) {
 260                 Log.info(MessageFormat.format(I18N.getString("message.result-dir"), outputDirectory.getAbsolutePath()));
 261             }
 262 
 263             return rootDirectory;
 264         } catch (IOException ex) {
 265             System.out.println("Exception: "+ex);
 266             ex.printStackTrace();
 267             return null;
 268         } finally {
 269             if (verbose) {
 270                 Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), getConfigRoot(p).getAbsolutePath()));
 271             } else {
 272                 cleanupConfigFiles(p);
 273             }
 274         }
 275 
 276     }
 277 
 278     @Override
 279     public String toString() {
 280         return "Windows Application Bundler";
 281     }
 282 
 283     private void copyApplication(Map<String, ? super Object> params, File appDirectory) throws IOException {
 284         RelativeFileSet appResource = APP_RESOURCES.fetchFrom(params);
 285         if (appResource == null) {
 286             throw new RuntimeException("Null app resources?");
 287         }
 288         File srcdir = appResource.getBaseDirectory();
 289         for (String fname : appResource.getIncludedFiles()) {


 324             idx++;
 325         }
 326 
 327 
 328         Map<String, String> overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params);
 329         idx = 1;
 330         for (Map.Entry<String, String> arg: overridableJVMOptions.entrySet()) {
 331             if (arg.getKey() == null || arg.getValue() == null) {
 332                 Log.info(I18N.getString("message.jvm-user-arg-is-null"));
 333             }
 334             else {
 335                 out.println("jvmuserarg."+idx+".name="+arg.getKey());
 336                 out.println("jvmuserarg."+idx+".value="+arg.getValue());
 337             }
 338             idx++;
 339         }
 340         out.close();
 341     }
 342 
 343     private void copyRuntime(Map<String, ? super Object> params, File runtimeDirectory) throws IOException {
 344         RelativeFileSet runtime = RUNTIME.fetchFrom(params);
 345         if (runtime == null) {
 346             //its ok, request to use system JRE
 347             return;
 348         }
 349         runtimeDirectory.mkdirs();
 350 
 351         File srcdir = runtime.getBaseDirectory();
 352         File destDir = new File(runtimeDirectory, srcdir.getName());
 353         Set<String> filesToCopy = runtime.getIncludedFiles();
 354         for (String fname : filesToCopy) {
 355             IOUtils.copyFile(
 356                     new File(srcdir, fname), new File(destDir, fname));
 357         }
 358     }
 359 
 360     @Override
 361     public String getName() {
 362         return I18N.getString("bundler.name");
 363     }
 364 
 365     @Override
 366     public String getDescription() {
 367         return I18N.getString("bundler.description");
 368     }
 369 
 370     @Override
 371     public String getID() {
 372         return "windows.app"; //KEY
 373     }
 374 
 375     @Override
 376     public BundleType getBundleType() {
 377         return BundleType.IMAGE;
 378     }
 379 
 380     @Override
 381     public Collection<BundlerParamInfo<?>> getBundleParameters() {
 382         return getAppBundleParameters();
 383     }
 384 
 385     public static Collection<BundlerParamInfo<?>> getAppBundleParameters() {
 386         return Arrays.asList(
 387                 APP_NAME,
 388                 APP_RESOURCES,
 389                 BUILD_ROOT,
 390                 CONFIG_ROOT,
 391                 ICON,
 392                 IDENTIFIER,
 393                 JVM_OPTIONS,
 394                 JVM_PROPERTIES,
 395                 MAIN_CLASS,
 396                 MAIN_JAR,
 397                 MAIN_JAR_CLASSPATH,
 398                 PREFERENCES_ID,
 399                 RAW_EXECUTABLE_URL,
 400                 RUNTIME,
 401                 USE_FX_PACKAGING,
 402                 USER_JVM_OPTIONS,
 403                 VERSION
 404         );
 405     }
 406 
 407     @Override
 408     public File execute(Map<String, ? super Object> params, File outputParentDir) {
 409         return doBundle(params, outputParentDir, false);
 410     }
 411 }


  24  */
  25 
  26 package com.sun.javafx.tools.packager.bundlers;
  27 
  28 import com.oracle.bundlers.AbstractBundler;
  29 import com.oracle.bundlers.BundlerParamInfo;
  30 import com.oracle.bundlers.StandardBundlerParam;
  31 import com.oracle.bundlers.windows.WindowsBundlerParam;
  32 import com.sun.javafx.tools.packager.Log;
  33 import com.sun.javafx.tools.resource.windows.WinResources;
  34 
  35 import java.io.File;
  36 import java.io.FileNotFoundException;
  37 import java.io.IOException;
  38 import java.io.PrintStream;
  39 import java.net.MalformedURLException;
  40 import java.net.URL;
  41 import java.text.MessageFormat;
  42 import java.util.*;
  43 
  44 import static com.oracle.bundlers.JreUtils.*;
  45 import static com.oracle.bundlers.StandardBundlerParam.*;
  46 import static com.oracle.bundlers.windows.WindowsBundlerParam.BIT_ARCH_64;
  47 import static com.oracle.bundlers.windows.WindowsBundlerParam.BIT_ARCH_64_RUNTIME;
  48 
  49 public class WinAppBundler extends AbstractBundler {
  50 
  51     private static final ResourceBundle I18N = 
  52             ResourceBundle.getBundle("com.oracle.bundlers.windows.WinAppBundler");
  53 
  54     public static final BundlerParamInfo<File> CONFIG_ROOT = new WindowsBundlerParam<>(
  55             I18N.getString("param.config-root.name"),
  56             I18N.getString("param.config-root.description"), 
  57             "configRoot",
  58             File.class, null, params -> {
  59                 File imagesRoot = new File(BUILD_ROOT.fetchFrom(params), "windows");
  60                 imagesRoot.mkdirs();
  61                 return imagesRoot;
  62             }, false, (s, p) -> null);
  63 
  64     //Subsetting of JRE is restricted.
  65     //JRE README defines what is allowed to strip:
  66     //   http://www.oracle.com/technetwork/java/javase/jre-7-readme-430162.html //TODO update when 8 goes GA
  67     public static final BundlerParamInfo<Rule[]> WIN_JRE_RULES = new StandardBundlerParam<>(
  68             "",
  69             "",
  70             ".win.runtime.rules",
  71             Rule[].class,
  72             null,
  73             params -> new Rule[]{
  74                 Rule.prefixNeg("\\bin\\new_plugin"),
  75                 Rule.prefixNeg("\\lib\\deploy"),
  76                 Rule.suffixNeg(".pdb"),
  77                 Rule.suffixNeg(".map"),
  78                 Rule.suffixNeg("axbridge.dll"),
  79                 Rule.suffixNeg("eula.dll"),
  80                 Rule.substrNeg("javacpl"),
  81                 Rule.suffixNeg("wsdetect.dll"),
  82                 Rule.substrNeg("eployjava1.dll"), //NP and IE versions
  83                 Rule.substrNeg("bin\\jp2"),
  84                 Rule.substrNeg("bin\\jpi"),
  85                 //Rule.suffixNeg("lib\\ext"), //need some of jars there for https to work
  86                 Rule.suffixNeg("ssv.dll"),
  87                 Rule.substrNeg("npjpi"),
  88                 Rule.substrNeg("npoji"),
  89                 Rule.suffixNeg(".exe"),
  90                 //keep core deploy files as JavaFX APIs use them
  91                 //Rule.suffixNeg("deploy.dll"),
  92                 Rule.suffixNeg("deploy.jar"),
  93                 //Rule.suffixNeg("javaws.jar"),
  94                 //Rule.suffixNeg("plugin.jar"),
  95                 Rule.suffix(".jar")
  96             },
  97             false,
  98             (s, p) -> null
  99     );
 100 
 101     public static final BundlerParamInfo<RelativeFileSet> WIN_RUNTIME = new StandardBundlerParam<>(
 102             RUNTIME.getName(),
 103             RUNTIME.getDescription(),
 104             RUNTIME.getID(),
 105             RelativeFileSet.class,
 106             null,
 107             params -> extractJreAsRelativeFileSet(System.getProperty("java.home"),
 108                     WIN_JRE_RULES.fetchFrom(params)),
 109             false,
 110             (s, p) -> extractJreAsRelativeFileSet(s,
 111                     WIN_JRE_RULES.fetchFrom(p))
 112     );
 113 
 114     private final static String EXECUTABLE_NAME = "WinLauncher.exe";
 115     private final static String EXECUTABLE_SVC_NAME = "WinLauncherSvc.exe";
 116 
 117     private static final String TOOL_ICON_SWAP="IconSwap.exe";
 118 
 119     public static final BundlerParamInfo<URL> RAW_EXECUTABLE_URL = new WindowsBundlerParam<>(
 120             I18N.getString("param.raw-executable-url.name"),
 121             I18N.getString("param.raw-executable-url.description"),
 122             "win.launcher.url",
 123             URL.class, null, params -> WinResources.class.getResource(EXECUTABLE_NAME), 
 124             false, (s, p) -> {
 125                 try {
 126                     return new URL(s);
 127                 } catch (MalformedURLException e) {
 128                     Log.info(e.toString());
 129                     return null;
 130                 }
 131             });
 132 
 133     public static final BundlerParamInfo<Boolean> REBRAND_EXECUTABLE = new WindowsBundlerParam<>(
 134             I18N.getString("param.rebrand-executable.name"),
 135             I18N.getString("param.rebrand-executable.description"),
 136             "win.launcher.rebrand",
 137             Boolean.class, null, params -> Boolean.TRUE, 
 138             false, (s, p) -> Boolean.valueOf(s));
 139 
 140     public WinAppBundler() {
 141         super();
 142         baseResourceLoader = WinResources.class;
 143     }
 144 
 145     public final static String WIN_BUNDLER_PREFIX =
 146             BUNDLER_PREFIX + "windows/";
 147 
 148     File getConfigRoot(Map<String, ? super Object> params) {
 149         return CONFIG_ROOT.fetchFrom(params);
 150     }
 151 
 152     @Override
 153     public boolean validate(Map<String, ? super Object> params) throws UnsupportedPlatformException, ConfigException {
 154         try {
 155             if (params == null) throw new ConfigException(
 156                     I18N.getString("error.parameters-null"),
 157                     I18N.getString("error.parameters-null.advice"));
 158 
 159             return doValidate(params);
 160         } catch (RuntimeException re) {
 161             throw new ConfigException(re);
 162         }
 163     }
 164 
 165     //to be used by chained bundlers, e.g. by EXE bundler to avoid
 166     // skipping validation if p.type does not include "image"
 167     boolean doValidate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException {
 168         if (!System.getProperty("os.name").toLowerCase().startsWith("win")) {
 169             throw new UnsupportedPlatformException();
 170         }
 171 
 172         if (WinResources.class.getResource(TOOL_ICON_SWAP) == null) {
 173             throw new ConfigException(
 174                     I18N.getString("error.no-windows-resources"),
 175                     I18N.getString("error.no-windows-resources.advice"));
 176         }
 177 
 178         if (SERVICE_HINT.fetchFrom(p) && WinResources.class.getResource(EXECUTABLE_SVC_NAME) == null) {
 179             throw new ConfigException(
 180                     I18N.getString("error.no-windows-resources"),
 181                     I18N.getString("error.no-windows-resources.advice"));
 182         }
 183 
 184         if (MAIN_JAR.fetchFrom(p) == null) {
 185             throw new ConfigException(
 186                     I18N.getString("error.no-application-jar"),
 187                     I18N.getString("error.no-application-jar.advice"));
 188         }
 189 
 190         //validate required inputs
 191         if (USE_FX_PACKAGING.fetchFrom(p)) {
 192             testRuntime(p, new String[] {"lib/ext/jfxrt.jar", "lib/jfxrt.jar"});
 193         }
 194 
 195         //validate runtime bit-architectire
 196         testRuntimeBitArchitecture(p);
 197 
 198         return true;
 199     }
 200 
 201     private static void testRuntimeBitArchitecture(Map<String, ? super Object> params) throws ConfigException {
 202         if ("true".equalsIgnoreCase(System.getProperty("fxpackager.disableBitArchitectureMismatchCheck"))) {
 203             Log.debug(I18N.getString("message.disable-bit-architecture-check"));
 204             return;
 205         }
 206 
 207         if (BIT_ARCH_64.fetchFrom(params) != BIT_ARCH_64_RUNTIME.fetchFrom(params)) {
 208             throw new ConfigException(
 209                     I18N.getString("error.bit-architecture-mismatch"),
 210                     I18N.getString("error.bit-architecture-mismatch.advice"));
 211         }
 212     }
 213 
 214     static String getAppName(Map<String, ? super Object>  p) {
 215         return APP_NAME.fetchFrom(p);
 216     }
 217 
 218     static String getAppSvcName(Map<String, ? super Object>  p) {
 219         return APP_NAME.fetchFrom(p) + "Svc";
 220     }
 221 
 222     //it is static for the sake of sharing with "Exe" bundles
 223     // that may skip calls to validate/bundle in this class!
 224     private static File getRootDir(File outDir, Map<String, ? super Object> p) {
 225         return new File(outDir, getAppName(p));
 226     }
 227 
 228     public static File getLauncher(File outDir, Map<String, ? super Object> p) {
 229         return new File(getRootDir(outDir, p), getAppName(p)+".exe");
 230     }
 231 
 232     public static File getLauncherSvc(File outDir, Map<String, ? super Object> p) {
 233         return new File(getRootDir(outDir, p), getAppName(p)+"Svc.exe");
 234     }
 235 
 236     private File getConfig_AppIcon(Map<String, ? super Object> params) {
 237         return new File(getConfigRoot(params), getAppName(params) + ".ico");
 238     }
 239 
 240     private final static String TEMPLATE_APP_ICON ="javalogo_white_48.ico";
 241 
 242     //remove
 243     protected void cleanupConfigFiles(Map<String, ? super Object> params) {
 244         if (getConfig_AppIcon(params) != null) {
 245             getConfig_AppIcon(params).delete();
 246         }
 247     }
 248 
 249     private void prepareConfigFiles(Map<String, ? super Object> params) throws IOException {
 250         File iconTarget = getConfig_AppIcon(params);
 251 
 252         File icon = ICON.fetchFrom(params);
 253         if (icon != null && icon.exists()) {
 254             fetchResource(WIN_BUNDLER_PREFIX + iconTarget.getName(),
 255                     I18N.getString("resource.application-icon"),
 256                     icon,
 257                     iconTarget,
 258                     VERBOSE.fetchFrom(params));
 259         } else {
 260             fetchResource(WIN_BUNDLER_PREFIX + iconTarget.getName(),
 261                     I18N.getString("resource.application-icon"),
 262                     WinAppBundler.TEMPLATE_APP_ICON,
 263                     iconTarget,
 264                     VERBOSE.fetchFrom(params));
 265         }
 266     }
 267 
 268     public boolean bundle(Map<String, ? super Object> p, File outputDirectory) {
 269         return doBundle(p, outputDirectory, false) != null;
 270     }
 271 
 272     File doBundle(Map<String, ? super Object> p, File outputDirectory, boolean dependentTask) {
 273         try {
 274             outputDirectory.mkdirs();
 275 
 276             if (!dependentTask) {
 277                 Log.info(MessageFormat.format(I18N.getString("message.creating-app-bundle"), getAppName(p), outputDirectory.getAbsolutePath()));
 278             }
 279 
 280             prepareConfigFiles(p);
 281 
 282             // Create directory structure
 283             File rootDirectory = getRootDir(outputDirectory, p);
 284             IOUtils.deleteRecursive(rootDirectory);
 285             rootDirectory.mkdirs();
 286 
 287             File appDirectory = new File(rootDirectory, "app");
 288             appDirectory.mkdirs();
 289             copyApplication(p, appDirectory);
 290 
 291             // Generate PkgInfo
 292             File pkgInfoFile = new File(appDirectory, "package.cfg");
 293             pkgInfoFile.createNewFile();
 294             writePkgInfo(p, pkgInfoFile);
 295 
 296             // Copy executable root folder
 297             File executableFile = getLauncher(outputDirectory, p);
 298             IOUtils.copyFromURL(
 299                     RAW_EXECUTABLE_URL.fetchFrom(p),
 300                     executableFile);
 301             executableFile.setExecutable(true, false);
 302 
 303             // Copy executable to install application as service
 304             if (SERVICE_HINT.fetchFrom(p)) {
 305                 File executableSvcFile = getLauncherSvc(outputDirectory, p);
 306                 IOUtils.copyFromURL(
 307                         WinResources.class.getResource(EXECUTABLE_SVC_NAME),
 308                         executableSvcFile);
 309                 executableSvcFile.setExecutable(true, false);
 310             }
 311 
 312             //Update branding of exe file
 313             if (REBRAND_EXECUTABLE.fetchFrom(p) && getConfig_AppIcon(p).exists()) {
 314                 //extract helper tool
 315                 File iconSwapTool = File.createTempFile("iconswap", ".exe");
 316                 iconSwapTool.delete();
 317                 IOUtils.copyFromURL(
 318                         WinResources.class.getResource(TOOL_ICON_SWAP),
 319                         iconSwapTool);
 320                 iconSwapTool.setExecutable(true, false);
 321                 iconSwapTool.deleteOnExit();
 322 
 323                 //run it on launcher file
 324                 executableFile.setWritable(true);
 325                 ProcessBuilder pb = new ProcessBuilder(
 326                         iconSwapTool.getAbsolutePath(),
 327                         getConfig_AppIcon(p).getAbsolutePath(),
 328                         executableFile.getAbsolutePath());
 329                 IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 330                 executableFile.setReadOnly();
 331                 iconSwapTool.delete();
 332             }
 333 
 334             // Copy runtime to PlugIns folder
 335             File runtimeDirectory = new File(rootDirectory, "runtime");
 336             copyRuntime(p, runtimeDirectory);
 337 
 338             IOUtils.copyFile(getConfig_AppIcon(p),
 339                     new File(getRootDir(outputDirectory, p), getAppName(p) + ".ico"));
 340 
 341             if (!dependentTask) {
 342                 Log.info(MessageFormat.format(I18N.getString("message.result-dir"), outputDirectory.getAbsolutePath()));
 343             }
 344 
 345             return rootDirectory;
 346         } catch (IOException ex) {
 347             System.out.println("Exception: "+ex);
 348             ex.printStackTrace();
 349             return null;
 350         } finally {
 351             if (VERBOSE.fetchFrom(p)) {
 352                 Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), getConfigRoot(p).getAbsolutePath()));
 353             } else {
 354                 cleanupConfigFiles(p);
 355             }
 356         }
 357 
 358     }
 359 
 360     @Override
 361     public String toString() {
 362         return "Windows Application Bundler";
 363     }
 364 
 365     private void copyApplication(Map<String, ? super Object> params, File appDirectory) throws IOException {
 366         RelativeFileSet appResource = APP_RESOURCES.fetchFrom(params);
 367         if (appResource == null) {
 368             throw new RuntimeException("Null app resources?");
 369         }
 370         File srcdir = appResource.getBaseDirectory();
 371         for (String fname : appResource.getIncludedFiles()) {


 406             idx++;
 407         }
 408 
 409 
 410         Map<String, String> overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params);
 411         idx = 1;
 412         for (Map.Entry<String, String> arg: overridableJVMOptions.entrySet()) {
 413             if (arg.getKey() == null || arg.getValue() == null) {
 414                 Log.info(I18N.getString("message.jvm-user-arg-is-null"));
 415             }
 416             else {
 417                 out.println("jvmuserarg."+idx+".name="+arg.getKey());
 418                 out.println("jvmuserarg."+idx+".value="+arg.getValue());
 419             }
 420             idx++;
 421         }
 422         out.close();
 423     }
 424 
 425     private void copyRuntime(Map<String, ? super Object> params, File runtimeDirectory) throws IOException {
 426         RelativeFileSet runtime = WIN_RUNTIME.fetchFrom(params);
 427         if (runtime == null) {
 428             //its ok, request to use system JRE
 429             return;
 430         }
 431         runtimeDirectory.mkdirs();
 432 
 433         File srcdir = runtime.getBaseDirectory();
 434         File destDir = new File(runtimeDirectory, srcdir.getName());
 435         Set<String> filesToCopy = runtime.getIncludedFiles();
 436         for (String fname : filesToCopy) {
 437             IOUtils.copyFile(
 438                     new File(srcdir, fname), new File(destDir, fname));
 439         }
 440     }
 441 
 442     @Override
 443     public String getName() {
 444         return I18N.getString("bundler.name");
 445     }
 446 
 447     @Override
 448     public String getDescription() {
 449         return I18N.getString("bundler.description");
 450     }
 451 
 452     @Override
 453     public String getID() {
 454         return "windows.app";
 455     }
 456 
 457     @Override
 458     public BundleType getBundleType() {
 459         return BundleType.IMAGE;
 460     }
 461 
 462     @Override
 463     public Collection<BundlerParamInfo<?>> getBundleParameters() {
 464         return getAppBundleParameters();
 465     }
 466 
 467     public static Collection<BundlerParamInfo<?>> getAppBundleParameters() {
 468         return Arrays.asList(
 469                 APP_NAME,
 470                 APP_RESOURCES,
 471                 BUILD_ROOT,
 472                 CONFIG_ROOT,
 473                 ICON,
 474                 IDENTIFIER,
 475                 JVM_OPTIONS,
 476                 JVM_PROPERTIES,
 477                 MAIN_CLASS,
 478                 MAIN_JAR,
 479                 MAIN_JAR_CLASSPATH,
 480                 PREFERENCES_ID,
 481                 RAW_EXECUTABLE_URL,
 482                 WIN_RUNTIME,
 483                 USE_FX_PACKAGING,
 484                 USER_JVM_OPTIONS,
 485                 VERSION
 486         );
 487     }
 488 
 489     @Override
 490     public File execute(Map<String, ? super Object> params, File outputParentDir) {
 491         return doBundle(params, outputParentDir, false);
 492     }
 493 }