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 }