I have external Processor.process(...) heavy method that can be running for a long time (tens of minutes) in normal case.
I want to show some loading screen with 'Cancel' button and run that Processor in background. I want to stop executing background task when 'Cancel' button clicked.
The problem is that I have a single call to Processor.process(...).
If I had some task in a loop, I would do checks like if( isCancelled() ){ break; } or similar on each iteration.
Is there any way to terminate|kill that heavy background task? Let's assume that Processor.process(...) is very long 'atomic' operation (I just don't want to change all the logic deep behind it to accomplish this single task)
Here is how I start loading screen and background task:
private void handleProcess(ActionEvent event) {
SomeService service = new SomeService();
// ... configuring service
Stage loadingStage = new Stage();
// ... configuring 'loading' stage
service.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
#SuppressWarnings("unchecked")
Entity entity = (Entity) t.getSource().getValue();
// ... do some stuff
loadingStage.close();
}
});
service.start(); // launch background process
loadingStage.show(); // show 'loading' screen
}
UPDATED Here is Service for background job:
private static class SomeService extends Service<Entity> {
private Thread threadToStopOnCancel; // added
// fields, getters, setters omitted
protected Task<Entity> createTask() {
// _params initialization omitted
return new Task<Entity>() {
protected Entity call() throws IOException {
threadToStopOnCancel = Thread.currentThread(); // added
return Processor.process(_params);
}
};
}
#SuppressWarnings("deprecation")
#Override
protected void cancelled() {
if (threadToStopOnCancel != null) { threadToStopOnCancel.stop(); } // added
super.cancelled();
}
For now, I call service.cancel(); from loading screen. But it just sets the service's state to 'CANCELED', and the task is still working in background.
In your service definition you can override the cancelled() method or pass an EventHandler to the setOnCancelled() method.
See here.
Related
I have the requirement to use AWS Simple Workflow (SWF) for an orchestration type of system design. There is parent application that is start this child workflow then signal the workflow to work on activities. I have a workflow that starts up and waits for signals to happen before it can start doing activity work. Once one activity is done then it will report back to by closing out the activity on the parent workflow.
How do I wait for the signal and also use the results from another activity that was invoked by a signal?
Do I need to look into the execution history for the result of an activity and not rely on doing this work in the decide?
Thanks for the help
Code Example:
#SuppressWarnings("unused")
#Slf4j
public class ChildWorkflowImpl implements ChildWorkflow {
private final Settable<Message> firstStepReceived = new Settable<>();
private final Settable<Message> secondStepReceived = new Settable<>();
#Autowired
private FirstActivityClient firstActivityClient;
#Autowired
private SecondActivityClient secondActivityClient;
#Autowired
private AmazonSimpleWorkflow amazonSimpleWorkflow;
#Override
public void startWorkflow(SubsystemMessage subsystemMessage) {
//wait for signal to start
new Task(firstStepReceived) {
#Override
protected void doExecute() throws Throwable {
//Initiate Activity
startStage(firstStepReceived.get(););
}
};
//wait for second signal but pass in data from first activity
new Task(secondStepReceived) {
#Override
protected void doExecute() throws Throwable {
}
};
}
public void firstStep(Message message) {
Promise<FirstActivityResponse> firstActivity = firstActivityClient.execute();
//wait for signal for disable
new Task(firstActivity) {
public void doExecute() {
//report back status for stage by closing parent activity
}
};
}
public void secondStep(FirstActivityResponse response) {
Promise<SecondActivityResponse> secondActivityResponse = secondActivityClient.execute(response);
new Task(secondActivityResponse) {
public void doExecute() {
//report back status for stage
}
};
}
}
You add a signal method to the workflow interface and use Settable to notify the other part of the workflow code about the signal. See Settable documentation from this documentation page.
BTW. I recommend looking at temporal.io which is a greatly improved version of SWF which supports synchronous programming without all these pesky tasks.
I am working on a JavaFX desktop application and I have one button that should read from the memory of an embedded device and print that into a JSON. I have implemented a Task that does that, and this Task is passed as argument to a new thread in the button event handler. The problem is, this only works once. After that, even though new threads are generated on button click, the call() method of the Task is never called again. Here is the code:
The Task definition:
Task readValDaemon = new Task<Void>() {
#Override
public Void call() {
//This functions reads from memory and writes the JSON
readDataHI(connection,commandListHI,statusHI);
return null;
}
};
The Thread creation:
readData.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Thread readValThread = new Thread(readValDaemon);
readValThread.setDaemon(true);
readValThread.start();
}
});
As observed in other answers, a Task is an implementation of FutureTask. From the Task documentation:
As with FutureTask, a Task is a one-shot class and cannot be reused. See Service for a reusable Worker.
So you cannot reuse a task. Second and subsequent attempts to run it will just silently fail.
You could just create a new task directly every time:
private Task<Void> createReadValTask() {
return new Task<Void>() {
#Override
public Void call() {
//This functions reads from memory and writes the JSON
readDataHI(connection,commandListHI,statusHI);
return null;
}
};
}
and then do
readData.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Thread readValThread = new Thread(createReadValTask());
readValThread.setDaemon(true);
readValThread.start();
}
});
You could also consider using a Service, which is designed for reuse. It basically encapsulates the "create a new task every time" functionality, but adds in a lot of useful UI callbacks. A Service also manages a thread pool for you (via an Executor), so you no longer need to worry that you may be creating too many thread. (The Executor can also be specified, if you want to control it.)
So, e.g.:
Service<Void> readValDaemon = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
public Void call() {
//This functions reads from memory and writes the JSON
readDataHI(connection,commandListHI,statusHI);
return null;
}
};
}
};
and then
readData.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
readValThread.restart();
}
});
If the mouse is clicked while the service is already running, this will automatically cancel the already running task, and restart a new one. You could add in checks if you wanted, or bind the disable state of readData to the state of the Service, if you wanted.
Task is kind of the wrong tool for this. It's very purposefully only designed to run once because it's a kind of future. It stores the result (in your case null) as a kind of memoization to avoid doing expensive operations more times than is necessary. So Task is best suited for situations where an expensive computation must be done just once, and usually you would want a result from it at some point down the line.
The documentation for Task is very thorough so I would give that a read.
In your case, just use a plain Runnable. You can use a lambda expression:
readData.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event)
{
Thread readValThread = new Thread(() -> readDataHI(a, b, c));
readValThread.setDaemon(true);
readValThread.start();
}
});
As an aside, creating threads manually isn't considered very good practice in modern Java. Strongly consider an ExecutorService instead.
I have a "little" issue with Service usage.
The code below doesn't work: text value isn't updated in HMI but its value is correct !!?
public class FilterController
{
#FXML
private TextField totalItemCount;
private final Service service = new Service() {
#Override
protected Task createTask()
{
return new Task<Void>() {
#Override protected Void call() throws Exception {
int x = (int) (Math.random() * 10000);
System.out.println("x = " + x);
try {
totalItemCount.setText(Integer.toString(x));
System.out.println("totalItemCount = " + totalItemCount.getText());
}
catch (Throwable ex)
{
System.err.println("Fail");
ex.printStackTrace();
}
return null;
}
};
}
#Override
protected void failed()
{
super.failed();
System.err.println("FAILED");
}
};
#FXML
public void handleFindProblemsEvent()
{
System.out.println("Handle Find Problems");
service.restart();
}
}
I don't have any error. Fail message isn't displayed, so I can think that job has been done but it's not the case.
Is it a bug or a bad usage ?
Thanks for your help.
Note: I use jre1.8.0_25
JavaFX is a single thread GUI toolkit, so every update of a GUI component has to be done on the main application (JavaFX) thread.
What you are doing there, is trying to update a TextField from a background thread and an IllegalStateException will get thrown.
The Task and Service classes are meant to compute something in the background and do a GUI update afterwards.
Like explained over here and over here, you should create a Task<Integer> and return the computed value. If this succeeds, you can retrieve the value in the succeeded() method with getValue() and set the value to the TextField.
The succeeded() method is getting called from the GUI Thread, so its safe to update the TextField here.
You have not called the failed() method anywhere.
I assume you Task is executed in its own thread so you need to sync calls to fx APIs with Platform.runLater
I have a listener to an SWT button which starts like this:
button1.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
Runnable runnable = new Runnable() {
public void run() {
String nextValue = text1.getText();
...
I need the current value of the Text field called text1 in the UI, but the last line getText() fails with
org.eclipse.swt.SWTException: Invalid thread access
I know about syncExec/asyncExec (my code has several) but other threads here at StackOverflow suggest you only need to use it when you want to update a field in the UI. What is the correct way to read a UI field inside a listener?
Here are some code fragments that demonstrate how to run code synchronously & asynchronously (copied from Lars Vogel's VERY useful site).
// Update the user interface asynchronously
Display.getDefault().asyncExec(new Runnable() {
public void run() {
// ... do any work that updates the screen ...
}
});
// Update the user interface synchronously
Display.getDefault().syncExec(new Runnable() {
public void run() {
// do any work that updates the screen ...
// remember to check if the widget
// still exists
// might happen if the part was closed
}
});
I have a window, with a Start- and Stop-Button. The Start-Button starts the algorithm and the stop-button should stop it. I use SwingWorker do run the algorithm in the background and normally calling worker.cancel(true) should stop the algorithm running. I also have a Label, that visualize the Status, e.g. if I press "Stop", then the Labeltext changes to "stopped", so the Problem isnt on the actionLister of the Button.
My code looks like this:
public class MainWindow extends JFrame implements ActionListener, WindowListener
{
// Some code, like generating JFrame, JButtons and other stuff not affencting the task.
Worker worker = new Worker();
public void actionPerformed(ActionEvent e)
{
boolean isStarted = false;
// Start Button
if (e.getSource() == this.buttonStart)
{
if(!isStarted)
{
System.out.println("start");
labelSuccess.setText("Mapping started!");
this.setEnabled(true);
worker.execute();
isStarted = false;
}
}
// Stop Button
if (e.getSource() == this.buttonStop)
{
labelSuccess.setText("Mapping stopped!");
worker.cancel(true);
}
}
class Worker extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
if(!isCancelled())
{
mapp();
Thread.sleep(60);
if (isCancelled()) {
System.out.println("SwingWorker - isCancelled");
}
}
return null;
}
}
At this Point, pressing the Stop-Button causes just a change of the Label-Text, but the algorithm in the background is still running. This now bothers me for quite a while and I just can't get it going.
Thanks a lot for any help, much appreciated.
edit1: I generate a new instance of worker now outside of actionPerformed, so now there is no new Worker generated on every mouse click.
Maybe if you use while instead of if on doInBackground() method of Worker class you will solve your problem. You must to put out of the while loop the mapp(), because you only want to invoke it one time. You should do something like this:
class Worker extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
mapp();
while(!isCancelled()){
Thread.sleep(60);
}
System.out.println("SwingWorker - isCancelled");
return null;
}
This link could be useful to understanding how to use SwingWorker.
EDIT:
As you can see on another questions like this or this, using SwingWorker has some problems to manage the cancel method, because this method Attempts to cancel execution of this task. This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason, like Oracle explains, and those "some other reasons" are discussed on the links I've posted.
You can do solve your problem using directly Threads. Your code would be something like this:
public class MainWindow extends JFrame implements ActionListener, WindowListener
{
// Some code, like generating JFrame, JButtons and other stuff not affencting the task.
final Thread th1 = new Thread(new Runnable() {
#Override
public void run() {
mapp();
}
});
public void actionPerformed(ActionEvent e)
{
boolean isStarted = false;
// Start Button
if (e.getSource() == this.buttonStart)
{
if(!isStarted)
{
System.out.println("start");
labelSuccess.setText("Mapping started!");
this.setEnabled(true);
th1.start();
isStarted = false;
}
}
// Stop Button
if (e.getSource() == this.buttonStop)
{
labelSuccess.setText("Mapping stopped!");
th1.stop();
}
}
This solutions uses the method stop(), which is deprecated, but it works. I've tried using interrupt(), but I don't know why the thread ran till finish the execution of mapp(). Obviously, using stop() is not the best method but it works stopping the mapp() execution before it finishes.
I recommend you to learn more about SwingWorker, Thread and Task to find the best solution to your problem.
Your problem is there is no loop in the worker: if you want to cancel a process using a flag, that process should check the flag from time to time, so if your method Worker.mapp() has to be stopped, check the flag there, no just before and after calling it.