Reputation: 361
I have created a Desktop Application using Java Swing. It takes some input from user, creates a config file and a batch file to run a python scripts. Many concerns are:
- I want the GUI to be in active mode when the batch file execution in progress
- There is a button like ShowLog in the app to check the console output at during execution. That should work on clicking
- I have a "Task in Progress" kind of message in GUI which should be replaced by "Task is Completed" when batch file execution is done
- A "Stop" button also is there to stop the batch file execution forcefully. That should work fine as well
(Note: The batch file execution will take hours to complete)
Can anybody come up with some ideas how I can achieve all these?
Upvotes: 0
Views: 1432
Reputation: 347244
As you seem to be aware, Swing is a single threaded framework, which means that anything that is run within the context of the Event Dispatching Thread will prevent it from updating the screen or responding to user input.
The basic solution would be to use a Thread
to run the batch process in, but this raises issues with synchornisation of updates to the UI, as you should never modify or interact with the UI from outside the context of the EDT.
A better solution would be to use a SwingWorker
, which provides you with the ability to run long running tasks in the background, but provides you with the ability to publish
updates to and process
updates within the context of the EDT, it also provides you with a done
method which is called after the doInBackground
method exits and is called within the context of the EDT.
Finally, it provides you with a cancel
option - This, however is where the problem occurs. Presumably you will be reading the input from the process in a secondary thread and will be waiting
for the process to exit within the same thread (SwingWorker
) you started it. SwingWorker
relies on the interrupt
funcitonality of Thread
which may not trigger the waitFor
method to return.
Having now gone a read the Process
documentation, waitFor
does throw an InterruptedException
if the current thread is interrupted by another thread while it is waiting, then the wait is ended and an InterruptedException is thrown.
This would suggest that when done
is called, you would need to call isCancelled
to check if the worker was cancelled or not. If it was you would need to call destroy
on the Process
and shut down any secondary Thread
s you might have running.
You could use an additional SwingWorker
to read the input from the process and utilise it's publish
/process
functionality to update the logs.
This would mean, you would start a SwingWorker
to execute your external process. This would presumably be done in response to some event, like a button push.
When this worker's doInBackground
method is called, it would execute the external process and call Process#waitFor
. This would stop the doInBackground
method from returning until the process has exited.
Before you call Process#waitFor
, you could create another SwingWorker
and pass the Process
's OutputStream
to it. This would allow this worker to process the output from the process independently. You would then be able to use this to send output of the process back to the EDT via the SwingWorker
's publish
/process
functionality which could be added to something like a JTextArea
.
This would save you a lot of hassle with dealing with SwingUtilities.invokeLater
.
Do you need the second work? That depends on what you want to the workers to do. I tend to process all the output of external process in separate threads and allow who ever created the process to use waitFor
, it isolates the responsibility a little more and prevents the IO from getting locked up an never reaching waitFor
, but that's just me.
Take a look at Concurrency in Swing for more details
Upvotes: 1
Reputation: 8347
You can run bat file in java with Runtime
Runtime.getRuntime().exec("cmd /c start your_batch_file.bat");
Upvotes: 0