All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javafx.concurrent.Task Maven / Gradle / Ivy

There is a newer version: 24-ea+15
Show newest version
/*
 * Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javafx.concurrent;

import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.Event;
import javafx.event.EventDispatchChain;
import javafx.event.EventHandler;
import javafx.event.EventTarget;
import javafx.event.EventType;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicReference;
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_CANCELLED;
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_FAILED;
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_RUNNING;
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_SCHEDULED;
import static javafx.concurrent.WorkerStateEvent.WORKER_STATE_SUCCEEDED;

/**
 * 

* A fully observable implementation of a {@link FutureTask}. {@code Task} exposes * additional state and observable properties useful for programming asynchronous * tasks in JavaFX, as defined in the {@link Worker} interface. An implementation * of Task must override the {@link javafx.concurrent.Task#call()} method. This method * is invoked on the background thread. Any state which is used in this method * must be safe to read and write from a background thread. For example, manipulating * a live scene graph from this method is unsafe and will result in runtime * exceptions. *

*

* Tasks are flexible and extremely useful for the encapsulation of "work". Because * {@link Service} is designed to execute a Task, any Tasks defined by the application * or library code can easily be used with a Service. Likewise, since Task extends * from FutureTask, it is very easy and natural to use a Task with the java concurrency * {@link java.util.concurrent.Executor} API. Since a Task is Runnable, you * can also call it directly (by invoking the {@link javafx.concurrent.Task#run()} method) * from another background thread. This allows for composition of work, or pass it to * a new Thread constructed and executed manually. Finally, since you can * manually create a new Thread, passing it a Runnable, it is possible to use * the following idiom: *

*
{@code
 *         Thread th = new Thread(task);
 *         th.setDaemon(true);
 *         th.start();
 *     }
*

* Note that this code sets the daemon flag of the Thread to true. If you * want a background thread to prevent the VM from exiting after the last * stage is closed, then you would want daemon to be false. However, if * you want the background threads to simply terminate after all the * stages are closed, then you must set daemon to true. *

*

* Although {@link java.util.concurrent.ExecutorService} defines several methods which * take a Runnable, you should generally limit yourself to using the execute * method inherited from {@link java.util.concurrent.Executor}. *

*

* As with FutureTask, a Task is a one-shot class and cannot be reused. See {@link Service} * for a reusable {@link Worker}. *

*

* Because the Task is designed for use with JavaFX GUI applications, it ensures * that every change to its public properties, as well as change notifications * for state, errors, and for event handlers, all occur on the main JavaFX application * thread. Accessing these properties from a background thread (including the * {@link #call()} method) will result in runtime exceptions being raised. The only exception * to this, is when initially configuring a Task, which may safely be done * from any thread. However, once the Task has been initialized and * started, it may only thereafter be used from the FX thread (except for those methods clearly * marked as being appropriate for the subclass to invoke from the background thread). *

*

* It is strongly encouraged that all Tasks be initialized with * immutable state upon which the Task will operate. This should be done by providing * a Task constructor which takes the parameters necessary for execution of the Task. * Immutable state makes it easy and safe to use from any thread and ensures * correctness in the presence of multiple threads. *

*

* In Java there is no reliable way to "kill" a thread in process. However, * when cancel is called on a Task, it is important that * the Task stop processing. A "run-away" Task might continue processing * and updating the message, text, and progress properties even after the * Task has been cancelled! In Java, cancelling a Task is a cooperative * endeavor. The user of the Task will request that it be cancelled, and * the author of the Task must check whether is has been cancelled within * the body of the call method. There are two ways this can * be done. First, the Task author may check the isCancelled method, * inherited from FutureTask, to see whether the Task has * been cancelled. Second, if the Task implementation makes use of any * blocking calls (such as NIO InterruptibleChannels or Thread.sleep) and * the task is cancelled while in such a blocking call, an * InterruptedException is thrown. Task implementations which have blocking * calls should recognize that an interrupted thread may be the signal for * a cancelled task and should double check the isCancelled method to ensure * that the InterruptedException was thrown due to the cancellation of the * Task. *

*

Examples

*

* The following set of examples demonstrate some of the most common uses of * Tasks. *

* *

A Simple Loop

* *

* The first example is a simple loop that does nothing particularly useful, * but demonstrates the fundamental aspects of writing a Task correctly. This * example will simply loop and print to standard out on each loop iteration. * When it completes, it returns the number of times it iterated. *

* *

 *     {@literal Task task = new Task()} {
 *         {@literal @Override} protected Integer call() throws Exception {
 *             int iterations;
 *             {@literal for (iterations = 0; iterations < 100000; iterations++)} {
 *                 if (isCancelled()) {
 *                     break;
 *                 }
 *                 System.out.println("Iteration " + iterations);
 *             }
 *             return iterations;
 *         }
 *     };
 * 
* *

* First, we define what type of value is returned from this Task. In this * case, we want to return the number of times we iterated, so we will * specify the Task to be of type Integer by using generics. Then, within * the implementation of the call method, we iterate from * 0 to 100000. On each iteration, we check to see whether this Task has * been cancelled. If it has been, then we break out of the loop and return * the number of times we iterated. Otherwise a message is printed to * the console and the iteration count increased and we continue looping. *

* *

* Checking for isCancelled() in the loop body is critical, otherwise the * developer may cancel the task, but the task will continue running * and updating both the progress and returning the incorrect result * from the end of the call method. A correct implementation * of a Task will always check for cancellation. *

* *

A Simple Loop With Progress Notification

* *

* Similar to the previous example, except this time we will modify the * progress of the Task in each iteration. Note that we have a choice * to make in the case of cancellation. Do we want to set the progress back * to -1 (indeterminate) when the Task is cancelled, or do we want to leave * the progress where it was at? In this case, let's leave the progress alone * and only update the message on cancellation, though updating the * progress after cancellation is a perfectly valid choice. *

* *

 *     {@literal Task task = new Task()} {
 *         {@literal @Override} protected Integer call() throws Exception {
 *             int iterations;
 *             {@literal for (iterations = 0; iterations < 10000000; iterations++)} {
 *                 if (isCancelled()) {
 *                     updateMessage("Cancelled");
 *                     break;
 *                 }
 *                 updateMessage("Iteration " + iterations);
 *                 updateProgress(iterations, 10000000);
 *             }
 *             return iterations;
 *         }
 *     };
 * 
* *

* As before, within the for loop we check whether the Task has been * cancelled. If it has been cancelled, we will update the Task's * message to indicate that it has been cancelled, and then break as * before. If the Task has not been cancelled, then we will update its * message to indicate the current iteration and then update the * progress to indicate the current progress. *

* *

A Simple Loop With Progress Notification And Blocking Calls

* *

* This example adds to the previous examples a blocking call. Because a * blocking call may thrown an InterruptedException, and because an * InterruptedException may occur as a result of the Task being cancelled, * we need to be sure to handle the InterruptedException and check on the * cancel state. *

* *

 *     {@literal Task task = new Task()} {
 *         {@literal @Override} protected Integer call() throws Exception {
 *             int iterations;
 *             {@literal for (iterations = 0; iterations < 1000; iterations++)} {
 *                 if (isCancelled()) {
 *                     updateMessage("Cancelled");
 *                     break;
 *                 }
 *                 updateMessage("Iteration " + iterations);
 *                 updateProgress(iterations, 1000);
 *
 *                 // Now block the thread for a short time, but be sure
 *                 // to check the interrupted exception for cancellation!
 *                 try {
 *                     Thread.sleep(100);
 *                 } catch (InterruptedException interrupted) {
 *                     if (isCancelled()) {
 *                         updateMessage("Cancelled");
 *                         break;
 *                     }
 *                 }
 *             }
 *             return iterations;
 *         }
 *     };
 * 
* *

* Here we have added to the body of the loop a Thread.sleep * call. Since this is a blocking call, I have to handle the potential * InterruptedException. Within the catch block, I will check whether * the Task has been cancelled, and if so, update the message accordingly * and break out of the loop. *

* *

A Task Which Takes Parameters

* *

* Most Tasks require some parameters in order to do useful work. For * example, a DeleteRecordTask needs the object or primary key to delete * from the database. A ReadFileTask needs the URI of the file to be read. * Because Tasks operate on a background thread, care must be taken to * make sure the body of the call method does not read or * modify any shared state. There are two techniques most useful for * doing this: using final variables, and passing variables to a Task * during construction. *

* *

* When using a Task as an anonymous class, the most natural way to pass * parameters to the Task is by using final variables. In this example, * we pass to the Task the total number of times the Task should iterate. *

* *

 *     final int totalIterations = 9000000;
 *     {@literal Task task = new Task()} {
 *         {@literal @Override} protected Integer call() throws Exception {
 *             int iterations;
 *             {@literal for (iterations = 0; iterations < totalIterations; iterations++)} {
 *                 if (isCancelled()) {
 *                     updateMessage("Cancelled");
 *                     break;
 *                 }
 *                 updateMessage("Iteration " + iterations);
 *                 updateProgress(iterations, totalIterations);
 *             }
 *             return iterations;
 *         }
 *     };
 * 
* *

* Since totalIterations is final, the call * method can safely read it and refer to it from a background thread. *

* *

* When writing Task libraries (as opposed to specific-use implementations), * we need to use a different technique. In this case, I will create an * IteratingTask which performs the same work as above. This time, since * the IteratingTask is defined in its own file, it will need to have * parameters passed to it in its constructor. These parameters are * assigned to final variables. *

* *

 *     {@literal public class IteratingTask extends Task} {
 *         private final int totalIterations;
 *
 *         public IteratingTask(int totalIterations) {
 *             this.totalIterations = totalIterations;
 *         }
 *
 *         {@literal @Override} protected Integer call() throws Exception {
 *             int iterations = 0;
 *             {@literal for (iterations = 0; iterations < totalIterations; iterations++)} {
 *                 if (isCancelled()) {
 *                     updateMessage("Cancelled");
 *                     break;
 *                 }
 *                 updateMessage("Iteration " + iterations);
 *                 updateProgress(iterations, totalIterations);
 *             }
 *             return iterations;
 *         }
 *     }
 * 
* *

And then when used:

* *
{@code
 *     IteratingTask task = new IteratingTask(8000000);
 * }
* *

In this way, parameters are passed to the IteratingTask in a safe * manner, and again, are final. Thus, the call method can * safely read this state from a background thread.

* *

WARNING: Do not pass mutable state to a Task and then operate on it * from a background thread. Doing so may introduce race conditions. In * particular, suppose you had a SaveCustomerTask which took a Customer * in its constructor. Although the SaveCustomerTask may have a final * reference to the Customer, if the Customer object is mutable, then it * is possible that both the SaveCustomerTask and some other application code * will be reading or modifying the state of the Customer from different * threads. Be very careful in such cases, that while a mutable object such * as this Customer is being used from a background thread, that it is * not being used also from another thread. In particular, if the background * thread is reading data from the database and updating the Customer object, * and the Customer object is bound to scene graph nodes (such as UI * controls), then there could be a violation of threading rules! For such * cases, modify the Customer object from the FX Application Thread rather * than from the background thread.

* *

 *     {@literal public class UpdateCustomerTask extends Task} {
 *         private final Customer customer;
 *
 *         public UpdateCustomerTask(Customer customer) {
 *             this.customer = customer;
 *         }
 *
 *         {@literal @Override} protected Customer call() throws Exception {
 *             // pseudo-code:
 *             //   query the database
 *             //   read the values
 *
 *             // Now update the customer
 *             Platform.runLater(new Runnable() {
 *                 {@literal @Override} public void run() {
 *                     customer.setFirstName(rs.getString("FirstName"));
 *                     // etc
 *                 }
 *             });
 *
 *             return customer;
 *         }
 *     }
 * 
* *

A Task Which Returns No Value

* *

* Many, if not most, Tasks should return a value upon completion. For * CRUD Tasks, one would expect that a "Create" Task would return the newly * created object or primary key, a "Read" Task would return the read * object, an "Update" task would return the number of records updated, * and a "Delete" task would return the number of records deleted. *

* *

* However sometimes there just isn't anything truly useful to return. * For example, I might have a Task which writes to a file. Task has built * into it a mechanism for indicating whether it has succeeded or failed * along with the number of bytes written (the progress), and thus there is * nothing really for me to return. In such a case, you can use the Void * type. This is a special type in the Java language which can only be * assigned the value of null. You would use it as follows: *

* *

 *     final String filePath = "/foo.txt";
 *     final String contents = "Some contents";
 *     {@literal Task task = new Task()} {
 *         {@literal @Override} protected Void call() throws Exception {
 *             File file = new File(filePath);
 *             FileOutputStream out = new FileOutputStream(file);
 *             // ... and other code to write the contents ...
 *
 *             // Return null at the end of a Task of type Void
 *             return null;
 *         }
 *     };
 * 
* *

A Task Which Returns An ObservableList

* *

Because the ListView, TableView, and other UI controls and scene graph * nodes make use of ObservableList, it is common to want to create and return * an ObservableList from a Task. When you do not care to display intermediate * values, the easiest way to correctly write such a Task is simply to * construct an ObservableList within the call method, and then * return it at the conclusion of the Task.

* *

 *     {@literal Task> task = new Task>}() {
 *         {@literal @Override protected ObservableList call() throws Exception} {
 *             updateMessage("Creating Rectangles");
 *             {@literal ObservableList results = FXCollections.observableArrayList();}
 *             {@literal for (int i=0; i<100; i++)} {
 *                 if (isCancelled()) break;
 *                 Rectangle r = new Rectangle(10, 10);
 *                 r.setX(10 * i);
 *                 results.add(r);
 *                 updateProgress(i, 100);
 *             }
 *             return results;
 *         }
 *     };
 * 
* *

In the above example, we are going to create 100 rectangles and return * them from this task. An ObservableList is created within the * call method, populated, and then returned.

* *

A Task Which Returns Partial Results

* *

Sometimes you want to create a Task which will return partial results. * Perhaps you are building a complex scene graph and want to show the * scene graph as it is being constructed. Or perhaps you are reading a large * amount of data over the network and want to display the entries in a * TableView as the data is arriving. In such cases, there is some shared state * available both to the FX Application Thread and the background thread. * Great care must be taken to never update shared state from any * thread other than the FX Application Thread.

* *

The easiest way to do this is to take advantage of the {@link #updateValue(Object)} method. * This method may be called repeatedly from the background thread. Updates are coalesced to * prevent saturation of the FX event queue. This means you can call it as frequently as * you like from the background thread but only the most recent set is ultimately set.

* *

 *     {@literal Task task = new Task()} {
 *         {@literal @Override} protected Long call() throws Exception {
 *             long a = 0;
 *             long b = 1;
 *             {@literal for (long i = 0; i < Long.MAX_VALUE; i++)} {
 *                 updateValue(a);
 *                 a += b;
 *                 b = a - b;
 *             }
 *             return a;
 *         }
 *     };
 * 
* *

Another way to do this is to expose a new property on the Task * which will represent the partial result. Then make sure to use * Platform.runLater when updating the partial result.

* *

 *     {@literal Task task = new Task()} {
 *         {@literal @Override} protected Long call() throws Exception {
 *             long a = 0;
 *             long b = 1;
 *             {@literal for (long i = 0; i < Long.MAX_VALUE; i++)} {
 *                 final long v = a;
 *                 Platform.runLater(new Runnable() {
 *                     {@literal @Override} public void run() {
 *                         updateValue(v);
 *                     }
 *                 }
 *                 a += b;
 *                 b = a - b;
 *             }
 *             return a;
 *         }
 *     };
 * 
* *

Suppose instead of updating a single value, you want to populate an ObservableList * with results as they are obtained. One approach is to expose a new property on the Task * which will represent the partial result. Then make sure to use * Platform.runLater when adding new items to the partial * result.

* *

 *     {@literal public class PartialResultsTask extends Task>} {
 *         // Uses Java 7 diamond operator
 *         {@literal private ReadOnlyObjectWrapper> partialResults =}
 *                 {@literal new ReadOnlyObjectWrapper<>(this, "partialResults",
 *                         FXCollections.observableArrayList(new ArrayList()));}
 *
 *         {@literal public final ObservableList getPartialResults()} { return partialResults.get(); }
 *         {@literal public final ReadOnlyObjectProperty> partialResultsProperty()} {
 *             return partialResults.getReadOnlyProperty();
 *         }
 *
 *         {@literal @Override protected ObservableList call() throws Exception} {
 *             updateMessage("Creating Rectangles...");
 *             {@literal for (int i=0; i<100; i++)} {
 *                 if (isCancelled()) break;
 *                 final Rectangle r = new Rectangle(10, 10);
 *                 r.setX(10 * i);
 *                 Platform.runLater(new Runnable() {
 *                     {@literal @Override} public void run() {
 *                         partialResults.get().add(r);
 *                     }
 *                 });
 *                 updateProgress(i, 100);
 *             }
 *             return partialResults.get();
 *         }
 *     }
 * 
* *

A Task Which Modifies The Scene Graph

* *

Generally, Tasks should not interact directly with the UI. Doing so * creates a tight coupling between a specific Task implementation and a * specific part of your UI. However, when you do want to create such a * coupling, you must ensure that you use Platform.runLater * so that any modifications of the scene graph occur on the * FX Application Thread.

* *

 *     final Group group = new Group();
 *     {@literal Task task = new Task()} {
 *         {@literal @Override} protected Void call() throws Exception {
 *             {@literal for (int i=0; i<100; i++)} {
 *                 if (isCancelled()) break;
 *                 final Rectangle r = new Rectangle(10, 10);
 *                 r.setX(10 * i);
 *                 Platform.runLater(new Runnable() {
 *                     {@literal @Override} public void run() {
 *                         group.getChildren().add(r);
 *                     }
 *                 });
 *             }
 *             return null;
 *         }
 *     };
 * 
* *

Reacting To State Changes Generically

* *

Sometimes you may want to write a Task which updates its progress, * message, text, or in some other way reacts whenever a state change * happens on the Task. For example, you may want to change the status * message on the Task on Failure, Success, Running, or Cancelled state changes. *

*

 *     {@literal Task task = new Task()} {
 *         {@literal @Override} protected Integer call() throws Exception {
 *             int iterations = 0;
 *             {@literal for (iterations = 0; iterations < 100000; iterations++)} {
 *                 if (isCancelled()) {
 *                     break;
 *                 }
 *                 System.out.println("Iteration " + iterations);
 *             }
 *             return iterations;
 *         }
 *
 *         {@literal @Override} protected void succeeded() {
 *             super.succeeded();
 *             updateMessage("Done!");
 *         }
 *
 *         {@literal @Override} protected void cancelled() {
 *             super.cancelled();
 *             updateMessage("Cancelled!");
 *         }
 *
 *         {@literal @Override} protected void failed() {
 *             super.failed();
 *             updateMessage("Failed!");
 *         }
 *     };
 * 
* @since JavaFX 2.0 */ public abstract class Task extends FutureTask implements Worker, EventTarget { /** * Used to send workDone updates in a thread-safe manner from the subclass * to the FX application thread and workDone related properties. AtomicReference * is used so as to coalesce updates such that we don't flood the event queue. */ private AtomicReference progressUpdate = new AtomicReference<>(); /** * Used to send message updates in a thread-safe manner from the subclass * to the FX application thread. AtomicReference is used so as to coalesce * updates such that we don't flood the event queue. */ private AtomicReference messageUpdate = new AtomicReference<>(); /** * Used to send title updates in a thread-safe manner from the subclass * to the FX application thread. AtomicReference is used so as to coalesce * updates such that we don't flood the event queue. */ private AtomicReference titleUpdate = new AtomicReference<>(); /** * Used to send value updates in a thread-safe manner from the subclass * to the FX application thread. AtomicReference is used so as to coalesce * updates such that we don't flood the event queue. */ private AtomicReference valueUpdate = new AtomicReference<>(); /** * This is used so we have a thread-safe way to ask whether the task was * started in the checkThread() method. */ private volatile boolean started = false; /** * Creates a new Task. */ public Task() { this(new TaskCallable()); } /** * This bit of construction trickery is necessary because otherwise there is * no way for the main constructor to both create the callable and maintain * a reference to it, which is necessary because an anonymous callable construction * cannot reference the implicit "this". We leverage an internal Callable * so that all the pre-built semantics around cancel and so forth are * handled correctly. * * @param callableAdapter non-null implementation of the * TaskCallable adapter */ private Task(final TaskCallable callableAdapter) { super(callableAdapter); callableAdapter.task = this; } /** * Invoked when the Task is executed, the call method must be overridden and * implemented by subclasses. The call method actually performs the * background thread logic. Only the updateProgress, updateMessage, updateValue and * updateTitle methods of Task may be called from code within this method. * Any other interaction with the Task from the background thread will result * in runtime exceptions. * * @return The result of the background work, if any. * @throws Exception an unhandled exception which occurred during the * background operation */ protected abstract V call() throws Exception; private ObjectProperty state = new SimpleObjectProperty<>(this, "state", State.READY); final void setState(State value) { // package access for the Service checkThread(); final State s = getState(); if (s != State.CANCELLED) { this.state.set(value); // Make sure the running flag is set setRunning(value == State.SCHEDULED || value == State.RUNNING); // Invoke the event handlers, and then call the protected methods. switch (state.get()) { case CANCELLED: fireEvent(new WorkerStateEvent(this, WORKER_STATE_CANCELLED)); cancelled(); break; case FAILED: fireEvent(new WorkerStateEvent(this, WORKER_STATE_FAILED)); failed(); break; case READY: // This even can never meaningfully occur, because the // Task begins life as ready and can never go back to it! break; case RUNNING: fireEvent(new WorkerStateEvent(this, WORKER_STATE_RUNNING)); running(); break; case SCHEDULED: fireEvent(new WorkerStateEvent(this, WORKER_STATE_SCHEDULED)); scheduled(); break; case SUCCEEDED: fireEvent(new WorkerStateEvent(this, WORKER_STATE_SUCCEEDED)); succeeded(); break; default: throw new AssertionError("Should be unreachable"); } } } @Override public final State getState() { checkThread(); return state.get(); } @Override public final ReadOnlyObjectProperty stateProperty() { checkThread(); return state; } /** * The onSchedule event handler is called whenever the Task state * transitions to the SCHEDULED state. * * @return the onScheduled event handler property * @since JavaFX 2.1 */ public final ObjectProperty> onScheduledProperty() { checkThread(); return getEventHelper().onScheduledProperty(); } /** * The onSchedule event handler is called whenever the Task state * transitions to the SCHEDULED state. * * @return the onScheduled event handler, if any * @since JavaFX 2.1 */ public final EventHandler getOnScheduled() { checkThread(); return eventHelper == null ? null : eventHelper.getOnScheduled(); } /** * The onSchedule event handler is called whenever the Task state * transitions to the SCHEDULED state. * * @param value the event handler, can be null to clear it * @since JavaFX 2.1 */ public final void setOnScheduled(EventHandler value) { checkThread(); getEventHelper().setOnScheduled(value); } /** * A protected convenience method for subclasses, called whenever the * state of the Task has transitioned to the SCHEDULED state. * This method is invoked on the FX Application Thread after the Task has been fully transitioned to * the new state. * @since JavaFX 2.1 */ protected void scheduled() { } /** * The onRunning event handler is called whenever the Task state * transitions to the RUNNING state. * * @return the onRunning event handler property * @since JavaFX 2.1 */ public final ObjectProperty> onRunningProperty() { checkThread(); return getEventHelper().onRunningProperty(); } /** * The onRunning event handler is called whenever the Task state * transitions to the RUNNING state. * * @return the onRunning event handler, if any * @since JavaFX 2.1 */ public final EventHandler getOnRunning() { checkThread(); return eventHelper == null ? null : eventHelper.getOnRunning(); } /** * The onRunning event handler is called whenever the Task state * transitions to the RUNNING state. * * @param value the event handler, can be null to clear it * @since JavaFX 2.1 */ public final void setOnRunning(EventHandler value) { checkThread(); getEventHelper().setOnRunning(value); } /** * A protected convenience method for subclasses, called whenever the * state of the Task has transitioned to the RUNNING state. * This method is invoked on the FX Application Thread after the Task has been fully transitioned to * the new state. * @since JavaFX 2.1 */ protected void running() { } /** * The onSucceeded event handler is called whenever the Task state * transitions to the SUCCEEDED state. * * @return the onSucceeded event handler property * @since JavaFX 2.1 */ public final ObjectProperty> onSucceededProperty() { checkThread(); return getEventHelper().onSucceededProperty(); } /** * The onSucceeded event handler is called whenever the Task state * transitions to the SUCCEEDED state. * * @return the onSucceeded event handler, if any * @since JavaFX 2.1 */ public final EventHandler getOnSucceeded() { checkThread(); return eventHelper == null ? null : eventHelper.getOnSucceeded(); } /** * The onSucceeded event handler is called whenever the Task state * transitions to the SUCCEEDED state. * * @param value the event handler, can be null to clear it * @since JavaFX 2.1 */ public final void setOnSucceeded(EventHandler value) { checkThread(); getEventHelper().setOnSucceeded(value); } /** * A protected convenience method for subclasses, called whenever the * state of the Task has transitioned to the SUCCEEDED state. * This method is invoked on the FX Application Thread after the Task has been fully transitioned to * the new state. * @since JavaFX 2.1 */ protected void succeeded() { } /** * The onCancelled event handler is called whenever the Task state * transitions to the CANCELLED state. * * @return the onCancelled event handler property * @since JavaFX 2.1 */ public final ObjectProperty> onCancelledProperty() { checkThread(); return getEventHelper().onCancelledProperty(); } /** * The onCancelled event handler is called whenever the Task state * transitions to the CANCELLED state. * * @return the onCancelled event handler, if any * @since JavaFX 2.1 */ public final EventHandler getOnCancelled() { checkThread(); return eventHelper == null ? null : eventHelper.getOnCancelled(); } /** * The onCancelled event handler is called whenever the Task state * transitions to the CANCELLED state. * * @param value the event handler, can be null to clear it * @since JavaFX 2.1 */ public final void setOnCancelled(EventHandler value) { checkThread(); getEventHelper().setOnCancelled(value); } /** * A protected convenience method for subclasses, called whenever the * state of the Task has transitioned to the CANCELLED state. * This method is invoked on the FX Application Thread after the Task has been fully transitioned to * the new state. * @since JavaFX 2.1 */ protected void cancelled() { } /** * The onFailed event handler is called whenever the Task state * transitions to the FAILED state. * * @return the onFailed event handler property * @since JavaFX 2.1 */ public final ObjectProperty> onFailedProperty() { checkThread(); return getEventHelper().onFailedProperty(); } /** * The onFailed event handler is called whenever the Task state * transitions to the FAILED state. * * @return the onFailed event handler, if any * @since JavaFX 2.1 */ public final EventHandler getOnFailed() { checkThread(); return eventHelper == null ? null : eventHelper.getOnFailed(); } /** * The onFailed event handler is called whenever the Task state * transitions to the FAILED state. * * @param value the event handler, can be null to clear it * @since JavaFX 2.1 */ public final void setOnFailed(EventHandler value) { checkThread(); getEventHelper().setOnFailed(value); } /** * A protected convenience method for subclasses, called whenever the * state of the Task has transitioned to the FAILED state. * This method is invoked on the FX Application Thread after the Task has been fully transitioned to * the new state. * @since JavaFX 2.1 */ protected void failed() { } private final ObjectProperty value = new SimpleObjectProperty<>(this, "value"); private void setValue(V v) { checkThread(); value.set(v); } @Override public final V getValue() { checkThread(); return value.get(); } @Override public final ReadOnlyObjectProperty valueProperty() { checkThread(); return value; } private final ObjectProperty exception = new SimpleObjectProperty<>(this, "exception"); private void _setException(Throwable value) { checkThread(); exception.set(value); } @Override public final Throwable getException() { checkThread(); return exception.get(); } @Override public final ReadOnlyObjectProperty exceptionProperty() { checkThread(); return exception; } private final DoubleProperty workDone = new SimpleDoubleProperty(this, "workDone", -1); private void setWorkDone(double value) { checkThread(); workDone.set(value); } @Override public final double getWorkDone() { checkThread(); return workDone.get(); } @Override public final ReadOnlyDoubleProperty workDoneProperty() { checkThread(); return workDone; } private final DoubleProperty totalWork = new SimpleDoubleProperty(this, "totalWork", -1); private void setTotalWork(double value) { checkThread(); totalWork.set(value); } @Override public final double getTotalWork() { checkThread(); return totalWork.get(); } @Override public final ReadOnlyDoubleProperty totalWorkProperty() { checkThread(); return totalWork; } private final DoubleProperty progress = new SimpleDoubleProperty(this, "progress", -1); private void setProgress(double value) { checkThread(); progress.set(value); } @Override public final double getProgress() { checkThread(); return progress.get(); } @Override public final ReadOnlyDoubleProperty progressProperty() { checkThread(); return progress; } private final BooleanProperty running = new SimpleBooleanProperty(this, "running", false); private void setRunning(boolean value) { checkThread(); running.set(value); } @Override public final boolean isRunning() { checkThread(); return running.get(); } @Override public final ReadOnlyBooleanProperty runningProperty() { checkThread(); return running; } private final StringProperty message = new SimpleStringProperty(this, "message", ""); @Override public final String getMessage() { checkThread(); return message.get(); } @Override public final ReadOnlyStringProperty messageProperty() { checkThread(); return message; } private final StringProperty title = new SimpleStringProperty(this, "title", ""); @Override public final String getTitle() { checkThread(); return title.get(); } @Override public final ReadOnlyStringProperty titleProperty() { checkThread(); return title; } @Override public final boolean cancel() { return cancel(true); } // Need to assert the modifyThread permission so an app can cancel // a task that it created (the default executor for the service runs in // its own thread group) // Note that this is needed when running with a security manager. private static final Permission modifyThreadPerm = new RuntimePermission("modifyThread"); @Override public boolean cancel(boolean mayInterruptIfRunning) { // Delegate to the super implementation to actually attempt to cancel this thing // Assert the modifyThread permission @SuppressWarnings("removal") boolean flag = AccessController.doPrivileged( (PrivilegedAction) () -> super.cancel(mayInterruptIfRunning), null, modifyThreadPerm); // If cancel succeeded (according to the semantics of the Future cancel method), // then we need to make sure the State flag is set appropriately if (flag) { // If this method was called on the FX application thread, then we can // just update the state directly and this will make sure that after // the cancel method was called, the state will be set correctly // (otherwise it would be indeterminate). However if the cancel method was // called off the FX app thread, then we must use runLater, and the // state flag will not be readable immediately after this call. However, // that would be the case anyway since these properties are not thread-safe. if (isFxApplicationThread()) { setState(State.CANCELLED); } else { runLater(() -> setState(State.CANCELLED)); } } // return the flag return flag; } /** * Updates the workDone, totalWork, * and progress properties. Calls to updateProgress * are coalesced and run later on the FX application thread, and calls * to updateProgress, even from the FX Application thread, may not * necessarily result in immediate updates to these properties, and * intermediate workDone values may be coalesced to save on event * notifications. max becomes the new value for * totalWork. *

* This method is safe to be called from any thread. *

* * @param workDone A value from Long.MIN_VALUE up to max. If the value is greater * than max, then it will be clamped at max. * If the value passed is negative then the resulting percent * done will be -1 (thus, indeterminate). * @param max A value from Long.MIN_VALUE to Long.MAX_VALUE. * @see #updateProgress(double, double) */ protected void updateProgress(long workDone, long max) { updateProgress((double)workDone, (double)max); } /** * Updates the workDone, totalWork, * and progress properties. Calls to updateProgress * are coalesced and run later on the FX application thread, and calls * to updateProgress, even from the FX Application thread, may not * necessarily result in immediate updates to these properties, and * intermediate workDone values may be coalesced to save on event * notifications. max becomes the new value for * totalWork. *

* This method is safe to be called from any thread. *

* * @param workDone A value from Double.MIN_VALUE up to max. If the value is greater * than max, then it will be clamped at max. * If the value passed is negative, or Infinity, or NaN, * then the resulting percentDone will be -1 (thus, indeterminate). * @param max A value from Double.MIN_VALUE to Double.MAX_VALUE. Infinity and NaN are treated as -1. * @since JavaFX 2.2 */ protected void updateProgress(double workDone, double max) { // Adjust Infinity / NaN to be -1 for both workDone and max. if (Double.isInfinite(workDone) || Double.isNaN(workDone)) { workDone = -1; } if (Double.isInfinite(max) || Double.isNaN(max)) { max = -1; } if (workDone < 0) { workDone = -1; } if (max < 0) { max = -1; } // Clamp the workDone if necessary so as not to exceed max if (workDone > max) { workDone = max; } if (isFxApplicationThread()) { _updateProgress(workDone, max); } else if (progressUpdate.getAndSet(new ProgressUpdate(workDone, max)) == null) { runLater(() -> { final ProgressUpdate update = progressUpdate.getAndSet(null); _updateProgress(update.workDone, update.totalWork); }); } } private void _updateProgress(double workDone, double max) { setTotalWork(max); setWorkDone(workDone); if (workDone == -1) { setProgress(-1); } else { setProgress(workDone / max); } } /** * Updates the message property. Calls to updateMessage * are coalesced and run later on the FX application thread, so calls * to updateMessage, even from the FX Application thread, may not * necessarily result in immediate updates to this property, and * intermediate message values may be coalesced to save on event * notifications. *

* This method is safe to be called from any thread. *

* * @param message the new message */ protected void updateMessage(String message) { if (isFxApplicationThread()) { this.message.set(message); } else { // As with the workDone, it might be that the background thread // will update this message quite frequently, and we need // to throttle the updates so as not to completely clobber // the event dispatching system. if (messageUpdate.getAndSet(message) == null) { runLater(new Runnable() { @Override public void run() { final String message = messageUpdate.getAndSet(null); Task.this.message.set(message); } }); } } } /** * Updates the title property. Calls to updateTitle * are coalesced and run later on the FX application thread, so calls * to updateTitle, even from the FX Application thread, may not * necessarily result in immediate updates to this property, and * intermediate title values may be coalesced to save on event * notifications. *

* This method is safe to be called from any thread. *

* * @param title the new title */ protected void updateTitle(String title) { if (isFxApplicationThread()) { this.title.set(title); } else { // As with the workDone, it might be that the background thread // will update this title quite frequently, and we need // to throttle the updates so as not to completely clobber // the event dispatching system. if (titleUpdate.getAndSet(title) == null) { runLater(new Runnable() { @Override public void run() { final String title = titleUpdate.getAndSet(null); Task.this.title.set(title); } }); } } } /** * Updates the value property. Calls to updateValue * are coalesced and run later on the FX application thread, so calls * to updateValue, even from the FX Application thread, may not * necessarily result in immediate updates to this property, and * intermediate values may be coalesced to save on event * notifications. *

* This method is safe to be called from any thread. *

* * @param value the new value * @since JavaFX 8.0 */ protected void updateValue(V value) { if (isFxApplicationThread()) { this.value.set(value); } else { // As with the workDone, it might be that the background thread // will update this value quite frequently, and we need // to throttle the updates so as not to completely clobber // the event dispatching system. if (valueUpdate.getAndSet(value) == null) { runLater(() -> Task.this.value.set(valueUpdate.getAndSet(null))); } } } /* * IMPLEMENTATION */ private void checkThread() { if (started && !isFxApplicationThread()) { throw new IllegalStateException("Task must only be used from the FX Application Thread"); } } // This method exists for the sake of testing, so I can subclass and override // this method in the test and not actually use Platform.runLater. void runLater(Runnable r) { Platform.runLater(r); } // This method exists for the sake of testing, so I can subclass and override // this method in the test and not actually use Platform.isFxApplicationThread. boolean isFxApplicationThread() { return Platform.isFxApplicationThread(); } /* ************************************************************************* * * * Event Dispatch * * * **************************************************************************/ private EventHelper eventHelper = null; private EventHelper getEventHelper() { if (eventHelper == null) { eventHelper = new EventHelper(this); } return eventHelper; } /** * Registers an event handler to this task. Any event filters are first * processed, then the specified onFoo event handlers, and finally any * event handlers registered by this method. As with other events * in the scene graph, if an event is consumed, it will not continue * dispatching. * * @param the specific event class of the handler * @param eventType the type of the events to receive by the handler * @param eventHandler the handler to register * @throws NullPointerException if the event type or handler is null * @since JavaFX 2.1 */ public final void addEventHandler( final EventType eventType, final EventHandler eventHandler) { checkThread(); getEventHelper().addEventHandler(eventType, eventHandler); } /** * Unregisters a previously registered event handler from this task. One * handler might have been registered for different event types, so the * caller needs to specify the particular event type from which to * unregister the handler. * * @param the specific event class of the handler * @param eventType the event type from which to unregister * @param eventHandler the handler to unregister * @throws NullPointerException if the event type or handler is null * @since JavaFX 2.1 */ public final void removeEventHandler( final EventType eventType, final EventHandler eventHandler) { checkThread(); getEventHelper().removeEventHandler(eventType, eventHandler); } /** * Registers an event filter to this task. Registered event filters get * an event before any associated event handlers. * * @param the specific event class of the filter * @param eventType the type of the events to receive by the filter * @param eventFilter the filter to register * @throws NullPointerException if the event type or filter is null * @since JavaFX 2.1 */ public final void addEventFilter( final EventType eventType, final EventHandler eventFilter) { checkThread(); getEventHelper().addEventFilter(eventType, eventFilter); } /** * Unregisters a previously registered event filter from this task. One * filter might have been registered for different event types, so the * caller needs to specify the particular event type from which to * unregister the filter. * * @param the specific event class of the filter * @param eventType the event type from which to unregister * @param eventFilter the filter to unregister * @throws NullPointerException if the event type or filter is null * @since JavaFX 2.1 */ public final void removeEventFilter( final EventType eventType, final EventHandler eventFilter) { checkThread(); getEventHelper().removeEventFilter(eventType, eventFilter); } /** * Sets the handler to use for this event type. There can only be one such * handler specified at a time. This handler is guaranteed to be called * first. This is used for registering the user-defined onFoo event * handlers. * * @param the specific event class of the handler * @param eventType the event type to associate with the given eventHandler * @param eventHandler the handler to register, or null to unregister * @throws NullPointerException if the event type is null * @since JavaFX 2.1 */ protected final void setEventHandler( final EventType eventType, final EventHandler eventHandler) { checkThread(); getEventHelper().setEventHandler(eventType, eventHandler); } /** * Fires the specified event. Any event filter encountered will * be notified and can consume the event. If not consumed by the filters, * the event handlers on this task are notified. If these don't consume the * event either, then all event handlers are called and can consume the * event. *

* This method must be called on the FX user thread. * * @param event the event to fire * @since JavaFX 2.1 */ public final void fireEvent(Event event) { checkThread(); getEventHelper().fireEvent(event); } @Override public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) { checkThread(); return getEventHelper().buildEventDispatchChain(tail); } /** * A struct like class that contains the last workDone update information. * What we do when updateProgress is called, is we create a new ProgressUpdate * object and store it. If it was null, then we fire off a new Runnable * using RunLater, which will eventually read the latest and set it to null * atomically. If it was not null, then we simply update it. */ private static final class ProgressUpdate { private final double workDone; private final double totalWork; private ProgressUpdate(double p, double m) { this.workDone = p; this.totalWork = m; } } /** * TaskCallable actually implements the Callable contract as defined for * the FutureTask class, and is necessary so as to allow us to intercept * the call() operation to update state on the Task as appropriate. * @param */ private static final class TaskCallable implements Callable { /** * The Task that is going to use this TaskCallable */ private Task task; /** * Create a TaskCallable. The concurrent and other fields MUST be set * immediately after creation. */ private TaskCallable() { } /** * Invoked by the system when it is time to run the client code. This * implementation is where we modify the state and other properties * and from which we invoke the events. * * @return The result of the Task call method * @throws Exception any exception which occurred */ @Override public V call() throws Exception { // If the Task is sent to an ExecutorService for execution, then we // will need to make sure that we transition first to the SCHEDULED // state before then transitioning to the RUNNING state. If the // Task was executed by a Service, then it will have already been // in the SCHEDULED state and setting it again here has no negative // effect. But we must ensure that SCHEDULED is visited before RUNNING // in all cases so that developer code can be consistent. task.started = true; task.runLater(() -> { task.setState(State.SCHEDULED); task.setState(State.RUNNING); }); // Go ahead and delegate to the wrapped callable try { final V result = task.call(); if (!task.isCancelled()) { // If it was not cancelled, then we take the return // value and set it as the result. task.runLater(() -> { // The result must be set first, so that when the // SUCCEEDED flag is set, the value will be available // The alternative is not the case, because you // can assume if the result is set, it has // succeeded. task.updateValue(result); task.setState(State.SUCCEEDED); }); return result; } else { // Since cancelled Future/FutureTask doesn't return any value, // the returned value is going to be trashed, so we can jus return null return null; } } catch (final Throwable th) { // Be sure to set the state after setting the cause of failure // so that developers handling the state change events have a // throwable to inspect when they get the FAILED state. Note // that the other way around is not important -- when a developer // observes the causeOfFailure is set to a non-null value, even // though the state has not yet been updated, he can infer that // it will be FAILED because it can be nothing other than FAILED // in that circumstance. task.runLater(() -> { task._setException(th); task.setState(State.FAILED); }); // Some error occurred during the call (it might be // an exception (either runtime or checked), or it might // be an error. In any case, we capture the throwable, // record it as the causeOfFailure, and then rethrow. However // since the Callable interface requires that we throw an // Exception (not Throwable), we have to wrap the exception // if it is not already one. if (th instanceof Exception) { throw (Exception) th; } else { throw new Exception(th); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy