1 /* 2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.oracle.tools.packager; 27 28 import com.sun.javafx.tools.packager.bundlers.BundleParams; 29 30 import java.io.File; 31 import java.io.IOException; 32 import java.io.StringReader; 33 import java.nio.file.Files; 34 import java.text.MessageFormat; 35 import java.util.*; 36 import java.util.function.BiFunction; 37 import java.util.function.Function; 38 import java.util.jar.Attributes; 39 import java.util.jar.JarFile; 40 import java.util.jar.Manifest; 41 import java.util.regex.Pattern; 42 43 public class StandardBundlerParam<T> extends BundlerParamInfo<T> { 44 45 public static final String MANIFEST_JAVAFX_MAIN ="JavaFX-Application-Class"; 46 public static final String MANIFEST_PRELOADER = "JavaFX-Preloader-Class"; 47 48 private static final ResourceBundle I18N = 49 ResourceBundle.getBundle(StandardBundlerParam.class.getName()); 50 51 public StandardBundlerParam(String name, String description, String id, 52 Class<T> valueType, 53 Function<Map<String, ? super Object>, T> defaultValueFunction, 54 BiFunction<String, Map<String, ? super Object>, T> stringConverter) { 55 this.name = name; 56 this.description = description; 57 this.id = id; 58 this.valueType = valueType; 59 this.defaultValueFunction = defaultValueFunction; 60 this.stringConverter = stringConverter; 61 } 62 63 public static final StandardBundlerParam<RelativeFileSet> APP_RESOURCES = 64 new StandardBundlerParam<>( 65 I18N.getString("param.app-resources.name"), 66 I18N.getString("param.app-resource.description"), 67 BundleParams.PARAM_APP_RESOURCES, 68 RelativeFileSet.class, 69 null, // no default. Required parameter 70 null // no string translation, tool must provide complex type 71 ); 72 73 @SuppressWarnings("unchecked") 74 public static final StandardBundlerParam<List<RelativeFileSet>> APP_RESOURCES_LIST = 75 new StandardBundlerParam<>( 76 I18N.getString("param.app-resources-list.name"), 77 I18N.getString("param.app-resource-list.description"), 78 BundleParams.PARAM_APP_RESOURCES + "List", 79 (Class<List<RelativeFileSet>>) (Object) List.class, 80 p -> new ArrayList<>(Arrays.asList(APP_RESOURCES.fetchFrom(p))), // Default is appResources, as a single item list 81 null // no string translation, tool must provide complex type 82 ); 83 84 public static final StandardBundlerParam<File> ICON = 85 new StandardBundlerParam<>( 86 I18N.getString("param.icon-file.name"), 87 I18N.getString("param.icon-file.description"), 88 BundleParams.PARAM_ICON, 89 File.class, 90 params -> null, 91 (s, p) -> new File(s) 92 ); 93 94 95 public static final StandardBundlerParam<String> MAIN_CLASS = 96 new StandardBundlerParam<>( 97 I18N.getString("param.main-class.name"), 98 I18N.getString("param.main-class.description"), 99 BundleParams.PARAM_APPLICATION_CLASS, 100 String.class, 101 params -> { 102 extractMainClassInfoFromAppResources(params); 103 return (String) params.get(BundleParams.PARAM_APPLICATION_CLASS); 104 }, 105 (s, p) -> s 106 ); 107 108 public static final StandardBundlerParam<String> APP_NAME = 109 new StandardBundlerParam<>( 110 I18N.getString("param.app-name.name"), 111 I18N.getString("param.app-name.description"), 112 BundleParams.PARAM_NAME, 113 String.class, 114 params -> { 115 String s = MAIN_CLASS.fetchFrom(params); 116 if (s == null) return null; 117 118 int idx = s.lastIndexOf("."); 119 if (idx >= 0) { 120 return s.substring(idx+1); 121 } 122 return s; 123 }, 124 (s, p) -> s 125 ); 126 127 private static Pattern TO_FS_NAME = Pattern.compile("\\s|[\\\\/?:*<>|]"); // keep out invalid/undesireable filename characters 128 129 public static final StandardBundlerParam<String> APP_FS_NAME = 130 new StandardBundlerParam<>( 131 I18N.getString("param.app-fs-name.name"), 132 I18N.getString("param.app-fs-name.description"), 133 "name.fs", 134 String.class, 135 params -> TO_FS_NAME.matcher(APP_NAME.fetchFrom(params)).replaceAll(""), 136 (s, p) -> s 137 ); 138 139 140 public static final StandardBundlerParam<String> VENDOR = 141 new StandardBundlerParam<>( 142 I18N.getString("param.vendor.name"), 143 I18N.getString("param.vendor.description"), 144 BundleParams.PARAM_VENDOR, 145 String.class, 146 params -> I18N.getString("param.vendor.default"), 147 (s, p) -> s 148 ); 149 150 public static final StandardBundlerParam<String> CATEGORY = 151 new StandardBundlerParam<>( 152 I18N.getString("param.category.name"), 153 I18N.getString("param.category.description"), 154 BundleParams.PARAM_CATEGORY, 155 String.class, 156 params -> I18N.getString("param.category.default"), 157 (s, p) -> s 158 ); 159 160 public static final StandardBundlerParam<String> DESCRIPTION = 161 new StandardBundlerParam<>( 162 I18N.getString("param.description.name"), 163 I18N.getString("param.description.description"), 164 BundleParams.PARAM_DESCRIPTION, 165 String.class, 166 params -> params.containsKey(APP_NAME.getID()) 167 ? APP_NAME.fetchFrom(params) 168 : I18N.getString("param.description.default"), 169 (s, p) -> s 170 ); 171 172 public static final StandardBundlerParam<String> COPYRIGHT = 173 new StandardBundlerParam<>( 174 I18N.getString("param.copyright.name"), 175 I18N.getString("param.copyright.description"), 176 BundleParams.PARAM_COPYRIGHT, 177 String.class, 178 params -> MessageFormat.format(I18N.getString("param.copyright.default"), new Date()), 179 (s, p) -> s 180 ); 181 182 // note that each bundler is likely to replace this one with their own converter 183 public static final StandardBundlerParam<RelativeFileSet> MAIN_JAR = 184 new StandardBundlerParam<>( 185 I18N.getString("param.main-jar.name"), 186 I18N.getString("param.main-jar.description"), 187 "mainJar", 188 RelativeFileSet.class, 189 params -> { 190 extractMainClassInfoFromAppResources(params); 191 return (RelativeFileSet) params.get("mainJar"); 192 }, 193 (s, p) -> { 194 for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(p)) { 195 File appResourcesRoot = rfs.getBaseDirectory(); 196 File f = new File(appResourcesRoot, s); 197 if (f.exists()) { 198 return new RelativeFileSet(appResourcesRoot, new LinkedHashSet<>(Arrays.asList(f))); 199 } 200 } 201 throw new IllegalArgumentException( 202 new ConfigException( 203 MessageFormat.format(I18N.getString("error.main-jar-does-not-exist"), s), 204 I18N.getString("error.main-jar-does-not-exist.advice"))); 205 } 206 ); 207 208 public static final StandardBundlerParam<String> CLASSPATH = 209 new StandardBundlerParam<>( 210 I18N.getString("param.classpath.name"), 211 I18N.getString("param.classpath.description"), 212 "classpath", 213 String.class, 214 params -> { 215 extractMainClassInfoFromAppResources(params); 216 String cp = (String) params.get("classpath"); 217 return cp == null ? "" : cp; 218 }, 219 (s, p) -> s.replace(File.pathSeparator, " ") 220 ); 221 222 public static final StandardBundlerParam<Boolean> USE_FX_PACKAGING = 223 new StandardBundlerParam<>( 224 I18N.getString("param.use-javafx-packaging.name"), 225 I18N.getString("param.use-javafx-packaging.description"), 226 "fxPackaging", 227 Boolean.class, 228 params -> { 229 extractMainClassInfoFromAppResources(params); 230 Boolean result = (Boolean) params.get("fxPackaging"); 231 return (result == null) ? Boolean.FALSE : result; 232 }, 233 (s, p) -> Boolean.valueOf(s) 234 ); 235 236 @SuppressWarnings("unchecked") 237 public static final StandardBundlerParam<List<String>> ARGUMENTS = 238 new StandardBundlerParam<>( 239 I18N.getString("param.arguments.name"), 240 I18N.getString("param.arguments.description"), 241 "arguments", 242 (Class<List<String>>) (Object) List.class, 243 params -> Collections.emptyList(), 244 (s, p) -> Arrays.asList(s.split("\\s+")) 245 ); 246 247 @SuppressWarnings("unchecked") 248 public static final StandardBundlerParam<List<String>> JVM_OPTIONS = 249 new StandardBundlerParam<>( 250 I18N.getString("param.jvm-options.name"), 251 I18N.getString("param.jvm-options.description"), 252 "jvmOptions", 253 (Class<List<String>>) (Object) List.class, 254 params -> Collections.emptyList(), 255 (s, p) -> Arrays.asList(s.split("\\s+")) 256 ); 257 258 @SuppressWarnings("unchecked") 259 public static final StandardBundlerParam<Map<String, String>> JVM_PROPERTIES = 260 new StandardBundlerParam<>( 261 I18N.getString("param.jvm-system-properties.name"), 262 I18N.getString("param.jvm-system-properties.description"), 263 "jvmProperties", 264 (Class<Map<String, String>>) (Object) Map.class, 265 params -> Collections.emptyMap(), 266 (s, params) -> { 267 Map<String, String> map = new HashMap<>(); 268 try { 269 Properties p = new Properties(); 270 p.load(new StringReader(s)); 271 for (Map.Entry<Object, Object> entry : p.entrySet()) { 272 map.put((String)entry.getKey(), (String)entry.getValue()); 273 } 274 } catch (IOException e) { 275 e.printStackTrace(); 276 } 277 return map; 278 } 279 ); 280 281 @SuppressWarnings("unchecked") 282 public static final StandardBundlerParam<Map<String, String>> USER_JVM_OPTIONS = 283 new StandardBundlerParam<>( 284 I18N.getString("param.user-jvm-options.name"), 285 I18N.getString("param.user-jvm-options.description"), 286 "userJvmOptions", 287 (Class<Map<String, String>>) (Object) Map.class, 288 params -> Collections.emptyMap(), 289 (s, params) -> { 290 Map<String, String> map = new HashMap<>(); 291 try { 292 Properties p = new Properties(); 293 p.load(new StringReader(s)); 294 for (Map.Entry<Object, Object> entry : p.entrySet()) { 295 map.put((String)entry.getKey(), (String)entry.getValue()); 296 } 297 } catch (IOException e) { 298 e.printStackTrace(); 299 } 300 return map; 301 } 302 ); 303 304 public static final StandardBundlerParam<String> TITLE = 305 new StandardBundlerParam<>( 306 I18N.getString("param.title.name"), 307 I18N.getString("param.title.description"), //?? but what does it do? 308 BundleParams.PARAM_TITLE, 309 String.class, 310 APP_NAME::fetchFrom, 311 (s, p) -> s 312 ); 313 314 315 // note that each bundler is likely to replace this one with their own converter 316 public static final StandardBundlerParam<String> VERSION = 317 new StandardBundlerParam<>( 318 I18N.getString("param.version.name"), 319 I18N.getString("param.version.description"), 320 BundleParams.PARAM_VERSION, 321 String.class, 322 params -> I18N.getString("param.version.default"), 323 (s, p) -> s 324 ); 325 326 public static final StandardBundlerParam<Boolean> SYSTEM_WIDE = 327 new StandardBundlerParam<>( 328 I18N.getString("param.system-wide.name"), 329 I18N.getString("param.system-wide.description"), 330 BundleParams.PARAM_SYSTEM_WIDE, 331 Boolean.class, 332 params -> null, 333 // valueOf(null) is false, and we actually do want null in some cases 334 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null : Boolean.valueOf(s) 335 ); 336 337 public static final StandardBundlerParam<Boolean> SERVICE_HINT = 338 new StandardBundlerParam<>( 339 I18N.getString("param.service-hint.name"), 340 I18N.getString("param.service-hint.description"), 341 BundleParams.PARAM_SERVICE_HINT, 342 Boolean.class, 343 params -> false, 344 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s) 345 ); 346 347 public static final StandardBundlerParam<Boolean> START_ON_INSTALL = 348 new StandardBundlerParam<>( 349 I18N.getString("param.start-on-install.name"), 350 I18N.getString("param.start-on-install.description"), 351 "startOnInstall", 352 Boolean.class, 353 params -> false, 354 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s) 355 ); 356 357 public static final StandardBundlerParam<Boolean> STOP_ON_UNINSTALL = 358 new StandardBundlerParam<>( 359 I18N.getString("param.stop-on-uninstall.name"), 360 I18N.getString("param.stop-on-uninstall.description"), 361 "stopOnUninstall", 362 Boolean.class, 363 params -> true, 364 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s) 365 ); 366 367 public static final StandardBundlerParam<Boolean> RUN_AT_STARTUP = 368 new StandardBundlerParam<>( 369 I18N.getString("param.run-at-startup.name"), 370 I18N.getString("param.run-at-startup.description"), 371 "runAtStartup", 372 Boolean.class, 373 params -> false, 374 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s) 375 ); 376 377 public static final StandardBundlerParam<Boolean> SIGN_BUNDLE = 378 new StandardBundlerParam<>( 379 I18N.getString("param.sign-bundle.name"), 380 I18N.getString("param.sign-bundle.description"), 381 "signBundle", 382 Boolean.class, 383 params -> null, 384 // valueOf(null) is false, and we actually do want null in some cases 385 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null : Boolean.valueOf(s) 386 ); 387 388 public static final StandardBundlerParam<Boolean> SHORTCUT_HINT = 389 new StandardBundlerParam<>( 390 I18N.getString("param.desktop-shortcut-hint.name"), 391 I18N.getString("param.desktop-shortcut-hint.description"), 392 BundleParams.PARAM_SHORTCUT, 393 Boolean.class, 394 params -> false, 395 // valueOf(null) is false, and we actually do want null in some cases 396 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s) 397 ); 398 399 public static final StandardBundlerParam<Boolean> MENU_HINT = 400 new StandardBundlerParam<>( 401 I18N.getString("param.menu-shortcut-hint.name"), 402 I18N.getString("param.menu-shortcut-hint.description"), 403 BundleParams.PARAM_MENU, 404 Boolean.class, 405 params -> true, 406 // valueOf(null) is false, and we actually do want null in some cases 407 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s) 408 ); 409 410 @SuppressWarnings("unchecked") 411 public static final StandardBundlerParam<List<String>> LICENSE_FILE = 412 new StandardBundlerParam<>( 413 I18N.getString("param.license-file.name"), 414 I18N.getString("param.license-file.description"), 415 BundleParams.PARAM_LICENSE_FILE, 416 (Class<List<String>>)(Object)List.class, 417 params -> Collections.<String>emptyList(), 418 (s, p) -> Arrays.asList(s.split(",")) 419 ); 420 421 public static final BundlerParamInfo<String> LICENSE_TYPE = 422 new StandardBundlerParam<> ( 423 I18N.getString("param.license-type.name"), 424 I18N.getString("param.license-type.description"), 425 BundleParams.PARAM_LICENSE_TYPE, 426 String.class, 427 params -> I18N.getString("param.license-type.default"), 428 (s, p) -> s 429 ); 430 431 public static final StandardBundlerParam<File> BUILD_ROOT = 432 new StandardBundlerParam<>( 433 I18N.getString("param.build-root.name"), 434 I18N.getString("param.build-root.description"), 435 "buildRoot", 436 File.class, 437 params -> { 438 try { 439 return Files.createTempDirectory("fxbundler").toFile(); 440 } catch (IOException ioe) { 441 return null; 442 } 443 }, 444 (s, p) -> new File(s) 445 ); 446 447 public static final StandardBundlerParam<String> IDENTIFIER = 448 new StandardBundlerParam<>( 449 I18N.getString("param.identifier.name"), 450 I18N.getString("param.identifier.description"), 451 BundleParams.PARAM_IDENTIFIER, 452 String.class, 453 params -> { 454 String s = MAIN_CLASS.fetchFrom(params); 455 if (s == null) return null; 456 457 int idx = s.lastIndexOf("."); 458 if (idx >= 1) { 459 return s.substring(0, idx); 460 } 461 return s; 462 }, 463 (s, p) -> s 464 ); 465 466 public static final StandardBundlerParam<String> PREFERENCES_ID = 467 new StandardBundlerParam<>( 468 I18N.getString("param.preferences-id.name"), 469 I18N.getString("param.preferences-id.description"), 470 "preferencesID", 471 String.class, 472 p -> Optional.ofNullable(IDENTIFIER.fetchFrom(p)).orElse("").replace('.', '/'), 473 (s, p) -> s 474 ); 475 476 public static final StandardBundlerParam<String> PRELOADER_CLASS = 477 new StandardBundlerParam<>( 478 I18N.getString("param.preloader.name"), 479 I18N.getString("param.preloader.description"), 480 "preloader", 481 String.class, 482 p -> null, 483 null 484 ); 485 486 public static final StandardBundlerParam<Boolean> VERBOSE = 487 new StandardBundlerParam<>( 488 I18N.getString("param.verbose.name"), 489 I18N.getString("param.verbose.description"), 490 "verbose", 491 Boolean.class, 492 params -> false, 493 // valueOf(null) is false, and we actually do want null in some cases 494 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s) 495 ); 496 497 public static final StandardBundlerParam<File> DROP_IN_RESOURCES_ROOT = 498 new StandardBundlerParam<>( 499 I18N.getString("param.drop-in-resources-root.name"), 500 I18N.getString("param.drop-in-resources-root.description"), 501 "dropinResourcesRoot", 502 File.class, 503 params -> null, 504 (s, p) -> new File(s) 505 ); 506 507 @SuppressWarnings("unchecked") 508 public static final StandardBundlerParam<List<Map<String, ? super Object>>> SECONDARY_LAUNCHERS = 509 new StandardBundlerParam<>( 510 I18N.getString("param.secondary-launchers.name"), 511 I18N.getString("param.secondary-launchers.description"), 512 "secondaryLaunchers", 513 (Class<List<Map<String, ? super Object>>>) (Object) List.class, 514 params -> new ArrayList<>(1), 515 // valueOf(null) is false, and we actually do want null in some cases 516 (s, p) -> null 517 ); 518 519 @SuppressWarnings("unchecked") 520 public static final StandardBundlerParam<List<Map<String, ? super Object>>> FILE_ASSOCIATIONS = 521 new StandardBundlerParam<>( 522 I18N.getString("param.file-associations.name"), 523 I18N.getString("param.file-associations.description"), 524 "fileAssociations", 525 (Class<List<Map<String, ? super Object>>>) (Object) List.class, 526 params -> new ArrayList<>(1), 527 // valueOf(null) is false, and we actually do want null in some cases 528 (s, p) -> null 529 ); 530 531 @SuppressWarnings("unchecked") 532 public static final StandardBundlerParam<List<String>> FA_EXTENSIONS = 533 new StandardBundlerParam<>( 534 I18N.getString("param.fa-extension.name"), 535 I18N.getString("param.fa-extension.description"), 536 "fileAssociation.extension", 537 (Class<List<String>>) (Object) List.class, 538 params -> null, // null means not matched to an extension 539 (s, p) -> Arrays.asList(s.split("(,|\\s)+")) 540 ); 541 542 @SuppressWarnings("unchecked") 543 public static final StandardBundlerParam<List<String>> FA_CONTENT_TYPE = 544 new StandardBundlerParam<>( 545 I18N.getString("param.fa-content-type.name"), 546 I18N.getString("param.fa-content-type.description"), 547 "fileAssociation.contentType", 548 (Class<List<String>>) (Object) List.class, 549 params -> null, // null means not matched to a content/mime type 550 (s, p) -> Arrays.asList(s.split("(,|\\s)+")) 551 ); 552 553 public static final StandardBundlerParam<String> FA_DESCRIPTION = 554 new StandardBundlerParam<>( 555 I18N.getString("param.fa-description.name"), 556 I18N.getString("param.fa-description.description"), 557 "fileAssociation.description", 558 String.class, 559 params -> APP_NAME.fetchFrom(params) + " File", 560 null 561 ); 562 563 public static final StandardBundlerParam<File> FA_ICON = 564 new StandardBundlerParam<>( 565 I18N.getString("param.fa-icon.name"), 566 I18N.getString("param.fa-icon.description"), 567 "fileAssociation.icon", 568 File.class, 569 ICON::fetchFrom, 570 (s, p) -> new File(s) 571 ); 572 573 public static void extractMainClassInfoFromAppResources(Map<String, ? super Object> params) { 574 boolean hasMainClass = params.containsKey(MAIN_CLASS.getID()); 575 boolean hasMainJar = params.containsKey(MAIN_JAR.getID()); 576 boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID()); 577 boolean hasPreloader = params.containsKey(PRELOADER_CLASS.getID()); 578 579 if (hasMainClass && hasMainJar && hasMainJarClassPath) { 580 return; 581 } 582 // it's a pair. The [0] is the srcdir [1] is the file relative to sourcedir 583 List<String[]> filesToCheck = new ArrayList<>(); 584 585 if (hasMainJar) { 586 RelativeFileSet rfs = MAIN_JAR.fetchFrom(params); 587 for (String s : rfs.getIncludedFiles()) { 588 filesToCheck.add(new String[]{rfs.getBaseDirectory().toString(), s}); 589 } 590 } else if (hasMainJarClassPath) { 591 for (String s : CLASSPATH.fetchFrom(params).split("\\s+")) { 592 filesToCheck.add(new String[] {APP_RESOURCES.fetchFrom(params).getBaseDirectory().toString(), s}); 593 } 594 } else { 595 List<RelativeFileSet> rfsl = APP_RESOURCES_LIST.fetchFrom(params); 596 if (rfsl == null || rfsl.isEmpty()) { 597 return; 598 } 599 for (RelativeFileSet rfs : rfsl) { 600 if (rfs == null) continue; 601 602 for (String s : rfs.getIncludedFiles()) { 603 filesToCheck.add(new String[]{rfs.getBaseDirectory().toString(), s}); 604 } 605 } 606 } 607 608 String declaredMainClass = (String) params.get(MAIN_CLASS.getID()); 609 610 // presume the set iterates in-order 611 for (String[] fnames : filesToCheck) { 612 try { 613 // only sniff jars 614 if (!fnames[1].toLowerCase().endsWith(".jar")) continue; 615 616 File file = new File(fnames[0], fnames[1]); 617 // that actually exist 618 if (!file.exists()) continue; 619 620 JarFile jf = new JarFile(file); 621 Manifest m = jf.getManifest(); 622 Attributes attrs = (m != null) ? m.getMainAttributes() : null; 623 624 if (attrs != null) { 625 String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS); 626 String fxMain = attrs.getValue(MANIFEST_JAVAFX_MAIN); 627 String preloaderClass = attrs.getValue(MANIFEST_PRELOADER); 628 if (hasMainClass) { 629 if (declaredMainClass.equals(fxMain)) { 630 params.put(USE_FX_PACKAGING.getID(), true); 631 } else if (declaredMainClass.equals(mainClass)) { 632 params.put(USE_FX_PACKAGING.getID(), false); 633 } else { 634 if (fxMain != null) { 635 Log.info(MessageFormat.format(I18N.getString("message.fx-app-does-not-match-specified-main"), fnames[1], fxMain, declaredMainClass)); 636 } 637 if (mainClass != null) { 638 Log.info(MessageFormat.format(I18N.getString("message.main-class-does-not-match-specified-main"), fnames[1], mainClass, declaredMainClass)); 639 } 640 continue; 641 } 642 } else { 643 if (fxMain != null) { 644 params.put(USE_FX_PACKAGING.getID(), true); 645 params.put(MAIN_CLASS.getID(), fxMain); 646 } else if (mainClass != null) { 647 params.put(USE_FX_PACKAGING.getID(), false); 648 params.put(MAIN_CLASS.getID(), mainClass); 649 } else { 650 continue; 651 } 652 } 653 if (!hasPreloader && preloaderClass != null) { 654 params.put(PRELOADER_CLASS.getID(), preloaderClass); 655 } 656 if (!hasMainJar) { 657 if (fnames[0] == null) { 658 fnames[0] = file.getParentFile().toString(); 659 } 660 params.put(MAIN_JAR.getID(), new RelativeFileSet(new File(fnames[0]), new LinkedHashSet<>(Arrays.asList(file)))); 661 } 662 if (!hasMainJarClassPath) { 663 String cp = attrs.getValue(Attributes.Name.CLASS_PATH); 664 params.put(CLASSPATH.getID(), cp == null ? "" : cp); 665 } 666 break; 667 } 668 } catch (IOException ignore) { 669 ignore.printStackTrace(); 670 } 671 } 672 } 673 674 public static void validateMainClassInfoFromAppResources(Map<String, ? super Object> params) throws ConfigException { 675 boolean hasMainClass = params.containsKey(MAIN_CLASS.getID()); 676 boolean hasMainJar = params.containsKey(MAIN_JAR.getID()); 677 boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID()); 678 679 if (hasMainClass && hasMainJar && hasMainJarClassPath) { 680 return; 681 } 682 683 extractMainClassInfoFromAppResources(params); 684 if (!params.containsKey(MAIN_CLASS.getID())) { 685 if (hasMainJar) { 686 throw new ConfigException( 687 MessageFormat.format(I18N.getString("error.no-main-class-with-main-jar"), 688 MAIN_JAR.fetchFrom(params)), 689 MessageFormat.format(I18N.getString("error.no-main-class-with-main-jar.advice"), 690 MAIN_JAR.fetchFrom(params))); 691 } else if (hasMainJarClassPath) { 692 throw new ConfigException( 693 I18N.getString("error.no-main-class-with-classpath"), 694 I18N.getString("error.no-main-class-with-classpath.advice")); 695 } else { 696 throw new ConfigException( 697 I18N.getString("error.no-main-class"), 698 I18N.getString("error.no-main-class.advice")); 699 } 700 } 701 } 702 }