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