简体   繁体   中英

Tomcat issue while running a linux command

I am trying to run a linux command in java code. The command is raspivid which I have placed on the server under test.sh file for live camera streaming. Everything works fine but the problem is the streaming stops after some minutes after starting the tomcat server. Like the streaming stops after 6-7 minutes while running the command in java but at the background the raspivid process is running. On the other hand when I run the same command without using java code it works fine. Is this an issue of tomcat heap or anything else which stops the streaming? Please help see the below code:

try {
         Process p = Runtime.getRuntime().exec(new String[]{"sudo","sh","/home/pi/test.sh"});
            BufferedReader stdInput = new BufferedReader(new
                 InputStreamReader(p.getInputStream()));

            BufferedReader stdError = new BufferedReader(new
                 InputStreamReader(p.getErrorStream()));

            // read the output from the command
            LOGGER.info("Here is the standard output of the command:\n");
            while ((s = stdInput.readLine()) != null) {
                LOGGER.info(s);
            }

            // read any errors from the attempted command
            LOGGER.info("Here is the standard error of the command (if any):\n");
            while ((s = stdError.readLine()) != null) {
                LOGGER.info(s);
            }

        }

The problem is that BufferedReader.readLine() blocks until a full line (terminated by any line end char sequences) can be read, and you don't read the 2 outputs of the process "parallel", one if its buffer gets filled and the process gets blocked.

You need to read the data outputted by the process.

A process has 2 output streams: standard output and error output. You have to read both because the process might write to both of those outputs.

The output streams of the process have buffers. If the buffer of an output stream is filled, attempt to write further data to that by the process is blocked.

Do something like this:

BufferedReader stdInput = new BufferedReader(
        new InputStreamReader(p.getInputStream()));

BufferedReader stdError = new BufferedReader(
        new InputStreamReader(p.getErrorStream()));

while (p.isAlive()) {
    while (stdInput.ready())
        LOGGER.info(stdInput.readLine());

    while (stdError.ready())
        LOGGER.info(stdError.readLine());

    Thread.sleep(1);
}

Problem with this solution (and fixing it):

This solution has an error. The process might not write full lines to its output. If that is the case, the process might still hang for example if it writes 1 character to its standard output then stdInput.readLine() would block (because it reads until a new line character is encountered) and if the process would keep writing to its error stream, when the buffer of the error stream is full, the process would be blocked.

So it would be better not to read the output streams of the buffer by lines but by characters (of course this makes logging it harder):

StringBuilder lineOut = new StringBuilder(); // std out buffer
StringBuilder lineErr = new StringBuilder(); // std err buffer

while (p.isAlive()) {
    while (stdInput.ready()) {
        // Append the character to a buffer or log if it is the line end
        char c = (char) stdInput.read();
        if (c == '\n') {  // On UNIX systems line separator is one char: '\n'
            LOGGER.info(lineOut.toString());
            lineOut.setLength(0);
        }
        else
            lineOut.append(c);
    }

    while (stdError.ready()) {
        // Append the character to a buffer or log if it is the line end
        char c = (char) stdError.read()
        if (c == '\n') {  // On UNIX systems line separator is one char: '\n'
            LOGGER.info(lineErr.toString());
            lineErr.setLength(0);
        }
        else
            lineErr.append(c);
    }

    Thread.sleep(1);
}

Alternative (cleaner, more simple) solution

Alternatively you could start 2 threads, one to read the standard output of the process and one to read the standard error of the process. This could simplfy things:

private static void consumeStream(final InputStream is) {
    new Thread() {
        @Override
        public void run() {
            BufferedReader r = new BufferedReader(new InputStreamReader(is));
            String line;
            while ((line = r.readLine()) != null)
                LOGGER.info(line);
        }
    }.start();
}

And using it:

consumeStream(p.getInputStream());
consumeStream(p.getErrorStream());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM