1 /*
   2  * Copyright (c) 2011, 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 
  31 /**
  32  * Class that is extended to define an optional preloader for a
  33  * JavaFX Application.
  34  * An application may contain a preloader that is used
  35  * to improve the application loading experience.
  36  *
  37  * <p>
  38  * A preloader is a small application that is started
  39  * before the main application to customize the startup experience.
  40  * The preloader:
  41  * </p>
  42  * <ul>
  43  * <li>gets notification of progress of loading application resources</li>
  44  * <li>gets notification of errors</li>
  45  * <li>gets notification of application initialization and startup</li>
  46  * <li>decides when application should become visible</li>
  47  * </ul>
  48  *
  49  * <p>
  50  * The default preloader is shown on top of the application Stage, which is not
  51  * visible until the preloader is visible. The preloader need to hide itself
  52  * to make the application visible. Good practice is to do this no earlier than
  53  * right before application.start() is called, as otherwise application itself
  54  * is not visible.
  55  * </p>
  56  *
  57  * <p>
  58  * The preloader may also cooperate with the application to achieve advanced
  59  * visual effects or share data (e.g. to implement login screen).
  60  * The preloader gets a reference to the application and may pull data it
  61  * needs for cooperation from the application if the application implements
  62  * an interface that the preloader knows about and relies upon. Generally it
  63  * is not recommended to design preloaders in such a way that an application
  64  * would call them directly, as this will result in bad user experience if
  65  * the application is signed and the preloader is not.
  66  * </p>
  67  *
  68  * <p>
  69  * If the application does not specify a preloader, then the default preloader
  70  * is used. Default preloader appearance can be customized
  71  * (set of parameters is TBD).
  72  * </p>
  73  *
  74  * <p>
  75  * Custom preloader implementations should follow these rules:
  76  * </p>
  77  * <ol>
  78  *  <li>a custom preloader class should extend Preloader</li>
  79  *  <li>classes needed for preloader need to be packaged in the separate jar.</li>
  80  * </ol>
  81  *
  82  * <p>
  83  * Applications may also send custom notification to the preloader using the
  84  * {@link #notifyPreloader notifyPreloader} method. This way a preloader may
  85  * also show application initialization progress.
  86  * </p>
  87  *
  88  * <p>
  89  * Note that preloaders are subject to the same rules as other JavaFX
  90  * applications including FX threading rules. In particular, the class
  91  * constructor and init() method will be called on a non-FX thread and start()
  92  * will be executed on the FX application thread.
  93  * This also means that the application constructor/init() will run concurrently
  94  * with preloader start().
  95  * </p>
  96  *
  97  * <p>
  98  * Callbacks on preloader notification will be delivered on the FX
  99  * application thread.
 100  * </p>
 101  *
 102  * <p>
 103  * Shutdown (including when stop() is called) is TBD.
 104  * </p>
 105  * @since JavaFX 2.0
 106  */
 107 public abstract class Preloader extends Application {
 108 
 109     // Too bad this isn't already available in a Java core class
 110     private static final String lineSeparator;
 111 
 112     static {
 113         String prop = AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty("line.separator"));
 114         lineSeparator = prop != null ? prop : "\n";
 115     }
 116 
 117     /**
 118      * Indicates download progress.
 119      * This method is called by the FX runtime to indicate progress while
 120      * application resources are being loaded. It will not be called to deliver
 121      * a ProgressNotification sent to {@link #notifyPreloader notifyPreloader}.
 122      *
 123      * <p>
 124      * The implementation of this method provided by the Preloader class
 125      * does nothing.
 126      * </p>
 127      *
 128      * @param info the progress notification
 129      */
 130     public void handleProgressNotification(ProgressNotification info) {
 131     }
 132 
 133     /**
 134      * Indicates a change in application state.
 135      * This method is called by the FX runtime as part of the
 136      * application life-cycle.
 137      *
 138      * <p>
 139      * The implementation of this method provided by the Preloader class
 140      * does nothing.
 141      * </p>
 142      *
 143      * @param info the state change notification
 144      */
 145     public void handleStateChangeNotification(StateChangeNotification info) {
 146     }
 147 
 148     /**
 149      * Indicates an application-generated notification.
 150      * This method is called by the FX runtime to deliver a notification sent
 151      * via {@link #notifyPreloader notifyPreloader}.
 152      *
 153      * <p>
 154      * Applications should not call this method directly, but should use
 155      * {@link #notifyPreloader notifyPreloader} instead to avoid mixed code dialog issues.
 156      * </p>
 157      *
 158      * <p>
 159      * The implementation of this method provided by the Preloader class
 160      * does nothing.
 161      * </p>
 162      *
 163      * @param info the application-generated notification
 164      */
 165     public void handleApplicationNotification(PreloaderNotification info) {
 166     }
 167 
 168     /**
 169      * Called when an error occurs.
 170      *
 171      * <p>
 172      * The implementation of this method provided by the Preloader class
 173      * returns false, indicating that the default error handler should
 174      * show the message to the user.
 175      * </p>
 176      *
 177      * @param info the error notification describing the cause of this error
 178      *
 179      * @return true if error was shown to the user by preloader and no
 180      * additional visualization is needed; otherwise, false.
 181      */
 182     public boolean handleErrorNotification(ErrorNotification info) {
 183         return false;
 184     }
 185 
 186 //    /**
 187 //     * Called when security or other system modal dialog is shown or hidden
 188 //     * (such as proxy auth dialog).
 189 //     *
 190 //     * <p>
 191 //     * The implementation of this method provided by the Preloader class
 192 //     * does nothing.
 193 //     * </p>
 194 //     *
 195 //     * @param info the UI notification
 196 //     */
 197 //    public void handleUINotification(UINotification info) {
 198 //        // TODO RT-19601: not used for now pending completion of JRE work
 199 ////        System.err.println("Preloader: handleUINotification = " + info);
 200 //    }
 201 
 202     // ------------------------------------------------------------------------
 203 
 204     /**
 205      * Marker interface for all Preloader notification.
 206      * @since JavaFX 2.0
 207      */
 208     public static interface PreloaderNotification {
 209     }
 210 
 211     /**
 212      * Preloader notification that reports an error.
 213      * This is delivered to preloader in case of problem with application startup.
 214      * @since JavaFX 2.0
 215      */
 216     public static class ErrorNotification implements PreloaderNotification {
 217         private String location;
 218         private String details = "";
 219         private Throwable cause;
 220 
 221         /**
 222          * Constructs an error notification.
 223          *
 224          * @param location the URL associated with an error (if any); may be null
 225          * @param details a string describing the error; must be non-null
 226          * @param cause the cause of the error; may be null
 227          */
 228         public ErrorNotification(String location, String details, Throwable cause) {
 229             if (details == null) throw new NullPointerException();
 230 
 231             this.location = location;
 232             this.details = details;
 233             this.cause = cause;
 234         }
 235 
 236         /**
 237          * Retrieves the URL associated with this error, if any.
 238          * For example, if there is a download or singing check error, this
 239          * will be the URL of the jar file that has the problem.
 240          * It may be null.
 241          *
 242          * @return the location, or null
 243          */
 244         public String getLocation() {
 245             return location;
 246         }
 247 
 248         /**
 249          * Retrieves the description of the error.
 250          * It may be the empty string, but is always non-null.
 251          *
 252          * @return the description of the error
 253          */
 254         public String getDetails() {
 255             return details;
 256         }
 257 
 258         /**
 259          * Retrieves the Exception or Error associated with this error
 260          * notification, if any. It may be null.
 261          *
 262          * @return the cause of the error, or null
 263          */
 264         public Throwable getCause() {
 265             return cause;
 266         }
 267 
 268         /**
 269          * Returns a string representation of this {@code ErrorNotification} object.
 270          * @return a string representation of this {@code ErrorNotification} object.
 271          */
 272         @Override public String toString() {
 273             StringBuilder str = new StringBuilder("Preloader.ErrorNotification: ");
 274             str.append(details);
 275             if (cause != null) {
 276                 str.append(lineSeparator).append("Caused by: ").append(cause.toString());
 277             }
 278             if (location != null) {
 279                 str.append(lineSeparator).append("Location: ").append(location);
 280             }
 281             return str.toString();
 282         }
 283     }
 284 
 285     /**
 286      * Preloader notification that reports progress. This is typically used to
 287      * report progress while downloading and initializing the application.
 288      * @since JavaFX 2.0
 289      */
 290     public static class ProgressNotification implements PreloaderNotification {
 291         private final double progress;
 292         private final String details;
 293 
 294         /**
 295          * Constructs a progress notification.
 296          *
 297          * @param progress a value indicating the progress.
 298          * A negative value for progress indicates that the progress is
 299          * indeterminate. A value between 0 and 1 indicates the amount
 300          * of progress. Any value greater than 1 is interpreted as 1.
 301          */
 302         public ProgressNotification(double progress) {
 303             this(progress, "");
 304         }
 305 
 306         // NOTE: We could consider exposing details in the future, but currently
 307         // have no plan to do so. This method is private for now.
 308         /**
 309          * Constructs a progress notification.
 310          *
 311          * @param progress a value indicating the progress.
 312          * A negative value for progress indicates that the progress is
 313          * indeterminate. A value between 0 and 1 indicates the amount
 314          * of progress. Any value greater than 1 is interpreted as 1.
 315          *
 316          * @param details the details of this notification
 317          */
 318         private ProgressNotification(double progress, String details) {
 319             this.progress = progress;
 320             this.details = details;
 321         }
 322 
 323         /**
 324          * Retrieves the progress for this notification. Progress is in the
 325          * range of 0 to 1, or is negative for indeterminate progress.
 326          *
 327          * @return the progress
 328          */
 329         public double getProgress() {
 330             return progress;
 331         }
 332 
 333         /**
 334          * Retrieves the details of the progress notification
 335          *
 336          * @return the details of this notification
 337          */
 338         private String getDetails() {
 339             return details;
 340         }
 341     }
 342 
 343     /**
 344      * A notification that signals a change in the application state.
 345      * A state change notification is sent to a preloader immediately prior
 346      * to loading
 347      * the application class (and constructing an instance), calling the
 348      * application init method, or calling the application start method.
 349      * @since JavaFX 2.0
 350      */
 351     public static class StateChangeNotification implements PreloaderNotification {
 352 
 353         /**
 354          * Enum that defines the type of change associated with this notification
 355          * @since JavaFX 2.0
 356          */
 357         public enum Type {
 358             /**
 359              * Indicates that the application class is about to be loaded and
 360              * constructed.
 361              */
 362             BEFORE_LOAD,
 363 
 364             /**
 365              * Indicates that the application's init method is about to be called.
 366              */
 367             BEFORE_INIT,
 368 
 369             /**
 370              * Indicates that the application's start method is about to be called.
 371              */
 372             BEFORE_START
 373         }
 374 
 375         private final Type notificationType;
 376         private final Application application;
 377 
 378         /**
 379          * Constructs a StateChangeNotification of the specified type.
 380          *
 381          * @param notificationType the type of this notification.
 382          */
 383         public StateChangeNotification(Type notificationType){
 384             this.notificationType = notificationType;
 385             this.application = null;
 386         }
 387 
 388         /**
 389          * Constructs an StateChangeNotification of the specified type for the
 390          * specified application.
 391          *
 392          * @param notificationType the type of this notification.
 393          * @param application the application instance associated with this
 394          * notification.
 395          */
 396         public StateChangeNotification(Type notificationType, Application application) {
 397             this.notificationType = notificationType;
 398             this.application = application;
 399         }
 400 
 401         /**
 402          * Returns the type of notification.
 403          *
 404          * @return one of: BEFORE_LOAD, BEFORE_INIT, BEFORE_START
 405          */
 406         public Type getType() {
 407             return notificationType;
 408         }
 409 
 410         /**
 411          * Returns the Application instance associated with this notification.
 412          * This is null for a BEFORE_LOAD notification and non-null for other
 413          * notification types.
 414          *
 415          * @return the Application instance or null.
 416          */
 417         public Application getApplication() {
 418             return application;
 419         }
 420     }
 421 
 422 //    /**
 423 //     * Used to signal about global modal dialogs to be shown that block
 424 //     * application launch. In particular proxy and security dialogs
 425 //     */
 426 //    public static class UINotification implements PreloaderNotification {
 427 //       //TODO RT-19601: implementation pending JRE work
 428 //    }
 429 
 430 }