1 /* 2 * Copyright (c) 2011, 2016, 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 com.oracle.tools.packager.Platform; 29 import java.io.File; 30 import java.io.IOException; 31 import java.lang.reflect.Field; 32 import java.lang.reflect.InvocationTargetException; 33 import java.lang.reflect.Method; 34 import java.net.URI; 35 import java.net.URL; 36 import java.net.URLClassLoader; 37 import java.net.URLDecoder; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Base64; 41 import java.util.LinkedList; 42 import java.util.List; 43 import java.util.jar.Attributes; 44 import java.util.jar.JarFile; 45 import java.util.jar.Manifest; 46 import javax.swing.JApplet; 47 import javax.swing.JFrame; 48 import javax.swing.SwingUtilities; 49 50 /** 51 * This class loads com.sun.javafx.application.LauncherImpl and calls the 52 * launchApplication method. 53 * 54 * It is used by packager to include it as part of the application jar file 55 * so that we can run and locate the JavaFX runtime. Note that we cannot 56 * assume that JavaFX is on the classpath so we must use reflection and not 57 * have any direct reference to any JavaFX runtime class. 58 * 59 * We will do the following: 60 * 61 * 1. Verify the version of Java and produce error message if not JDK6+ 62 * 63 * 2. Locate the jar file from which the Main class was launched. Read the 64 * jar manifest and extract 65 * the application class using the JavaFX-Application-Class manifest entry. 66 * Alternatively, we will read the application class from a system property. 67 * 68 * 3. Try to locate the JavaFX runtime by loading the 69 * com.sun.javafx.application.LauncherImpl class using the following mechanisms 70 * in order: 71 * 72 * A. Try loading it directly in case it is on the classpath. 73 * B. If the javafx.runtime.path System Property is set, try 74 * loading it from ${javafx.runtime.path}/lib/ext/jfxrt.jar 75 * (or lib/jfxrt.jar) 76 * C. Look for a cobundled JavaFX in the current jre 77 * D. If on Windows, read the registry key associated with the JavaFX 78 * runtime (if running in a 64-bit JVM, use the 64-bit path) 79 * 80 * 4. Create a custom URLClassLoader from the appropriate jar files, and then 81 * call the launchApplication method. If the application class is not a 82 * subclass of javafx.application.Application then we will call the main 83 * method in the application class instead. 84 * 85 * 5. If the LauncherImpl class cannot be found, then show a Swing dialog 86 * (again, using reflection). 87 */ 88 89 public class Main { 90 private static boolean verbose = false; 91 private static final String fxApplicationClassName = "javafx.application.Application"; 92 private static final String fxLaunchClassName = "com.sun.javafx.application.LauncherImpl"; 93 private static final String manifestAppClass = "JavaFX-Application-Class"; 94 private static final String manifestPreloaderClass = "JavaFX-Preloader-Class"; 95 private static final String manifestFallbackClass = "JavaFX-Fallback-Class"; 96 private static final String manifestClassPath = "JavaFX-Class-Path"; 97 98 //Manifest entry to explicitly disable autoproxy config 99 // Unless it has "Auto" value it will disable proxy 100 private static final String manifestAutoProxy = "JavaFX-Feature-Proxy"; 101 102 //Experimental hook to simplify adding "au" logic to native bundles 103 private static final String manifestUpdateHook = "X-JavaFX-Update-Hook"; 104 105 // JavaFX family version that this Launcher is compatible with 106 private static final String JAVAFX_FAMILY_VERSION = "2."; 107 108 // Minimum JavaFX version required to run the app 109 // (keep separate from JAVAFX_FAMILY_VERSION check as 110 // we want 2.2.1 SDK to be ok to run app that needs 2.1.0 111 // and prefix based match is not enough) 112 // NOTE: This should be refactored so that the version gets supplied 113 // from the build environment, but we do NOT want another class or 114 // property file in the app bundle! Are there any other options 115 // besides java source code preprocessing?) 116 private static final String JAVAFX_REQUIRED_VERSION = "2.1.0"; 117 118 private static final String ZERO_VERSION = "0.0.0"; 119 120 //application jar attributes 121 private static Attributes attrs = null; 122 123 private static URL fileToURL(File file) throws IOException { 124 return file.getCanonicalFile().toURI().toURL(); 125 } 126 127 private static Method findLaunchMethod(File jfxRtPath, String fxClassPath) { 128 final Class[] argTypes = 129 new Class[] { Class.class, Class.class, (new String[0]).getClass() }; 130 131 try { 132 ArrayList urlList = new ArrayList(); 133 134 // Add in the elements of the classpath 135 String cp = System.getProperty("java.class.path"); 136 if (cp != null) { 137 while (cp.length() > 0) { 138 int pathSepIdx = cp.indexOf(File.pathSeparatorChar); 139 if (pathSepIdx < 0) { 140 String pathElem = cp; 141 urlList.add(fileToURL(new File(pathElem))); 142 break; 143 } else if (pathSepIdx > 0) { 144 String pathElem = cp.substring(0, pathSepIdx); 145 urlList.add(fileToURL(new File(pathElem))); 146 } 147 cp = cp.substring(pathSepIdx + 1); 148 } 149 } 150 151 // Add in the jars from the JavaFX-Class-Path entry 152 cp = fxClassPath; 153 if (cp != null) { 154 //these are relative paths. if app is not in the current dir 155 // we may resolve them incorrectly ... 156 // try to find main jar and build absolute paths 157 File baseDir = null; 158 try { 159 String path = Main.class.getProtectionDomain().getCodeSource().getLocation().getPath(); 160 //path will have encoded spaces, etc. => need to restore 161 String decodedPath = URLDecoder.decode(path, "UTF-8"); 162 baseDir = new File(decodedPath).getParentFile(); 163 if (!baseDir.exists()) { 164 baseDir = null; 165 } 166 } catch (Exception e) {} 167 while (cp.length() > 0) { 168 int pathSepIdx = cp.indexOf(" "); 169 if (pathSepIdx < 0) { 170 String pathElem = cp; 171 File f = (baseDir == null) ? 172 new File(pathElem) : new File(baseDir, pathElem); 173 urlList.add(fileToURL(f)); 174 break; 175 } else if (pathSepIdx > 0) { 176 String pathElem = cp.substring(0, pathSepIdx); 177 File f = (baseDir == null) ? 178 new File(pathElem) : new File(baseDir, pathElem); 179 urlList.add(fileToURL(f)); 180 } 181 cp = cp.substring(pathSepIdx + 1); 182 } 183 } 184 185 // Add JavaFX runtime jar and deployment jars 186 if (jfxRtPath != null) { 187 File jfxRtLibPath = new File(jfxRtPath, "lib"); 188 File jfxRtLibExtPath = new File(jfxRtLibPath, "ext"); 189 File jfxRtJar = new File(jfxRtLibExtPath, "jfxrt.jar"); 190 if (!jfxRtJar.canRead()) { 191 // Legacy support for old file location 192 jfxRtJar = new File(jfxRtLibPath, "jfxrt.jar"); 193 } 194 urlList.add(fileToURL(jfxRtJar)); 195 File deployJar = new File(jfxRtLibPath, "deploy.jar"); 196 //in the dev environment deploy.jars will not be part of 197 // built SDK unless it is windows 198 //However, hopefully java is used from relatively new java 199 // and we can add deploy jars from there? 200 //If no deploy jars are found we will treat it as runtime error 201 if (!deployJar.exists()) { 202 deployJar = getDeployJarFromJRE(); 203 } 204 if (deployJar != null) { 205 URL deployJarURL = fileToURL(deployJar); 206 urlList.add(deployJarURL); 207 urlList.add(new URL(deployJarURL, "plugin.jar")); 208 urlList.add(new URL(deployJarURL, "javaws.jar")); 209 } else { 210 if (verbose) { 211 System.err.println("Skip JavaFX Runtime at " 212 + jfxRtPath + " as no deploy jars found."); 213 } 214 return null; 215 } 216 } 217 218 URL[] urls = (URL[])urlList.toArray(new URL[0]); 219 if (verbose) { 220 System.err.println("===== URL list"); 221 for (int i = 0; i < urls.length; i++) { 222 System.err.println("" + urls[i]); 223 } 224 System.err.println("====="); 225 } 226 227 ClassLoader urlClassLoader = new URLClassLoader(urls, null); 228 Class launchClass = Class.forName(fxLaunchClassName, true, 229 urlClassLoader); 230 Method m = launchClass.getMethod("launchApplication", argTypes); 231 if (m != null) { 232 Thread.currentThread().setContextClassLoader(urlClassLoader); 233 return m; 234 } 235 } catch (Exception ex) { 236 if (jfxRtPath != null) { 237 ex.printStackTrace(); 238 } 239 } 240 241 return null; 242 } 243 244 private static Method findLaunchMethodInClasspath(String fxClassPath) { 245 return findLaunchMethod(null, fxClassPath); 246 } 247 248 private static Method findLaunchMethodInJar(String jfxRtPathName, String fxClassPath) { 249 File jfxRtPath = new File(jfxRtPathName); 250 251 // Verify that we can read <jfxRtPathName>/lib/ext/jfxrt.jar 252 File jfxRtLibPath = new File(jfxRtPath, "lib"); 253 File jfxRtLibExtPath = new File(jfxRtLibPath, "ext"); 254 File jfxRtJar = new File(jfxRtLibExtPath, "jfxrt.jar"); 255 if (!jfxRtJar.canRead()) { 256 File jfxRtJar2 = new File(jfxRtLibPath, "jfxrt.jar"); 257 if (!jfxRtJar2.canRead()) { 258 if (verbose) { 259 System.err.println("Unable to read " + jfxRtJar.toString() 260 + " or " + jfxRtJar2.toString()); 261 } 262 return null; 263 } 264 } 265 266 return findLaunchMethod(jfxRtPath, fxClassPath); 267 } 268 269 // convert version string in the form of x.y.z into int array of (x,y.z) 270 // return the array if version string can be converted. 271 // otherwise retun null 272 private static int[] convertVersionStringtoArray(String version) { 273 int[] v = new int[3]; 274 if (version == null) { 275 return null; 276 } 277 278 String s[] = version.split("\\."); 279 if (s.length == 3) { 280 v[0] = Integer.parseInt(s[0]); 281 v[1] = Integer.parseInt(s[1]); 282 v[2] = Integer.parseInt(s[2]); 283 return v; 284 } 285 // version string passed in is bad 286 return null; 287 } 288 289 // compare the two version array a1 and a2 290 // return 0 if the two array contains the same version information 291 // (or both are invalid version specs) 292 // return 1 if a2 is greater than a1 293 // return -1 if a2 is less than a1 294 private static int compareVersionArray(int[] a1, int[] a2) { 295 boolean isValid1 = (a1 != null) && (a1.length == 3); 296 boolean isValid2 = (a2 != null) && (a2.length == 3); 297 298 // both bad 299 if (!isValid1 && !isValid2) { 300 return 0; 301 } 302 303 // a2 < a1 304 if (!isValid2) { 305 return -1; 306 } 307 308 // a2 > a1 309 if (!isValid1) { 310 return 1; 311 } 312 313 for (int i = 0; i < a1.length; i++) { 314 if (a2[i] > a1[i]) { 315 return 1; 316 } 317 if (a2[i] < a1[i]) { 318 return -1; 319 } 320 } 321 322 return 0; 323 } 324 325 private static File getDeployJarFromJRE() { 326 final String javaHome = System.getProperty("java.home"); 327 if (verbose) { 328 System.err.println("java.home = " + javaHome); 329 } 330 if (javaHome == null || javaHome.equals("")) { 331 return null; 332 } 333 334 File jreLibPath = new File(javaHome, "lib"); 335 File deployJar = new File(jreLibPath, "deploy.jar"); 336 337 if (deployJar.exists()) { 338 return deployJar; 339 } 340 return null; 341 } 342 343 /** 344 * If we are on Windows, look in the system registry for the 345 * installed JavaFX runtime. 346 * 347 * @return the path to the JavaFX Runtime or null 348 */ 349 private static String lookupRegistry() { 350 if (Platform.getPlatform() != Platform.WINDOWS) { 351 return null; 352 } 353 354 try { 355 // Load deploy.jar, get a Config instance and load the native 356 // libraries; then load the windows registry class and lookup 357 // the method to get the windows registry entry 358 359 File deployJar = getDeployJarFromJRE(); 360 if (deployJar == null) { 361 return null; 362 } 363 364 URL[] urls = new URL[]{fileToURL(deployJar)}; 365 if (verbose) { 366 System.err.println(">>>> URL to deploy.jar = " + urls[0]); 367 } 368 369 ClassLoader deployClassLoader = new URLClassLoader(urls, null); 370 371 try { 372 // Load and initialize the native deploy library, ignore exception 373 String configClassName = "com.sun.deploy.config.Config"; 374 Class configClass = Class.forName(configClassName, true, 375 deployClassLoader); 376 Method m = configClass.getMethod("getInstance", null); 377 Object config = m.invoke(null, null); 378 m = configClass.getMethod("loadDeployNativeLib", null); 379 m.invoke(config, null); 380 } catch (Exception ex) { 381 // Ignore any exception, since JDK7 no longer has this method 382 } 383 384 String winRegistryWrapperClassName = 385 "com.sun.deploy.association.utility.WinRegistryWrapper"; 386 387 Class winRegistryWrapperClass = Class.forName( 388 winRegistryWrapperClassName, true, deployClassLoader); 389 390 Method mGetSubKeys = winRegistryWrapperClass.getMethod( 391 "WinRegGetSubKeys", new Class[]{ 392 Integer.TYPE, 393 String.class, 394 Integer.TYPE 395 }); 396 397 Field HKEY_LOCAL_MACHINE_Field2 = 398 winRegistryWrapperClass.getField("HKEY_LOCAL_MACHINE"); 399 final int HKEY_LOCAL_MACHINE2 = HKEY_LOCAL_MACHINE_Field2.getInt(null); 400 final String registryKey = "Software\\Oracle\\JavaFX\\"; 401 402 // Read the registry and find all installed JavaFX runtime versions 403 // under HKLM\Software\Oracle\JavaFX\ 404 String[] fxVersions = (String[]) mGetSubKeys.invoke(null, new Object[]{ 405 new Integer(HKEY_LOCAL_MACHINE2), 406 registryKey, 407 new Integer(255) 408 }); 409 410 if (fxVersions == null) { 411 // No JavaFX runtime installed in the system 412 return null; 413 } 414 String version = ZERO_VERSION; 415 // Iterate thru all installed JavaFX runtime verions in the system 416 for (int i = 0; i < fxVersions.length; i++) { 417 // get the latest version that is compatibible with the 418 // launcher JavaFX family version and meets minimum version requirement 419 if (fxVersions[i].startsWith(JAVAFX_FAMILY_VERSION) 420 && fxVersions[i].compareTo(JAVAFX_REQUIRED_VERSION) >= 0) { 421 int[] v1Array = convertVersionStringtoArray(version); 422 int[] v2Array = convertVersionStringtoArray(fxVersions[i]); 423 if (compareVersionArray(v1Array, v2Array) > 0) { 424 version = fxVersions[i]; 425 } 426 } else { 427 if (verbose) { 428 System.err.println(" Skip version " + fxVersions[i] 429 + " (required=" + JAVAFX_REQUIRED_VERSION + ")"); 430 } 431 } 432 } 433 434 if (version.equals(ZERO_VERSION)) { 435 // No installed JavaFX runtime compatible with this Launcher 436 return null; 437 } 438 439 // Read the registry entry for: Software\Oracle\JavaFX\<version> 440 String winRegistryClassName = "com.sun.deploy.util.WinRegistry"; 441 Class winRegistryClass = Class.forName(winRegistryClassName, true, 442 deployClassLoader); 443 Method mGet = winRegistryClass.getMethod("getString", new Class[]{ 444 Integer.TYPE, 445 String.class, 446 String.class 447 }); 448 Field HKEY_LOCAL_MACHINE_Field = winRegistryClass.getField("HKEY_LOCAL_MACHINE"); 449 final int HKEY_LOCAL_MACHINE = HKEY_LOCAL_MACHINE_Field.getInt(null); 450 String path = (String) mGet.invoke(null, new Object[]{ 451 new Integer(HKEY_LOCAL_MACHINE), 452 registryKey + version, 453 "Path" 454 }); 455 if (verbose) { 456 System.err.println("FOUND KEY: " + registryKey + version + " = " + path); 457 } 458 return path; 459 } catch (Exception ex) { 460 ex.printStackTrace(); 461 } 462 463 return null; 464 } 465 466 private static Attributes getJarAttributes() throws Exception { 467 String theClassFile = "Main.class"; 468 Class theClass = Main.class; 469 String classUrlString = theClass.getResource(theClassFile).toString(); 470 if (!classUrlString.startsWith("jar:file:") || classUrlString.indexOf("!") == -1){ 471 return null; 472 } 473 // Strip out the "jar:" and everything after and including the "!" 474 String urlString = classUrlString.substring(4, classUrlString.lastIndexOf("!")); 475 File jarFile = new File(new URI(urlString).getPath()); 476 String jarName = jarFile.getCanonicalPath(); 477 478 Attributes attr; 479 JarFile jf = null; 480 try { 481 jf = new JarFile(jarName); 482 Manifest mf = jf.getManifest(); 483 attr = mf.getMainAttributes(); 484 } finally { 485 if (jf != null) { 486 try { 487 jf.close(); 488 } catch (Exception ex) { 489 /* swallow the exception */ 490 } 491 } 492 } 493 return attr; 494 } 495 496 private static String decodeBase64(String inp) throws IOException { 497 return new String(Base64.getDecoder().decode(inp)); 498 } 499 500 private static String[] getAppArguments(Attributes attrs) { 501 List args = new LinkedList(); 502 503 try { 504 int idx = 1; 505 String argNamePrefix = "JavaFX-Argument-"; 506 while (attrs.getValue(argNamePrefix + idx) != null) { 507 args.add(decodeBase64(attrs.getValue(argNamePrefix + idx))); 508 idx++; 509 } 510 511 String paramNamePrefix = "JavaFX-Parameter-Name-"; 512 String paramValuePrefix = "JavaFX-Parameter-Value-"; 513 idx = 1; 514 while (attrs.getValue(paramNamePrefix + idx) != null) { 515 String k = decodeBase64(attrs.getValue(paramNamePrefix + idx)); 516 String v = null; 517 if (attrs.getValue(paramValuePrefix + idx) != null) { 518 v = decodeBase64(attrs.getValue(paramValuePrefix + idx)); 519 } 520 args.add("--" + k + "=" + (v != null ? v : "")); 521 idx++; 522 } 523 } catch (IOException ioe) { 524 System.err.println("Failed to extract application parameters"); 525 ioe.printStackTrace(); 526 } 527 528 529 return (String[]) args.toArray(new String[0]); 530 } 531 532 // Return the application class name, either from the property or from the 533 // jar file 534 private static String getAppName(Attributes attrs, boolean preloader) { 535 String propName = preloader 536 ? "javafx.preloader.class" 537 : "javafx.application.class"; 538 539 String className = System.getProperty(propName); 540 if (className != null && className.length() != 0) { 541 return className; 542 } 543 544 String appName; 545 546 //this only true in the dev environment if run out of jar 547 if (attrs == null) { 548 return "TEST"; 549 } 550 551 if (preloader) { 552 appName = (String)attrs.getValue(manifestPreloaderClass); 553 if (appName == null || appName.length() == 0) { 554 if (verbose) { 555 System.err.println("Unable to find preloader class name"); 556 } 557 return null; 558 } 559 return appName; 560 } else { 561 appName = (String)attrs.getValue(manifestAppClass); 562 if (appName == null || appName.length() == 0) { 563 System.err.println("Unable to find application class name"); 564 return null; 565 } 566 return appName; 567 } 568 } 569 570 private static Class getAppClass(String appName) { 571 try { 572 // load the user's JavaFX class but do *not* initialize! 573 if (verbose) { 574 System.err.println("Try calling Class.forName(" + appName 575 + ") using classLoader = " 576 + Thread.currentThread().getContextClassLoader()); 577 } 578 Class appClass = Class.forName(appName, false, 579 Thread.currentThread().getContextClassLoader()); 580 if (verbose) { 581 System.err.println("found class: " + appClass); 582 } 583 return appClass; 584 } catch (NoClassDefFoundError ncdfe) { 585 ncdfe.printStackTrace(); 586 errorExit("Unable to find class: " + appName); 587 } catch (ClassNotFoundException cnfe) { 588 cnfe.printStackTrace(); 589 errorExit("Unable to find class: " + appName); 590 } 591 592 return null; 593 } 594 595 //try to install webstart proxy to avoid asking user for proxy info 596 private static void tryToSetProxy() { 597 try { 598 if (attrs != null) { 599 String proxySetting = attrs.getValue(manifestAutoProxy); 600 if (proxySetting != null && !"auto".equals(proxySetting.toLowerCase())) { 601 if (verbose) { 602 System.out.println("Auto proxy detection is disabled in manifest."); 603 } 604 return; 605 } 606 } 607 608 //if explicit proxy settings are proxided we will skip autoproxy 609 //Note: we only check few most popular settings. 610 if (System.getProperty("http.proxyHost") != null 611 || System.getProperty("https.proxyHost") != null 612 || System.getProperty("ftp.proxyHost") != null 613 || System.getProperty("socksProxyHost") != null) { 614 if (verbose) { 615 System.out.println("Explicit proxy settings detected. Skip autoconfig."); 616 System.out.println(" http.proxyHost=" + System.getProperty("http.proxyHost")); 617 System.out.println(" https.proxyHost=" + System.getProperty("https.proxyHost")); 618 System.out.println(" ftp.proxyHost=" + System.getProperty("ftp.proxyHost")); 619 System.out.println(" socksProxyHost=" + System.getProperty("socksProxyHost")); 620 } 621 return; 622 } 623 if (System.getProperty("javafx.autoproxy.disable") != null) { 624 if (verbose) { 625 System.out.println("Disable autoproxy on request."); 626 } 627 return; 628 } 629 630 Class sm = Class.forName("com.sun.deploy.services.ServiceManager", 631 true, 632 Thread.currentThread().getContextClassLoader()); 633 Class params[] = {Integer.TYPE}; 634 Method setservice = sm.getDeclaredMethod("setService", params); 635 String osname = System.getProperty("os.name"); 636 637 String servicename = null; 638 if (osname.startsWith("Win")) { 639 servicename = "STANDALONE_TIGER_WIN32"; 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 }