src/demo/share/jpackager/JNLPConverter/src/jnlp/converter/JNLPConverter.java
branchJDK-8200758-branch
changeset 56963 eaca4369b068
equal deleted inserted replaced
56962:a769ad2d40d6 56963:eaca4369b068
       
     1 /*
       
     2  * Copyright (c) 2018, 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.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 package jnlp.converter;
       
    25 
       
    26 import java.io.BufferedWriter;
       
    27 import java.io.File;
       
    28 import java.io.FileNotFoundException;
       
    29 import java.io.FileOutputStream;
       
    30 import java.io.FileWriter;
       
    31 import java.io.IOException;
       
    32 import java.io.InputStream;
       
    33 import java.io.PrintWriter;
       
    34 import java.net.HttpURLConnection;
       
    35 import java.net.URI;
       
    36 import java.net.URL;
       
    37 import java.nio.file.Files;
       
    38 import java.nio.file.Path;
       
    39 import java.util.ArrayList;
       
    40 import java.util.Enumeration;
       
    41 import java.util.List;
       
    42 import java.util.jar.JarEntry;
       
    43 import java.util.jar.JarFile;
       
    44 import jnlp.converter.parser.JNLPDesc;
       
    45 import jnlp.converter.parser.JNLPDesc.AssociationDesc;
       
    46 import jnlp.converter.parser.JNLPDesc.IconDesc;
       
    47 import jnlp.converter.parser.ResourcesDesc.JARDesc;
       
    48 import jnlp.converter.parser.XMLFormat;
       
    49 
       
    50 public class JNLPConverter {
       
    51 
       
    52     private final Options options;
       
    53     private JNLPDesc jnlpd = null;
       
    54     private final List<String> launchArgs = new ArrayList<>();
       
    55 
       
    56     private String downloadFolder = null;
       
    57     private String jnlpDownloadFolder = null;
       
    58     private static String jnlpDownloadFolderStatic;
       
    59     private String jarDownloadFolder = null;
       
    60     private String iconDownloadFolder = null;
       
    61     private String propDownloadFolder = null;
       
    62 
       
    63     private static String jpackagerPath = null;
       
    64 
       
    65     private static boolean markFileToDelete = false;
       
    66 
       
    67     private static final String FA_EXTENSIONS = "extension";
       
    68     private static final String FA_CONTENT_TYPE = "mime-type";
       
    69     private static final String FA_DESCRIPTION = "description";
       
    70     private static final String FA_ICON = "icon";
       
    71 
       
    72     public JNLPConverter(Options options) {
       
    73         this.options = options;
       
    74         jnlpDownloadFolderStatic = getJnlpDownloadFolder();
       
    75         markFileToDelete = (options.keep() == null);
       
    76     }
       
    77 
       
    78     public String [] getLaunchArgs() {
       
    79         return launchArgs.toArray(new String[0]);
       
    80     }
       
    81 
       
    82     public void convert() {
       
    83         try {
       
    84             loadJNLPDesc();
       
    85             downloadResources();
       
    86             validate();
       
    87             buildLaunchArgs();
       
    88             saveLaunchArgs();
       
    89             runJPackager();
       
    90         } catch (Exception ex) {
       
    91             Log.error(ex.getLocalizedMessage());
       
    92         }
       
    93     }
       
    94 
       
    95     private JNLPDesc getJNLPD(String jnlp) throws Exception {
       
    96         URL codebase = getCodeBase(jnlp);
       
    97         byte[] bits = HTTPHelper.getJNLPBits(jnlp, jnlp);
       
    98         return XMLFormat.parse(bits, codebase, jnlp);
       
    99     }
       
   100 
       
   101     private void loadJNLPDesc() throws Exception {
       
   102         String jnlp = options.getJNLP();
       
   103         jnlpd = getJNLPD(jnlp);
       
   104 
       
   105         // Check for required options in case of FX
       
   106         if (jnlpd.isFXApp()) {
       
   107             if (!options.isRuntimeImageSet()) {
       
   108                 throw new Exception("This is a JavaFX Web-Start application which requires a runtime image capable of running JavaFX applications, which can be specified by the jpackager option --runtime-image (using --jpackager-options).");
       
   109             }
       
   110         }
       
   111 
       
   112         // Check href. It can be same as URL we provided or new one
       
   113         // if JNLP has different href or codebase. We assume that
       
   114         // XMLFormat.parse() will handle any errors in href and codebase
       
   115         // correctly.
       
   116         String href = jnlpd.getHref();
       
   117         if (href != null && !href.equalsIgnoreCase(jnlp)) {
       
   118             if (href.startsWith("file:")) {
       
   119                 URI hrefURI = new URI(href);
       
   120                 URI jnlpURI = new URI(jnlp);
       
   121 
       
   122                 String hrefPath = hrefURI.getPath();
       
   123                 String jnlpPath = jnlpURI.getPath();
       
   124 
       
   125                 if (!hrefPath.equalsIgnoreCase(jnlpPath)) {
       
   126                     jnlp = href;
       
   127                     jnlpd = getJNLPD(jnlp);
       
   128                 }
       
   129             } else {
       
   130                 jnlp = href;
       
   131                 jnlpd = getJNLPD(jnlp);
       
   132             }
       
   133         }
       
   134 
       
   135         if (jnlpd.getName() == null) {
       
   136             jnlpd.setName(getNameFromURL(jnlp));
       
   137         }
       
   138     }
       
   139 
       
   140     private static String getNameFromURL(String url) throws IOException {
       
   141         int index;
       
   142         int index1 = url.lastIndexOf('/');
       
   143         int index2 = url.lastIndexOf('\\');
       
   144 
       
   145         if (index1 >= index2) {
       
   146             index = index1;
       
   147         } else {
       
   148             index = index2;
       
   149         }
       
   150 
       
   151         if (index != -1) {
       
   152             String name = url.substring(index + 1, url.length());
       
   153             if (name.endsWith(".jnlp")) {
       
   154                 return name.substring(0, name.length() - 5);
       
   155             }
       
   156         }
       
   157 
       
   158         return null;
       
   159     }
       
   160 
       
   161     private URL getCodeBase(String jnlp) throws Exception {
       
   162         int index = jnlp.lastIndexOf('/');
       
   163         if (index != -1) {
       
   164             if (HTTPHelper.isHTTPUrl(jnlp)) {
       
   165                 return new URL(jnlp.substring(0, index + 1));
       
   166             } else {
       
   167                 String codeBasePath = jnlp.substring(0, index);
       
   168                 if (!codeBasePath.endsWith("/")) {
       
   169                     codeBasePath += "/";
       
   170                 }
       
   171                 return new URI(codeBasePath).toURL();
       
   172             }
       
   173         }
       
   174 
       
   175         return null;
       
   176     }
       
   177 
       
   178     public static void markFileToDelete(String file) {
       
   179         if (file == null || file.isEmpty()) {
       
   180             return;
       
   181         }
       
   182 
       
   183         if (markFileToDelete) {
       
   184             try {
       
   185                 File f = new File(file);
       
   186                 f.deleteOnExit();
       
   187             } catch (Exception e) {
       
   188                 // Print exception, but do not fail conversion.
       
   189                 Log.warning(e.getLocalizedMessage());
       
   190             }
       
   191         }
       
   192     }
       
   193 
       
   194     public static void deleteFile(String file) {
       
   195         try {
       
   196             File f = new File(file);
       
   197             f.delete();
       
   198         } catch (Exception e) {
       
   199             Log.warning(e.getLocalizedMessage());
       
   200         }
       
   201     }
       
   202 
       
   203     private void downloadResources() throws Exception {
       
   204         List<JARDesc> jars = jnlpd.getResources();
       
   205         for (JARDesc jar : jars) {
       
   206             if (jar.getVersion() != null) {
       
   207                 if (!jnlpd.isVersionEnabled()) {
       
   208                     throw new Exception("Error: Version based download protocol is not supported without -Djnlp.versionEnabled=true.");
       
   209                 }
       
   210             }
       
   211 
       
   212             String destFile = null;
       
   213             if (HTTPHelper.isHTTPUrl(jar.getLocation().toString())) {
       
   214                 if (jar.getVersion() != null) {
       
   215                     try {
       
   216                         destFile = HTTPHelper.downloadFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
       
   217                     } catch (HTTPHelperException ex) {
       
   218                         if (ex.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
       
   219                             System.out.println("Error downloading versioned JAR from " + jar.getVersionLocation());
       
   220                             System.out.println(ex.getMessage());
       
   221                             System.out.println("Downloading " + jar.getLocation() + " instead.");
       
   222                             destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
       
   223                         } else {
       
   224                             throw ex;
       
   225                         }
       
   226                     }
       
   227                 } else {
       
   228                     destFile = HTTPHelper.downloadFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
       
   229                 }
       
   230                 markFileToDelete(destFile);
       
   231             } else {
       
   232                 if (jar.getVersion() != null) {
       
   233                     try {
       
   234                         destFile = HTTPHelper.copyFile(jar.getVersionLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
       
   235                     } catch (FileNotFoundException ex) {
       
   236                         System.out.println("Error copying versioned JAR from " + jar.getVersionLocation());
       
   237                         System.out.println(ex.getMessage());
       
   238                         System.out.println("Copying " + jar.getLocation() + " instead.");
       
   239                         destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
       
   240                     }
       
   241                 } else {
       
   242                     destFile = HTTPHelper.copyFile(jar.getLocation().toString(), getJarDownloadFolder(), HTTPHelper.getFileNameFromURL(jar.getLocation().toString()));
       
   243                 }
       
   244                 markFileToDelete(destFile);
       
   245             }
       
   246 
       
   247             if (jar.isNativeLib()) {
       
   248                 unpackNativeLib(destFile);
       
   249                 deleteFile(destFile);
       
   250             } else {
       
   251                 jnlpd.addFile(jar.getName());
       
   252             }
       
   253         }
       
   254 
       
   255         IconDesc icon = jnlpd.getIcon();
       
   256         if (icon != null) {
       
   257             String destFile;
       
   258 
       
   259             if (HTTPHelper.isHTTPUrl(icon.getLocation())) {
       
   260                 destFile = HTTPHelper.downloadFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation()));
       
   261             } else {
       
   262                 destFile = HTTPHelper.copyFile(icon.getLocation(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(icon.getLocation()));
       
   263             }
       
   264 
       
   265             markFileToDelete(destFile);
       
   266             icon.setLocalLocation(destFile);
       
   267         }
       
   268 
       
   269         AssociationDesc [] associations = jnlpd.getAssociations();
       
   270         if (associations != null) {
       
   271             for (AssociationDesc association : associations) {
       
   272                 if (association.getIconUrl() != null) {
       
   273                     String destFile;
       
   274                     if (HTTPHelper.isHTTPUrl(association.getIconUrl())) {
       
   275                         destFile = HTTPHelper.downloadFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl()));
       
   276                     } else {
       
   277                         destFile = HTTPHelper.copyFile(association.getIconUrl(), getIconDownloadFolder(), HTTPHelper.getFileNameFromURL(association.getIconUrl()));
       
   278                     }
       
   279 
       
   280                     markFileToDelete(destFile);
       
   281                     association.setIconLocalLocation(destFile);
       
   282                 }
       
   283             }
       
   284         }
       
   285     }
       
   286 
       
   287     public void unpackNativeLib(String file) throws IOException {
       
   288         try (JarFile jarFile = new JarFile(file)) {
       
   289             Enumeration entries = jarFile.entries();
       
   290 
       
   291             while (entries.hasMoreElements()) {
       
   292                 JarEntry entry = (JarEntry) entries.nextElement();
       
   293 
       
   294                 // Skip directories
       
   295                 if (entry.isDirectory()) {
       
   296                     continue;
       
   297                 }
       
   298 
       
   299                 String entryName = entry.getName();
       
   300                 // Skip anything in sub-directories
       
   301                 if (entryName.contains("\\") || entryName.contains("/")) {
       
   302                     continue;
       
   303                 }
       
   304 
       
   305                 // Skip anything not ending with .dll, .dylib or .so
       
   306                 if (!entryName.endsWith(".dll") && !entryName.endsWith(".dylib") && !entryName.endsWith(".so")) {
       
   307                     continue;
       
   308                 }
       
   309 
       
   310                 File destFile = new File(getJarDownloadFolder(), entryName);
       
   311                 if (destFile.exists()) {
       
   312                     Log.warning(destFile.getAbsolutePath() + " already exist and will not be overwriten by native library from " + file + ".");
       
   313                     continue;
       
   314                 }
       
   315 
       
   316                 InputStream inputStream = jarFile.getInputStream(entry);
       
   317                 FileOutputStream outputStream = new FileOutputStream(destFile);
       
   318 
       
   319                 byte[] buffer = new byte[HTTPHelper.BUFFER_SIZE];
       
   320                 int length;
       
   321                 do {
       
   322                     length = inputStream.read(buffer);
       
   323                     if (length > 0) {
       
   324                         outputStream.write(buffer, 0, length);
       
   325                     }
       
   326                 } while (length > 0);
       
   327 
       
   328                 jnlpd.addFile(entryName);
       
   329             }
       
   330         }
       
   331     }
       
   332 
       
   333     private void validate() {
       
   334         if (jnlpd.getMainJar() == null) {
       
   335             Log.error("Cannot find main jar");
       
   336         }
       
   337 
       
   338         if (jnlpd.getMainClass() == null) {
       
   339             Log.error("Cannot find main class");
       
   340         }
       
   341     }
       
   342 
       
   343     private void addLaunchArg(String arg, List<String> launchArgs) {
       
   344         if (arg != null && !arg.isEmpty()) {
       
   345             if (!options.isOptionPresent(arg)){
       
   346                 launchArgs.add(arg);
       
   347             } else {
       
   348                 Log.info(arg + " generated by JNLPConverter is dropped, since it is overwriten via --jpackager-options");
       
   349             }
       
   350         }
       
   351     }
       
   352 
       
   353     private void addLaunchArg(String arg, String value, List<String> launchArgs) {
       
   354         if (arg != null && !arg.isEmpty() && value != null && !value.isEmpty()) {
       
   355             if (!options.isOptionPresent(arg)){
       
   356                 launchArgs.add(arg);
       
   357                 launchArgs.add(value);
       
   358             } else {
       
   359                 Log.info(arg + "=" + value +" generated by JNLPConverter is dropped, since it is overwriten via --jpackager-options");
       
   360             }
       
   361         }
       
   362     }
       
   363 
       
   364     private void displayLaunchArgs() {
       
   365         if (Log.isVerbose()) {
       
   366             System.out.println();
       
   367             System.out.println("jpackager launch arguments (each argument starts on new line):");
       
   368             launchArgs.forEach((arg) -> {
       
   369                 System.out.println(arg);
       
   370             });
       
   371         }
       
   372     }
       
   373 
       
   374     private static int fileAssociationsCount = 0;
       
   375     private String getFileAssociationsFile() {
       
   376         String file = getPropDownloadFolder();
       
   377         file += File.separator;
       
   378         file += "fileAssociation";
       
   379         file += String.valueOf(fileAssociationsCount);
       
   380         file += ".properties";
       
   381 
       
   382         fileAssociationsCount++;
       
   383 
       
   384         return file;
       
   385     }
       
   386 
       
   387     private void buildLaunchArgs() {
       
   388         if (options.createImage()) {
       
   389             addLaunchArg("create-image", launchArgs);
       
   390         } else if (options.createInstaller()) {
       
   391             if (options.getInstallerType() == null) {
       
   392                 addLaunchArg("create-installer", launchArgs);
       
   393             } else {
       
   394                 addLaunchArg("create-installer", options.getInstallerType(), launchArgs);
       
   395             }
       
   396         }
       
   397 
       
   398         // Set verbose for jpackager if it is set for us.
       
   399         if (options.verbose()) {
       
   400             addLaunchArg("--verbose", launchArgs);
       
   401         }
       
   402 
       
   403         addLaunchArg("--input", getJarDownloadFolder(), launchArgs);
       
   404         addLaunchArg("--output", options.getOutput(), launchArgs);
       
   405         addLaunchArg("--name", jnlpd.getName(), launchArgs);
       
   406         addLaunchArg("--version", jnlpd.getVersion(), launchArgs);
       
   407         addLaunchArg("--vendor", jnlpd.getVendor(), launchArgs);
       
   408         addLaunchArg("--description", jnlpd.getDescription(), launchArgs);
       
   409         addLaunchArg("--icon", jnlpd.getIconLocation(), launchArgs);
       
   410         addLaunchArg("--main-jar", jnlpd.getMainJar(), launchArgs);
       
   411         addLaunchArg("--class", jnlpd.getMainClass(), launchArgs);
       
   412 
       
   413         addFiles(launchArgs);
       
   414         addArguments(launchArgs);
       
   415         addJVMArgs(launchArgs);
       
   416 
       
   417         if (jnlpd.isDesktopHint()) {
       
   418             if (Platform.isWindows()) {
       
   419                 addLaunchArg("--win-shortcut", launchArgs);
       
   420             } else {
       
   421                 Log.warning("Ignoring shortcut hint, since it is not supported on current platform.");
       
   422             }
       
   423         }
       
   424 
       
   425         if (jnlpd.isMenuHint()) {
       
   426             if (Platform.isWindows()) {
       
   427                 addLaunchArg("--win-menu", launchArgs);
       
   428                 addLaunchArg("--win-menu-group", jnlpd.getSubMenu(), launchArgs);
       
   429             } else {
       
   430                 Log.warning("Ignoring menu hint, since it is not supported on current platform.");
       
   431             }
       
   432         }
       
   433 
       
   434         AssociationDesc [] associations = jnlpd.getAssociations();
       
   435         if (associations != null) {
       
   436             for (AssociationDesc association : associations) {
       
   437                 String file = getFileAssociationsFile();
       
   438                 markFileToDelete(file);
       
   439 
       
   440                 try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
       
   441                     if (association.getExtensions() != null && association.getMimeType() != null) {
       
   442                         out.println(FA_EXTENSIONS + "=" + quote(association.getExtensions()));
       
   443                         out.println(FA_CONTENT_TYPE + "=" + quote(association.getMimeType()));
       
   444 
       
   445                         if (association.getMimeDescription() != null) {
       
   446                             out.println(FA_DESCRIPTION + "=" + association.getMimeDescription());
       
   447                         }
       
   448 
       
   449                         if (association.getIconLocalLocation() != null) {
       
   450                             out.println(FA_ICON + "=" + quote(association.getIconLocalLocation()));
       
   451                         }
       
   452 
       
   453                         addLaunchArg("--file-associations", file, launchArgs);
       
   454                     }
       
   455                 } catch (Exception ex) {
       
   456                     Log.warning(ex.toString());
       
   457                     if (association.getExtensions() != null) {
       
   458                         Log.warning("File assoication for " + association.getExtensions() + " will be ignored due to exception above.");
       
   459                     }
       
   460                 }
       
   461             }
       
   462         }
       
   463 
       
   464         // Add options from --jpackager-options
       
   465         List<String> jpackagerOptions = options.getJPackagerOptions();
       
   466         jpackagerOptions.forEach((option) -> {
       
   467             launchArgs.add(option);
       
   468         });
       
   469 
       
   470         displayLaunchArgs();
       
   471     }
       
   472 
       
   473     private String getCommandFileName() {
       
   474         Platform platform = Platform.getPlatform();
       
   475         switch (platform) {
       
   476             case WINDOWS:
       
   477                 return "run_jpackager.bat";
       
   478             case LINUX:
       
   479                 return "run_jpackager.sh";
       
   480             case MAC:
       
   481                 return "run_jpackager.sh";
       
   482             default:
       
   483                 Log.error("Cannot determine platform type.");
       
   484                 return "";
       
   485         }
       
   486     }
       
   487 
       
   488     private void saveLaunchArgs() {
       
   489         if (options.keep() != null) {
       
   490             File keepFolder = new File(options.keep());
       
   491             String cmdFile = keepFolder.getAbsolutePath() + File.separator + getCommandFileName();
       
   492             try (PrintWriter out = new PrintWriter(cmdFile)) {
       
   493                 out.print(getJPackagerPath());
       
   494                 launchArgs.forEach((arg) -> {
       
   495                     out.print(" ");
       
   496 
       
   497                     if (arg.contains(" ")) {
       
   498                         int len = arg.length();
       
   499                         if (len >= 1) {
       
   500                             if (arg.charAt(0) != '"' && arg.charAt(len - 1) != '"') {
       
   501                                 out.print("\"" + arg + "\"");
       
   502                             } else {
       
   503                                 if (Platform.isWindows()) {
       
   504                                     out.print(arg);
       
   505                                 } else {
       
   506                                     arg = escapeQuote(arg);
       
   507                                     out.print("\"" + arg + "\"");
       
   508                                 }
       
   509                             }
       
   510                         }
       
   511                     } else {
       
   512                         out.print(arg);
       
   513                     }
       
   514                 });
       
   515             } catch (FileNotFoundException ex) {
       
   516                 Log.error("Cannot save file with command line: " + ex.getLocalizedMessage());
       
   517             }
       
   518         }
       
   519     }
       
   520 
       
   521     private void runJPackager() {
       
   522         List<String> command = new ArrayList<>();
       
   523         command.add(getJPackagerPath());
       
   524         command.addAll(launchArgs);
       
   525 
       
   526         ProcessBuilder builder = new ProcessBuilder();
       
   527         builder.inheritIO();
       
   528         builder.command(command);
       
   529 
       
   530         try {
       
   531             Process process = builder.start();
       
   532             int exitCode = process.waitFor();
       
   533             if (exitCode != 0) {
       
   534                 Log.warning("jpackager retrun non zero code: " + exitCode);
       
   535             }
       
   536         } catch (IOException | InterruptedException ex) {
       
   537             Log.error(ex.getMessage());
       
   538         }
       
   539     }
       
   540 
       
   541     private void addFileList(String arg, List<String> filesToAdd, List<String> launchArgs) {
       
   542         if (filesToAdd.isEmpty()) {
       
   543             return;
       
   544         }
       
   545 
       
   546         String filesArg = "";
       
   547         for (int i = 0; i < filesToAdd.size(); i++) {
       
   548             filesArg += quote(filesToAdd.get(i));
       
   549             if ((i + 1) != filesToAdd.size()) {
       
   550                 filesArg += File.pathSeparator;
       
   551             }
       
   552         }
       
   553 
       
   554         launchArgs.add(arg);
       
   555         launchArgs.add(filesArg);
       
   556     }
       
   557 
       
   558     private void addFiles(List<String> launchArgs) {
       
   559         addFileList("--files", jnlpd.getFiles(), launchArgs);
       
   560     }
       
   561 
       
   562     private void addArguments(List<String> launchArgs) {
       
   563         List<String> arguments = jnlpd.getArguments();
       
   564         if (arguments.isEmpty()) {
       
   565             return;
       
   566         }
       
   567 
       
   568         String argsStr = "";
       
   569         for (int i = 0; i < arguments.size(); i++) {
       
   570             String arg = arguments.get(i);
       
   571             argsStr += quote(arg);
       
   572             if ((i + 1) != arguments.size()) {
       
   573                 argsStr += " ";
       
   574             }
       
   575         }
       
   576 
       
   577         launchArgs.add("--arguments");
       
   578         if (Platform.isWindows()) {
       
   579             if (argsStr.contains(" ")) {
       
   580                 if (argsStr.contains("\"")) {
       
   581                     argsStr = escapeQuote(argsStr);
       
   582                 }
       
   583                 argsStr = "\"" + argsStr + "\"";
       
   584             }
       
   585         }
       
   586         launchArgs.add(argsStr);
       
   587     }
       
   588 
       
   589     private void addJVMArgs(List<String> launchArgs) {
       
   590         List<String> jvmArgs = jnlpd.getVMArgs();
       
   591         if (jvmArgs.isEmpty()) {
       
   592             return;
       
   593         }
       
   594 
       
   595         String jvmArgsStr = "";
       
   596         for (int i = 0; i < jvmArgs.size(); i++) {
       
   597             String arg = jvmArgs.get(i);
       
   598             jvmArgsStr += quote(arg);
       
   599             if ((i + 1) != jvmArgs.size()) {
       
   600                 jvmArgsStr += " ";
       
   601             }
       
   602         }
       
   603 
       
   604         launchArgs.add("--jvm-args");
       
   605         if (Platform.isWindows()) {
       
   606             if (jvmArgsStr.contains(" ")) {
       
   607                 if (jvmArgsStr.contains("\"")) {
       
   608                     jvmArgsStr = escapeQuote(jvmArgsStr);
       
   609                 }
       
   610                 jvmArgsStr = "\"" + jvmArgsStr + "\"";
       
   611             }
       
   612         }
       
   613         launchArgs.add(jvmArgsStr);
       
   614     }
       
   615 
       
   616     private String quote(String in) {
       
   617         if (in == null) {
       
   618             return null;
       
   619         }
       
   620 
       
   621         if (in.isEmpty()) {
       
   622             return "";
       
   623         }
       
   624 
       
   625         if (!in.contains("=")) {
       
   626             // Not a property
       
   627             if (in.contains(" ")) {
       
   628                 in = escapeQuote(in);
       
   629                 return "\"" + in + "\"";
       
   630             }
       
   631             return in;
       
   632         }
       
   633 
       
   634         if (!in.contains(" ")) {
       
   635             return in; // No need to quote
       
   636         }
       
   637 
       
   638         int paramIndex = in.indexOf("=");
       
   639         if (paramIndex <= 0) {
       
   640             return in; // Something wrong, just skip quoting
       
   641         }
       
   642 
       
   643         String param = in.substring(0, paramIndex);
       
   644         String value = in.substring(paramIndex + 1);
       
   645 
       
   646         if (value.length() == 0) {
       
   647             return in; // No need to quote
       
   648         }
       
   649 
       
   650         value = escapeQuote(value);
       
   651 
       
   652         return param + "=" + "\"" + value + "\"";
       
   653     }
       
   654 
       
   655     private String escapeQuote(String in) {
       
   656         if (in == null) {
       
   657             return null;
       
   658         }
       
   659 
       
   660         if (in.isEmpty()) {
       
   661             return "";
       
   662         }
       
   663 
       
   664         if (in.contains("\"")) {
       
   665             // Use code points to preserve non-ASCII chars
       
   666             StringBuilder sb = new StringBuilder();
       
   667             int codeLen = in.codePointCount(0, in.length());
       
   668             for (int i = 0; i < codeLen; i++) {
       
   669                 int code = in.codePointAt(i);
       
   670                 // Note: No need to escape '\' on Linux or OS X.
       
   671                 // jpackager expects us to pass arguments and properties with quotes and spaces as a map
       
   672                 // with quotes being escaped with additional \ for internal quotes.
       
   673                 // So if we want two properties below:
       
   674                 // -Djnlp.Prop1=Some "Value" 1
       
   675                 // -Djnlp.Prop2=Some Value 2
       
   676                 // jpackager will need:
       
   677                 // "-Djnlp.Prop1=\"Some \\"Value\\" 1\" -Djnlp.Prop2=\"Some Value 2\""
       
   678                 // but since we using ProcessBuilder to run jpackager we will need to escape
       
   679                 // our escape symbols as well, so we will need to pass string below to ProcessBuilder:
       
   680                 // "-Djnlp.Prop1=\\\"Some \\\\\\\"Value\\\\\\\" 1\\\" -Djnlp.Prop2=\\\"Some Value 2\\\""
       
   681                 switch (code) {
       
   682                     case '"':
       
   683                         // " -> \" -> \\\"
       
   684                         if (i == 0 || in.codePointAt(i - 1) != '\\') {
       
   685                             if (Platform.isWindows()) {
       
   686                                 sb.appendCodePoint('\\');
       
   687                                 sb.appendCodePoint('\\');
       
   688                             }
       
   689                             sb.appendCodePoint('\\');
       
   690                             sb.appendCodePoint(code);
       
   691                         }
       
   692                         break;
       
   693                     case '\\':
       
   694                         // We need to escape already escaped symbols as well
       
   695                         if ((i + 1) < codeLen) {
       
   696                             int nextCode = in.codePointAt(i + 1);
       
   697                             if (nextCode == '"') {
       
   698                                 // \" -> \\\"
       
   699                                 sb.appendCodePoint('\\');
       
   700                                 sb.appendCodePoint('\\');
       
   701                                 sb.appendCodePoint('\\');
       
   702                                 sb.appendCodePoint(nextCode);
       
   703                             } else {
       
   704                                 sb.appendCodePoint('\\');
       
   705                                 sb.appendCodePoint(code);
       
   706                             }
       
   707                         } else {
       
   708                             if (Platform.isWindows()) {
       
   709                                 sb.appendCodePoint('\\');
       
   710                             }
       
   711                             sb.appendCodePoint(code);
       
   712                         }
       
   713                         break;
       
   714                     default:
       
   715                         sb.appendCodePoint(code);
       
   716                         break;
       
   717                 }
       
   718             }
       
   719             return sb.toString();
       
   720         }
       
   721 
       
   722         return in;
       
   723     }
       
   724 
       
   725     public synchronized String getDownloadFolder() {
       
   726         if (downloadFolder == null) {
       
   727             try {
       
   728                 File file;
       
   729                 if (options.keep() == null) {
       
   730                     Path path = Files.createTempDirectory("JNLPConverter");
       
   731                     file = path.toFile();
       
   732                     file.deleteOnExit();
       
   733                 } else {
       
   734                     file = new File(options.keep());
       
   735                     if (!file.exists()) {
       
   736                         file.mkdir();
       
   737                     }
       
   738                 }
       
   739 
       
   740                 downloadFolder = file.getAbsolutePath();
       
   741             } catch (IOException e) {
       
   742                 Log.error(e.getLocalizedMessage());
       
   743             }
       
   744         }
       
   745 
       
   746         return downloadFolder;
       
   747     }
       
   748 
       
   749     public final synchronized String getJnlpDownloadFolder() {
       
   750         if (jnlpDownloadFolder == null) {
       
   751             File file = new File(getDownloadFolder() + File.separator + "jnlp");
       
   752             file.mkdir();
       
   753             markFileToDelete(getDownloadFolder() + File.separator + "jnlp");
       
   754             jnlpDownloadFolder = file.getAbsolutePath();
       
   755         }
       
   756 
       
   757         return jnlpDownloadFolder;
       
   758     }
       
   759 
       
   760     public static String getJnlpDownloadFolderStatic() {
       
   761         return jnlpDownloadFolderStatic;
       
   762     }
       
   763 
       
   764     public synchronized String getJarDownloadFolder() {
       
   765         if (jarDownloadFolder == null) {
       
   766             File file = new File(getDownloadFolder() + File.separator + "jar");
       
   767             file.mkdir();
       
   768             markFileToDelete(getDownloadFolder() + File.separator + "jar");
       
   769             jarDownloadFolder = file.getAbsolutePath();
       
   770         }
       
   771 
       
   772         return jarDownloadFolder;
       
   773     }
       
   774 
       
   775     public synchronized String getIconDownloadFolder() {
       
   776         if (iconDownloadFolder == null) {
       
   777             File file = new File(getDownloadFolder() + File.separator + "icon");
       
   778             file.mkdir();
       
   779             markFileToDelete(getDownloadFolder() + File.separator + "icon");
       
   780             iconDownloadFolder = file.getAbsolutePath();
       
   781         }
       
   782 
       
   783         return iconDownloadFolder;
       
   784     }
       
   785 
       
   786     public synchronized String getPropDownloadFolder() {
       
   787         if (propDownloadFolder == null) {
       
   788             File file = new File(getDownloadFolder() + File.separator + "prop");
       
   789             file.mkdir();
       
   790             markFileToDelete(getDownloadFolder() + File.separator + "prop");
       
   791             propDownloadFolder = file.getAbsolutePath();
       
   792         }
       
   793 
       
   794         return propDownloadFolder;
       
   795     }
       
   796 
       
   797     public synchronized static String getJPackagerPath() {
       
   798         if (jpackagerPath == null) {
       
   799             jpackagerPath = System.getProperty("java.home");
       
   800             jpackagerPath += File.separator;
       
   801             jpackagerPath += "bin";
       
   802             jpackagerPath += File.separator;
       
   803 
       
   804             Platform platform = Platform.getPlatform();
       
   805             switch (platform) {
       
   806                 case WINDOWS:
       
   807                     jpackagerPath += "jpackager.exe";
       
   808                     break;
       
   809                 case LINUX:
       
   810                     jpackagerPath += "jpackager";
       
   811                     break;
       
   812                 case MAC:
       
   813                     jpackagerPath += "jpackager";
       
   814                     break;
       
   815                 default:
       
   816                     Log.error("Cannot determine platform type.");
       
   817                     break;
       
   818             }
       
   819 
       
   820             Log.verbose("jpackager: " + jpackagerPath);
       
   821         }
       
   822 
       
   823         return jpackagerPath;
       
   824     }
       
   825 
       
   826     public static String getIconFormat(String icon) {
       
   827         // GIF, JPEG, ICO, or PNG
       
   828         if (icon.toLowerCase().endsWith(".gif")) {
       
   829             return "GIF";
       
   830         } else if (icon.toLowerCase().endsWith(".jpg")) {
       
   831             return "JPEG";
       
   832         } else if (icon.toLowerCase().endsWith(".ico")) {
       
   833             return "ICO";
       
   834         } else if (icon.toLowerCase().endsWith(".png")) {
       
   835             return "PNG";
       
   836         }
       
   837 
       
   838         return "UNKNOWN";
       
   839     }
       
   840 
       
   841     public static boolean isIconSupported(String icon) {
       
   842         Platform platform = Platform.getPlatform();
       
   843         switch (platform) {
       
   844             case WINDOWS:
       
   845                 if (icon.endsWith(".ico")) {
       
   846                     return true;
       
   847                 } else {
       
   848                     Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Windows for file " + icon + ".");
       
   849                     return false;
       
   850                 }
       
   851             case LINUX:
       
   852                 if (icon.endsWith(".png")) {
       
   853                     return true;
       
   854                 } else {
       
   855                     Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on Linux for file " + icon + ".");
       
   856                     return false;
       
   857                 }
       
   858             case MAC:
       
   859                 Log.warning("Icon file format (" + getIconFormat(icon) + ") is not supported on OS X for file " + icon + ".");
       
   860                 return false;
       
   861         }
       
   862 
       
   863         return false;
       
   864     }
       
   865 }