简体   繁体   中英

PrintWriter.print blocks when the string is too long

I'm writing a wrapper program in java that's just supposed to pass arguments to other processes by writing to their standard in streams, and reading the response from their standard out streams. However, when the String I try to pass in is too large, PrintWriter.print simply blocks. No error, just freezes. Is there a good workaround for this?

Relevant code

public class Wrapper {
    PrintWriter writer;

    public Wrapper(String command){
        start(command);
    }

    public void call(String args){
        writer.println(args); // Blocks here
        writer.flush();

        //Other code
    }

    public void start(String command) {
        try {
            ProcessBuilder pb = new ProcessBuilder(command.split(" "));
            pb.redirectErrorStream(true);
            process = pb.start();
            // STDIN of the process.
            writer = new PrintWriter(new OutputStreamWriter(process.getOutputStream(), "UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Process ended catastrophically.");
        }
    }


}

If I try using

writer.print(args);
writer.print("\n");

it can handle a larger string before freezing, but still ultimately locks up. Is there maybe a buffered stream way to fix this? Does print block on the processes stream having enough space or something?

Update

In response to some answers and comments, I've included more information.

  • Operating System is Windows 7
  • BufferedWriter slows the run time, but didn't stop it from blocking eventually.
  • Strings could get very long, as large as 100,000 characters
  • The Process input is consumed, but by line ie Scanner.nextLine();

Test code

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

import ProcessRunner.Wrapper;


public class test {

    public static void main(String[] args){
        System.out.println("Building...");
        Wrapper w = new Wrapper("java echo");
        System.out.println("Calling...");
        String market = "aaaaaa";
        for(int i = 0; i < 1000; i++){
            try {
                System.out.println(w.call(market, 1000));
            } catch (InterruptedException | ExecutionException
                    | TimeoutException e) {

                System.out.println("Timed out");
            }
            market = market + market;
            System.out.println("Size = " + market.length());
        }
        System.out.println("Stopping...");
        try {
            w.stop();
        } catch (IOException e) {

            e.printStackTrace();
            System.out.println("Stop failed :(");
        }
    }
}

Test Process:

You have to first compile this file, and make sure the .class is in the same folder as the test .class file

import java.util.Scanner;


public class echo {

    public static void main(String[] args){
            while(true){
            Scanner stdIn = new Scanner(System.in);
            System.out.println(stdIn.nextLine());
            }
    }

}

I suspect that what is happening here is that the external process is writing to its standard output. Since your Java code doesn't read it, it eventually fills the external process's standard out (or err) pipe. That blocks the external process, which means that it can read from its input pipe .... and your Java process freezes.


If this is the problem, then using a buffered writer won't fix it. You either need to read the external processes output or redirect it to a file (eg "/dev/null" on Linux)

Writing to any pipe or socket by any means in java.io blocks if the peer is slower reading than you are writing.

Nothing you can do about it.

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