I am wanting to execute a task at a regular interval from my JavaFX application. The task pulls data from a remote stream.
While I know I could use a Timer as suggested below:
JavaFX periodic background task
I believe this should be able to be done using the JavaFX Service object. There's mention in the Javadoc about specifying a custom executor (ScheduledThreadPoolExecutor comes to mind here), but how would one specify the period and delay? Ideally, this would use the Service's usual start, reset, restart, and state bindings...
public class MyFirstLineService extends Service<String> {
private StringProperty url = new SimpleStringProperty(this, "url");
public final void setUrl(String value) { url.set(value); }
public final String getUrl() { return url.get(); }
public final StringProperty urlProperty() { return url; }
public MyFirstLineService() {
setExecutor(new ScheduledThreadPoolExecutor());
}
protected Task createTask() {
final String _url = getUrl();
return new Task<String>() {
protected String call() throws Exception {
URL u = new URL(_url);
BufferedReader in = new BufferedReader(
new InputStreamReader(u.openStream()));
String result = in.readLine();
in.close();
return result;
}
};
}
}
A ScheduledService was requested in the JavaFX issue tracker - RT18702.
The tracker includes source for a preliminary implementation which has not been incorporated in the 2.2 branch. If needed, you could take a look at that source and see if it helps improve on your solution.
I have found a way to do this, building on the comments from sarcan above...
One can create a Timeline object, normally used to animate UI elements, as a timer that operates on the FX Application Thread. Using this its possible to restart the Service object, which then performs the long-running operation on a background thread, yet preserves property access and updates via the FX Application Thread bindings.
Ex:
final MyFirstLineService svc = new MyFirstLineService();
final Duration oneFrameAmt = Duration.seconds(5);
final KeyFrame oneFrame = new KeyFrame(oneFrameAmt,
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent evt) {
svc.restart();
}
});
Timeline timer = TimelineBuilder.create()
.cycleCount(Animation.INDEFINITE)
.keyFrames(oneFrame)
.build();
timer.playFromStart();
Run into the same problem. An example with ScheduledThreadPoolExecutor could be:
ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
Service s = new Service(){
#Override
protected Task<String> createTask() {
return new Task<String>() {
#Override
protected String call() throws Exception {
// code...
}
};
}
}
// created tasks shall be run in thread pool
s.setExecutor(executor);
// start the service the first time
s.start();
// restart the service every 5 seconds
s.scheduleWithFixedDelay(new Runnable() {
#Override
public void run() {
// need runLater here since trigger needs to be in the javaFX thread
Platform.runLater(new Runnable() {
#Override
public void run() {
checker.restart();
}
});
}
}, 0, 5, TimeUnit.SECONDS);
}
Could be solved with a Timer but then you lost the JavaFX data binding capability (as you already mentioned). Looking forward to Java8!
Related
I'm making a program which sends some data to the Matlab and receives output from matlab. To do that, I used matlabcontrol library.
Problem
After I click Java FX application button to submit data, matlabcontrol opens Matlab for further calculations. But when java opens the matlab, Java FX application stuck with wait cursor.Then starts to work again after Matlab finishes the process of calculation.
What I did
public void runner()
{
Platform.runLater(new Runnable()
{
#Override
public void run()
{
firstFunc();
}
});
Platform.runLater(new Runnable()
{
#Override
public void run()
{
secondFunc();
}
});
}
public void firstFunc()
{
// This function controls UI while Matlab does it's calculations
double progress = 0.2;
progressLabel.setText(progress*100+"% Completed");
progressBar.setProgress(progress);
}
public void secondFunc()
{
// This method creates matlab connection and handle matlab
firstClass mainFunc = new firstClass(pathSelected);
mainFunc.func();
}
So I used Platform runLater to run two methods separately. But still my program stuck with wait cursor when Matlab starts to functioning.
I also used threads to run these functions in parallel. But had the same issue. How can I correct this. Any help?
Update
As described in this question, I did use service and task with countdownlatch. But still didn't get what I wanted. In there,
Service<Void> service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
//Background work
final CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(new Runnable() {
#Override
public void run() {
try{
//FX Stuff done here
firstFunc();
}finally{
latch.countDown();
}
}
});
latch.await();
//Keep with the background work
// I added matlab calling function here.
secondFunc();
return null;
}
};
}
};
service.start();
latch await and let background work to carry on. But in my case, my FX application shows a progress bar. So it should always update while background task happens. In here, it finishes FX task and moves to background task. I didn't get what I wanted. please help.
I had to use Service, Task and CountDownLatch to accomplish this task as I mentioned in Question Update part.
Also, I had to run another task inside the firstFunc, where I did update the progress. Like in this answer.
i wrote a small application, which among other things makes a REST call to a JIRA REST API do download issues. I have a progress bar indicating the progress of the download. However, actually i need to make two REST calls. The first one needs to finished before the second one starts. Between them i need to update the UI.
Below is some example code:
// Loads the dialog from the fxml file and shows it
#FXML
public void showProgressDialog() {
mainApp.showDownloadProgressDialog();
}
// Retrieves some meta information
public void firstDownload() {
Task<Void> task = new Task<Void>() {
#Override
public Void call() {
// do something
}
};
new Thread(task).start();
}
// Do some UI update
// Retrieves the actual issues
public void secondDownload() {
Task<Void> task = new Task<Void>() {
#Override
public Void call() {
for (int i = 0; i < limit; i++) {
// so something
updateProgress((double) i / (double) limit, max);
}
}
};
new Thread(task).start();
}
// Do some UI update
How can i ensure that all functionality shown above is executed in exactly this order?
Thanks for any help!
You could use a EcecutorService with a single thread to control the scheduling:
ExecutorService service = Executors.newSingleThreadExecutor();
service.submit(task1);
service.submit(task2);
// shutdown after last submitted task
service.shutdown();
For JavaFX UI node, if I register an event handler:
final MenuItem buyItem = new MenuItem("Buy");
buyItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
String symbol = row.getItem().getSymbol();
String instrumentID = row.getItem().getInstrumentID();
.....
}
);
I can assume code inside handle() will always be executed in JavaFX application Thread, so there is no need to wrap them inside Platform. runLater.
But when I work with javafx.concurrent.Task:
import javafx.concurrent.Task;
public class BuyTask extends Task<Map<String, Object>> {
......
}
BuyTask buyTask = new BuyTask(this.api, params);
Thread buyThread = new Thread(buyTask);
buyThread.start();
buyTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(final WorkerStateEvent workerStateEvent) {
Map<String, Object> result = (Map) workerStateEvent.getSource().getValue();
.......
Platform.runLater(new Runnable() {
#Override public void run() {
portfolioService.restart();
}
});
}
}
In which thread is the task's event handler executed in? As I need to do perform restart on a javafx.concurrent.ScheduledService -> portfolioService.restart() which mentioned must be done in JavaFX Application Thread, I wrap it inside Platform.runLater.
But is it required? Will this task event handler always being executed in JavaFX Application Thread as well? Thanks!
The JavaFX documentation for Task has the answer:
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.
So - no, there is no need to wrap the call with Platform.runLater.
I'd like to effectively "poll" on a JavaFX TableView so that if a job is created in the database by another user the current user would pick it up (let's say every 5 seconds).
I have tried using Timer;
new Timer().schedule(new TimerTask() {
#Override
public void run() {
try {
newI(connection, finalQuery, adminID);
} catch (SQLException e) {
e.printStackTrace();
}
}
}, 0, 5000);
However this gets the following error: Exception in thread "Timer-0" java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = Timer-0 which I assume means that it is not supported in JavaFX? How am I able to periodically update the TableView in JavaFX?
You can use a ScehduleService- something like this...
private class MyTimerService extends ScheduledService<Collection<MyDTO>> {
#Override
protected Task<Collection<MyDTO>> createTask() {
return new Task<Collection<MyDTO>>() {
#Override
protected Collection<MyDTO> call() throws ClientProtocolException, IOException {
//Do your work here to build the collection (or what ever DTO).
return yourCollection;
}
};
}
}
//Instead of time in your code above, set up your schedule and repeat period.
service = new MyTimerService () ;
//How long the repeat is
service.setPeriod(Duration.seconds(5));
//How long the initial wait is
service.setDelay(Duration.seconds(5));
service.setOnSucceeded(event -> Platform.runLater(() -> {
//where items are the details in your table
items = service.getValue();
}));
//start the service
service.start();
I'm implementing a GUI for a console application, and I need to do some actions (for example, parse an XML file) in a specified time interval. I decided to use javax.swing.Timer alongside SwingWorker to be sure that these actions will not make my application unresponsive.
I had implemented the timer this way:
public class DataUpdateTimer extends Timer {
private String dataFlowControllerXML = null;
private DataUpdateWorker dataUpdateWorker = null;
public class DataUpdateWorker extends SwingWorker {
private String dataFlowControllerXML = null;
DataUpdateWorker(String dataFlowControllerXML) {
super();
this.dataFlowControllerXML = dataFlowControllerXML;
}
#Override
protected Boolean doInBackground() throws Exception {
Thread.sleep(300);
return Boolean.TRUE;
}
}
public class DataUpdateIntervalListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
DataUpdateTimer timer = (DataUpdateTimer)e.getSource();
DataUpdateWorker dataUpdateWorker = timer.getDataUpdateWorker();
if (dataUpdateWorker != null)
if (dataUpdateWorker.isDone()) {
Boolean updateResult = Boolean.FALSE;
try {
updateResult = (Boolean)dataUpdateWorker.get();
} catch (InterruptedException ex) {
} catch (ExecutionException ex) {
}
dataUpdateWorker = null;
}
// Creating new worker thread here
if (dataUpdateWorker == null) {
timer.dataUpdateWorker = new DataUpdateWorker(timer.dataFlowControllerXML);
// Starting a new worker thread for parsing Data Flow Controller's XML
timer.dataUpdateWorker.execute();
return;
}
}
}
DataUpdateTimer(Integer dataUpdateInterval, String dataFlowControllerXML) {
super(dataUpdateInterval.intValue(), null);
this.dataFlowControllerXML = dataFlowControllerXML;
addActionListener(new DataUpdateIntervalListener());
}
#Override
public void stop() {
super.stop();
if (dataUpdateWorker != null) {
if (!dataUpdateWorker.isDone() || !dataUpdateWorker.isCancelled())
dataUpdateWorker.cancel(true);
}
}
}
...and use it as follows:
new DataUpdateTimer(1000, dataFlowControllerXML).start();
Everything works as I wish. Timer creates new a SwingWorker instance and executes it. After the worker is done, the new one is created and executed.
The thing I'm confused by is that after the worker's thread is done I still can see it running in Netbeans' debugging window (for example, as SwingWorker-pool-3-thread-1) or in Windows Task Manager (the number of running threads doesn't decrease after the thread is done). The number of SwingWorker threads is limited to 10, but having them running embarrasses me.
In the case of simple thread usage:
Thread th = new Thread(new Runnable() {
public void run() {
int a = 0;
}
});
th.start();
This thread automatically disappears after execution.
Is this SwingWorker behavior normal?
Yes, this is normal. As the thread's name suggests, the swing workers' model (background) actions are being delegated to a thread pool. When the work is done the thread is returned to the pool so another worker can use it. This eliminates some overhead in creating/destroying threads which can be expensive.
By the way, the background threads won't stick around forever. Looking at the source for SwingWorker I see:
//from SwingWorker.java (c) Sun Microsystems/Oracle 2009
executorService =
new ThreadPoolExecutor(1, MAX_WORKER_THREADS,
10L, TimeUnit.MINUTES,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
This indicates that the threads will die off after being idle for 10 minutes.