Nothing happens until the external python command finish running in java - java

I have a logText(String text) function which basically shows some text in a text area. I need some text to be shown before running the external python command. But the text is shown after the execution of external python command.
Here is my code.
logText("Please Wait Until The Testing Is Finished");
logText("Starting Testing...");
String command = "python3 python/Predict.py";
try {
String line;
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
error.close();
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
input.close();
OutputStream outputStream = process.getOutputStream();
PrintStream printStream = new PrintStream(outputStream);
printStream.println();
printStream.flush();
printStream.close();
logText("Images Created At Output Directory");
logText("Testing Completed");
} catch (IOException ex) {
Logger.getLogger(MasterFrame.class.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(MasterFrame.class.getName()).log(Level.SEVERE, null, ex);
}
My logText() function.
private void logText(String logText) {
if (logArea.getText().equals("")) {
logArea.setText(">>> " + logText);
} else {
logArea.append(System.lineSeparator() + ">>> " + logText);
}
}
How can I show the text in the text area before execution of python command?

what you are doing is a classic example of running long-running code on GUI thread and as a result, freezing the main thread. The solution is the same as always (no matter what language or framework): run the long-running work in a background thread such as a new Thread(...) or a SwingWorker.
for example:
logText("Please Wait Until The Testing Is Finished");
logText("Starting Testing...");
String command = "python3 python/Predict.py";
new Thread(new Runnable() {
public void run() {
try {
String line;
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
error.close();
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
input.close();
OutputStream outputStream = process.getOutputStream();
PrintStream printStream = new PrintStream(outputStream);
printStream.println();
printStream.flush();
printStream.close();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Here, we can safely update the GUI
// because we'll be called from the
// event dispatch thread
logText("Images Created At Output Directory");
logText("Testing Completed");
}
});
} catch (IOException ex) {
Logger.getLogger(MasterFrame.class.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(MasterFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
}).start();
p.s. as you can see in the comments the last two commands in the new thread needed to be called with SwingUtilities.invokeLater(...).

Related

Starting external application inside Java

I'm having trouble starting an application from my JavaFX GUI. I'm using ProcessBuilder. It creates the process, but the application won't launch until I close my Java program. Is it because that specific program is waiting for arguments or something wrong with my code?
#FXML
private void runWorldpac() {
try {
ProcessBuilder process = new ProcessBuilder("C:\\speedDIAL\\speedDIAL.exe");
Process p = process.start();
} catch (IOException e) {
e.printStackTrace();
}
}
External application starts but won't allow any interaction with the original application until i close this external program. Tried running a new thread, same result.
Here's the new code:
try {
ProcessBuilder process = new ProcessBuilder("C:\\speedDIAL\\speedDIAL.exe");
Map<String, String> environ = process.environment();
Process p = process.start();
InputStream is = p.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
//System.out.println(line);
}
System.out.println("Program terminated!");
} catch (IOException e) {
e.printStackTrace();
}
Read that article, good info. Also read another good example on here. It's running in a new thread now, but my program is waiting for the external application to finish before it continues, I understand that's usually desired, but not in this case, how can i disable that?
Wait for the production of the exit value in a new thread. Something like:
try {
ProcessBuilder pBuilder = new ProcessBuilder("C:\\speedDIAL\\speedDIAL.exe");
// don't forget to handle the error stream, and so
// either combine error stream with input stream, as shown here
// or gobble it separately
pBuilder.redirectErrorStream(true);
final Process process = pBuilder.start();
final InputStream is = process.getInputStream();
// in case you need to send information back to the process
// get its output stream. Don't forget to close when through with it
final OutputStream os = process.getOutputStream();
// thread to handle or gobble text sent from input stream
new Thread(() -> {
// try with resources
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is));) {
String line = null;
while ((line = reader.readLine()) != null) {
// TODO: handle line
}
} catch (IOException ex) {
ex.printStackTrace();
}
}).start();
// thread to get exit value from process without blocking
Thread waitForThread = new Thread(() -> {
try {
int exitValue = process.waitFor();
// TODO: handle exit value here
} catch (InterruptedException e) {
e.printStackTrace();
}
});
waitForThread.start();
// if you want to join after a certain time:
long timeOut = 4000;
waitForThread.join(timeOut);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}

processbuilder monitor log file in remote server

