Process won't run unless printing output + Processbuilder - java

I've come across a strange issue. I've used process builder several times to call an executable from a program but have never encountered this before. For debug purposes I made a method which prints the output of the executable to System.out. Everything worked fine and my program nicely exported all of the test gifs I ran.
When it came time to run this program properly for 1000+ gifs I commented out the printout method to improve performance. Once the whole program had run I come back to find that the exportGif did not work. The program ran with no errors but the calling of the process simply did not export the gifs as expected.
After isolating lines in the printout method it seems that the deciding bit of code is the reader.readLine(). Why would this be the case? The executable should have already run, the debug method should only read the output stream after the fact, correct? I'd rather not loop through it's output stream every time as it causes the program to slow considerably.
private void printProcessOutput(Process process){
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder builder = new StringBuilder();
String line = null;
try{
while ( (line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
}catch(IOException e){
e.printStackTrace();
}
System.out.println(builder.toString());
}
private void exportGIF(String dirPath) throws IOException {
List<String> lines = Arrays.asList("/Users/IdeaProjects/MasterFormat/MasterFormat-Java/MasterFormat/timMaster_4.1.png \"{200.0,467.0}\"");
Path headImageFile = Paths.get(System.getProperty("user.dir") + File.separator + "headImageInfo.txt");
Files.write(headImageFile, lines, Charset.forName("UTF-8"));
String templatePath = dirPath + File.separator + "template.mp4";
String outputPath = dirPath + File.separator;
String headImagePath = headImageFile.toString();
String gifExportExecPath = "/Users/IdeaProjects/MasterFormat/MasterFormat-Java/MasterFormat/GIFExport";
Process process = new ProcessBuilder(gifExportExecPath, "-s", templatePath, "-o", outputPath, "-h", headImagePath).start();
printProcessOutput(process);
Files.delete(headImageFile);
}
EDIT
One thing I should add. I noticed that when I comment out the debug method it clocks through all 1000+ iterations in less than ten minutes, But, of course the gifs do not export (the executable doesn't run...? Not sure).
When I include the printout method it is a lot slower. I tried running it overnight but it got stuck after 183 iterations. I've tried profiling to see if it was causing some thrashing but the GC seems to run fine.

You need to consume the output of the Process or it may hang. So you can't comment out printProcessOutput(process);. Instead, comment out the lines that actually do the printing:
try{
while ( (line = reader.readLine()) != null) {
//builder.append(line);
//builder.append(System.getProperty("line.separator"));
}
} catch(IOException e){
e.printStackTrace();
}
//System.out.println(builder.toString());
I generally use this method, which also redirects the error stream:
public static void runProcess(ProcessBuilder pb) throws IOException {
pb.redirectErrorStream(true);
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
//System.out.println(line);
}
}

Related

"Squared" characters appearing in jframe

I'm trying to make a Java program which calls some other software as a command from cmd, receives the output, and prints it in a Jframe. The problem is that when it prints there are some characters (Which seem to be just spaces) which become squares. I think these characters are not recognized correctly. The homsimpl command is just from CHOMP (Computational Homology Project).
I tried to remove all the HTML code from the Strings, the squared characters were still there. When I put System.out.println(s) or System.out.println(Outp) it prints correctly in the output, without the squared characters. I also tried to change the font, but with any font I tried to use, the squared characters were still squared in the JFrame. If I copy the output text and paste it to be in the JFrame the squared characters disappear, but I can't solve the problem like that.
The JFrame appears like this:
and the Output shows:
HOMSIMPL, ver. 0.01, 11/09/04. Copyright (C) 1997-2013 by Pawel Pilarczyk.
This is free software. No warranty. Consult 'license.txt' for details.
[Tech info: simpl 4, chain 12, addr 4, intgr 2.]
Reading simplices to X from 'Simplicial.sim'... 4 simplices read.
Collapsing faces in X... .. 0 removed, 14 left.
Creating the chain complex of X... .. Done.
Time used so far: 0.00 sec (0.000 min).
Computing the homology of X over the ring of integers...
Reducing D_2: 0 + 3 reductions made.
Reducing D_1: 3 + 0 reductions made.
H_0 = Z
H_1 = 0
H_2 = Z
Saving generators of X to 'Simplicial.txt'... Done.
Total time used: 0.02 sec (0.000 min).
[Press Ctrl-C to exit.]
Thank you for using this software. We appreciate your business.
A simplified version of my program follows:
import java.io.*;
import javax.swing.*;
public class Test {
public static void main(String[] args)
throws IOException {
PrintWriter Cod = new PrintWriter("Simplicial.sim");
Cod.println("{1,2,3}");
Cod.println("{1,2,4}");
Cod.println("{1,3,4}");
Cod.println("{2,3,4}");
Cod.close();
Process p = Runtime.getRuntime().exec("cmd /c \"homsimpl Simplicial.sim -g Simplicial.txt\"");
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(p.getErrorStream()));
String s = null;
JFrame f = new JFrame();
JPanel pa = new JPanel();
JLabel la = new JLabel();
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(pa);
String Outp="";
JScrollPane pane = new JScrollPane(pa);
pa.add(la);
f.setContentPane(pane);
f.setVisible(true);
while ((s = stdInput.readLine()) != null) {
Outp=Outp+"<br/>"+s;
la.setText("<html>"+Outp+"</html>");
System.out.println(s);
}
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
}
}
Actually, this is a problem that has nothing to do with Swing. As far as i can see from the name of the file you are writing to ("Simplicial.sim), your OS language is not English. This is a usual problem when executing a command to a windows console. In order to make myself clear, i will use an example of a command that will cause a problem like this. Lets take the command that (indirectly) gives you the installed version of Microsoft Office in a windows PC. This command is reg query \"HKEY_CLASSES_ROOT\\Word.Application\\CurVer\".
Now, let's execute it to CMD:
You can easily see that there are Greek words in the answer we get. (So in your case, something similar but not English).
This is the problem. When you new BufferedReader(new InputStreamReader(p.getInputStream())); you take the exact same input with console. Since the InputStream does not use UTF-8 encoding, it cannot read Greek. So, the problem becomes now Reading InputStream as UTF-8.
Without loosing any time, let's change new BufferedReader(new InputStreamReader(p.getInputStream())); to new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF-8));
Guess what? Its a loss. We are still getting the same characters (a bit different actually but still not readable). I wish i could explain why...
The solution you have to follow here is writing a batch that will change the code page of the console to UTF-8 and then execute the command you want. After that, the input is being read flawlessly.
You can run this example (do not ignore comments) in order to see it better:
public class CmdUnicode {
private final static String COMMAND = "reg query \"HKEY_CLASSES_ROOT\\Word.Application\\CurVer\"";
public static void main(String[] args) throws IOException {
System.out.println(executeCmd());
System.out.println();
System.out.println("-------------");
System.out.println();
System.out.println(executeCmdBat());
}
private static String executeCmdBat() throws IOException {
File batFile = writeBatchFile();
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("cmd /c " + batFile.getAbsolutePath());
return streamToString(proc.getInputStream());
}
private static File writeBatchFile() throws IOException {
File file = File.createTempFile("test", ".bat");
StringBuilder sb = new StringBuilder();
sb.append("#echo off"); // Do not show cmd window
sb.append(System.lineSeparator());
sb.append("chcp 65001"); // Enable unicode to cmd
sb.append(System.lineSeparator());
sb.append(COMMAND);
try (PrintWriter out = new PrintWriter(file)) {
out.write(sb.toString());
out.flush(); // Is this required? :P
}
return file;
}
private static String executeCmd() throws IOException {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(COMMAND);
return streamToString(proc.getInputStream());
}
private static String streamToString(InputStream stream) throws IOException {
StringBuilder sb = new StringBuilder();
String line = null;
try (BufferedReader stdInput = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
while ((line = stdInput.readLine()) != null) {
sb.append(line);
sb.append(System.lineSeparator());
}
}
return sb.toString();
}
}
The console prints to me:
HKEY_CLASSES_ROOT\Word.Application\CurVer
(??????????) REG_SZ Word.Application.16
-------------
Active code page: 65001
HKEY_CLASSES_ROOT\Word.Application\CurVer
(Default) REG_SZ Word.Application.16
If you insist of getting the input after executing the command in your language you will have to search somewhere else (or probably make another topic here) and if you find anything that works, let me know....
And just for the record Προεπιλογή translates to Default :)

Java FFmpeg no output

I´ve run into problem. I want to convert video using ffmpeg but it gives me no output
public void convert(String inputFile, String outputFile, String ... optionalParams) {
ProcessBuilder processBuilder = new ProcessBuilder("ffmpeg", "-i", "\"" + inputFile.trim() +"\"", "\""+ outputFile.trim() + "\"");
DownloadRecord downloadRecord = table.getItems().get(0);
downloadRecord.setStatus("Downloading");
// Try to execute process
try {
// Set the working directory
processBuilder.directory(new File(workingDirectory));
//Start the process
Process process = processBuilder.start();
// Read the output from cmd
BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader ra = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line;
String errline;
while ((line = r.readLine()) != null) {
System.out.println(line);
}
while ((errline = ra.readLine()) != null) {
System.out.println(errline);
}
process.waitFor();
System.out.println("the end");
} catch(IOException | InterruptedException e) {
System.out.println(e.toString());
}
}
I've been searching on stackoverflow and find some solutions, none worked. What I tried and figured out so far
No output or error output
I tried to remove backslashes from ProcessBuilder, it
also gives me no output
I tried to let the program running, but it never finishes
I tried to use full path to the ffmpeg, no changes
I tried to run the video, no error
I am using
Netbeans IDE so I tried clean and rebuild project, no change
process also never finishes
I would like from it an output. Does someone know what I am doing wrong here ?
I fixed it by reinstalling the ffmpeg. Just went ffmpeg website downloaded newest version, replaced files in folder and it works
Edit:
It just works for files with less thatn 2 mins for some reason, more thatn 2 mins files are behaving like this
I start converting, it will not convert entirely until program runs. After I exit the program it will finish. It´s strange behaviour.

Java process stuck at getRuntime().exec() [duplicate]

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
There are many reasons that waitFor() doesn't return.
But it usually boils down to the fact that the executed command doesn't quit.
This, again, can have many reasons.
One common reason is that the process produces some output and you don't read from the appropriate streams. This means that the process is blocked as soon as the buffer is full and waits for your process to continue reading. Your process in turn waits for the other process to finish (which it won't because it waits for your process, ...). This is a classical deadlock situation.
You need to continually read from the processes input stream to ensure that it doesn't block.
There's a nice article that explains all the pitfalls of Runtime.exec() and shows ways around them called "When Runtime.exec() won't" (yes, the article is from 2000, but the content still applies!)
It appears you are not reading the output before waiting for it to finish. This is fine only if the output doesn't fill the buffer. If it does, it will wait until you read the output, catch-22.
Perhaps you have some errors which you are not reading. This would case the application to stop and waitFor to wait forever. A simple way around this is to re-direct the errors to the regular output.
ProcessBuilder pb = new ProcessBuilder("tasklist");
pb.redirectErrorStream(true);
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null)
System.out.println("tasklist: " + line);
process.waitFor();
Also from Java doc:
java.lang
Class Process
Because some native platforms only provide limited buffer size for standard input and
output streams, failure to promptly write the input stream or read the output stream of
the subprocess may cause the subprocess to block, and even deadlock.
Fail to clear the buffer of input stream (which pipes to the output stream of subprocess)
from Process may lead to a subprocess blocking.
Try this:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
I would like to add something to the previous answers but since I don't have the rep to comment, I will just add an answer. This is directed towards android users which are programming in Java.
Per the post from RollingBoy, this code almost worked for me:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
In my case, the waitFor() was not releasing because I was executing a statement with no return ("ip adddr flush eth0"). An easy way to fix this is to simply ensure you always return something in your statement. For me, that meant executing the following: "ip adddr flush eth0 && echo done". You can read the buffer all day, but if there is nothing ever returned, your thread will never release its wait.
Hope that helps someone!
There are several possibilities:
You haven't consumed all the output on the process's stdout.
You haven't consumed all the output on the process's stderr.
The process is waiting for input from you and you haven't provided it, or you haven't closed the process's stdin.
The process is spinning in a hard loop.
As others have mentioned you have to consume stderr and stdout.
Compared to the other answers, since Java 1.7 it is even more easy. You do not have to create threads yourself anymore to read stderr and stdout.
Just use the ProcessBuilder and use the methods redirectOutput in combination with either redirectError or redirectErrorStream.
String directory = "/working/dir";
File out = new File(...); // File to write stdout to
File err = new File(...); // File to write stderr to
ProcessBuilder builder = new ProcessBuilder();
builder.directory(new File(directory));
builder.command(command);
builder.redirectOutput(out); // Redirect stdout to file
if(out == err) {
builder.redirectErrorStream(true); // Combine stderr into stdout
} else {
builder.redirectError(err); // Redirect stderr to file
}
Process process = builder.start();
For the same reason you can also use inheritIO() to map Java console with external app console like:
ProcessBuilder pb = new ProcessBuilder(appPath, arguments);
pb.directory(new File(appFile.getParent()));
pb.inheritIO();
Process process = pb.start();
int success = process.waitFor();
You should try consume output and error in the same while
private void runCMD(String CMD) throws IOException, InterruptedException {
System.out.println("Standard output: " + CMD);
Process process = Runtime.getRuntime().exec(CMD);
// Get input streams
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line = "";
String newLineCharacter = System.getProperty("line.separator");
boolean isOutReady = false;
boolean isErrorReady = false;
boolean isProcessAlive = false;
boolean isErrorOut = true;
boolean isErrorError = true;
System.out.println("Read command ");
while (process.isAlive()) {
//Read the stdOut
do {
isOutReady = stdInput.ready();
//System.out.println("OUT READY " + isOutReady);
isErrorOut = true;
isErrorError = true;
if (isOutReady) {
line = stdInput.readLine();
isErrorOut = false;
System.out.println("=====================================================================================" + line + newLineCharacter);
}
isErrorReady = stdError.ready();
//System.out.println("ERROR READY " + isErrorReady);
if (isErrorReady) {
line = stdError.readLine();
isErrorError = false;
System.out.println("ERROR::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" + line + newLineCharacter);
}
isProcessAlive = process.isAlive();
//System.out.println("Process Alive " + isProcessAlive);
if (!isProcessAlive) {
System.out.println(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Process DIE " + line + newLineCharacter);
line = null;
isErrorError = false;
process.waitFor(1000, TimeUnit.MILLISECONDS);
}
} while (line != null);
//Nothing else to read, lets pause for a bit before trying again
System.out.println("PROCESS WAIT FOR");
process.waitFor(100, TimeUnit.MILLISECONDS);
}
System.out.println("Command finished");
}
I think I observed a similar problem: some processes started, seemed to run successfully but never completed. The function waitFor() was waiting forever except if I killed the process in Task Manager.
However, everything worked well in cases the length of the command line was 127 characters or shorter. If long file names are inevitable you may want to use environmental variables, which may allow you keeping the command line string short. You can generate a batch file (using FileWriter) in which you set your environmental variables before calling the program you actually want to run.
The content of such a batch could look like:
set INPUTFILE="C:\Directory 0\Subdirectory 1\AnyFileName"
set OUTPUTFILE="C:\Directory 2\Subdirectory 3\AnotherFileName"
set MYPROG="C:\Directory 4\Subdirectory 5\ExecutableFileName.exe"
%MYPROG% %INPUTFILE% %OUTPUTFILE%
Last step is running this batch file using Runtime.
Here is a method that works for me.
NOTE: There is some code within this method that may not apply to you, so try and ignore it. For example "logStandardOut(...), git-bash, etc".
private String exeShellCommand(String doCommand, String inDir, boolean ignoreErrors) {
logStandardOut("> %s", doCommand);
ProcessBuilder builder = new ProcessBuilder();
StringBuilder stdOut = new StringBuilder();
StringBuilder stdErr = new StringBuilder();
boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
if (isWindows) {
String gitBashPathForWindows = "C:\\Program Files\\Git\\bin\\bash";
builder.command(gitBashPathForWindows, "-c", doCommand);
} else {
builder.command("bash", "-c", doCommand);
}
//Do we need to change dirs?
if (inDir != null) {
builder.directory(new File(inDir));
}
//Execute it
Process process = null;
BufferedReader brStdOut;
BufferedReader brStdErr;
try {
//Start the command line process
process = builder.start();
//This hangs on a large file
// https://stackoverflow.com/questions/5483830/process-waitfor-never-returns
//exitCode = process.waitFor();
//This will have both StdIn and StdErr
brStdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
brStdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
//Get the process output
String line = null;
String newLineCharacter = System.getProperty("line.separator");
while (process.isAlive()) {
//Read the stdOut
while ((line = brStdOut.readLine()) != null) {
stdOut.append(line + newLineCharacter);
}
//Read the stdErr
while ((line = brStdErr.readLine()) != null) {
stdErr.append(line + newLineCharacter);
}
//Nothing else to read, lets pause for a bit before trying again
process.waitFor(100, TimeUnit.MILLISECONDS);
}
//Read anything left, after the process exited
while ((line = brStdOut.readLine()) != null) {
stdOut.append(line + newLineCharacter);
}
//Read anything left, after the process exited
while ((line = brStdErr.readLine()) != null) {
stdErr.append(line + newLineCharacter);
}
//cleanup
if (brStdOut != null) {
brStdOut.close();
}
if (brStdErr != null) {
brStdOut.close();
}
//Log non-zero exit values
if (!ignoreErrors && process.exitValue() != 0) {
String exMsg = String.format("%s%nprocess.exitValue=%s", stdErr, process.exitValue());
throw new ExecuteCommandException(exMsg);
}
} catch (ExecuteCommandException e) {
throw e;
} catch (Exception e) {
throw new ExecuteCommandException(stdErr.toString(), e);
} finally {
//Log the results
logStandardOut(stdOut.toString());
logStandardError(stdErr.toString());
}
return stdOut.toString();
}
Asynchronous reading of stream combined with avoiding Wait with a timeout will solve the problem.
You can find a page explaining this here http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/
public static void main(String[] args) throws PyException, IOException, InterruptedException
these should be the exceptions thrown

Shell command not executed

I have a weird problem when trying to execute a shell command from within a java program. Since there exist thousands of websites that explain how to do it I used the following recommended code:
public String executeShellCommand (String command)
{
try
{
StringBuffer sb = new StringBuffer();
String line = "";
Process p = Runtime.getRuntime().exec(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
while ((line = reader.readLine()) != null)
sb.append(line + "\n");
p.waitFor();
return sb.toString();
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
Acutally, when I try to execute for instance ls -aF is works fine and I get some output as a result. Therefore I'm pretty sure that the above code is, in principal, correct. However, I got another program I'd like to run and that produces a file as an output. I would like to execute it the above way but it never is executed and no output file is generated. Also I do not get any error, warnings or whatsoever in java. When copy and pasting the actual command argument string into the console the execution of the programm/command directly in the shell works fine and the output file is generated. So the command I pass to the method is also correct.
Are there additional things I need to pay attention to when trying to execute a shell command from within java?
UPDATE: I modified my code according to the suggestions. However, it is still hanging:
public String executeShellCommand(List<String> command, String logfile, boolean waitForProcess) { try {
ProcessBuilder pb = new ProcessBuilder(command);
System.out.println("pb.toString() = " + pb.toString());
Process p = pb.start();
System.out.println("2");
BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
BufferedReader out = new BufferedReader(new InputStreamReader(p.getInputStream()));
System.out.println("3");
StringBuilder errSb = new StringBuilder();
StringBuilder outSb = new StringBuilder();
String line;
System.out.println("4");
while ((line = err.readLine()) != null) { // <--- code hangs here
errSb.append(line + "\n");
System.out.println("errSb = " + errSb.toString());
}
System.out.println("4a");
while ((line = out.readLine()) != null) {
outSb.append(line + "\n");
System.out.println("outSb = " + outSb.toString());
}
System.out.println("5");
if(waitForProcess) {
System.out.println("Wait for process");
p.waitFor();
} else {
System.out.println("Sleep 5000");
Thread.sleep(5000);
}
System.out.println("6");
//Log result to file
if(logfile != null) {
OutputStreamWriter outWriter = new OutputStreamWriter(new FileOutputStream(logfile));
outWriter.write(errSb.toString());
outWriter.close();
}
return errSb.toString();
} catch(Exception e) { e.printStackTrace(); } return null; }
This will block if your command writes too many characters to stderr. Like for sdtout, Java redirect stderr through a pipe, and if you do not read the pipe, it can fill up and block (size of the pipe is probably less than 256 bytes). To avoid that, you need to read from the Process.getErrorStream(), preferable from another thread as the main thread is busy reading from the Process.getInputStream().
A simpler way to avoid that is to use the ProcessBuilder class instead of Runtime.exec() and ProcessBuilder.redirectErrorStream(true) so that both stdout and stderr are merged into the Process.getInputStream()
As per Process javadoc :
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.
You are calling p.waitFor(). If we carefully read the waitFor() documentation:
Causes the current thread to wait, if necessary, until the process represented by this Process object has terminated.
You are waiting for a process which hangs, because its error stream and output stream are never read.
What you should do, is to read these streams:
p.start();
BufferedReader err= new BufferedReader(new InputStreamReader(p.getErrorStream()));
BufferedReader out = new BufferedReader(new InputStreamReader(p.getOutputStream()));
StringBuilder errSb = new StringBuilder();
StringBuilder outSb = new Stringbuilder();
String line;
while ((line = err.readLine()) != null) {
errSb.append(line);
}
while ((line = out.readLine()) != null) {
outSB.append(line);
}
int retCode = p.waitFor(); //0 for success
System.out.println(retCode);
System.err.println(errSB.toString());
You should always read the error stream when calling external programs via the Process class, else you may find yourself in this odd situation where a process hangs forever. (well until someone else -the operating system, another application, etc- kills it, more exactly).
I've also noticed that you use the Runtime.getRuntime() which is not the recommended way to run external programs, starting with java 1.5, as per javadoc:
As of 1.5, ProcessBuilder.start() is the preferred way to create a Process.
ProcessBuilder pb = new ProcessBuilder("ls" , "-aF");
Process p = pb.start();

How to print logs from another jar line by line?

I have created a Java application that has 2 Jar files. Jar1 is used to initialize and run Jar2, using this code :
Process process = runtime.exec( "java -jar Jar2.jar" );
printLogs( process );
.
.
.
private static boolean printLogs( Process process ) {
try {
BufferedInputStream logStream = new BufferedInputStream( process.getInputStream() );
String logs = "";
int buffer = 0;
while ( ( buffer = logStream.read() ) != -1 ) {
logs += (char)buffer;
}
if( !logs.isEmpty() ) logger.debug( logs );
} catch (IOException e) {}
return true;
}
I print many logs from Jar2 using Log4J, i.e.
logger.debug( "..." );
But none of the logs in Jar2 were printed to the console. I figured out it's because the logs are returned to Jar1 and not to the console, So i printed the returning stream using the above code. Logs are now printed fine but after all Jar2 process ends up, then all logs are printed at once in Jar1.
The question is: Can i print each log line in Jar2 in time instead of waiting all Jar2 process to end ?
Because Jar2 is a long process, and it is important that i can see those logs while the application is processing.
The whole thing is quite messed up. You shouldn't need two separate archives and Runtime.exec()
However, one usually uses BufferedReader.readLines to read text lines. Note that the problem simply vanishes if you log each line at the moment you read it:
BufferedReader input = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
String line = null;
while ((line = input.readLine()) != null) {
System.out.println(line);
}
Your code waits to the child process to complete because you logged the line after the stream ends (ie after the subprocess has terminated)
Here is a demo program which uses a long running Ruby program as the watched process
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Subprocess {
static final String[] program = new String[] {
"ruby",
"-e" ,
"(1..5).each{|i|sleep 1;puts i;STDOUT.flush}"
};
public static void main(String[] args) throws IOException {
ProcessBuilder builder = new ProcessBuilder(program);
builder.redirectErrorStream();
Process child = builder.start();
String line = null;
BufferedReader in = new BufferedReader(
new InputStreamReader(child.getInputStream()));
while ((line = in.readLine()) != null)
System.out.println(line);
}
}
Your code seems to be waiting for the logStream to get to EOF before actually writing logs (which occurs when the process exits). Try refactoring it to read it character-by-character, then logging the accumulated character buffer whenever you see a newline (and EOF, of course - so you get the last line).
With the help of this post i was able to fix this :
Runtime.exec never returns when reading system.in
I have used the ProcessBuilder too.

Categories

Resources