0

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.

Kai
  • 38,985
  • 14
  • 88
  • 103
Brad
  • 4,457
  • 10
  • 56
  • 93

3 Answers3

2

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);
    }

}
Raffaele
  • 20,627
  • 6
  • 47
  • 86
  • 1
    +1 for being messed up. Also, logs would be simpler if you just use System.out.println directly rather than `debug`: otherwise you end up with two timestamps, etc., and other logging guff on each line – artbristol Sep 18 '12 at 12:35
  • Good catch :) I didn't think of it.. Unless he used an "empty" layout to get rid of the formatting stuffs of the second logging call (which is weird), and used some other appender to justify the `debug()` instead of `System.out` – Raffaele Sep 18 '12 at 12:39
  • The main idea is that i want to use "Runtime.exec()". I do execute some other code that must be in Jar1. I have tried your code but it still prints all logs after Jar2 process is complete, which is logical. As you said; because "code waits to the child process to complete because you logged the line after the stream ends" ! – Brad Sep 18 '12 at 12:50
  • Maybe Jar2's output is buffered. I updated my answer to execute a long running Ruby script. Note that without `STDOUT.flush`, the Java program waits for the process to end. You could set Log4j [to flush immediately](http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/WriterAppender.html#setImmediateFlush(boolean)), even if this should be the default behavior – Raffaele Sep 18 '12 at 15:19
0

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).

Faelkle
  • 476
  • 3
  • 6
0

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.

Community
  • 1
  • 1
Brad
  • 4,457
  • 10
  • 56
  • 93