I am really unfamiliar with working with threads, so I was hoping someone could help me figure out the best way to do this.
I have a JButton in my java application...when you click on the button, I have a Process Builder that creates a process which executes some external python code. The python code generates some files, and this can take some time. When the python code is done executing, I need to load those files into an applet within my Java application.
In its current form, I have a p.waitFor() within the code that calls the external python file...so when you click on the button, the button hangs (the entire application hangs actually) until the process is done. Obviously, I want the user to be able to interact with the rest of the application while this process is going on, but as soon as it's done, I want my application to know about it, so that it can load the files into the applet.
What is the best way to do this?
Thanks for your help.
You should use SwingWorker to invoke the Python process on a background thread. This way your UI will remain responsive whilst the long-running task runs.
// Define Action.
Action action = new AbstractAction("Do It") {
public void actionPerformed(ActionEvent e) {
runBackgroundTask();
}
}
// Install Action into JButton.
JButton btn = new JButton(action);
private void runBackgroundTask() {
new SwingWorker<Void, Void>() {
{
// Disable action until task is complete to prevent concurrent tasks.
action.setEnabled(false);
}
// Called on the Swing thread when background task completes.
protected void done() {
action.setEnabled(true);
try {
// No result but calling get() will propagate any exceptions onto Swing thread.
get();
} catch(Exception ex) {
// Handle exception
}
}
// Called on background thread
protected Void doInBackground() throws Exception {
// Add ProcessBuilder code here!
return null; // No result so simply return null.
}
}.execute();
}
You really want to create a new thread for monitoring your new process. As you've discovered, using just one thread for both the UI and monitoring the child process will make the UI seem to hang while the child process runs.
Here's some example code that assumes the existence of a log4j logger which I think will illustrate one possible approach...
Runtime runtime = Runtime.getRuntime();
String[] command = { "myShellCommand", "firstArgument" };
try {
boolean done = false;
int exitValue = 0;
Process proc = runtime.exec(command);
while (!done) {
try {
exitValue = proc.exitValue();
done = true;
} catch (IllegalThreadStateException e) {
// This exception will be thrown only if the process is still running
// because exitValue() will not be a valid method call yet...
logger.info("Process is still running...")
}
}
if (exitValue != 0) {
// Child process exited with non-zero exit code - do something about failure.
logger.info("Deletion failure - exit code " + exitValue);
}
} catch (IOException e) {
// An exception thrown by runtime.exec() which would mean myShellCommand was not
// found in the path or something like that...
logger.info("Deletion failure - error: " + e.getMessage());
}
// If no errors were caught above, the child is now finished with a zero exit code
// Move on happily
Related
I am actually using sockets listeners in a Java program.
The idea is to let my program run until it catches an external event. As soon as it catches it, I have to update my layout.
In order to let my application run while it was listening on a specific port, I did something like this :
// new thread allows me to let my program continue
new Thread() {
public void run() {
try {
while (true) {
try (ServerSocket listener = new ServerSocket(59090)) {
System.out.println("The server is running...");
// listening
while (true) {
try (Socket socket = listener.accept()) {
// catches event
System.out.println("event caught");
InputStream raw = socket.getInputStream();
headerData = getHeaders(raw);
// some code
// ...
// ...
// Update UI
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
The idea is that I constantly listen on the port 59090 and I execute my specific code once I use that port and that works well.
The problem now is when I catch an event.
It seems that I can't act on the UI if I am not in the "main" Thread. Am I right ?
Is it possible to send an information to my "main thread" in order to tell it to update UI ? Otherwise is it possible to "switch" my main thread to this one ?
EDIT:
I've seen that in Android you could use AsyncTask and the method onPostExecute() allows to send informations to the UI thread.
onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.
Is there an equivalent in Java?
Thank you
I'm trying to call some code within the vaadin framework which is longer running that will update the screen using push, however if the process is taking too long I want to be able to cancel it.
With that in mind I'm trying to use Guava's SimpleTimeLimiter class but no matter what I do I can't seem to stop the Vaadin process from stopping. I've tried both to put the SimpleTimeLimiter inside UI.getCurrent().access() method and outside of it but they both just continue to execute the process even if SimpleTimeLimiter throws a TimeoutException. However if I use the same code with a normal thread it seems to work...
public static void limitExecutionTime(Consumer<UI> lambda)
{
UI currentUI = UI.getCurrent();
UI.getCurrent().access(() ->
{
try
{
SimpleTimeLimiter.create(Executors.newSingleThreadExecutor()).callWithTimeout(new Callable<Void>()
{
#Override
public Void call()
{
// This is needed to deal how Vaadin 8 handles UI's
UI.setCurrent(currentUI);
lambda.accept();
return null;
}
}, 1, TimeUnit.SECONDS);
} catch (TimeoutException | InterruptedException | ExecutionException e) {
NotificationUtils.showError("Execution took beyond the maximum allowed time.");
currentUI.push();
}
});
}
In the above code if the method takes more than 1 second it will throw a TimeoutException and put up the notification window. However it will continue to execute the lambda.
As a result I've tried to do the opposite and put UI.getCurrent().access() in the public Void call() method but this had the exact same result...
You should call UI.access after your background task is ready to update it with some data. You use access method to do changes on the page that the user is viewing.
Background task execution
In your example, you are missing a way to pass task cancellation message to call method. In order to prepare for task cancellation from external event (for example cancel button click) then you need to take this into account in inside the task. The following example shows how you can offer cancel method using Future.cancel.
private void onCancelClick(Button.ClickEvent clickEvent) {
// This method is called from Vaadin UI thread. We will signal
// background task thread to stop.
futureResult.cancel(true);
}
Inside the actual task this can be handled in the following ways
private void simulateLongAndSlowCalculation() {
while (moreWorkTodo) {
if (Thread.currentThread().isInterrupted()) {
return;
}
try {
doSomeBlockingCallThatCanBeInterrupted();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
Starting task and UI.access
When starting task then view should create task and submit it to executor service.
private void onButtonClick(Button.ClickEvent clickEvent) {
// This runTask creates important link to current UI and the background task.
// "this" object in these onTask methods is the UI object that we want
// to update. We need to have someway to pass UI object to background
// thread. UI.getCurrent() could be a parameter that is passed to the
// task as well.
Future<String> futureResult = taskService.runTask(
this::onTaskDone,
this::onTaskCancel,
this::onTaskProgress);
progressDialog = new ProgressDialog(futureResult);
progressDialog.show();
}
Now UI.access method is only needed when we want to update UI. In this example, that can happen in the following cases
Task completed successfully
Task progress was updated
Task got cancelled
Note that all of the following methods this refers to the UI object that started the task. So we are updating the correct UI with result and not some other user's UI.
You should not need to call UI.setCurrent in your code.
private void onTaskProgress(double progress) {
logger.info("onTaskProgress: {}", progress);
access(() -> progressDialog.setProgress(progress));
}
private void onTaskCancel() {
logger.info("onTaskCancel");
access(() -> {
progressDialog.close();
setResult("Cancelled");
});
}
private void onTaskDone(String result) {
logger.info("onTaskDone");
access(() -> {
progressDialog.close();
setResult(result);
});
}
Example project
I pushed another project to github that shows how to cancel a background task from cancel button:
https://github.com/m1kah/vaadin-background-task
Edit: Added sections about background tasks and UI.access. Updated example project link to another example.
I have the following code:
public class Cancelling {
public static void main(String args[]) {
ToBeCancelled tbc = new ToBeCancelled();
ForkJoinPool pool = new ForkJoinPool(1);
Future<?> future = pool.submit(tbc);
try {
Thread.sleep(3000);
} catch (InterruptedException ie) {}
future.cancel(true);
if (future.isCancelled())
System.out.println("The task has been cancelled");
}
}
With the ToBeCancelled class being:
public class ToBeCancelled implements Runnable {
public void run() {
try {
Thread.sleep(5000); // should throw exception here
} catch (Exception e) {
return; // should exit
}
System.out.println("I should never be able to print this");
}
}
The main thread should start, wait for 3 seconds, and then cancel the ToBeCancelled task by using future.cancel(true). It then should print The task has been cancelled, while the task never gets to print its message.
At least, this is what happens when I start it from console.
As I start it from a GUI application with a TextArea where the output is redirected to, that's not the case. The main method does print The task has been cancelled, but the task also prints I should never be able to print this.
This is driving me insane. From what I understand the task should receive its cancel command while on the Thread.sleep(5000) method, which would fire an exception that is consequently caught and makes the thread return. But it doesn't happen and yet the main thinks it has been cancelled. It's like the cancel method is completely ignored by the task.
I've tried everything I could think of, checking on the returned value of cancel, making the task wait longer, using Thread.currentThread().isInterrupted(), but nothing works.
I feel like I'm missing something really simple, but I just can't find what it is. Any idea?
In case anyone thinks it might be something on the GUI application, this is the method that starts the program:
public static void StartProgram(String name) {
try {
Method m = Class.forName(name).getDeclaredMethod("main",String[].class);
Object[] args = new Object[1];
String s[] = new String[2];
s[0] = tf1.getText();
s[1] = tf2.getText();
args[0] = s;
t = new Thread(new ProgramStarter(args, m));
t.start();
} catch (Exception e) {
e.printStackTrace();
}
}
With ProgramStarter being:
public class ProgramStarter implements Runnable {
private Object[] args;
private Method m;
public ProgramStarter(Object args[], Method m) {
this.args = args;
this.m = m;
}
public void run() {
try {
m.invoke(null, args);
} catch (Exception e) {
e.printStackTrace();
}
}
}
The problem is that your verification is wrong. You think that your code works when running from the console but in fact, it fails in all cases. When running from the console your main thread ends after the attempt to cancel the future and the JVM will terminate as there are only daemon threads left in the JVM. Due to the JVM termination you don’t notice that the cancellation did not work.
When adding a sleep at the end of your main method to delay the JVM termination you will notice that "I should never be able to print this" is printed when running from the console as well. So the only difference between GUI and console version is that the running Event Dispatch Thread prevents the JVM from terminating so you see that it doesn’t work.
The bottom line is: don’t use ForkJoinPool unless you have a reason for this.
Since you just want submit to a simple single-background-thread executor, you can create the executor using Executors.newFixedThreadPool(1). This has less unexpected behavior: it’s thread is non-daemon by default and it’s Future will cancel with interruption as expected.
So I have been trying to implement a progress indicator with no luck. I am not sure I understand managing threads with JavaFx very well, despite having read a bit about the Platform.RunLater and Tasks. So here is my use case.
My program allows users to connect to a database and look at some of the schemas and other objects in the database. Sometimes connecting to a large database and pulling up all its tables and info takes a while, so I would like to show a progress indicator. I am not trying to update the progress at all I would just like to make the progress indicator visible at a value of -1 while the process is running to pull everything from the database. Ideally I will have a progress indicator loaded in from an FXML file invisible. When I start the process of pulling info from the database I would like to make it visible.
When trying to make my progress visible it never showed up, so I decide to start out having it visible and making it invisible, just to see what happens. The progress indicator rotated nicely when I opened the program up, but as soon as I try to connect to the database it stopped rotating and just froze. I assume this is what happens when I try to make it visible too which is why it was never showing up.
The following is my current code, I would appreciate any detailed help with explanations so I can understand what is going on. Thanks
from the method that is doing most of the work.
//make progress indicator visible
pi.setVisible(true);
// separate non-FX thread
ExtractorThread t = new ExtractorThread();
t.setCp(cp);
t.start();
//Wait until the thread is done
try{
t.join();
}
catch(Exception e){
e.printStackTrace();
}
//Retrieve the dbextractor from the thread
DbExtractor dbe = t.getDbe();
//move on to the next page in the application
this.caster.goToDataSource(c, cp, dbe);
The ExtractorThread which does the work.
private class ExtractorThread extends Thread{
private ConnectionProperties cp;
private DbExtractor dbe;
public void run() {
dbe = new DbExtractor(cp);
try {
dbe.extract();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public DbExtractor getDbe() {
return dbe;
}
public void setCp(ConnectionProperties cp) {
this.cp = cp;
}
}
If I am supposed to use the Platform.RunLater I am not sure where to use it or why. Any help would be appreciated, thanks!
Use the javafx.concurrent API. Extend Task instead of Thread:
private class ExtractorThread extends Task<DbExtractor>{
private ConnectionProperties cp;
public DbExtractor call() throws Exception {
dbe = new DbExtractor(cp);
dbe.extract();
return dbe;
}
public void setCp(ConnectionProperties cp) {
this.cp = cp;
}
}
Then do:
//make progress indicator visible
pi.setVisible(true);
// separate non-FX thread
final ExtractorThread t = new ExtractorThread();
t.setCp(cp);
t.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
public void handle(WorkerStateEvent event) {
DbExtractor dbExtractor = t.getValue();
this.caster.goToDataSource(c, cp, dbe);
}
});
t.setOnFailed(...); // similarly, to handle exceptions
new Thread(t).start();
I don't code JavaFX, and so I can't give you chapter and verse, but this line:
t.join();
will block the calling code until the background thread is through. Don't do this. Instead use some type of listener to get notified when the background thread finishes. If this were Swing, I'd use a PropertyChangeListener added to a SwingWorker to notify me when the background thread was done. I think that you can still use a PropertyChangeListener to do a similar thing with with JavaFX, but I cannot tell you if this would represent the canonical solution.
Also, don't extend Thread but instead implement Runnable. This won't fix your problem but is basic Java common sense.
I have a program where I am loading a file while at the same time I am displaying a window to inform the user that the file is being loaded. I decided to make a FileLoader class that was a SwingWorker which actually handled loading the file and a ProgressWindow that implements PropertyChangeListener to inform the user about the status of the SwingWorker that was passed into it.
My code currently looks like this:
FileLoader loader = new FileLoader(filePath);
new ProgressWindow(loader, "Loading File", "Loading File");
//ProgressWindow's constructor calls loader.execute() inherited from SwingWorker
doc = loader.get(); //GUI Freezes when called
The problem is that whenever I call loader.get(), it freezes the GUI, thus the progress bar in the Progress Window doesn't run and the whole thing is pointless. As far as I can tell, this is because the thread controlling the GUI is the same thread that calls loader.get(), which goes on hold while loader.execute() is running.
So far, I've tried creating a new thread for either the loader.get() command or the loader.execute() method, and calling SwingUtilities.invokeLater() on the thread, but then the whole program freezes.
I've considered creating a ChangeListener for when SwingWorker.isDone() and then running loader.get(), but this would require some reworking of my code that I would rather not do.
Could anyone tell me what the best way is to get this to work?
get() is like join() in that it will block until called, and will wait for the SwingWorker to finish before being called. Using it wrongly can completely nullify all the advantages of using a SwingWorker in the first place.
Solution: Don't call get() until you know that the SwingWorker is done with its processing, by either calling it in the SwingWorker's done() method, or if you need to call it from the calling code, then in a PropertyChangeListener that has been added to the SwingWorker when the SwingWorker's "state" property is SwingWorker.StateValue.DONE.
Something like:
final FileLoader loader = new FileLoader(filePath);
loader.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
// since DONE is enum, no need for equals(...) method
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
try {
loader.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
});
new ProgressWindow(loader, "Loading File", "Loading File");
Note: code not compiled nor tested
Edit: try/catch added.
So far, I've tried creating a new thread for either the loader.get() command or the loader.execute() method, and calling SwingUtilities.invokeLater() on the thread, but then the whole program freezes.
If you call SwingUtilities.invokeLater() on the thread that will execute the thread in the EDT which freezes the GUI. Instead, run the thread by calling it's start() method and only use SwingUtilities.invokeLater() when you need to update the progress bar in the PropertyChangeListener.
I have create a WorkerThread class which take care of Threads and GUI current/main thread .
i have put my GUI application in construct() method of WorkerThread when an event fire to start XXXServer then all threads are activate and GUI work smoothlly wihout freeze. have a look.
/**
* Action Event
*
* #see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent ae) {
log.info("actionPerformed begin..." + ae.getActionCommand());
try {
if (ae.getActionCommand().equals(btnStart.getText())) {
final int portNumber = 9990;
try {
WorkerThread workerThread = new WorkerThread(){
public Object construct(){
log.info("Initializing the Server GUI...");
// initializing the Server
try {
xxxServer = new XXXServer(portNumber);
xxxServer.start();
btnStart.setEnabled(false);
} catch (IOException e) {
// TODO Auto-generated catch block
log.info("actionPerformed() Start button ERROR IOEXCEPTION..." + e.getMessage());
e.printStackTrace();
}
return null;
}
};workerThread.start();
} catch (Exception e) {
log.info("actionPerformed() Start button ERROR..." + e.getMessage());
e.printStackTrace();
}
} else if (ae.getActionCommand().equals(btnStop.getText())) {
log.info("Exit..." + btnStop.getText());
closeWindow();
}
} catch (Exception e) {
log
.info("Error in ServerGUI actionPerformed==="
+ e.getMessage());
}
}