1 /* 2 * Copyright (c) 2011, 2013, 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.javafx.main; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.lang.reflect.Field; 31 import java.lang.reflect.InvocationTargetException; 32 import java.lang.reflect.Method; 33 import java.net.URI; 34 import java.net.URL; 35 import java.net.URLClassLoader; 36 import java.net.URLDecoder; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Base64; 40 import java.util.LinkedList; 41 import java.util.List; 42 import java.util.jar.Attributes; 43 import java.util.jar.JarFile; 44 import java.util.jar.Manifest; 45 import javax.swing.JApplet; 46 import javax.swing.JFrame; 47 import javax.swing.SwingUtilities; 48 49 /** 50 * This class loads com.sun.javafx.application.LauncherImpl and calls the 51 * launchApplication method. 52 * 53 * It is used by packager to include it as part of the application jar file 54 * so that we can run and locate the JavaFX runtime. Note that we cannot 55 * assume that JavaFX is on the classpath so we must use reflection and not 56 * have any direct reference to any JavaFX runtime class. 57 * 58 * We will do the following: 59 * 60 * 1. Verify the version of Java and produce error message if not JDK6+ 61 * 62 * 2. Locate the jar file from which the Main class was launched. Read the 63 * jar manifest and extract 64 * the application class using the JavaFX-Application-Class manifest entry. 65 * Alternatively, we will read the application class from a system property. 66 * 67 * 3. Try to locate the JavaFX runtime by loading the 68 * com.sun.javafx.application.LauncherImpl class using the following mechanisms 69 * in order: 70 * 71 * A. Try loading it directly in case it is on the classpath. 72 * B. If the javafx.runtime.path System Property is set, try 73 * loading it from ${javafx.runtime.path}/lib/ext/jfxrt.jar 74 * (or lib/jfxrt.jar) 75 * C. Look for a cobundled JavaFX in the current jre 76 * D. If on Windows, read the registry key associated with the JavaFX 77 * runtime (if running in a 64-bit JVM, use the 64-bit path) 78 * 79 * 4. Create a custom URLClassLoader from the appropriate jar files, and then 80 * call the launchApplication method. If the application class is not a 81 * subclass of javafx.application.Application then we will call the main 82 * method in the application class instead. 83 * 84 * 5. If the LauncherImpl class cannot be found, then show a Swing dialog 85 * (again, using reflection). 86 */ 87 88 public class Main { 89 private static boolean verbose = false; 90 private static final String fxApplicationClassName = "javafx.application.Application"; 91 private static final String fxLaunchClassName = "com.sun.javafx.application.LauncherImpl"; 92 private static final String manifestAppClass = "JavaFX-Application-Class"; 93 private static final String manifestPreloaderClass = "JavaFX-Preloader-Class"; 94 private static final String manifestFallbackClass = "JavaFX-Fallback-Class"; 95 private static final String manifestClassPath = "JavaFX-Class-Path"; 96 97 //Manifest entry to explicitly disable autoproxy config 98 // Unless it has "Auto" value it will disable proxy 99 private static final String manifestAutoProxy = "JavaFX-Feature-Proxy"; 100 101 //Experimental hook to simplify adding "au" logic to native bundles 102 private static final String manifestUpdateHook = "X-JavaFX-Update-Hook"; 103 104 // JavaFX family version that this Launcher is compatible with 105 private static final String JAVAFX_FAMILY_VERSION = "2."; 106 107 // Minimum JavaFX version required to run the app 108 // (keep separate from JAVAFX_FAMILY_VERSION check as 109 // we want 2.2.1 SDK to be ok to run app that needs 2.1.0 110 // and prefix based match is not enough) 111 // NOTE: This should be refactored so that the version gets supplied 112 // from the build environment, but we do NOT want another class or 113 // property file in the app bundle! Are there any other options 114 // besides java source code preprocessing?) 115 private static final String JAVAFX_REQUIRED_VERSION = "2.1.0"; 116 117 private static final String ZERO_VERSION = "0.0.0"; 118 119 //application jar attributes 120 private static Attributes attrs = null; 121 122 private static URL fileToURL(File file) throws IOException { 123 return file.getCanonicalFile().toURI().toURL(); 124 } 125 126 private static Method findLaunchMethod(File jfxRtPath, String fxClassPath) { 127 final Class[] argTypes = 128 new Class[] { Class.class, Class.class, (new String[0]).getClass() }; 129 130 try { 131 ArrayList urlList = new ArrayList(); 132 133 // Add in the elements of the classpath 134 String cp = System.getProperty("java.class.path"); 135 if (cp != null) { 136 while (cp.length() > 0) { 137 int pathSepIdx = cp.indexOf(File.pathSeparatorChar); 138 if (pathSepIdx < 0) { 139 String pathElem = cp; 140 urlList.add(fileToURL(new File(pathElem))); 141 break; 142 } else if (pathSepIdx > 0) { 143 String pathElem = cp.substring(0, pathSepIdx); 144 urlList.add(fileToURL(new File(pathElem))); 145 } 146 cp = cp.substring(pathSepIdx + 1); 147 } 148 } 149 150 // Add in the jars from the JavaFX-Class-Path entry 151 cp = fxClassPath; 152 if (cp != null) { 153 //these are relative paths. if app is not in the current dir 154 // we may resolve them incorrectly ... 155 // try to find main jar and build absolute paths 156 File baseDir = null; 157 try { 158 String path = Main.class.getProtectionDomain().getCodeSource().getLocation().getPath(); 159 //path will have encoded spaces, etc. => need to restore 160 String decodedPath = URLDecoder.decode(path, "UTF-8"); 161 baseDir = new File(decodedPath).getParentFile(); 162 if (!baseDir.exists()) { 163 baseDir = null; 164 } 165 } catch (Exception e) {} 166 while (cp.length() > 0) { 167 int pathSepIdx = cp.indexOf(" "); 168 if (pathSepIdx < 0) { 169 String pathElem = cp; 170 File f = (baseDir == null) ? 171 new File(pathElem) : new File(baseDir, pathElem); 172 urlList.add(fileToURL(f)); 173 break; 174 } else if (pathSepIdx > 0) { 175 String pathElem = cp.substring(0, pathSepIdx); 176 File f = (baseDir == null) ? 177 new File(pathElem) : new File(baseDir, pathElem); 178 urlList.add(fileToURL(f)); 179 } 180 cp = cp.substring(pathSepIdx + 1); 181 } 182 } 183 184 // Add JavaFX runtime jar and deployment jars 185 if (jfxRtPath != null) { 186 File jfxRtLibPath = new File(jfxRtPath, "lib"); 187 File jfxRtLibExtPath = new File(jfxRtLibPath, "ext"); 188 File jfxRtJar = new File(jfxRtLibExtPath, "jfxrt.jar"); 189 if (!jfxRtJar.canRead()) { 190 // Legacy support for old file location 191 jfxRtJar = new File(jfxRtLibPath, "jfxrt.jar"); 192 } 193 urlList.add(fileToURL(jfxRtJar)); 194 File deployJar = new File(jfxRtLibPath, "deploy.jar"); 195 //in the dev environment deploy.jars will not be part of 196 // built SDK unless it is windows 197 //However, hopefully java is used from relatively new java 198 // and we can add deploy jars from there? 199 //If no deploy jars are found we will treat it as runtime error 200 if (!deployJar.exists()) { 201 deployJar = getDeployJarFromJRE(); 202 } 203 if (deployJar != null) { 204 URL deployJarURL = fileToURL(deployJar); 205 urlList.add(deployJarURL); 206 urlList.add(new URL(deployJarURL, "plugin.jar")); 207 urlList.add(new URL(deployJarURL, "javaws.jar")); 208 } else { 209 if (verbose) { 210 System.err.println("Skip JavaFX Runtime at " 211 + jfxRtPath + " as no deploy jars found."); 212 } 213 return null; 214 } 215 } 216 217 URL[] urls = (URL[])urlList.toArray(new URL[0]); 218 if (verbose) { 219 System.err.println("===== URL list"); 220 for (int i = 0; i < urls.length; i++) { 221 System.err.println("" + urls[i]); 222 } 223 System.err.println("====="); 224 } 225 226 ClassLoader urlClassLoader = new URLClassLoader(urls, null); 227 Class launchClass = Class.forName(fxLaunchClassName, true, 228 urlClassLoader); 229 Method m = launchClass.getMethod("launchApplication", argTypes); 230 if (m != null) { 231 Thread.currentThread().setContextClassLoader(urlClassLoader); 232 return m; 233 } 234 } catch (Exception ex) { 235 if (jfxRtPath != null) { 236 ex.printStackTrace(); 237 } 238 } 239 240 return null; 241 } 242 243 private static Method findLaunchMethodInClasspath(String fxClassPath) { 244 return findLaunchMethod(null, fxClassPath); 245 } 246 247 private static Method findLaunchMethodInJar(String jfxRtPathName, String fxClassPath) { 248 File jfxRtPath = new File(jfxRtPathName); 249 250 // Verify that we can read <jfxRtPathName>/lib/ext/jfxrt.jar 251 File jfxRtLibPath = new File(jfxRtPath, "lib"); 252 File jfxRtLibExtPath = new File(jfxRtLibPath, "ext"); 253 File jfxRtJar = new File(jfxRtLibExtPath, "jfxrt.jar"); 254 if (!jfxRtJar.canRead()) { 255 File jfxRtJar2 = new File(jfxRtLibPath, "jfxrt.jar"); 256 if (!jfxRtJar2.canRead()) { 257 if (verbose) { 258 System.err.println("Unable to read " + jfxRtJar.toString() 259 + " or " + jfxRtJar2.toString()); 260 } 261 return null; 262 } 263 } 264 265 return findLaunchMethod(jfxRtPath, fxClassPath); 266 } 267 268 // convert version string in the form of x.y.z into int array of (x,y.z) 269 // return the array if version string can be converted. 270 // otherwise retun null 271 private static int[] convertVersionStringtoArray(String version) { 272 int[] v = new int[3]; 273 if (version == null) { 274 return null; 275 } 276 277 String s[] = version.split("\\."); 278 if (s.length == 3) { 279 v[0] = Integer.parseInt(s[0]); 280 v[1] = Integer.parseInt(s[1]); 281 v[2] = Integer.parseInt(s[2]); 282 return v; 283 } 284 // version string passed in is bad 285 return null; 286 } 287 288 // compare the two version array a1 and a2 289 // return 0 if the two array contains the same version information 290 // (or both are invalid version specs) 291 // return 1 if a2 is greater than a1 292 // return -1 if a2 is less than a1 293 private static int compareVersionArray(int[] a1, int[] a2) { 294 boolean isValid1 = (a1 != null) && (a1.length == 3); 295 boolean isValid2 = (a2 != null) && (a2.length == 3); 296 297 // both bad 298 if (!isValid1 && !isValid2) { 299 return 0; 300 } 301 302 // a2 < a1 303 if (!isValid2) { 304 return -1; 305 } 306 307 // a2 > a1 308 if (!isValid1) { 309 return 1; 310 } 311 312 for (int i = 0; i < a1.length; i++) { 313 if (a2[i] > a1[i]) { 314 return 1; 315 } 316 if (a2[i] < a1[i]) { 317 return -1; 318 } 319 } 320 321 return 0; 322 } 323 324 private static File getDeployJarFromJRE() { 325 final String javaHome = System.getProperty("java.home"); 326 if (verbose) { 327 System.err.println("java.home = " + javaHome); 328 } 329 if (javaHome == null || javaHome.equals("")) { 330 return null; 331 } 332 333 File jreLibPath = new File(javaHome, "lib"); 334 File deployJar = new File(jreLibPath, "deploy.jar"); 335 336 if (deployJar.exists()) { 337 return deployJar; 338 } 339 return null; 340 } 341 342 /** 343 * If we are on Windows, look in the system registry for the 344 * installed JavaFX runtime. 345 * 346 * @return the path to the JavaFX Runtime or null 347 */ 348 private static String lookupRegistry() { 349 if (!System.getProperty("os.name").startsWith("Win")) { 350 return null; 351 } 352 353 try { 354 // Load deploy.jar, get a Config instance and load the native 355 // libraries; then load the windows registry class and lookup 356 // the method to get the windows registry entry 357 358 File deployJar = getDeployJarFromJRE(); 359 if (deployJar == null) { 360 return null; 361 } 362 363 URL[] urls = new URL[]{fileToURL(deployJar)}; 364 if (verbose) { 365 System.err.println(">>>> URL to deploy.jar = " + urls[0]); 366 } 367 368 ClassLoader deployClassLoader = new URLClassLoader(urls, null); 369 370 try { 371 // Load and initialize the native deploy library, ignore exception 372 String configClassName = "com.sun.deploy.config.Config"; 373 Class configClass = Class.forName(configClassName, true, 374 deployClassLoader); 375 Method m = configClass.getMethod("getInstance", null); 376 Object config = m.invoke(null, null); 377 m = configClass.getMethod("loadDeployNativeLib", null); 378 m.invoke(config, null); 379 } catch (Exception ex) { 380 // Ignore any exception, since JDK7 no longer has this method 381 } 382 383 String winRegistryWrapperClassName = 384 "com.sun.deploy.association.utility.WinRegistryWrapper"; 385 386 Class winRegistryWrapperClass = Class.forName( 387 winRegistryWrapperClassName, true, deployClassLoader); 388 389 Method mGetSubKeys = winRegistryWrapperClass.getMethod( 390 "WinRegGetSubKeys", new Class[]{ 391 Integer.TYPE, 392 String.class, 393 Integer.TYPE 394 }); 395 396 Field HKEY_LOCAL_MACHINE_Field2 = 397 winRegistryWrapperClass.getField("HKEY_LOCAL_MACHINE"); 398 final int HKEY_LOCAL_MACHINE2 = HKEY_LOCAL_MACHINE_Field2.getInt(null); 399 final String registryKey = "Software\\Oracle\\JavaFX\\"; 400 401 // Read the registry and find all installed JavaFX runtime versions 402 // under HKLM\Software\Oracle\JavaFX\ 403 String[] fxVersions = (String[]) mGetSubKeys.invoke(null, new Object[]{ 404 new Integer(HKEY_LOCAL_MACHINE2), 405 registryKey, 406 new Integer(255) 407 }); 408 409 if (fxVersions == null) { 410 // No JavaFX runtime installed in the system 411 return null; 412 } 413 String version = ZERO_VERSION; 414 // Iterate thru all installed JavaFX runtime verions in the system 415 for (int i = 0; i < fxVersions.length; i++) { 416 // get the latest version that is compatibible with the 417 // launcher JavaFX family version and meets minimum version requirement 418 if (fxVersions[i].startsWith(JAVAFX_FAMILY_VERSION) 419 && fxVersions[i].compareTo(JAVAFX_REQUIRED_VERSION) >= 0) { 420 int[] v1Array = convertVersionStringtoArray(version); 421 int[] v2Array = convertVersionStringtoArray(fxVersions[i]); 422 if (compareVersionArray(v1Array, v2Array) > 0) { 423 version = fxVersions[i]; 424 } 425 } else { 426 if (verbose) { 427 System.err.println(" Skip version " + fxVersions[i] 428 + " (required=" + JAVAFX_REQUIRED_VERSION + ")"); 429 } 430 } 431 } 432 433 if (version.equals(ZERO_VERSION)) { 434 // No installed JavaFX runtime compatible with this Launcher 435 return null; 436 } 437 438 // Read the registry entry for: Software\Oracle\JavaFX\<version> 439 String winRegistryClassName = "com.sun.deploy.util.WinRegistry"; 440 Class winRegistryClass = Class.forName(winRegistryClassName, true, 441 deployClassLoader); 442 Method mGet = winRegistryClass.getMethod("getString", new Class[]{ 443 Integer.TYPE, 444 String.class, 445 String.class 446 }); 447 Field HKEY_LOCAL_MACHINE_Field = winRegistryClass.getField("HKEY_LOCAL_MACHINE"); 448 final int HKEY_LOCAL_MACHINE = HKEY_LOCAL_MACHINE_Field.getInt(null); 449 String path = (String) mGet.invoke(null, new Object[]{ 450 new Integer(HKEY_LOCAL_MACHINE), 451 registryKey + version, 452 "Path" 453 }); 454 if (verbose) { 455 System.err.println("FOUND KEY: " + registryKey + version + " = " + path); 456 } 457 return path; 458 } catch (Exception ex) { 459 ex.printStackTrace(); 460 } 461 462 return null; 463 } 464 465 private static Attributes getJarAttributes() throws Exception { 466 String theClassFile = "Main.class"; 467 Class theClass = Main.class; 468 String classUrlString = theClass.getResource(theClassFile).toString(); 469 if (!classUrlString.startsWith("jar:file:") || classUrlString.indexOf("!") == -1){ 470 return null; 471 } 472 // Strip out the "jar:" and everything after and including the "!" 473 String urlString = classUrlString.substring(4, classUrlString.lastIndexOf("!")); 474 File jarFile = new File(new URI(urlString).getPath()); 475 String jarName = jarFile.getCanonicalPath(); 476 477 Attributes attr; 478 JarFile jf = null; 479 try { 480 jf = new JarFile(jarName); 481 Manifest mf = jf.getManifest(); 482 attr = mf.getMainAttributes(); 483 } finally { 484 if (jf != null) { 485 try { 486 jf.close(); 487 } catch (Exception ex) { 488 /* swallow the exception */ 489 } 490 } 491 } 492 return attr; 493 } 494 495 private static String decodeBase64(String inp) throws IOException { 496 return new String(Base64.getDecoder().decode(inp)); 497 } 498 499 private static String[] getAppArguments(Attributes attrs) { 500 List args = new LinkedList(); 501 502 try { 503 int idx = 1; 504 String argNamePrefix = "JavaFX-Argument-"; 505 while (attrs.getValue(argNamePrefix + idx) != null) { 506 args.add(decodeBase64(attrs.getValue(argNamePrefix + idx))); 507 idx++; 508 } 509 510 String paramNamePrefix = "JavaFX-Parameter-Name-"; 511 String paramValuePrefix = "JavaFX-Parameter-Value-"; 512 idx = 1; 513 while (attrs.getValue(paramNamePrefix + idx) != null) { 514 String k = decodeBase64(attrs.getValue(paramNamePrefix + idx)); 515 String v = null; 516 if (attrs.getValue(paramValuePrefix + idx) != null) { 517 v = decodeBase64(attrs.getValue(paramValuePrefix + idx)); 518 } 519 args.add("--" + k + "=" + (v != null ? v : "")); 520 idx++; 521 } 522 } catch (IOException ioe) { 523 System.err.println("Failed to extract application parameters"); 524 ioe.printStackTrace(); 525 } 526 527 528 return (String[]) args.toArray(new String[0]); 529 } 530 531 // Return the application class name, either from the property or from the 532 // jar file 533 private static String getAppName(Attributes attrs, boolean preloader) { 534 String propName = preloader 535 ? "javafx.preloader.class" 536 : "javafx.application.class"; 537 538 String className = System.getProperty(propName); 539 if (className != null && className.length() != 0) { 540 return className; 541 } 542 543 String appName; 544 545 //this only true in the dev environment if run out of jar 546 if (attrs == null) { 547 return "TEST"; 548 } 549 550 if (preloader) { 551 appName = (String)attrs.getValue(manifestPreloaderClass); 552 if (appName == null || appName.length() == 0) { 553 if (verbose) { 554 System.err.println("Unable to find preloader class name"); 555 } 556 return null; 557 } 558 return appName; 559 } else { 560 appName = (String)attrs.getValue(manifestAppClass); 561 if (appName == null || appName.length() == 0) { 562 System.err.println("Unable to find application class name"); 563 return null; 564 } 565 return appName; 566 } 567 } 568 569 private static Class getAppClass(String appName) { 570 try { 571 // load the user's JavaFX class but do *not* initialize! 572 if (verbose) { 573 System.err.println("Try calling Class.forName(" + appName 574 + ") using classLoader = " 575 + Thread.currentThread().getContextClassLoader()); 576 } 577 Class appClass = Class.forName(appName, false, 578 Thread.currentThread().getContextClassLoader()); 579 if (verbose) { 580 System.err.println("found class: " + appClass); 581 } 582 return appClass; 583 } catch (NoClassDefFoundError ncdfe) { 584 ncdfe.printStackTrace(); 585 errorExit("Unable to find class: " + appName); 586 } catch (ClassNotFoundException cnfe) { 587 cnfe.printStackTrace(); 588 errorExit("Unable to find class: " + appName); 589 } 590 591 return null; 592 } 593 594 //try to install webstart proxy to avoid asking user for proxy info 595 private static void tryToSetProxy() { 596 try { 597 if (attrs != null) { 598 String proxySetting = attrs.getValue(manifestAutoProxy); 599 if (proxySetting != null && !"auto".equals(proxySetting.toLowerCase())) { 600 if (verbose) { 601 System.out.println("Auto proxy detection is disabled in manifest."); 602 } 603 return; 604 } 605 } 606 607 //if explicit proxy settings are proxided we will skip autoproxy 608 //Note: we only check few most popular settings. 609 if (System.getProperty("http.proxyHost") != null 610 || System.getProperty("https.proxyHost") != null 611 || System.getProperty("ftp.proxyHost") != null 612 || System.getProperty("socksProxyHost") != null) { 613 if (verbose) { 614 System.out.println("Explicit proxy settings detected. Skip autoconfig."); 615 System.out.println(" http.proxyHost=" + System.getProperty("http.proxyHost")); 616 System.out.println(" https.proxyHost=" + System.getProperty("https.proxyHost")); 617 System.out.println(" ftp.proxyHost=" + System.getProperty("ftp.proxyHost")); 618 System.out.println(" socksProxyHost=" + System.getProperty("socksProxyHost")); 619 } 620 return; 621 } 622 if (System.getProperty("javafx.autoproxy.disable") != null) { 623 if (verbose) { 624 System.out.println("Disable autoproxy on request."); 625 } 626 return; 627 } 628 629 Class sm = Class.forName("com.sun.deploy.services.ServiceManager", 630 true, 631 Thread.currentThread().getContextClassLoader()); 632 Class params[] = {Integer.TYPE}; 633 Method setservice = sm.getDeclaredMethod("setService", params); 634 String osname = System.getProperty("os.name"); 635 636 String servicename = null; 637 if (osname.startsWith("Win")) { 638 servicename = "STANDALONE_TIGER_WIN32"; 639 640 } else if (osname.contains("Mac")) { 641 servicename = "STANDALONE_TIGER_MACOSX"; 642 } else { 643 servicename = "STANDALONE_TIGER_UNIX"; 644 } 645 Object values[] = new Object[1]; 646 Class pt = Class.forName("com.sun.deploy.services.PlatformType", 647 true, 648 Thread.currentThread().getContextClassLoader()); 649 values[0] = pt.getField(servicename).get(null); 650 setservice.invoke(null, values); 651 652 Class dps = Class.forName( 653 "com.sun.deploy.net.proxy.DeployProxySelector", 654 true, 655 Thread.currentThread().getContextClassLoader()); 656 Method m = dps.getDeclaredMethod("reset", new Class[0]); 657 m.invoke(null, new Object[0]); 658 if (verbose) { 659 System.out.println("Autoconfig of proxy is completed."); 660 } 661 } catch (Exception e) { 662 if (verbose) { 663 System.out.println("Failed to autoconfig proxy due to "+e); 664 } 665 } 666 } 667 668 private static void processUpdateHook(String updateHookName) { 669 if (updateHookName == null) { 670 return; 671 } 672 673 try { 674 // load UpdateHook class 675 if (verbose) { 676 System.err.println("Try calling Class.forName(" + updateHookName 677 + ") using classLoader = " 678 + Thread.currentThread().getContextClassLoader()); 679 } 680 Class hookClass = Class.forName(updateHookName, false, 681 Thread.currentThread().getContextClassLoader()); 682 if (verbose) { 683 System.err.println("found class: " + hookClass.getCanonicalName()); 684 } 685 686 Method mainMethod = hookClass.getMethod("main", 687 new Class[] { (new String[0]).getClass() }); 688 String args[] = null; 689 mainMethod.invoke(null, new Object[] {args}); 690 691 } catch (Exception ex) { 692 if (verbose) { 693 System.err.println("Failed to run update hook: "+ex.getMessage()); 694 ex.printStackTrace(); 695 } 696 } 697 } 698 699 private static void launchApp(Method launchMethod, 700 String appName, 701 String preloaderName, 702 String updateHookName, 703 String[] args) { 704 705 Class preloaderClass = null; 706 if (preloaderName != null) { 707 preloaderClass = getAppClass(preloaderName); 708 } 709 Class appClass = getAppClass(appName); 710 Class fxApplicationClass = null; 711 try { 712 fxApplicationClass = Class.forName(fxApplicationClassName, 713 true, Thread.currentThread().getContextClassLoader()); 714 } catch (NoClassDefFoundError ex) { 715 errorExit("Cannot find " + fxApplicationClassName); 716 } catch (ClassNotFoundException ex) { 717 errorExit("Cannot find " + fxApplicationClassName); 718 } 719 720 if (fxApplicationClass.isAssignableFrom(appClass)) { 721 try { 722 if (verbose) { 723 System.err.println("launchApp: Try calling " 724 + launchMethod.getDeclaringClass().getName() + "." 725 + launchMethod.getName()); 726 } 727 tryToSetProxy(); 728 processUpdateHook(updateHookName); 729 launchMethod.invoke(null, new Object[] { appClass, preloaderClass, args }); 730 } catch (InvocationTargetException ex) { 731 ex.printStackTrace(); 732 errorExit("Exception while running Application"); 733 } catch (Exception ex) { 734 ex.printStackTrace(); 735 errorExit("Unable to invoke launch method"); 736 } 737 } else { 738 try { 739 if (verbose) { 740 System.err.println("Try calling " + appClass.getName() 741 + ".main(String[])"); 742 } 743 Method mainMethod = appClass.getMethod("main", 744 new Class[] { (new String[0]).getClass() }); 745 mainMethod.invoke(null, new Object[] { args }); 746 } catch (Exception ex) { 747 ex.printStackTrace(); 748 errorExit("Unable to invoke main method"); 749 } 750 } 751 } 752 753 // Check the JRE version. Exit with error if < 1.6 754 private static boolean checkJre() { 755 if (verbose) { 756 System.err.println("java.version = " 757 + System.getProperty("java.version")); 758 System.err.println("java.runtime.version = " 759 + System.getProperty("java.runtime.version")); 760 } 761 762 // Check for minimum JRE version 763 if (isOldJRE()) { 764 showFallback(true); 765 return false; 766 } 767 return true; 768 } 769 770 private static Method findLaunchMethod(String fxClassPath) { 771 Method launchMethod; 772 773 // Try to find JavaFX LauncherImpl class on classpath 774 if (verbose) { 775 System.err.println("1) Try existing classpath..."); 776 } 777 launchMethod = findLaunchMethodInClasspath(fxClassPath); 778 if (launchMethod != null) { 779 return launchMethod; 780 } 781 782 // Check for javafx.runtime.path variable; if set, look for the 783 // JavaFX LauncherImpl class there. 784 if (verbose) { 785 System.err.println("2) Try javafx.runtime.path property..."); 786 } 787 String javafxRuntimePath = System.getProperty("javafx.runtime.path"); 788 if (javafxRuntimePath != null) { 789 if (verbose) { 790 System.err.println(" javafx.runtime.path = " + javafxRuntimePath); 791 } 792 launchMethod = findLaunchMethodInJar(javafxRuntimePath, fxClassPath); 793 } 794 if (launchMethod != null) { 795 return launchMethod; 796 } 797 798 if (verbose) { 799 System.err.println("3) Look for cobundled JavaFX ... " + 800 "[java.home="+System.getProperty("java.home")); 801 } 802 launchMethod = findLaunchMethodInJar( 803 System.getProperty("java.home"), fxClassPath); 804 if (launchMethod != null) { 805 return launchMethod; 806 } 807 808 // Check the platform registry for this architecture. 809 if (verbose) { 810 System.err.println("4) Look in the OS platform registry..."); 811 } 812 javafxRuntimePath = lookupRegistry(); 813 if (javafxRuntimePath != null) { 814 if (verbose) { 815 System.err.println(" Installed JavaFX runtime found in: " 816 + javafxRuntimePath); 817 } 818 launchMethod = findLaunchMethodInJar(javafxRuntimePath, fxClassPath); 819 if (launchMethod != null) { 820 return launchMethod; 821 } 822 } 823 824 return launchMethod; 825 } 826 827 public static void main(String [] args) { 828 // Set verbose flag 829 verbose = Boolean.getBoolean("javafx.verbose"); 830 831 // First check the minimum JRE 832 if (!checkJre()) { 833 return; 834 } 835 836 // Load the main jar manifest attributes 837 try { 838 attrs = getJarAttributes(); 839 } catch (Exception ex) { 840 ex.printStackTrace(); 841 errorExit("Unable to load jar manifest"); 842 } 843 844 // Next get the application name 845 String appName = getAppName(attrs, false); 846 if (verbose) { 847 System.err.println("appName = " + appName); 848 } 849 if (appName == null) { 850 errorExit("Unable to find application class name"); 851 } 852 853 // Next get the preloader name 854 String preloaderName = getAppName(attrs, true); 855 if (verbose) { 856 System.err.println("preloaderName = " + preloaderName); 857 } 858 859 String embeddedArgs[] = getAppArguments(attrs); 860 if (verbose) { 861 System.err.println("embeddedArgs = " + Arrays.toString(embeddedArgs)); 862 System.err.println("commandLineArgs = " + Arrays.toString(args)); 863 } 864 865 String updateHook = (String) attrs.getValue(manifestUpdateHook); 866 if (verbose && updateHook != null) { 867 System.err.println("updateHook = " + updateHook); 868 } 869 870 // Get JavaFX-Class-Path entry 871 String fxClassPath; 872 if (attrs != null) { 873 fxClassPath = (String)attrs.getValue(manifestClassPath); 874 } else { 875 fxClassPath = ""; 876 } 877 878 Method launchMethod = findLaunchMethod(fxClassPath); 879 if (launchMethod != null) { 880 launchApp(launchMethod, appName, preloaderName, updateHook, 881 args.length > 0 ? args: embeddedArgs); 882 return; 883 } 884 885 showFallback(false); 886 } 887 888 private static void showFallback(final boolean jreError) { 889 SwingUtilities.invokeLater(new Runnable() { 890 public void run() { 891 JFrame f = new JFrame("JavaFX Launcher"); 892 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 893 JApplet japp = null; 894 895 //try to use custom fallback if available 896 if (attrs != null && attrs.getValue(manifestFallbackClass) != null) { 897 Class customFallback = null; 898 try { 899 customFallback = Class.forName( 900 attrs.getValue(manifestFallbackClass), false, 901 Thread.currentThread().getContextClassLoader()); 902 } catch (ClassNotFoundException ce) { 903 System.err.println("Custom fallback class is not found: " 904 + attrs.getValue(manifestFallbackClass)); 905 } 906 907 //If custom fallback attribute actually points to the 908 // default JavaFX fallback we want to use other way to launch it 909 if (customFallback != null 910 && !NoJavaFXFallback.class.getName().equals( 911 customFallback.getName())) { 912 try { 913 japp = (JApplet) customFallback.newInstance(); 914 } catch (Exception e) { 915 System.err.println("Failed to instantiate custom fallback " 916 + customFallback.getName() + " due to " + e); 917 } 918 } 919 } 920 921 //custom fallback missing or we fail to init it 922 if (japp == null) { 923 //custom fallback will need to figure reason of error 924 //on its own. Generic fallback gets extra input. 925 japp = new NoJavaFXFallback( 926 jreError, !jreError, JAVAFX_REQUIRED_VERSION); 927 f.getContentPane().add(japp); //could be old JRE! use content pane 928 f.pack(); 929 f.setVisible(true); 930 } else { 931 japp.init(); 932 f.getContentPane().add(japp); //could be old JRE! use content pane 933 japp.start(); 934 f.pack(); 935 f.setVisible(true); 936 } 937 938 } 939 }); 940 } 941 942 private static void errorExit(final String string) { 943 try { 944 Runnable runnable = new Runnable() { 945 public void run() { 946 try { 947 Class componentClass = Class.forName("java.awt.Component"); 948 Class jOptionPaneClass = Class.forName("javax.swing.JOptionPane"); 949 Field ERROR_MESSAGE_Field = jOptionPaneClass.getField("ERROR_MESSAGE"); 950 final int ERROR_MESSAGE = ERROR_MESSAGE_Field.getInt(null); 951 Method showMessageDialogMethod = jOptionPaneClass.getMethod( 952 "showMessageDialog", 953 new Class[] { componentClass, 954 Object.class, 955 String.class, 956 Integer.TYPE }); 957 showMessageDialogMethod.invoke(null, new Object[] { 958 null, string, "JavaFX Launcher Error", 959 new Integer(ERROR_MESSAGE) }); 960 } catch (Exception ex) { 961 ex.printStackTrace(); 962 } 963 } 964 }; 965 966 Class swingUtilsClass = Class.forName("javax.swing.SwingUtilities"); 967 Method invokeAndWaitMethod = swingUtilsClass.getMethod("invokeAndWait", 968 new Class[] { Runnable.class }); 969 invokeAndWaitMethod.invoke(null, new Object[] { runnable }); 970 if (verbose) { 971 System.err.println("Done with invoke and wait"); 972 } 973 } catch (Exception ex) { 974 ex.printStackTrace(); 975 } 976 977 System.exit(1); 978 } 979 980 // Package-scope method used to check minimum JRE version 981 static boolean isOldJRE() { 982 return getJavaVersionAsFloat() < 160.18f; //< 6u18 983 } 984 985 static float getJavaVersionAsFloat() { 986 String versionString = System.getProperty("java.version", "1.5.0"); 987 988 StringBuffer sb = new StringBuffer(); 989 990 int firstDot = versionString.indexOf("."); 991 sb.append(versionString.substring(0,firstDot)); 992 993 int secondDot = versionString.indexOf(".", firstDot+1); 994 sb.append(versionString.substring(firstDot+1, secondDot)); 995 996 int underscore = versionString.indexOf("_", secondDot+1); 997 if (underscore >= 0) { 998 int dash = versionString.indexOf("-", underscore+1); 999 if (dash < 0) { 1000 dash = versionString.length(); 1001 } 1002 sb.append(versionString.substring(secondDot+1, underscore)). 1003 append("."). 1004 append(versionString.substring(underscore+1, dash)); 1005 } else { 1006 int dash = versionString.indexOf("-", secondDot+1); 1007 if (dash < 0) { 1008 dash = versionString.length(); 1009 } 1010 sb.append(versionString.substring(secondDot+1, dash)); 1011 } 1012 1013 float version = 150.0f; 1014 try { 1015 version = Float.parseFloat(sb.toString()); 1016 } catch (NumberFormatException e) {} 1017 1018 return version; 1019 } 1020 1021 }