1 /*
   2  * Copyright (c) 2010, 2018, 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 javafx.application;
  27 
  28 import java.security.AccessController;
  29 import java.security.PrivilegedAction;
  30 import java.util.List;
  31 import java.util.Map;
  32 
  33 import javafx.application.Preloader.PreloaderNotification;
  34 import javafx.scene.Scene;
  35 import javafx.stage.Stage;
  36 
  37 import com.sun.javafx.application.LauncherImpl;
  38 import com.sun.javafx.application.ParametersImpl;
  39 import com.sun.javafx.application.PlatformImpl;
  40 import com.sun.javafx.css.StyleManager;
  41 
  42 /**
  43  * Application class from which JavaFX applications extend.
  44  *
  45  * <p><b>Life-cycle</b></p>
  46  * <p>
  47  * The entry point for JavaFX applications is the Application class. The
  48  * JavaFX runtime does the following, in order, whenever an application is
  49  * launched:
  50  * </p>
  51  * <ol>
  52  * <li>Starts the JavaFX runtime, if not already started
  53  * (see {@link Platform#startup(Runnable)} for more information)</li>
  54  * <li>Constructs an instance of the specified Application class</li>
  55  * <li>Calls the {@link #init} method</li>
  56  * <li>Calls the {@link #start} method</li>
  57  * <li>Waits for the application to finish, which happens when either of
  58  * the following occur:
  59  * <ul>
  60  * <li>the application calls {@link Platform#exit}</li>
  61  * <li>the last window has been closed and the {@code implicitExit}
  62  * attribute on {@code Platform} is true</li>
  63  * </ul></li>
  64  * <li>Calls the {@link #stop} method</li>
  65  * </ol>
  66  * <p>Note that the {@code start} method is abstract and must be overridden.
  67  * The {@code init} and {@code stop} methods have concrete implementations
  68  * that do nothing.</p>
  69  * <p>The {@code Application} subclass must be declared public and must have a
  70  * public no-argument constructor.</p>
  71  *
  72  * <p>Calling {@link Platform#exit} is the preferred way to explicitly terminate
  73  * a JavaFX Application. Directly calling {@link System#exit} is
  74  * an acceptable alternative, but doesn't allow the Application {@link #stop}
  75  * method to run.
  76  * </p>
  77  *
  78  * <p>A JavaFX Application should not attempt to use JavaFX after the
  79  * FX toolkit has terminated or from a ShutdownHook, that is, after the
  80  * {@link #stop} method returns or {@link System#exit} is called.
  81  * </p>
  82  *
  83  * <p><b>Deploying an Application as a Module</b></p>
  84  * <p>
  85  * If the {@code Application} subclass is in a named module then that class
  86  * must be accessible to the {@code javafx.graphics} module.
  87  * Otherwise, an exception will be thrown when the application is launched.
  88  * This means that
  89  * in addition to the class itself being declared public, the module must
  90  * {@link Module#isExported(String,Module) export}
  91  * (or {@link Module#isOpen(String,Module) open}) the containing package to
  92  * at least the {@code javafx.graphics} module.
  93  * </p>
  94  * <p>
  95  * For example, if {@code com.foo.MyApplication} is in the {@code foo.app}
  96  * module, the {@code module-info.java} might look like this:
  97  * </p>
  98 <pre>{@code module foo.app {
  99     exports com.foo to javafx.graphics;
 100 }}</pre>
 101 *
 102  * <p><b>Parameters</b></p>
 103  * <p>
 104  * Application parameters are available by calling the {@link #getParameters}
 105  * method from the {@link #init} method, or any time after the {@code init}
 106  * method has been called.
 107  * </p>
 108  *
 109  * <p><b>Threading</b></p>
 110  * <p>
 111  * JavaFX creates an application thread for running the application start
 112  * method, processing input events, and running animation timelines. Creation
 113  * of JavaFX {@link Scene} and {@link Stage} objects as well as modification of
 114  * scene graph operations to <em>live</em> objects (those objects already
 115  * attached to a scene) must be done on the JavaFX application thread.
 116  * </p>
 117  *
 118  * <p>
 119  * The Java launcher loads and initializes the specified Application class
 120  * on the JavaFX Application Thread. If there is no main method in the
 121  * Application class, or if the main method calls Application.launch(), then
 122  * an instance of the Application is then constructed on the JavaFX Application
 123  * Thread.
 124  * </p>
 125  *
 126  * <p>
 127  * The {@code init} method is called on the launcher thread, not on the
 128  * JavaFX Application Thread.
 129  * This means that an application must not construct a {@link Scene}
 130  * or a {@link Stage} in the {@code init} method.
 131  * An application may construct other JavaFX objects in the {@code init}
 132  * method.
 133  * </p>
 134  *
 135  * <p>
 136  * All the unhandled exceptions on the JavaFX application thread that occur during
 137  * event dispatching, running animation timelines, or any other code, are forwarded
 138  * to the thread's {@link java.lang.Thread.UncaughtExceptionHandler uncaught
 139  * exception handler}.
 140  * </p>
 141  *
 142  * <p><b>Example</b></p>
 143  * <p>The following example will illustrate a simple JavaFX application.</p>
 144  * <pre>{@code
 145 import javafx.application.Application;
 146 import javafx.scene.Group;
 147 import javafx.scene.Scene;
 148 import javafx.scene.shape.Circle;
 149 import javafx.stage.Stage;
 150 
 151 public class MyApp extends Application {
 152     public void start(Stage stage) {
 153         Circle circ = new Circle(40, 40, 30);
 154         Group root = new Group(circ);
 155         Scene scene = new Scene(root, 400, 300);
 156 
 157         stage.setTitle("My JavaFX Application");
 158         stage.setScene(scene);
 159         stage.show();
 160     }
 161 }
 162  * }</pre>
 163  *
 164  * <p>The above example will produce the following:</p>
 165  * <p><img src="doc-files/Application.png" alt="A black circle in the top left
 166  * corner of scene"></p>
 167  *
 168  * @see Platform
 169  *
 170  * @since JavaFX 2.0
 171  */
 172 public abstract class Application {
 173     /**
 174      * Constant for user agent stylesheet for the "Caspian" theme. Caspian
 175      * is the theme that shipped as default in JavaFX 2.x.
 176      * @since JavaFX 8.0
 177      */
 178     public static final String STYLESHEET_CASPIAN = "CASPIAN";
 179     /**
 180      * Constant for user agent stylesheet for the "Modena" theme. Modena
 181      * is the default theme for JavaFX 8.x.
 182      * @since JavaFX 8.0
 183      */
 184     public static final String STYLESHEET_MODENA = "MODENA";
 185 
 186     /**
 187      * Launch a standalone application. This method is typically called
 188      * from the main method(). It must not be called more than once or an
 189      * exception will be thrown.
 190      *
 191      * <p>
 192      * The launch method does not return until the application has exited,
 193      * either via a call to Platform.exit or all of the application windows
 194      * have been closed.
 195      * The class specified by the {@code appClass} argument must be
 196      * a public subclass of {@code Application}
 197      * with a public no-argument constructor, in a package that is
 198      * {@link Module#isExported(String,Module) exported}
 199      * (or {@link Module#isOpen(String,Module) open}) to at least the
 200      * {@code javafx.graphics} module, or a RuntimeException will be thrown.
 201      *
 202      * <p>
 203      * Typical usage is:
 204      * <pre>
 205      *     public static void main(String[] args) {
 206      *         Application.launch(MyApp.class, args);
 207      *     }
 208      * </pre>
 209      * where <code>MyApp</code> is a subclass of Application.
 210      *
 211      * @param appClass the application class that is constructed and executed
 212      *        by the launcher.
 213      * @param args the command line arguments passed to the application.
 214      *             An application may get these parameters using the
 215      *             {@link #getParameters()} method.
 216      *
 217      * @throws IllegalStateException if this method is called more than once.
 218      * @throws IllegalArgumentException if <code>appClass</code> is not a
 219      *         subclass of <code>Application</code>.
 220      * @throws RuntimeException if there is an error launching the
 221      * JavaFX runtime, or if the application class cannot be constructed
 222      * (e.g., if the class is not public or is not in an exported package), or
 223      * if an Exception or Error is thrown by the Application constructor, init
 224      * method, start method, or stop method.
 225      */
 226     public static void launch(Class<? extends Application> appClass, String... args) {
 227         LauncherImpl.launchApplication(appClass, args);
 228     }
 229 
 230     /**
 231      * Launch a standalone application. This method is typically called
 232      * from the main method(). It must not be called more than once or an
 233      * exception will be thrown.
 234      * This is equivalent to {@code launch(TheClass.class, args)} where
 235      * {@code TheClass} is the
 236      * immediately enclosing class of the method that called launch.
 237      * It must be a public subclass of {@code Application}
 238      * with a public no-argument constructor, in a package that is
 239      * {@link Module#isExported(String,Module) exported}
 240      * (or {@link Module#isOpen(String,Module) open}) to at least the
 241      * {@code javafx.graphics} module, or a RuntimeException will be thrown.
 242      *
 243      * <p>
 244      * The launch method does not return until the application has exited,
 245      * either via a call to Platform.exit or all of the application windows
 246      * have been closed.
 247      *
 248      * <p>
 249      * Typical usage is:
 250      * <pre>
 251      *     public static void main(String[] args) {
 252      *         Application.launch(args);
 253      *     }
 254      * </pre>
 255      *
 256      * @param args the command line arguments passed to the application.
 257      *             An application may get these parameters using the
 258      *             {@link #getParameters()} method.
 259      *
 260      * @throws IllegalStateException if this method is called more than once.
 261      * @throws RuntimeException if there is an error launching the
 262      * JavaFX runtime, or if the application class cannot be constructed
 263      * (e.g., if the class is not public or is not in an exported package), or
 264      * if an Exception or Error is thrown by the Application constructor, init
 265      * method, start method, or stop method.
 266      */
 267     public static void launch(String... args) {
 268         // Figure out the right class to call
 269         StackTraceElement[] cause = Thread.currentThread().getStackTrace();
 270 
 271         boolean foundThisMethod = false;
 272         String callingClassName = null;
 273         for (StackTraceElement se : cause) {
 274             // Skip entries until we get to the entry for this class
 275             String className = se.getClassName();
 276             String methodName = se.getMethodName();
 277             if (foundThisMethod) {
 278                 callingClassName = className;
 279                 break;
 280             } else if (Application.class.getName().equals(className)
 281                     && "launch".equals(methodName)) {
 282 
 283                 foundThisMethod = true;
 284             }
 285         }
 286 
 287         if (callingClassName == null) {
 288             throw new RuntimeException("Error: unable to determine Application class");
 289         }
 290 
 291         try {
 292             Class theClass = Class.forName(callingClassName, false,
 293                                Thread.currentThread().getContextClassLoader());
 294             if (Application.class.isAssignableFrom(theClass)) {
 295                 Class<? extends Application> appClass = theClass;
 296                 LauncherImpl.launchApplication(appClass, args);
 297             } else {
 298                 throw new RuntimeException("Error: " + theClass
 299                         + " is not a subclass of javafx.application.Application");
 300             }
 301         } catch (RuntimeException ex) {
 302             throw ex;
 303         } catch (Exception ex) {
 304             throw new RuntimeException(ex);
 305         }
 306     }
 307 
 308     /**
 309      * Constructs a new {@code Application} instance.
 310      */
 311     public Application() {
 312     }
 313 
 314     /**
 315      * The application initialization method. This method is called immediately
 316      * after the Application class is loaded and constructed. An application may
 317      * override this method to perform initialization prior to the actual starting
 318      * of the application.
 319      *
 320      * <p>
 321      * The implementation of this method provided by the Application class does nothing.
 322      * </p>
 323      *
 324      * <p>
 325      * NOTE: This method is not called on the JavaFX Application Thread. An
 326      * application must not construct a Scene or a Stage in this
 327      * method.
 328      * An application may construct other JavaFX objects in this method.
 329      * </p>
 330      * @throws java.lang.Exception if something goes wrong
 331      */
 332     public void init() throws Exception {
 333     }
 334 
 335     /**
 336      * The main entry point for all JavaFX applications.
 337      * The start method is called after the init method has returned,
 338      * and after the system is ready for the application to begin running.
 339      *
 340      * <p>
 341      * NOTE: This method is called on the JavaFX Application Thread.
 342      * </p>
 343      *
 344      * @param primaryStage the primary stage for this application, onto which
 345      * the application scene can be set.
 346      * Applications may create other stages, if needed, but they will not be
 347      * primary stages.
 348      * @throws java.lang.Exception if something goes wrong
 349      */
 350     public abstract void start(Stage primaryStage) throws Exception;
 351 
 352     /**
 353      * This method is called when the application should stop, and provides a
 354      * convenient place to prepare for application exit and destroy resources.
 355      *
 356      * <p>
 357      * The implementation of this method provided by the Application class does nothing.
 358      * </p>
 359      *
 360      * <p>
 361      * NOTE: This method is called on the JavaFX Application Thread.
 362      * </p>
 363      * @throws java.lang.Exception if something goes wrong
 364      */
 365     public void stop() throws Exception {
 366     }
 367 
 368     private HostServices hostServices = null;
 369 
 370     /**
 371      * Gets the HostServices provider for this application. This provides
 372      * the ability to get the code base and document base for this application,
 373      * and to show a web page in a browser.
 374      *
 375      * @return the HostServices provider
 376      */
 377     public final HostServices getHostServices() {
 378         synchronized (this) {
 379             if (hostServices == null) {
 380                 hostServices = new HostServices(this);
 381             }
 382             return hostServices;
 383         }
 384     }
 385 
 386     /**
 387      * Retrieves the parameters for this Application, including any arguments
 388      * passed on the command line.
 389      *
 390      * <p>
 391      * NOTE: this method should not be called from the Application constructor,
 392      * as it will return null. It may be called in the init() method or any
 393      * time after that.
 394      * </p>
 395      *
 396      * @return the parameters for this Application, or null if called from the
 397      * constructor.
 398      */
 399     public final Parameters getParameters() {
 400         return ParametersImpl.getParameters(this);
 401     }
 402 
 403     /**
 404      * Notifies the preloader with an application-generated notification.
 405      * Application code calls this method with a PreloaderNotification that is
 406      * delivered to the
 407      * {@link Preloader#handleApplicationNotification
 408      * Preloader.handleApplicationNotification} method.
 409      * This is primarily useful for cases where an application wants the
 410      * preloader to show progress during a long application initialization
 411      * step.
 412      *
 413      * <p>
 414      * NOTE: the notification will be delivered only to the preloader's
 415      * handleApplicationNotification() method; this means, for example, that
 416      * if this method is called with a ProgressNotification, that notification
 417      * will not be delivered to the {@link Preloader#handleProgressNotification
 418      * Preloader.handleProgressNotification}
 419      * method.
 420      * </p>
 421      *
 422      * @param info the application-generated preloader notification
 423      */
 424     public final void notifyPreloader(PreloaderNotification info) {
 425         LauncherImpl.notifyPreloader(this, info);
 426     }
 427 
 428     /**
 429      * Encapsulates the set of parameters for an application. This includes
 430      * arguments passed on the command line.
 431      *
 432      * <p>
 433      * Note that the application and the preloader both get the same set
 434      * of parameters for a given run of an application.
 435      * </p>
 436      * @since JavaFX 2.0
 437      */
 438     public static abstract class Parameters {
 439 
 440         /**
 441          * Constructs a new {@code Parameters} instance.
 442          */
 443         public Parameters() {
 444         }
 445 
 446         /**
 447          * Retrieves a read-only list of the raw arguments. This list
 448          * may be empty, but is never null. In the case of a standalone
 449          * application, it is the ordered list of arguments specified on the
 450          * command line.
 451          * For
 452          * named parameters, each &lt;name,value&gt; pair is represented as
 453          * a single argument of the form: "--name=value".
 454          *
 455          * @return a read-only list of raw application arguments
 456          */
 457         public abstract List<String> getRaw();
 458 
 459         /**
 460          * Retrieves a read-only list of the unnamed parameters. This list
 461          * may be empty, but is never null. The named parameters, that is
 462          * the parameters that are represented as &lt;name,value&gt; pairs, are
 463          * filtered out.
 464          *
 465          * @return a read-only list of unnamed parameters.
 466          */
 467         public abstract List<String> getUnnamed();
 468 
 469         /**
 470          * Retrieves a read-only map of the named parameters. It may be
 471          * empty, but is never null.
 472          * Named parameters include any command line
 473          * arguments of the form: "--name=value".
 474          *
 475          * @return a read-only map of named parameters.
 476          */
 477         public abstract Map<String, String> getNamed();
 478 
 479     }
 480 
 481     private static String userAgentStylesheet = null;
 482 
 483     /**
 484      * Get the user agent stylesheet used by the whole application. This is
 485      * used to provide default styling for all ui controls and other nodes.
 486      * A value of null means the platform default stylesheet is being used.
 487      * <p>
 488      * NOTE: This method must be called on the JavaFX Application Thread.
 489      * </p>
 490      *
 491      * @return The URL to the stylesheet as a String.
 492      * @since JavaFX 8.0
 493      */
 494     public static String getUserAgentStylesheet() {
 495         return userAgentStylesheet;
 496     }
 497 
 498     /**
 499      * Set the user agent stylesheet used by the whole application. This is used
 500      * to provide default styling for all ui controls and other nodes. Each
 501      * release of JavaFX may have a new default value for this so if you need
 502      * to guarantee consistency you will need to call this method and choose
 503      * what default you would like for your application. A value of null will
 504      * restore the platform default stylesheet. This property can also be set
 505      * on the command line with {@code -Djavafx.userAgentStylesheetUrl=[URL]}
 506      * Setting it on the command line overrides anything set using this method
 507      * in code.
 508      * <p>
 509      * NOTE: This method must be called on the JavaFX Application Thread.
 510      * </p>
 511      *
 512      *
 513      * @param url The URL to the stylesheet as a String.
 514      * @since JavaFX 8.0
 515      */
 516     public static void setUserAgentStylesheet(String url) {
 517         userAgentStylesheet = url;
 518         if (url == null) {
 519             PlatformImpl.setDefaultPlatformUserAgentStylesheet();
 520         } else {
 521             PlatformImpl.setPlatformUserAgentStylesheet(url);
 522         }
 523     }
 524 }