Abhishek Mukherjee
Abhishek Mukherjee

Reputation: 361

Launch a windows batch file from Java GUI Application

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

Answers (2)

MadProgrammer
MadProgrammer

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 Threads 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

Dark Knight
Dark Knight

Reputation: 8347

You can run bat file in java with Runtime

Runtime.getRuntime().exec("cmd /c start your_batch_file.bat");

Upvotes: 0

Related Questions