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