1 /*
   2  * Copyright (c) 2010, 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.concurrent;
  27 
  28 import javafx.application.Platform;
  29 import javafx.beans.property.BooleanProperty;
  30 import javafx.beans.property.DoubleProperty;
  31 import javafx.beans.property.ObjectProperty;
  32 import javafx.beans.property.ReadOnlyBooleanProperty;
  33 import javafx.beans.property.ReadOnlyDoubleProperty;
  34 import javafx.beans.property.ReadOnlyObjectProperty;
  35 import javafx.beans.property.ReadOnlyStringProperty;
  36 import javafx.beans.property.SimpleBooleanProperty;
  37 import javafx.beans.property.SimpleDoubleProperty;
  38 import javafx.beans.property.SimpleObjectProperty;
  39 import javafx.beans.property.SimpleStringProperty;
  40 import javafx.beans.property.StringProperty;
  41 import javafx.beans.value.ChangeListener;
  42 import javafx.beans.value.ObservableValue;
  43 import javafx.event.Event;
  44 import javafx.event.EventDispatchChain;
  45 import javafx.event.EventHandler;
  46 import javafx.event.EventTarget;
  47 import javafx.event.EventType;
  48 import java.security.AccessController;
  49 import java.security.AccessControlContext;
  50 import java.security.PrivilegedAction;
  51 import java.util.concurrent.BlockingQueue;
  52 import java.util.concurrent.Executor;
  53 import java.util.concurrent.LinkedBlockingQueue;
  54 import java.util.concurrent.ThreadFactory;
  55 import java.util.concurrent.ThreadPoolExecutor;
  56 import java.util.concurrent.TimeUnit;
  57 import sun.util.logging.PlatformLogger;
  58 import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_CANCELLED;
  59 import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_FAILED;
  60 import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_READY;
  61 import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_RUNNING;
  62 import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_SCHEDULED;
  63 import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_SUCCEEDED;
  64 
  65 /**
  66  * <p>
  67  *     A Service is a non-visual component encapsulating the information required
  68  *     to perform some work on one or more background threads. As part of the
  69  *     JavaFX UI library, the Service knows about the JavaFX Application thread
  70  *     and is designed to relieve the application developer from the burden
  71  *     of managing multithreaded code that interacts with the user interface. As
  72  *     such, all of the methods and state on the Service are intended to be
  73  *     invoked exclusively from the JavaFX Application thread. The only exception
  74  *     to this, is when initially configuring a Service, which may safely be done
  75  *     from any thread, and initially starting a Service, which may also safely
  76  *     be done from any thread. However, once the Service has been initialized and
  77  *     started, it may only thereafter be used from the FX thread.
  78  * </p>
  79  * <p>
  80  *     A Service creates and manages a {@link Task} that performs the work
  81  *     on the background thread.
  82  *     Service implements {@link Worker}. As such, you can observe the state of
  83  *     the background task and optionally cancel it. Service is a reusable
  84  *     Worker, meaning that it can be reset and restarted. Due to this, a Service
  85  *     can be constructed declaratively and restarted on demand.
  86  *     Once a Service is started, it will schedule its Task and listen for
  87  *     changes to the state of the Task. A Task does not hold a reference to the
  88  *     Service that started it, meaning that a running Task will not prevent
  89  *     the Service from being garbage collected.
  90  * </p>
  91  * <p>
  92  *     If an {@link java.util.concurrent.Executor} is specified on the Service,
  93  *     then it will be used to actually execute the service. Otherwise,
  94  *     a daemon thread will be created and executed. If you wish to create
  95  *     non-daemon threads, then specify a custom Executor (for example,
  96  *     you could use a {@link ThreadPoolExecutor} with a custom
  97  *     {@link java.util.concurrent.ThreadFactory}).
  98  * </p>
  99  * <p>
 100  *     Because a Service is intended to simplify declarative use cases, subclasses
 101  *     should expose as properties the input parameters to the work to be done.
 102  *     For example, suppose I wanted to write a Service which read the first line
 103  *     from any URL and returned it as a String. Such a Service might be defined,
 104  *     such that it had a single property, {@code url}. It might be implemented
 105  *     as:
 106  * </p>
 107  *     <pre><code>
 108  *     public static class FirstLineService extends Service&lt;String&gt; {
 109  *         private StringProperty url = new SimpleStringProperty(this, "url");
 110  *         public final void setUrl(String value) { url.set(value); }
 111  *         public final String getUrl() { return url.get(); }
 112  *         public final StringProperty urlProperty() { return url; }
 113  *
 114  *         protected Task createTask() {
 115  *             final String _url = getUrl();
 116  *             return new Task&lt;String&gt;() {
 117  *                 protected String call() throws Exception {
 118  *                     URL u = new URL(_url);
 119  *                     BufferedReader in = new BufferedReader(
 120  *                             new InputStreamReader(u.openStream()));
 121  *                     String result = in.readLine();
 122  *                     in.close();
 123  *                     return result;
 124  *                 }
 125  *             };
 126  *         }
 127  *     }
 128  *     </code></pre>
 129  * <p>
 130  *     The Service by default uses a thread pool Executor with some unspecified
 131  *     default or maximum thread pool size. This is done so that naive code
 132  *     will not completely swamp the system by creating thousands of Threads.
 133  * </p>
 134  * @param <V> the type of object returned by the Service.
 135  * @since JavaFX 2.0
 136  */
 137 public abstract class Service<V> implements Worker<V>, EventTarget {
 138     /**
 139      * Logger used in the case of some uncaught exceptions
 140      */
 141     private static final PlatformLogger LOG = PlatformLogger.getLogger(Service.class.getName());
 142 
 143     /*
 144         The follow chunk of static state is for defining the default Executor used
 145         with the Service. This is based on pre-existing JavaFX Script code and
 146         experience with JavaFX Script. It was necessary to have a thread pool by default
 147         because we found naive code could totally overwhelm the system otherwise
 148         by spawning thousands of threads for fetching resources, for example.
 149         We also set the priority and daemon status of the thread in its thread
 150         factory.
 151      */
 152     private static final int THREAD_POOL_SIZE = 32;
 153     private static final long THREAD_TIME_OUT = 1000;
 154 
 155     /**
 156      * Because the ThreadPoolExecutor works completely backwards from what we want (ie:
 157      * it doesn't increase thread count beyond the core pool size unless the queue is full),
 158      * our queue has to be smart in that it will REJECT an item in the queue unless the
 159      * thread size in the EXECUTOR is > 32, in which case we will queue up.
 160      */
 161     private static final BlockingQueue<Runnable> IO_QUEUE = new LinkedBlockingQueue<Runnable>() {
 162         @Override public boolean offer(Runnable runnable) {
 163             if (EXECUTOR.getPoolSize() < THREAD_POOL_SIZE) {
 164                 return false;
 165             }
 166             return super.offer(runnable);
 167         }
 168     };
 169 
 170     // Addition of doPrivileged added due to RT-19580
 171     private static final ThreadGroup THREAD_GROUP = AccessController.doPrivileged((PrivilegedAction<ThreadGroup>) () -> new ThreadGroup("javafx concurrent thread pool"));
 172     private static final Thread.UncaughtExceptionHandler UNCAUGHT_HANDLER = (thread, throwable) -> {
 173         // Ignore IllegalMonitorStateException, these are thrown from the ThreadPoolExecutor
 174         // when a browser navigates away from a page hosting an applet that uses
 175         // asynchronous tasks. These exceptions generally do not cause loss of functionality.
 176         if (!(throwable instanceof IllegalMonitorStateException)) {
 177             LOG.warning("Uncaught throwable in " + THREAD_GROUP.getName(), throwable);
 178         }
 179     };
 180 
 181     // Addition of doPrivileged added due to RT-19580
 182     private static final ThreadFactory THREAD_FACTORY = run -> AccessController.doPrivileged((PrivilegedAction<Thread>) () -> {
 183         final Thread th = new Thread(THREAD_GROUP, run);
 184         th.setUncaughtExceptionHandler(UNCAUGHT_HANDLER);
 185         th.setPriority(Thread.MIN_PRIORITY);
 186         th.setDaemon(true);
 187         return th;
 188     });
 189 
 190     private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
 191             2, THREAD_POOL_SIZE,
 192             THREAD_TIME_OUT, TimeUnit.MILLISECONDS,
 193             IO_QUEUE, THREAD_FACTORY, new ThreadPoolExecutor.AbortPolicy());
 194 
 195     static {
 196         EXECUTOR.allowCoreThreadTimeOut(true);
 197     }
 198 
 199     private final ObjectProperty<State> state = new SimpleObjectProperty<>(this, "state", State.READY);
 200     @Override public final State getState() { checkThread(); return state.get(); }
 201     @Override public final ReadOnlyObjectProperty<State> stateProperty() { checkThread(); return state; }
 202 
 203     private final ObjectProperty<V> value = new SimpleObjectProperty<>(this, "value");
 204     @Override public final V getValue() { checkThread(); return value.get(); }
 205     @Override public final ReadOnlyObjectProperty<V> valueProperty() { checkThread(); return value; }
 206 
 207     private final ObjectProperty<Throwable> exception = new SimpleObjectProperty<>(this, "exception");
 208     @Override public final Throwable getException() { checkThread(); return exception.get(); }
 209     @Override public final ReadOnlyObjectProperty<Throwable> exceptionProperty() { checkThread(); return exception; }
 210 
 211     private final DoubleProperty workDone = new SimpleDoubleProperty(this, "workDone", -1);
 212     @Override public final double getWorkDone() { checkThread(); return workDone.get(); }
 213     @Override public final ReadOnlyDoubleProperty workDoneProperty() { checkThread(); return workDone; }
 214 
 215     private final DoubleProperty totalWorkToBeDone = new SimpleDoubleProperty(this, "totalWork", -1);
 216     @Override public final double getTotalWork() { checkThread(); return totalWorkToBeDone.get(); }
 217     @Override public final ReadOnlyDoubleProperty totalWorkProperty() { checkThread(); return totalWorkToBeDone; }
 218 
 219     private final DoubleProperty progress = new SimpleDoubleProperty(this, "progress", -1);
 220     @Override public final double getProgress() { checkThread(); return progress.get(); }
 221     @Override public final ReadOnlyDoubleProperty progressProperty() { checkThread(); return progress; }
 222 
 223     private final BooleanProperty running = new SimpleBooleanProperty(this, "running", false);
 224     @Override public final boolean isRunning() { checkThread(); return running.get(); }
 225     @Override public final ReadOnlyBooleanProperty runningProperty() { checkThread(); return running; }
 226 
 227     private final StringProperty message = new SimpleStringProperty(this, "message", "");
 228     @Override public final String getMessage() { checkThread(); return message.get(); }
 229     @Override public final ReadOnlyStringProperty messageProperty() { checkThread(); return message; }
 230 
 231     private final StringProperty title = new SimpleStringProperty(this, "title", "");
 232     @Override public final String getTitle() { checkThread(); return title.get(); }
 233     @Override public final ReadOnlyStringProperty titleProperty() { checkThread(); return title; }
 234 
 235     /**
 236      * The executor to use for running this Service. If no executor is specified, then
 237      * a new daemon thread will be created and used for running the Service using some
 238      * default executor.
 239      */
 240     private final ObjectProperty<Executor> executor = new SimpleObjectProperty<>(this, "executor");
 241     public final void setExecutor(Executor value) { checkThread(); executor.set(value); }
 242     public final Executor getExecutor() { checkThread(); return executor.get(); }
 243     public final ObjectProperty<Executor> executorProperty() { checkThread(); return executor; }
 244 
 245     /**
 246      * The onReady event handler is called whenever the Task state transitions
 247      * to the READY state.
 248      *
 249      * @return the onReady event handler property
 250      * @since JavaFX 2.1
 251      */
 252     public final ObjectProperty<EventHandler<WorkerStateEvent>> onReadyProperty() {
 253         checkThread();
 254         return getEventHelper().onReadyProperty();
 255     }
 256 
 257     /**
 258      * The onReady event handler is called whenever the Task state transitions
 259      * to the READY state.
 260      *
 261      * @return the onReady event handler, if any
 262      * @since JavaFX 2.1
 263      */
 264     public final EventHandler<WorkerStateEvent> getOnReady() {
 265         checkThread();
 266         return eventHelper == null ? null : eventHelper.getOnReady();
 267     }
 268 
 269     /**
 270      * The onReady event handler is called whenever the Task state transitions
 271      * to the READY state.
 272      *
 273      * @param value the event handler, can be null to clear it
 274      * @since JavaFX 2.1
 275      */
 276     public final void setOnReady(EventHandler<WorkerStateEvent> value) {
 277         checkThread();
 278         getEventHelper().setOnReady(value);
 279     }
 280 
 281     /**
 282      * A protected convenience method for subclasses, called whenever the
 283      * state of the Service has transitioned to the READY state.
 284      * This method is invoked after the Service has been fully transitioned to the new state.
 285      * @since JavaFX 2.1
 286      */
 287     protected void ready() { }
 288 
 289     /**
 290      * The onSchedule event handler is called whenever the Task state
 291      * transitions to the SCHEDULED state.
 292      *
 293      * @return the onScheduled event handler property
 294      * @since JavaFX 2.1
 295      */
 296     public final ObjectProperty<EventHandler<WorkerStateEvent>> onScheduledProperty() {
 297         checkThread();
 298         return getEventHelper().onScheduledProperty();
 299     }
 300 
 301     /**
 302      * The onSchedule event handler is called whenever the Task state
 303      * transitions to the SCHEDULED state.
 304      *
 305      * @return the onScheduled event handler, if any
 306      * @since JavaFX 2.1
 307      */
 308     public final EventHandler<WorkerStateEvent> getOnScheduled() {
 309         checkThread();
 310         return eventHelper == null ? null : eventHelper.getOnScheduled();
 311     }
 312 
 313     /**
 314      * The onSchedule event handler is called whenever the Task state
 315      * transitions to the SCHEDULED state.
 316      *
 317      * @param value the event handler, can be null to clear it
 318      * @since JavaFX 2.1
 319      */
 320     public final void setOnScheduled(EventHandler<WorkerStateEvent> value) {
 321         checkThread();
 322         getEventHelper().setOnScheduled(value);
 323     }
 324 
 325     /**
 326      * A protected convenience method for subclasses, called whenever the
 327      * state of the Service has transitioned to the SCHEDULED state.
 328      * This method is invoked after the Service has been fully transitioned to the new state.
 329      * @since JavaFX 2.1
 330      */
 331     protected void scheduled() { }
 332 
 333     /**
 334      * The onRunning event handler is called whenever the Task state
 335      * transitions to the RUNNING state.
 336      *
 337      * @return the onRunning event handler property
 338      * @since JavaFX 2.1
 339      */
 340     public final ObjectProperty<EventHandler<WorkerStateEvent>> onRunningProperty() {
 341         checkThread();
 342         return getEventHelper().onRunningProperty();
 343     }
 344 
 345     /**
 346      * The onRunning event handler is called whenever the Task state
 347      * transitions to the RUNNING state.
 348      *
 349      * @return the onRunning event handler, if any
 350      * @since JavaFX 2.1
 351      */
 352     public final EventHandler<WorkerStateEvent> getOnRunning() {
 353         checkThread();
 354         return eventHelper == null ? null : eventHelper.getOnRunning();
 355     }
 356 
 357     /**
 358      * The onRunning event handler is called whenever the Task state
 359      * transitions to the RUNNING state.
 360      *
 361      * @param value the event handler, can be null to clear it
 362      * @since JavaFX 2.1
 363      */
 364     public final void setOnRunning(EventHandler<WorkerStateEvent> value) {
 365         checkThread();
 366         getEventHelper().setOnRunning(value);
 367     }
 368 
 369     /**
 370      * A protected convenience method for subclasses, called whenever the
 371      * state of the Service has transitioned to the RUNNING state.
 372      * This method is invoked after the Service has been fully transitioned to the new state.
 373      * @since JavaFX 2.1
 374      */
 375     protected void running() { }
 376 
 377     /**
 378      * The onSucceeded event handler is called whenever the Task state
 379      * transitions to the SUCCEEDED state.
 380      *
 381      * @return the onSucceeded event handler property
 382      * @since JavaFX 2.1
 383      */
 384     public final ObjectProperty<EventHandler<WorkerStateEvent>> onSucceededProperty() {
 385         checkThread();
 386         return getEventHelper().onSucceededProperty();
 387     }
 388 
 389     /**
 390      * The onSucceeded event handler is called whenever the Task state
 391      * transitions to the SUCCEEDED state.
 392      *
 393      * @return the onSucceeded event handler, if any
 394      * @since JavaFX 2.1
 395      */
 396     public final EventHandler<WorkerStateEvent> getOnSucceeded() {
 397         checkThread();
 398         return eventHelper == null ? null : eventHelper.getOnSucceeded();
 399     }
 400 
 401     /**
 402      * The onSucceeded event handler is called whenever the Task state
 403      * transitions to the SUCCEEDED state.
 404      *
 405      * @param value the event handler, can be null to clear it
 406      * @since JavaFX 2.1
 407      */
 408     public final void setOnSucceeded(EventHandler<WorkerStateEvent> value) {
 409         checkThread();
 410         getEventHelper().setOnSucceeded(value);
 411     }
 412 
 413     /**
 414      * A protected convenience method for subclasses, called whenever the
 415      * state of the Service has transitioned to the SUCCEEDED state.
 416      * This method is invoked after the Service has been fully transitioned to the new state.
 417      * @since JavaFX 2.1
 418      */
 419     protected void succeeded() { }
 420 
 421     /**
 422      * The onCancelled event handler is called whenever the Task state
 423      * transitions to the CANCELLED state.
 424      *
 425      * @return the onCancelled event handler property
 426      * @since JavaFX 2.1
 427      */
 428     public final ObjectProperty<EventHandler<WorkerStateEvent>> onCancelledProperty() {
 429         checkThread();
 430         return getEventHelper().onCancelledProperty();
 431     }
 432 
 433     /**
 434      * The onCancelled event handler is called whenever the Task state
 435      * transitions to the CANCELLED state.
 436      *
 437      * @return the onCancelled event handler, if any
 438      * @since JavaFX 2.1
 439      */
 440     public final EventHandler<WorkerStateEvent> getOnCancelled() {
 441         checkThread();
 442         return eventHelper == null ? null : eventHelper.getOnCancelled();
 443     }
 444 
 445     /**
 446      * The onCancelled event handler is called whenever the Task state
 447      * transitions to the CANCELLED state.
 448      *
 449      * @param value the event handler, can be null to clear it
 450      * @since JavaFX 2.1
 451      */
 452     public final void setOnCancelled(EventHandler<WorkerStateEvent> value) {
 453         checkThread();
 454         getEventHelper().setOnCancelled(value);
 455     }
 456 
 457     /**
 458      * A protected convenience method for subclasses, called whenever the
 459      * state of the Service has transitioned to the CANCELLED state.
 460      * This method is invoked after the Service has been fully transitioned to the new state.
 461      * @since JavaFX 2.1
 462      */
 463     protected void cancelled() { }
 464 
 465     /**
 466      * The onFailed event handler is called whenever the Task state
 467      * transitions to the FAILED state.
 468      *
 469      * @return the onFailed event handler property
 470      * @since JavaFX 2.1
 471      */
 472     public final ObjectProperty<EventHandler<WorkerStateEvent>> onFailedProperty() {
 473         checkThread();
 474         return getEventHelper().onFailedProperty();
 475     }
 476 
 477     /**
 478      * The onFailed event handler is called whenever the Task state
 479      * transitions to the FAILED state.
 480      *
 481      * @return the onFailed event handler, if any
 482      * @since JavaFX 2.1
 483      */
 484     public final EventHandler<WorkerStateEvent> getOnFailed() {
 485         checkThread();
 486         return eventHelper == null ? null : eventHelper.getOnFailed();
 487     }
 488 
 489     /**
 490      * The onFailed event handler is called whenever the Task state
 491      * transitions to the FAILED state.
 492      *
 493      * @param value the event handler, can be null to clear it
 494      * @since JavaFX 2.1
 495      */
 496     public final void setOnFailed(EventHandler<WorkerStateEvent> value) {
 497         checkThread();
 498         getEventHelper().setOnFailed(value);
 499     }
 500 
 501     /**
 502      * A protected convenience method for subclasses, called whenever the
 503      * state of the Service has transitioned to the FAILED state.
 504      * This method is invoked after the Service has been fully transitioned to the new state.
 505      * @since JavaFX 2.1
 506      */
 507     protected void failed() { }
 508 
 509     /**
 510      * A reference to the last task that was executed. I need this reference so that in the
 511      * restart method I can cancel the currently running task, and so the cancel method
 512      * can cancel the currently running task.
 513      */
 514     private Task<V> task;
 515 
 516     /**
 517      * This boolean is set to true once the Service has been initially started. You can initialize
 518      * the Service from any thread, and you can initially start it from any thread. But any
 519      * subsequent usage of the service's methods must occur on the FX application thread.
 520      */
 521     private volatile boolean startedOnce = false;
 522 
 523     /**
 524      * Create a new Service.
 525      */
 526     protected Service() {
 527         // Add a listener to the state, such that we can fire the correct event
 528         // notifications whenever the state of the Service has changed.
 529         state.addListener((observableValue, old, value1) -> {
 530 
 531             // Invoke the appropriate event handler
 532             switch (value1) {
 533                 case CANCELLED:
 534                     fireEvent(new WorkerStateEvent(Service.this, WORKER_STATE_CANCELLED));
 535                     cancelled();
 536                     break;
 537                 case FAILED:
 538                     fireEvent(new WorkerStateEvent(Service.this, WORKER_STATE_FAILED));
 539                     failed();
 540                     break;
 541                 case READY:
 542                     fireEvent(new WorkerStateEvent(Service.this, WORKER_STATE_READY));
 543                     ready();
 544                     break;
 545                 case RUNNING:
 546                     fireEvent(new WorkerStateEvent(Service.this, WORKER_STATE_RUNNING));
 547                     running();
 548                     break;
 549                 case SCHEDULED:
 550                     fireEvent(new WorkerStateEvent(Service.this, WORKER_STATE_SCHEDULED));
 551                     scheduled();
 552                     break;
 553                 case SUCCEEDED:
 554                     fireEvent(new WorkerStateEvent(Service.this, WORKER_STATE_SUCCEEDED));
 555                     succeeded();
 556                     break;
 557                 default: throw new AssertionError("Should be unreachable");
 558             }
 559         });
 560     }
 561 
 562     /**
 563      * Cancels any currently running Task, if any. The state will be set to CANCELLED.
 564      * @return returns true if the cancel was successful
 565      */
 566     @Override public boolean cancel() {
 567         checkThread();
 568         if (task == null) {
 569             if (state.get() == State.CANCELLED || state.get() == State.SUCCEEDED) {
 570                 return false;
 571             }
 572             state.set(State.CANCELLED);
 573             return true;
 574         } else {
 575             return task.cancel(true);
 576         }
 577     }
 578 
 579     /**
 580      * Cancels any currently running Task, if any, and restarts this Service. The state
 581      * will be reset to READY prior to execution. This method should only be called on
 582      * the FX application thread.
 583      */
 584     public void restart() {
 585         checkThread();
 586 
 587         // Cancel the current task, if there is one
 588         if (task != null) {
 589             task.cancel();
 590             task = null;
 591 
 592             // RT-20880: IllegalStateException thrown from Service#restart()
 593             // The problem is that the reset method explodes if the state
 594             // is SCHEDULED or RUNNING. Although we have cancelled the
 595             // task above, it is possible that cancelling does not change
 596             // state to the CANCELLED state. However we still need to
 597             // succeed in resetting. I believe that although the old task is
 598             // still running away, everything is about to be unbound so
 599             // we really can just let the old task run and create a new
 600             // task and the Service will be none the wiser.
 601             state.unbind();
 602             state.set(State.CANCELLED);
 603         }
 604 
 605         // Reset
 606         reset();
 607 
 608         // Start the thing up again.
 609         start();
 610     }
 611 
 612     /**
 613      * Resets the Service. May only be called while in one of the finish states,
 614      * that is, SUCCEEDED, FAILED, or CANCELLED, or when READY. This method should
 615      * only be called on the FX application thread.
 616      */
 617     public void reset() {
 618         checkThread();
 619         final State s = getState();
 620         if (s == State.SCHEDULED || s == State.RUNNING) {
 621             throw new IllegalStateException();
 622         }
 623 
 624         task = null;
 625         state.unbind();
 626         state.set(State.READY);
 627         value.unbind();
 628         value.set(null);
 629         exception.unbind();
 630         exception.set(null);
 631         workDone.unbind();
 632         workDone.set(-1);
 633         totalWorkToBeDone.unbind();
 634         totalWorkToBeDone.set(-1);
 635         progress.unbind();
 636         progress.set(-1);
 637         running.unbind();
 638         running.set(false);
 639         message.unbind();
 640         message.set("");
 641         title.unbind();
 642         title.set("");
 643     }
 644 
 645     /**
 646      * Starts this Service. The Service must be in the READY state to succeed in this call.
 647      * This method should only be called on the FX application thread.
 648      */
 649     public void start() {
 650         checkThread();
 651 
 652         if (getState() != State.READY) {
 653             throw new IllegalStateException(
 654                     "Can only start a Service in the READY state. Was in state " + getState());
 655         }
 656 
 657         // Create the task
 658         task = createTask();
 659 
 660         // Wire up all the properties so they use this task
 661         state.bind(task.stateProperty());
 662         value.bind(task.valueProperty());
 663         exception.bind(task.exceptionProperty());
 664         workDone.bind(task.workDoneProperty());
 665         totalWorkToBeDone.bind(task.totalWorkProperty());
 666         progress.bind(task.progressProperty());
 667         running.bind(task.runningProperty());
 668         message.bind(task.messageProperty());
 669         title.bind(task.titleProperty());
 670 
 671         // Record that start has been called once, so we don't allow it to be called again from
 672         // any thread other than the fx thread
 673         startedOnce = true;
 674 
 675         if (!isFxApplicationThread()) {
 676             runLater(() -> {
 677                 // Advance the task to the "SCHEDULED" state
 678                 task.setState(State.SCHEDULED);
 679 
 680                 // Start the task
 681                 executeTask(task);
 682             });
 683         } else {
 684             // Advance the task to the "SCHEDULED" state
 685             task.setState(State.SCHEDULED);
 686 
 687             // Start the task
 688             executeTask(task);
 689         }
 690     }
 691 
 692     /**
 693      * This is used by ScheduledService to cancel a Service that is in the READY state. The problem is
 694      * that a ScheduledService will iterate from SUCCEEDED to READY, and then call start() to transition
 695      * from READY to SCHEDULED and kick off a new iteration. However, if from the SUCCEEDED event handler
 696      * a developer calls "cancel" to stop a ScheduledService, we have a problem, since the Service
 697      * specification does not allow to transition from a terminal state (SUCCEEDED) to another terminal
 698      * state (CANCELLED), but this is clearly what we need to do. So what this method will do is allow
 699      * us to transition from the READY state to the CANCELLED state, by transitioning through SCHEDULED
 700      */
 701     void cancelFromReadyState() {
 702         state.set(State.SCHEDULED);
 703         state.set(State.CANCELLED);
 704     }
 705 
 706     /**
 707      * <p>
 708      *     Uses the <code>executor</code> defined on this Service to execute the
 709      *     given task. If the <code>executor</code> is null, then a default
 710      *     executor is used which will create a new daemon thread on which to
 711      *     execute this task.
 712      * </p>
 713      * <p>
 714      *     This method is intended only to be called by the Service
 715      *     implementation.
 716      * </p>
 717      * @param task a non-null task to execute
 718      * @since JavaFX 2.1
 719      */
 720     protected void executeTask(final Task<V> task) {
 721         final AccessControlContext acc = AccessController.getContext();
 722         final Executor e = getExecutor() != null ? getExecutor() : EXECUTOR;
 723         e.execute(() -> {
 724             AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 725                 task.run();
 726                 return null;
 727             }, acc);
 728         });
 729     }
 730 
 731     /***************************************************************************
 732      *                                                                         *
 733      *                         Event Dispatch                                  *
 734      *                                                                         *
 735      **************************************************************************/
 736 
 737     private EventHelper eventHelper = null;
 738     private EventHelper getEventHelper() {
 739         if (eventHelper == null) {
 740             eventHelper = new EventHelper(this);
 741         }
 742         return eventHelper;
 743     }
 744 
 745     /**
 746      * Registers an event handler to this task. Any event filters are first
 747      * processed, then the specified onFoo event handlers, and finally any
 748      * event handlers registered by this method. As with other events
 749      * in the scene graph, if an event is consumed, it will not continue
 750      * dispatching.
 751      *
 752      * @param <T> the specific event class of the handler
 753      * @param eventType the type of the events to receive by the handler
 754      * @param eventHandler the handler to register
 755      * @throws NullPointerException if the event type or handler is null
 756      * @since JavaFX 2.1
 757      */
 758     public final <T extends Event> void addEventHandler(
 759             final EventType<T> eventType,
 760             final EventHandler<? super T> eventHandler) {
 761         checkThread();
 762         getEventHelper().addEventHandler(eventType, eventHandler);
 763     }
 764 
 765     /**
 766      * Unregisters a previously registered event handler from this task. One
 767      * handler might have been registered for different event types, so the
 768      * caller needs to specify the particular event type from which to
 769      * unregister the handler.
 770      *
 771      * @param <T> the specific event class of the handler
 772      * @param eventType the event type from which to unregister
 773      * @param eventHandler the handler to unregister
 774      * @throws NullPointerException if the event type or handler is null
 775      * @since JavaFX 2.1
 776      */
 777     public final <T extends Event> void removeEventHandler(
 778             final EventType<T> eventType,
 779             final EventHandler<? super T> eventHandler) {
 780         checkThread();
 781         getEventHelper().removeEventHandler(eventType, eventHandler);
 782     }
 783 
 784     /**
 785      * Registers an event filter to this task. Registered event filters get
 786      * an event before any associated event handlers.
 787      *
 788      * @param <T> the specific event class of the filter
 789      * @param eventType the type of the events to receive by the filter
 790      * @param eventFilter the filter to register
 791      * @throws NullPointerException if the event type or filter is null
 792      * @since JavaFX 2.1
 793      */
 794     public final <T extends Event> void addEventFilter(
 795             final EventType<T> eventType,
 796             final EventHandler<? super T> eventFilter) {
 797         checkThread();
 798         getEventHelper().addEventFilter(eventType, eventFilter);
 799     }
 800 
 801     /**
 802      * Unregisters a previously registered event filter from this task. One
 803      * filter might have been registered for different event types, so the
 804      * caller needs to specify the particular event type from which to
 805      * unregister the filter.
 806      *
 807      * @param <T> the specific event class of the filter
 808      * @param eventType the event type from which to unregister
 809      * @param eventFilter the filter to unregister
 810      * @throws NullPointerException if the event type or filter is null
 811      * @since JavaFX 2.1
 812      */
 813     public final <T extends Event> void removeEventFilter(
 814             final EventType<T> eventType,
 815             final EventHandler<? super T> eventFilter) {
 816         checkThread();
 817         getEventHelper().removeEventFilter(eventType, eventFilter);
 818     }
 819 
 820     /**
 821      * Sets the handler to use for this event type. There can only be one such
 822      * handler specified at a time. This handler is guaranteed to be called
 823      * first. This is used for registering the user-defined onFoo event
 824      * handlers.
 825      *
 826      * @param <T> the specific event class of the handler
 827      * @param eventType the event type to associate with the given eventHandler
 828      * @param eventHandler the handler to register, or null to unregister
 829      * @throws NullPointerException if the event type is null
 830      * @since JavaFX 2.1
 831      */
 832     protected final <T extends Event> void setEventHandler(
 833             final EventType<T> eventType,
 834             final EventHandler<? super T> eventHandler) {
 835         checkThread();
 836         getEventHelper().setEventHandler(eventType, eventHandler);
 837     }
 838 
 839     /**
 840      * Fires the specified event. Any event filter encountered will
 841      * be notified and can consume the event. If not consumed by the filters,
 842      * the event handlers on this task are notified. If these don't consume the
 843      * event either, then all event handlers are called and can consume the
 844      * event.
 845      * <p>
 846      * This method must be called on the FX user thread.
 847      *
 848      * @param event the event to fire
 849      * @since JavaFX 2.1
 850      */
 851     protected final void fireEvent(Event event) {
 852         checkThread();
 853         getEventHelper().fireEvent(event);
 854     }
 855 
 856     @Override
 857     public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) {
 858         checkThread();
 859         return getEventHelper().buildEventDispatchChain(tail);
 860     }
 861 
 862     /**
 863      * Invoked after the Service is started on the JavaFX Application Thread.
 864      * Implementations should save off any state into final variables prior to
 865      * creating the Task, since accessing properties defined on the Service
 866      * within the background thread code of the Task will result in exceptions.
 867      *
 868      * For example:
 869      * <pre><code>
 870      *     protected Task createTask() {
 871      *         final String url = myService.getUrl();
 872      *         return new Task&lt;String&gt;() {
 873      *             protected String call() {
 874      *                 URL u = new URL("http://www.oracle.com");
 875      *                 BufferedReader in = new BufferedReader(
 876      *                         new InputStreamReader(u.openStream()));
 877      *                 String result = in.readLine();
 878      *                 in.close();
 879      *                 return result;
 880      *             }
 881      *         }
 882      *     }
 883      * </code></pre>
 884      *
 885      * <p>
 886      *     If the Task is a pre-defined class (as opposed to being an
 887      *     anonymous class), and if it followed the recommended best-practice,
 888      *     then there is no need to save off state prior to constructing
 889      *     the Task since its state is completely provided in its constructor.
 890      * </p>
 891      *
 892      * <pre><code>
 893      *     protected Task createTask() {
 894      *         // This is safe because getUrl is called on the FX Application
 895      *         // Thread and the FirstLineReaderTasks stores it as an
 896      *         // immutable property
 897      *         return new FirstLineReaderTask(myService.getUrl());
 898      *     }
 899      * </code></pre>
 900      * @return the Task to execute
 901      */
 902     protected abstract Task<V> createTask();
 903 
 904     void checkThread() {
 905         if (startedOnce && !isFxApplicationThread()) {
 906             throw new IllegalStateException("Service must only be used from the FX Application Thread");
 907         }
 908     }
 909 
 910     // This method exists for the sake of testing, so I can subclass and override
 911     // this method in the test and not actually use Platform.runLater.
 912     void runLater(Runnable r) {
 913         Platform.runLater(r);
 914     }
 915 
 916     // This method exists for the sake of testing, so I can subclass and override
 917     // this method in the test and not actually use Platform.isFxApplicationThread.
 918     boolean isFxApplicationThread() {
 919         return Platform.isFxApplicationThread();
 920     }
 921 }