|
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 } |