1 /*
   2  * Copyright (c) 2010, 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.sun.javafx.application;
  27 
  28 import javafx.application.Application;
  29 import javafx.application.Preloader;
  30 import javafx.application.Preloader.ErrorNotification;
  31 import javafx.application.Preloader.PreloaderNotification;
  32 import javafx.application.Preloader.StateChangeNotification;
  33 import javafx.stage.Stage;
  34 import java.io.File;
  35 import java.io.IOException;
  36 import java.lang.reflect.Constructor;
  37 import java.lang.reflect.InvocationTargetException;
  38 import java.lang.reflect.Layer;
  39 import java.lang.reflect.Method;
  40 import java.lang.reflect.Module;
  41 import java.net.MalformedURLException;
  42 import java.net.URL;
  43 import java.net.URLClassLoader;
  44 import java.security.AccessController;
  45 import java.security.PrivilegedAction;
  46 import java.text.Normalizer;
  47 import java.util.ArrayList;
  48 import java.util.LinkedList;
  49 import java.util.List;
  50 import java.util.concurrent.CountDownLatch;
  51 import java.util.concurrent.atomic.AtomicBoolean;
  52 import java.util.concurrent.atomic.AtomicReference;
  53 import java.util.jar.Attributes;
  54 import java.util.jar.JarFile;
  55 import java.util.jar.Manifest;
  56 import java.util.Base64;
  57 import java.util.Optional;
  58 import com.sun.javafx.jmx.MXExtension;
  59 import com.sun.javafx.runtime.SystemProperties;
  60 import com.sun.javafx.stage.StageHelper;
  61 
  62 
  63 public class LauncherImpl {
  64     /**
  65      * When passed as launchMode to launchApplication, tells the method that
  66      * launchName is the name of the JavaFX application class to launch.
  67      */
  68     public static final String LAUNCH_MODE_CLASS = "LM_CLASS";
  69 
  70     /**
  71      * When passed as launchMode to launchApplication, tells the method that
  72      * launchName is a path to a JavaFX application jar file to be launched.
  73      */
  74     public static final String LAUNCH_MODE_JAR = "LM_JAR";
  75 
  76     /**
  77      * When passed as launchMode to launchApplication, tells the method that
  78      * launchName is the name of the JavaFX application class within a module
  79      * to be launched. Either the class name will be provided or the main class
  80      * will be defined in the module's descriptor.
  81      */
  82     public static final String LAUNCH_MODE_MODULE = "LM_MODULE";
  83 
  84     // set to true to debug launch issues from Java launcher
  85     private static final boolean trace = false;
  86 
  87     // set system property javafx.verbose to true to make the launcher noisy
  88     private static final boolean verbose;
  89 
  90     private static final String MF_MAIN_CLASS = "Main-Class";
  91     private static final String MF_JAVAFX_MAIN = "JavaFX-Application-Class";
  92     private static final String MF_JAVAFX_PRELOADER = "JavaFX-Preloader-Class";
  93     private static final String MF_JAVAFX_CLASS_PATH = "JavaFX-Class-Path";
  94     private static final String MF_JAVAFX_FEATURE_PROXY = "JavaFX-Feature-Proxy";
  95     private static final String MF_JAVAFX_ARGUMENT_PREFIX = "JavaFX-Argument-";
  96     private static final String MF_JAVAFX_PARAMETER_NAME_PREFIX = "JavaFX-Parameter-Name-";
  97     private static final String MF_JAVAFX_PARAMETER_VALUE_PREFIX = "JavaFX-Parameter-Value-";
  98 
  99     // Set to true to simulate a slow download progress
 100     private static final boolean simulateSlowProgress = false;
 101 
 102     // Ensure that launchApplication method is only called once
 103     private static AtomicBoolean launchCalled = new AtomicBoolean(false);
 104 
 105     // Flag indicating that the toolkit has been started
 106     private static final AtomicBoolean toolkitStarted = new AtomicBoolean(false);
 107 
 108     // Exception found during launching
 109     private static volatile RuntimeException launchException = null;
 110 
 111     // The current preloader, used for notification in the standalone
 112     // launcher mode
 113     private static Preloader currentPreloader = null;
 114 
 115     // Saved preloader class from the launchApplicationWithArgs method (called
 116     // from the Java 8 launcher). It is used in the case where we call main,
 117     // which is turn calls into launchApplication.
 118     private static Class<? extends Preloader> savedPreloaderClass = null;
 119 
 120     // The following is used to determine whether the main() method
 121     // has set the CCL in the case where main is called after the FX toolkit
 122     // is started.
 123     private static ClassLoader savedMainCcl = null;
 124 
 125     static {
 126         verbose = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
 127                 Boolean.getBoolean("javafx.verbose"));
 128     }
 129 
 130     /**
 131      * This method is called by the Application.launch method.
 132      * It must not be called more than once or an exception will be thrown.
 133      *
 134      * Note that it is always called on a thread other than the FX application
 135      * thread, since that thread is only created at startup.
 136      *
 137      * @param appClass application class
 138      * @param args command line arguments
 139      */
 140     @SuppressWarnings("unchecked")
 141     public static void launchApplication(final Class<? extends Application> appClass,
 142             final String[] args) {
 143 
 144         Class<? extends Preloader> preloaderClass = savedPreloaderClass;
 145 
 146         if (preloaderClass == null) {
 147             String preloaderByProperty = AccessController.doPrivileged((PrivilegedAction<String>) () ->
 148                     System.getProperty("javafx.preloader"));
 149             if (preloaderByProperty != null) {
 150                 try {
 151                     preloaderClass = (Class<? extends Preloader>) Class.forName(preloaderByProperty,
 152                             false, appClass.getClassLoader());
 153                 } catch (Exception e) {
 154                     System.err.printf("Could not load preloader class '" + preloaderByProperty +
 155                             "', continuing without preloader.");
 156                     e.printStackTrace();
 157                 }
 158             }
 159         }
 160 
 161         launchApplication(appClass, preloaderClass, args);
 162     }
 163 
 164     /**
 165      * This method is called by the standalone launcher.
 166      * It must not be called more than once or an exception will be thrown.
 167      *
 168      * Note that it is always called on a thread other than the FX application
 169      * thread, since that thread is only created at startup.
 170      *
 171      * @param appClass application class
 172      * @param preloaderClass preloader class, may be null
 173      * @param args command line arguments
 174      */
 175     public static void launchApplication(final Class<? extends Application> appClass,
 176             final Class<? extends Preloader> preloaderClass,
 177             final String[] args) {
 178 
 179         if (launchCalled.getAndSet(true)) {
 180             throw new IllegalStateException("Application launch must not be called more than once");
 181         }
 182 
 183         if (! Application.class.isAssignableFrom(appClass)) {
 184             throw new IllegalArgumentException("Error: " + appClass.getName()
 185                     + " is not a subclass of javafx.application.Application");
 186         }
 187 
 188         if (preloaderClass != null && ! Preloader.class.isAssignableFrom(preloaderClass)) {
 189             throw new IllegalArgumentException("Error: " + preloaderClass.getName()
 190                     + " is not a subclass of javafx.application.Preloader");
 191         }
 192 
 193 //        System.err.println("launch standalone app: preloader class = "
 194 //                + preloaderClass);
 195 
 196         // Create a new Launcher thread and then wait for that thread to finish
 197         final CountDownLatch launchLatch = new CountDownLatch(1);
 198         Thread launcherThread = new Thread(() -> {
 199             try {
 200                 launchApplication1(appClass, preloaderClass, args);
 201             } catch (RuntimeException rte) {
 202                 launchException = rte;
 203             } catch (Exception ex) {
 204                 launchException =
 205                     new RuntimeException("Application launch exception", ex);
 206             } catch (Error err) {
 207                 launchException =
 208                     new RuntimeException("Application launch error", err);
 209             } finally {
 210                 launchLatch.countDown();
 211             }
 212         });
 213         launcherThread.setName("JavaFX-Launcher");
 214         launcherThread.start();
 215 
 216         // Wait for FX launcher thread to finish before returning to user
 217         try {
 218             launchLatch.await();
 219         } catch (InterruptedException ex) {
 220             throw new RuntimeException("Unexpected exception: ", ex);
 221         }
 222 
 223         if (launchException != null) {
 224             throw launchException;
 225         }
 226     }
 227 
 228     /**
 229      * This method is called by the Java launcher. This allows us to be launched
 230      * directly from the command line via "java -jar fxapp.jar",
 231      * "java -cp path some.fx.App", or "java -m module/some.fx.App". The launchMode
 232      * argument must be one of "LM_CLASS", "LM_JAR", or "LM_MODULE" or execution will
 233      * abort with an error.
 234      *
 235      * @param launchName The path to a jar file, the application class name to launch,
 236      * or the module and optional class name to launch
 237      * @param launchMode The method of launching the application, one of LM_JAR,
 238      * LM_CLASS, or LM_MODULE
 239      * @param args Application arguments from the command line
 240      */
 241     public static void launchApplication(final String launchName,
 242             final String launchMode,
 243             final String[] args) {
 244 
 245         if (verbose) {
 246             System.err.println("JavaFX launchApplication method: launchMode="
 247                     + launchMode);
 248         }
 249 
 250         /*
 251          * For now, just open the jar and get JavaFX-Application-Class and
 252          * JavaFX-Preloader and pass them to launchApplication. In the future
 253          * we'll need to load requested jar files and set up the proxy
 254          */
 255         String mainClassName = null;
 256         String preloaderClassName = null;
 257         String[] appArgs = args;
 258         ClassLoader appLoader = null;
 259         Module mainModule = null;
 260 
 261         if (launchMode.equals(LAUNCH_MODE_JAR)) {
 262             Attributes jarAttrs = getJarAttributes(launchName);
 263             if (jarAttrs == null) {
 264                 abort(null, "Can't get manifest attributes from jar");
 265             }
 266 
 267             // If we ever need to check JavaFX-Version, do that here...
 268 
 269             // Support JavaFX-Class-Path, but warn that it's deprecated if used
 270             String fxClassPath = jarAttrs.getValue(MF_JAVAFX_CLASS_PATH);
 271             if (fxClassPath != null) {
 272                 if (fxClassPath.trim().length() == 0) {
 273                     fxClassPath = null;
 274                 } else {
 275                     if (verbose) {
 276                         System.err.println("WARNING: Application jar uses deprecated JavaFX-Class-Path attribute."
 277                                +" Please use Class-Path instead.");
 278                     }
 279 
 280                     /*
 281                      * create a new ClassLoader to pull in the requested jar files
 282                      * OK if it returns null, that just means we didn't need to load
 283                      * anything
 284                      */
 285                     appLoader = setupJavaFXClassLoader(new File(launchName), fxClassPath);
 286                 }
 287             }
 288 
 289             // Support JavaFX-Feature-Proxy (only supported setting is 'auto', anything else is ignored)
 290             String proxySetting = jarAttrs.getValue(MF_JAVAFX_FEATURE_PROXY);
 291             if (proxySetting != null && "auto".equals(proxySetting.toLowerCase())) {
 292                 trySetAutoProxy();
 293             }
 294 
 295             // process arguments and parameters if no args have been passed by the launcher
 296             if (args.length == 0) {
 297                 appArgs = getAppArguments(jarAttrs);
 298             }
 299 
 300             // grab JavaFX-Application-Class
 301             mainClassName = jarAttrs.getValue(MF_JAVAFX_MAIN);
 302             if (mainClassName == null) {
 303                 // fall back on Main-Class if no JAC
 304                 mainClassName = jarAttrs.getValue(MF_MAIN_CLASS);
 305                 if (mainClassName == null) {
 306                     // Should not happen as the launcher enforces the presence of Main-Class
 307                     abort(null, "JavaFX jar manifest requires a valid JavaFX-Appliation-Class or Main-Class entry");
 308                 }
 309             }
 310             mainClassName = mainClassName.trim();
 311 
 312             // grab JavaFX-Preloader-Class
 313             preloaderClassName = jarAttrs.getValue(MF_JAVAFX_PRELOADER);
 314             if (preloaderClassName != null) {
 315                 preloaderClassName = preloaderClassName.trim();
 316             }
 317         } else if (launchMode.equals(LAUNCH_MODE_CLASS)) {
 318             mainClassName = launchName;
 319         } else if (launchMode.equals(LAUNCH_MODE_MODULE)) {
 320             // This is largely copied from java.base/sun.launcher.LauncherHelper
 321             int i = launchName.indexOf('/');
 322             String moduleName;
 323             if (i == -1) {
 324                 moduleName = launchName;
 325                 mainClassName = null;
 326             } else {
 327                 moduleName = launchName.substring(0, i);
 328                 mainClassName = launchName.substring(i+1);
 329             }
 330 
 331             // main module is in the boot layer
 332             Layer layer = Layer.boot();
 333             Optional<Module> om = layer.findModule(moduleName);
 334             if (!om.isPresent()) {
 335                 // should not happen
 336                 throw new InternalError("Module " + moduleName + " not in boot Layer");
 337             }
 338             mainModule = om.get();
 339 
 340             // get main class from module descriptor
 341             if (mainClassName == null) {
 342                 Optional<String> omc = mainModule.getDescriptor().mainClass();
 343                 if (!omc.isPresent()) {
 344                     abort(null, "Module %1$s does not have a MainClass attribute, use -m <module>/<main-class>",
 345                             moduleName);
 346                 }
 347                 mainClassName = omc.get();
 348             }
 349         } else {
 350             abort(new IllegalArgumentException(
 351                     "The launchMode argument must be one of LM_CLASS, LM_JAR or LM_MODULE"),
 352                     "Invalid launch mode: %1$s", launchMode);
 353         }
 354 
 355         // fall back if no MF_JAVAFX_PRELOADER attribute, or not launching using -jar
 356         if (preloaderClassName == null) {
 357             preloaderClassName = System.getProperty("javafx.preloader");
 358         }
 359 
 360         if (mainClassName == null) {
 361             abort(null, "No main JavaFX class to launch");
 362         }
 363 
 364         // check if we have to load through a custom classloader
 365         if (appLoader != null) {
 366             try {
 367                 // reload this class through the app classloader
 368                 Class<?> launcherClass = appLoader.loadClass(LauncherImpl.class.getName());
 369 
 370                 // then invoke the second part of this launcher using reflection
 371                 Method lawa = launcherClass.getMethod("launchApplicationWithArgs",
 372                         new Class[] { Module.class, String.class, String.class, (new String[0]).getClass()});
 373 
 374                 // set the thread context class loader before we continue, or it won't load properly
 375                 Thread.currentThread().setContextClassLoader(appLoader);
 376                 lawa.invoke(null, new Object[] {null, mainClassName, preloaderClassName, appArgs});
 377             } catch (Exception e) {
 378                 abort(e, "Exception while launching application");
 379             }
 380         } else {
 381             launchApplicationWithArgs(mainModule, mainClassName, preloaderClassName, appArgs);
 382         }
 383     }
 384 
 385     // wrapper for Class.forName that handles cases where diacritical marks in the name
 386     // cause the class to not be loaded, also largely copied from LauncherHelper.java
 387     // this method returns null if the class cannot be loaded
 388     private static Class<?> loadClass(final Module mainModule, final String className) {
 389         Class<?> clz = null;
 390         final ClassLoader loader = Thread.currentThread().getContextClassLoader();
 391 
 392         // loader is ignored for modular mode
 393         // the only time we need to use a separate loader is LM_JAR with
 394         // a MF_JAVAFX_CLASS_PATH attribute which is deprecated
 395 
 396         if (mainModule != null) {
 397             clz = Class.forName(mainModule, className);
 398         } else {
 399             try {
 400                 clz = Class.forName(className, true, loader);
 401             } catch (ClassNotFoundException | NoClassDefFoundError cnfe) {}
 402         }
 403 
 404         if (clz == null && System.getProperty("os.name", "").contains("OS X")
 405                     && Normalizer.isNormalized(className, Normalizer.Form.NFD)) {
 406             // macOS may have decomposed diacritical marks in mainClassName
 407             // recompose them and try again
 408             String cn = Normalizer.normalize(className, Normalizer.Form.NFC);
 409 
 410             if (mainModule != null) {
 411                 clz = Class.forName(mainModule, cn);
 412             } else {
 413                 try {
 414                     clz = Class.forName(cn, true, loader);
 415                 } catch (ClassNotFoundException | NoClassDefFoundError cnfe) {}
 416             }
 417         }
 418 
 419         return clz;
 420     }
 421 
 422     // Must be public since we could be called from a different class loader
 423     public static void launchApplicationWithArgs(final Module mainModule,
 424             final String mainClassName,
 425             final String preloaderClassName, String[] args) {
 426         try {
 427             startToolkit();
 428         } catch (InterruptedException ex) {
 429             abort(ex, "Toolkit initialization error", mainClassName);
 430         }
 431 
 432         Class<? extends Application> appClass;
 433         Class<? extends Preloader> preClass = null;
 434         Class<?> tempAppClass = null;
 435 
 436         final AtomicReference<Class<?>> tmpClassRef = new AtomicReference<>();
 437         final AtomicReference<Class<? extends Preloader>> preClassRef = new AtomicReference<>();
 438         PlatformImpl.runAndWait(() -> {
 439             Class<?> clz = loadClass(mainModule, mainClassName);
 440             if (clz == null) {
 441                 if (mainModule != null) {
 442                     abort(null, "Missing JavaFX application class %1$s in module %2$s",
 443                             mainClassName, mainModule.getName());
 444                 } else {
 445                     abort(null, "Missing JavaFX application class %1$s", mainClassName);
 446                 }
 447             }
 448 
 449             tmpClassRef.set(clz);
 450 
 451             if (preloaderClassName != null) {
 452                 // TODO: modular preloader?
 453                 clz = loadClass(null, preloaderClassName);
 454                 if (clz == null) {
 455                     abort(null, "Missing JavaFX preloader class %1$s", preloaderClassName);
 456                 }
 457 
 458                 if (!Preloader.class.isAssignableFrom(clz)) {
 459                     abort(null, "JavaFX preloader class %1$s does not extend javafx.application.Preloader", clz.getName());
 460                 }
 461                 preClassRef.set(clz.asSubclass(Preloader.class));
 462             }
 463         });
 464         preClass = preClassRef.get();
 465         tempAppClass = tmpClassRef.get();
 466 
 467         // Save the preloader class in a static field for later use when
 468         // main calls back into launchApplication.
 469         savedPreloaderClass = preClass;
 470 
 471         // If there is a public static void main(String[]) method then call it
 472         // otherwise just hand off to the other launchApplication method
 473 
 474         Exception theEx = null;
 475         try {
 476             Method mainMethod = tempAppClass.getMethod("main",
 477                     new Class[] { (new String[0]).getClass() });
 478             if (verbose) {
 479                 System.err.println("Calling main(String[]) method");
 480             }
 481             savedMainCcl = Thread.currentThread().getContextClassLoader();
 482             mainMethod.invoke(null, new Object[] { args });
 483             return;
 484         } catch (NoSuchMethodException | IllegalAccessException ex) {
 485             theEx = ex;
 486             savedPreloaderClass = null;
 487             if (verbose) {
 488                 System.err.println("WARNING: Cannot access application main method: " + ex);
 489             }
 490         } catch (InvocationTargetException ex) {
 491             ex.printStackTrace();
 492             abort(null, "Exception running application %1$s", tempAppClass.getName());
 493             return;
 494         }
 495 
 496         // Verify appClass extends Application
 497         if (!Application.class.isAssignableFrom(tempAppClass)) {
 498             abort(theEx, "JavaFX application class %1$s does not extend javafx.application.Application", tempAppClass.getName());
 499         }
 500         appClass = tempAppClass.asSubclass(Application.class);
 501 
 502         if (verbose) {
 503             System.err.println("Launching application directly");
 504         }
 505         launchApplication(appClass, preClass, args);
 506     }
 507 
 508     private static URL fileToURL(File file) throws IOException {
 509         return file.getCanonicalFile().toURI().toURL();
 510     }
 511 
 512     private static ClassLoader setupJavaFXClassLoader(File appJar, String fxClassPath) {
 513         try {
 514             File baseDir = appJar.getParentFile();
 515             ArrayList jcpList = new ArrayList();
 516 
 517             // Add in the jars from the JavaFX-Class-Path entry
 518             // TODO: should check current classpath for duplicate entries and ignore them
 519             String cp = fxClassPath;
 520             if (cp != null) {
 521                 // these paths are relative to baseDir, which should be the
 522                 // directory containing the app jar file
 523                 while (cp.length() > 0) {
 524                     int pathSepIdx = cp.indexOf(" ");
 525                     if (pathSepIdx < 0) {
 526                         String pathElem = cp;
 527                         File f = (baseDir == null) ?
 528                                 new File(pathElem) : new File(baseDir, pathElem);
 529                         if (f.exists()) {
 530                             jcpList.add(fileToURL(f));
 531                         } else if (verbose) {
 532                             System.err.println("Class Path entry \""+pathElem
 533                                     +"\" does not exist, ignoring");
 534                         }
 535                         break;
 536                     } else if (pathSepIdx > 0) {
 537                         String pathElem = cp.substring(0, pathSepIdx);
 538                         File f = (baseDir == null) ?
 539                                 new File(pathElem) : new File(baseDir, pathElem);
 540                         if (f.exists()) {
 541                             jcpList.add(fileToURL(f));
 542                         } else if (verbose) {
 543                             System.err.println("Class Path entry \""+pathElem
 544                                     +"\" does not exist, ignoring");
 545                         }
 546                     }
 547                     cp = cp.substring(pathSepIdx + 1);
 548                 }
 549             }
 550 
 551             // don't bother if there's nothing to add
 552             if (!jcpList.isEmpty()) {
 553                 ArrayList<URL> urlList = new ArrayList<URL>();
 554 
 555                 // prepend the existing classpath
 556                 // this will already have the app jar, so no need to worry about it
 557                 cp = System.getProperty("java.class.path");
 558                 if (cp != null) {
 559                     while (cp.length() > 0) {
 560                         int pathSepIdx = cp.indexOf(File.pathSeparatorChar);
 561                         if (pathSepIdx < 0) {
 562                             String pathElem = cp;
 563                             urlList.add(fileToURL(new File(pathElem)));
 564                             break;
 565                         } else if (pathSepIdx > 0) {
 566                             String pathElem = cp.substring(0, pathSepIdx);
 567                             urlList.add(fileToURL(new File(pathElem)));
 568                         }
 569                         cp = cp.substring(pathSepIdx + 1);
 570                     }
 571                 }
 572 
 573                 // we have to add the current code source to the new class loader, or the app won't load
 574                 URL jfxRtURL = LauncherImpl.class.getProtectionDomain().getCodeSource().getLocation();
 575                 urlList.add(jfxRtURL);
 576 
 577                 // and finally append the JavaFX-Class-Path entries
 578                 urlList.addAll(jcpList);
 579 
 580                 URL[] urls = (URL[])urlList.toArray(new URL[0]);
 581                 if (verbose) {
 582                     System.err.println("===== URL list");
 583                     for (int i = 0; i < urls.length; i++) {
 584                         System.err.println("" + urls[i]);
 585                     }
 586                     System.err.println("=====");
 587                 }
 588                 return new URLClassLoader(urls, null);
 589             }
 590         } catch (Exception ex) {
 591             if (trace) {
 592                 System.err.println("Exception creating JavaFX class loader: "+ex);
 593                 ex.printStackTrace();
 594             }
 595         }
 596         return null;
 597     }
 598 
 599     private static void trySetAutoProxy() {
 600         // if explicit proxy settings are proxided we will skip autoproxy
 601         // Note: we only check few most popular settings.
 602         if (System.getProperty("http.proxyHost") != null
 603              || System.getProperty("https.proxyHost") != null
 604              || System.getProperty("ftp.proxyHost") != null
 605              || System.getProperty("socksProxyHost") != null) {
 606            if (verbose) {
 607                System.out.println("Explicit proxy settings detected. Skip autoconfig.");
 608                System.out.println("  http.proxyHost=" + System.getProperty("http.proxyHost"));
 609                System.out.println("  https.proxyHost=" + System.getProperty("https.proxyHost"));
 610                System.out.println("  ftp.proxyHost=" + System.getProperty("ftp.proxyHost"));
 611                System.out.println("  socksProxyHost=" + System.getProperty("socksProxyHost"));
 612            }
 613            return;
 614         }
 615         if (System.getProperty("javafx.autoproxy.disable") != null) {
 616             if (verbose) {
 617                 System.out.println("Disable autoproxy on request.");
 618             }
 619             return;
 620         }
 621 
 622         try {
 623             Class sm = Class.forName("com.sun.deploy.services.ServiceManager");
 624             Class params[] = {Integer.TYPE};
 625             Method setservice = sm.getDeclaredMethod("setService", params);
 626             String osname = System.getProperty("os.name");
 627 
 628             String servicename;
 629             if (osname.startsWith("Win")) {
 630                 servicename = "STANDALONE_TIGER_WIN32";
 631             } else if (osname.contains("Mac")) {
 632                 servicename = "STANDALONE_TIGER_MACOSX";
 633             } else {
 634                 servicename = "STANDALONE_TIGER_UNIX";
 635             }
 636             Object values[] = new Object[1];
 637             Class pt = Class.forName("com.sun.deploy.services.PlatformType");
 638             values[0] = pt.getField(servicename).get(null);
 639             setservice.invoke(null, values);
 640 
 641             Class dps = Class.forName(
 642                     "com.sun.deploy.net.proxy.DeployProxySelector");
 643             Method m = dps.getDeclaredMethod("reset", new Class[0]);
 644             m.invoke(null, new Object[0]);
 645 
 646             if (verbose) {
 647                 System.out.println("Autoconfig of proxy is completed.");
 648             }
 649         } catch (Exception e) {
 650             if (verbose) {
 651                 System.err.println("Failed to autoconfig proxy due to "+e);
 652             }
 653             if (trace) {
 654                 e.printStackTrace();
 655             }
 656         }
 657     }
 658 
 659     private static String decodeBase64(String inp) throws IOException {
 660         return new String(Base64.getDecoder().decode(inp));
 661     }
 662 
 663     private static String[] getAppArguments(Attributes attrs) {
 664         List args = new LinkedList();
 665 
 666         try {
 667             int idx = 1;
 668             String argNamePrefix = MF_JAVAFX_ARGUMENT_PREFIX;
 669             while (attrs.getValue(argNamePrefix + idx) != null) {
 670                 args.add(decodeBase64(attrs.getValue(argNamePrefix + idx)));
 671                 idx++;
 672             }
 673 
 674             String paramNamePrefix = MF_JAVAFX_PARAMETER_NAME_PREFIX;
 675             String paramValuePrefix = MF_JAVAFX_PARAMETER_VALUE_PREFIX;
 676             idx = 1;
 677             while (attrs.getValue(paramNamePrefix + idx) != null) {
 678                 String k = decodeBase64(attrs.getValue(paramNamePrefix + idx));
 679                 String v = null;
 680                 if (attrs.getValue(paramValuePrefix + idx) != null) {
 681                     v = decodeBase64(attrs.getValue(paramValuePrefix + idx));
 682                 }
 683                 args.add("--" + k + "=" + (v != null ? v : ""));
 684                 idx++;
 685             }
 686         } catch (IOException ioe) {
 687             if (verbose) {
 688                 System.err.println("Failed to extract application parameters");
 689             }
 690             ioe.printStackTrace();
 691         }
 692 
 693         return (String[]) args.toArray(new String[0]);
 694     }
 695 
 696     // FIXME: needs localization, since these are presented to the user
 697     private static void abort(final Throwable cause, final String fmt, final Object... args) {
 698         String msg = String.format(fmt, args);
 699         if (msg != null) {
 700             System.err.println(msg);
 701         }
 702 
 703         if (trace) {
 704             if (cause != null) {
 705                 cause.printStackTrace();
 706             } else {
 707                 Thread.dumpStack();
 708             }
 709         }
 710         System.exit(1);
 711     }
 712 
 713     private static Attributes getJarAttributes(String jarPath) {
 714         JarFile jarFile = null;
 715         try {
 716             jarFile = new JarFile(jarPath);
 717             Manifest manifest = jarFile.getManifest();
 718             if (manifest == null) {
 719                 abort(null, "No manifest in jar file %1$s", jarPath);
 720             }
 721             return manifest.getMainAttributes();
 722         } catch (IOException ioe) {
 723             abort(ioe, "Error launching jar file %1%s", jarPath);
 724         } finally {
 725             try {
 726                 jarFile.close();
 727             } catch (IOException ioe) {}
 728         }
 729         return null;
 730     }
 731 
 732     private static void startToolkit() throws InterruptedException {
 733         if (toolkitStarted.getAndSet(true)) {
 734             return;
 735         }
 736 
 737         if (SystemProperties.isDebug()) {
 738             MXExtension.initializeIfAvailable();
 739         }
 740 
 741         final CountDownLatch startupLatch = new CountDownLatch(1);
 742 
 743         // Note, this method is called on the FX Application Thread
 744         PlatformImpl.startup(() -> startupLatch.countDown());
 745 
 746         // Wait for FX platform to start
 747         startupLatch.await();
 748     }
 749 
 750     private static volatile boolean error = false;
 751     private static volatile Throwable pConstructorError = null;
 752     private static volatile Throwable pInitError = null;
 753     private static volatile Throwable pStartError = null;
 754     private static volatile Throwable pStopError = null;
 755     private static volatile Throwable constructorError = null;
 756     private static volatile Throwable initError = null;
 757     private static volatile Throwable startError = null;
 758     private static volatile Throwable stopError = null;
 759 
 760     private static void launchApplication1(final Class<? extends Application> appClass,
 761             final Class<? extends Preloader> preloaderClass,
 762             final String[] args) throws Exception {
 763 
 764         startToolkit();
 765 
 766         if (savedMainCcl != null) {
 767             /*
 768              * The toolkit was already started by the java launcher, and the
 769              * main method of the application class was called. Check to see
 770              * whether the CCL has been changed. If so, then we need
 771              * to pass the context class loader to the FX app thread so that it
 772              * correctly picks up the current setting.
 773              */
 774             final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
 775             if (ccl != null && ccl != savedMainCcl) {
 776                 PlatformImpl.runLater(() -> {
 777                     Thread.currentThread().setContextClassLoader(ccl);
 778                 });
 779             }
 780         }
 781 
 782         final AtomicBoolean pStartCalled = new AtomicBoolean(false);
 783         final AtomicBoolean startCalled = new AtomicBoolean(false);
 784         final AtomicBoolean exitCalled = new AtomicBoolean(false);
 785         final AtomicBoolean pExitCalled = new AtomicBoolean(false);
 786         final CountDownLatch shutdownLatch = new CountDownLatch(1);
 787         final CountDownLatch pShutdownLatch = new CountDownLatch(1);
 788 
 789         final PlatformImpl.FinishListener listener = new PlatformImpl.FinishListener() {
 790             @Override public void idle(boolean implicitExit) {
 791                 if (!implicitExit) {
 792                     return;
 793                 }
 794 
 795 //                System.err.println("JavaFX Launcher: system is idle");
 796                 if (startCalled.get()) {
 797                     shutdownLatch.countDown();
 798                 } else if (pStartCalled.get()) {
 799                     pShutdownLatch.countDown();
 800                 }
 801             }
 802 
 803             @Override public void exitCalled() {
 804 //                System.err.println("JavaFX Launcher: received exit notification");
 805                 exitCalled.set(true);
 806                 shutdownLatch.countDown();
 807             }
 808         };
 809         PlatformImpl.addListener(listener);
 810 
 811         try {
 812             final AtomicReference<Preloader> pldr = new AtomicReference<>();
 813             if (preloaderClass != null) {
 814                 // Construct an instance of the preloader on the FX thread, then
 815                 // call its init method on this (launcher) thread. Then call
 816                 // the start method on the FX thread.
 817                 PlatformImpl.runAndWait(() -> {
 818                     try {
 819                         Constructor<? extends Preloader> c = preloaderClass.getConstructor();
 820                         pldr.set(c.newInstance());
 821                         // Set startup parameters
 822                         ParametersImpl.registerParameters(pldr.get(), new ParametersImpl(args));
 823                     } catch (Throwable t) {
 824                         System.err.println("Exception in Preloader constructor");
 825                         pConstructorError = t;
 826                         error = true;
 827                     }
 828                 });
 829             }
 830             currentPreloader = pldr.get();
 831 
 832             // Call init method unless exit called or error detected
 833             if (currentPreloader != null && !error && !exitCalled.get()) {
 834                 try {
 835                     // Call the application init method (on the Launcher thread)
 836                     currentPreloader.init();
 837                 } catch (Throwable t) {
 838                     System.err.println("Exception in Preloader init method");
 839                     pInitError = t;
 840                     error = true;
 841                 }
 842             }
 843 
 844             // Call start method unless exit called or error detected
 845             if (currentPreloader != null && !error && !exitCalled.get()) {
 846                 // Call the application start method on FX thread
 847                 PlatformImpl.runAndWait(() -> {
 848                     try {
 849                         pStartCalled.set(true);
 850 
 851                         // Create primary stage and call preloader start method
 852                         final Stage primaryStage = new Stage();
 853                         StageHelper.setPrimary(primaryStage, true);
 854                         currentPreloader.start(primaryStage);
 855                     } catch (Throwable t) {
 856                         System.err.println("Exception in Preloader start method");
 857                         pStartError = t;
 858                         error = true;
 859                     }
 860                 });
 861 
 862                 // Notify preloader of progress
 863                 if (!error && !exitCalled.get()) {
 864                     notifyProgress(currentPreloader, 0.0);
 865                 }
 866             }
 867 
 868             // Construct an instance of the application on the FX thread, then
 869             // call its init method on this (launcher) thread. Then call
 870             // the start method on the FX thread.
 871             final AtomicReference<Application> app = new AtomicReference<>();
 872             if (!error && !exitCalled.get()) {
 873                 if (currentPreloader != null) {
 874                     if (simulateSlowProgress) {
 875                         for (int i = 0; i < 100; i++) {
 876                             notifyProgress(currentPreloader, (double)i / 100.0);
 877                             Thread.sleep(10);
 878                         }
 879                     }
 880                     notifyProgress(currentPreloader, 1.0);
 881                     notifyStateChange(currentPreloader,
 882                             StateChangeNotification.Type.BEFORE_LOAD, null);
 883                 }
 884 
 885                 PlatformImpl.runAndWait(() -> {
 886                     try {
 887                         Constructor<? extends Application> c = appClass.getConstructor();
 888                         app.set(c.newInstance());
 889                         // Set startup parameters
 890                         ParametersImpl.registerParameters(app.get(), new ParametersImpl(args));
 891                         PlatformImpl.setApplicationName(appClass);
 892                     } catch (Throwable t) {
 893                         System.err.println("Exception in Application constructor");
 894                         constructorError = t;
 895                         error = true;
 896                     }
 897                 });
 898             }
 899             final Application theApp = app.get();
 900 
 901             // Call init method unless exit called or error detected
 902             if (!error && !exitCalled.get()) {
 903                 if (currentPreloader != null) {
 904                     notifyStateChange(currentPreloader,
 905                             StateChangeNotification.Type.BEFORE_INIT, theApp);
 906                 }
 907 
 908                 try {
 909                     // Call the application init method (on the Launcher thread)
 910                     theApp.init();
 911                 } catch (Throwable t) {
 912                     System.err.println("Exception in Application init method");
 913                     initError = t;
 914                     error = true;
 915                 }
 916             }
 917 
 918             // Call start method unless exit called or error detected
 919             if (!error && !exitCalled.get()) {
 920                 if (currentPreloader != null) {
 921                     notifyStateChange(currentPreloader,
 922                             StateChangeNotification.Type.BEFORE_START, theApp);
 923                 }
 924                 // Call the application start method on FX thread
 925                 PlatformImpl.runAndWait(() -> {
 926                     try {
 927                         startCalled.set(true);
 928 
 929                         // Create primary stage and call application start method
 930                         final Stage primaryStage = new Stage();
 931                         StageHelper.setPrimary(primaryStage, true);
 932                         theApp.start(primaryStage);
 933                     } catch (Throwable t) {
 934                         System.err.println("Exception in Application start method");
 935                         startError = t;
 936                         error = true;
 937                     }
 938                 });
 939             }
 940 
 941             if (!error) {
 942                 shutdownLatch.await();
 943 //                System.err.println("JavaFX Launcher: time to call stop");
 944             }
 945 
 946             // Call stop method if start was called
 947             if (startCalled.get()) {
 948                 // Call Application stop method on FX thread
 949                 PlatformImpl.runAndWait(() -> {
 950                     try {
 951                         theApp.stop();
 952                     } catch (Throwable t) {
 953                         System.err.println("Exception in Application stop method");
 954                         stopError = t;
 955                         error = true;
 956                     }
 957                 });
 958             }
 959 
 960             if (error) {
 961                 if (pConstructorError != null) {
 962                     throw new RuntimeException("Unable to construct Preloader instance: "
 963                             + appClass, pConstructorError);
 964                 } else if (pInitError != null) {
 965                     throw new RuntimeException("Exception in Preloader init method",
 966                             pInitError);
 967                 } else if(pStartError != null) {
 968                     throw new RuntimeException("Exception in Preloader start method",
 969                             pStartError);
 970                 } else if (pStopError != null) {
 971                     throw new RuntimeException("Exception in Preloader stop method",
 972                             pStopError);
 973                 } else if (constructorError != null) {
 974                     String msg = "Unable to construct Application instance: " + appClass;
 975                     if (!notifyError(msg, constructorError)) {
 976                         throw new RuntimeException(msg, constructorError);
 977                     }
 978                 } else if (initError != null) {
 979                     String msg = "Exception in Application init method";
 980                     if (!notifyError(msg, initError)) {
 981                         throw new RuntimeException(msg, initError);
 982                     }
 983                 } else if(startError != null) {
 984                     String msg = "Exception in Application start method";
 985                     if (!notifyError(msg, startError)) {
 986                         throw new RuntimeException(msg, startError);
 987                     }
 988                 } else if (stopError != null) {
 989                     String msg = "Exception in Application stop method";
 990                     if (!notifyError(msg, stopError)) {
 991                         throw new RuntimeException(msg, stopError);
 992                     }
 993                 }
 994             }
 995         } finally {
 996             PlatformImpl.removeListener(listener);
 997             // Workaround until RT-13281 is implemented
 998             // Don't call exit if we detect an error in javaws mode
 999 //            PlatformImpl.tkExit();
1000             final boolean isJavaws = System.getSecurityManager() != null;
1001             if (error && isJavaws) {
1002                 System.err.println("Workaround until RT-13281 is implemented: keep toolkit alive");
1003             } else {
1004                 PlatformImpl.tkExit();
1005             }
1006         }
1007     }
1008 
1009     private static void notifyStateChange(final Preloader preloader,
1010             final StateChangeNotification.Type type,
1011             final Application app) {
1012 
1013         PlatformImpl.runAndWait(() -> preloader.handleStateChangeNotification(
1014             new StateChangeNotification(type, app)));
1015     }
1016 
1017     private static void notifyProgress(final Preloader preloader, final double d) {
1018         PlatformImpl.runAndWait(() -> preloader.handleProgressNotification(
1019                 new Preloader.ProgressNotification(d)));
1020     }
1021 
1022     private static boolean notifyError(final String msg, final Throwable constructorError) {
1023         final AtomicBoolean result = new AtomicBoolean(false);
1024         PlatformImpl.runAndWait(() -> {
1025             if (currentPreloader != null) {
1026                 try {
1027                     ErrorNotification evt = new ErrorNotification(null, msg, constructorError);
1028                     boolean rval = currentPreloader.handleErrorNotification(evt);
1029                     result.set(rval);
1030                 } catch (Throwable t) {
1031                     t.printStackTrace();
1032                 }
1033             }
1034         });
1035 
1036         return result.get();
1037     }
1038 
1039     private static void notifyCurrentPreloader(final PreloaderNotification pe) {
1040         PlatformImpl.runAndWait(() -> {
1041             if (currentPreloader != null) {
1042                 currentPreloader.handleApplicationNotification(pe);
1043             }
1044         });
1045     }
1046 
1047     private static Method notifyMethod = null;
1048 
1049     public static void notifyPreloader(Application app, final PreloaderNotification info) {
1050         if (launchCalled.get()) {
1051             // Standalone launcher mode
1052             notifyCurrentPreloader(info);
1053             return;
1054         }
1055 
1056         synchronized (LauncherImpl.class) {
1057             if (notifyMethod == null) {
1058                 final String fxPreloaderClassName =
1059                         "com.sun.deploy.uitoolkit.impl.fx.FXPreloader";
1060                 try {
1061                     Class fxPreloaderClass = Class.forName(fxPreloaderClassName);
1062                     notifyMethod = fxPreloaderClass.getMethod(
1063                             "notifyCurrentPreloader", PreloaderNotification.class);
1064                 } catch (Exception ex) {
1065                     ex.printStackTrace();
1066                     return;
1067                 }
1068             }
1069         }
1070 
1071         try {
1072             // Call using reflection: FXPreloader.notifyCurrentPreloader(pe)
1073             notifyMethod.invoke(null, info);
1074         } catch (Exception ex) {
1075             ex.printStackTrace();
1076         }
1077     }
1078 
1079     // Not an instantiable class.
1080     private LauncherImpl() {
1081         // Should never get here.
1082         throw new InternalError();
1083     }
1084 
1085 }