1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.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 }