I am trying to:
Execute Bash script
Read log file
I have following code using processBuilder which has readLog function that reads log file and executeBash which executes the command and then destroys processbuilder's readlog process. But as readLog file is reading a live log file executeBash never gets called.
How do I read the log file in real time and display it simultaneously with executeBash?
public void performAction(OssSystem system) {
readLog(system);
executeBash(system);
}
public void executeBash(OssSystem system) {
try {
Process process = new ProcessBuilder().command("/bin/sh", system.getCommand_path(), system.getParam()).start();
BufferedReader reader
= new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder builder = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
int status = process.waitFor();
cmdResult = builder.toString() + " command status " + status;
int exitValue = process.exitValue();
readProcess.destroy();
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
String logOutput;
Process readProcess;
public void readLog(OssSystem system) {
String user = system.getUsername() + "#" + system.getIp();
String cmd = "tail -f " + system.getLog_dir() + " | less";
try {
readProcess = new ProcessBuilder().command("/usr/bin/ssh", user, cmd).start();
int status = readProcess.waitFor();
BufferedReader reader
= new BufferedReader(new InputStreamReader(readProcess.getInputStream()));
StringBuilder builder = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
logOutput = builder.toString() + " command status " + status;
} catch (IOException ex) {
Logger.getLogger(Shell.class.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(Shell.class.getName()).log(Level.SEVERE, null, ex);
}
}
According to javadoc,
Causes the current thread to wait, if necessary, until the process
represented by this Process object has terminated. This method returns
immediately if the subprocess has already terminated. If the
subprocess has not yet terminated, the calling thread will be blocked
until the subprocess exits.
So the readProcess.waitFor(); will block the main thread and you can never expect executeBash(system); to be called before readProcess has terminated.
You can start a thread to launch readProcess.
public void performAction(OssSystem system) {
readLog(system);
ExecuteBashThread thread = new ExecuteBashThread(system);
thread.start();
}
class ExecuteBashThread extends Thread{
private OssSystem system;
public ExecuteBashThread(OssSystem system){
this.system = system;
}
#Override
public void run(){
executeBash(system);
}
}

Saving errors to a file handler

With the code below, I'm trying to simulate the command shell, I even created a command and called it (Showerrlog) to help the user seeing his invalid commands that he entered during his current work session, as you can see, I did that using filehandler, which will save the wrong commands in a log file. But as you know filehandler will start a new file for each new working session, and the new file will be named as (file.log, file.log.1, file.log.2, etc) and so on, the question is: how to make the program to avoid opening a new file everytime, in other words isn't there any other way that the program will just format the previous work session and add the new one instead?
Or at least how to make the program open the last log file which belongs to the current work session ?
public class WithEyul implements Runnable {
String command;
public WithEyul(String command) {
this.command = command;
}
#Override
public void run() {
List<String> input = new ArrayList<String>();
StringTokenizer tokenizer = new StringTokenizer(command);
while (tokenizer.hasMoreTokens()) {
input.add(tokenizer.nextToken());
}
ProcessBuilder pb = new ProcessBuilder(input);
// ProcessBuilder creates a process corresponding to the input command
// now start the process
BufferedReader br = null;
try {
Process proc = pb.start();
// obtain the input and output streams
InputStream is = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
br = new BufferedReader(isr);
// read what the process returned
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
} catch (java.io.IOException ioe) {
try {
System.err.println("Error");
Logger logger = Logger.getLogger("Testing");
FileHandler fh = new FileHandler("E:/MyLogFile.log");
logger.addHandler(fh);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter);
logger.info(command);
} catch (SecurityException e) {
printStackTrace();
} catch (IOException ex) {
Logger.getLogger(WithEyul.class.getName()).log(Level.SEVERE, null, ex);
}
} finally {
if (br != null) {
try {
br.close();
} catch (IOException ex) {
Logger.getLogger(WithEyul.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}
and here is the main method class
public class TestProcessBuilder {
static void createProcess(String command) throws java.io.IOException {
Thread t = new Thread(new WithEyul(command));
t.start();
}
public static void main(String[] args) throws java.io.IOException {
String commandLine;
File wd;
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
System.out.println("\n\n***** Welcome to the Java Command Shell *****");
System.out.println("If you want to exit the shell, type END and press RETURN.\n");
// we break out with ‘END’
while (true) {
// show the Java shell prompt and read what command they entered
System.out.print("jsh>");
commandLine = console.readLine();
// if user entered a return, just loop again
if (commandLine.equals("")) {
continue;
}
if (commandLine.equalsIgnoreCase("Showerrlog")) {
try {
// Runtime.getRuntime().exec("E:\\MyLogFile.log");
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().open(new File("E:\\MyLogFile.log"));
}
} catch (IOException ex) {
Logger.getLogger(WithEyul.class.getName()).log(Level.SEVERE, null, ex);
}
}
if (commandLine.toLowerCase().equals("end")) { //User wants to end shell
System.out.println("\n***** Command Shell Terminated. See you next time. BYE for now. *****\n");
System.exit(0);
}
createProcess(commandLine);
}
}
}
You could use the FileHandler constructor that allows you to specify the rotation, limit, and append options.
new FileHandler("E:/MyLogFile.log", 0, 1, true);
The FileHandler can rotate for a number of reasons that are out of your control. If you don't want to deal with file rotation you could open a FileOutputStream and wrap that with a StreamHandler. However, you will have to handle file locking conflicts.
You should also avoid creating and adding a handler that points to the same target file everytime an error is generated. You should install the handler on startup and store a string reference to your logger.

Runtime.exec() won't finish properly

I've created a WebApp that relies on external scripts to gather query request by the user (internal software). I've tested, with sucess, the WebApp off the glassfish server provided by netbeans but whenever I try and upload my App to a third party server (Apache Tomcat) I run into the issue of the process.getRuntime exitValue never being written and the WebApp never gets to the result page....
This is the code that I have implemented so far:
Update --> The code now works after reading both stderr and stdin:
pd = new ProcessBuilder("runscript.bat");
pd.redirectErrorStream(true);
process = pd.start();
StreamGobbler inputGobbler = new StreamGobbler(process.getInputStream(), "Input");
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(),"Error");
errorGobbler.start();
inputGobbler.start();
int exitVal = -1;
try {
exitVal = process.waitFor();
} catch (InterruptedException ex) {
//log Error
}
class StreamGobbler extends Thread {
OutputWriterReader outWR = new OutputWriterReader();
BufferedWriter deadWriter;
InputStream is;
// reads everything from is until empty.
StreamGobbler(InputStream is, String type) {
this.is = is;
createWatch(type);
}
// depending on if Error stream or Input Stream
private void createWatch(String type){
try {
if(type.equals("Error"))
deadWriter = new BufferedWriter(new FileWriter("StdError.txt"));
else if(type.equals("Input"))
deadWriter = new BufferedWriter(new FileWriter("StdInput.txt"));
} catch (IOException ex) {
//log Error
}
}
#Override
public void run() {
try {
InputStreamReader isr = new InputStreamReader(this.is);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ( (line = br.readLine()) != null)
deadWriter.append(line);
deadWriter.flush();
deadWriter.close();
} catch (IOException ioe) {
//log Error
}
}
}
Any Suggerstions? Thanks in advance for any help
The process may not be complete when you call exitValue() on it.
Before process.exitValue() call add:
process.waitFor();

Reading streams from java Runtime.exec

I have the following snippet of code:
Process proc = runtime.exec(command);
errorGobbler = new ErrorStreamGobbler(proc.getErrorStream(), logErrors, mdcMap);
outputGobbler = new OutputStreamGobbler(proc.getInputStream(), mdcMap);
executor.execute(errorGobbler);
executor.execute(outputGobbler);
processExitCode = proc.waitFor();
where the gobblers are Runnables which use a BufferedReader to read the input and error streams of the executing process. While this works most of the time, I get the occasional window (of about 2 minutes or so) where I get the processExitCode as 0, which indicates normal termination but there is nothing in the input and error streams - nothing to even indicate end-of-stream.
Like I indicated before, this works most of the time but this failure occurs every once in a while - and I am totally puzzled. Any ideas?
Rags
I've struggled with the same kind of issues.
I can't remember what exactly was wrong (maybe I forgot to flush / close the streams correctly or something ...).
Anyway, here is what I came up with.
/**
* Handle communication with a process, reading its output/error and feeding its input
* #param process The process to execute
* #param _in Reader that will feed the input pipe of the process
* #param out Writer that will receive the output of the process
* #param err Writer that will receive the error pipe of the process
*/
public static void communicate(
Process process,
final Reader _in,
final Writer out,
final Writer err)
{
// Buffer the input reader
final BufferedReader in = new BufferedReader(_in);
// Final versions of the the params, to be used within the threads
final BufferedReader stdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
final BufferedReader stdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
final BufferedWriter stdIn = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
// Thread that reads std out and feeds the writer given in input
new Thread() {
#Override public void run() {
String line;
try {
while ((line = stdOut.readLine()) != null) {
out.write(line + newline);
}
} catch (Exception e) {throw new Error(e);}
try {
out.flush();
out.close();
} catch (IOException e) { /* Who cares ?*/ }
}
}.start(); // Starts now
// Thread that reads std err and feeds the writer given in input
new Thread() {
#Override public void run() {
String line;
try {
while ((line = stdErr.readLine()) != null) {
err.write(line + newline);
}
} catch (Exception e) {throw new Error(e);}
try {
err.flush();
err.close();
} catch (IOException e) { /* Who cares ?*/ }
}
}.start(); // Starts now
// Thread that reads the std in given in input and that feeds the input of the process
new Thread() {
#Override public void run() {
String line;
try {
while ((line = in.readLine()) != null) {
stdIn.write(line + newline);
}
} catch (Exception e) {throw new Error(e);}
try {
stdIn.flush();
stdIn.close();
} catch (IOException e) { /* Who cares ?*/ }
}
}.start(); // Starts now
// Wait until the end of the process
try {
process.waitFor();
} catch (Exception e) {
throw new Error(e);
}
} // End of #communicate
I hope this helps.

Categories