3

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 i.e 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());
            }
    }

}
Community
  • 1
  • 1
Cain
  • 264
  • 1
  • 7
  • You seem to know about bufferization, so why did not you try using a `BufferedWriter` ? – Dici Jul 24 '15 at 22:57
  • @Dici I'm not really sure how to go from a String object to a BufferedWriter, and also not sure this would solve the problem, so was holding back on struggling through that until I get a little confirmation or guidance. Would that solve the problem do you think? – Cain Jul 24 '15 at 23:11
  • @Cain The API for `BufferedWriter` is [documented](https://docs.oracle.com/javase/7/docs/api/java/io/BufferedWriter.html), after all. – user207421 Jul 25 '15 at 04:27
  • I'd like to try reproducing the problem. Q: What is the OS (Linux? Windows? Other?) Q: How long is the string? 100's characters? 1000's? 64,000 characters (or longer)? ALSO: Frankly, think trying buffered writer is a good idea. – paulsm4 Jul 25 '15 at 20:20
  • 1
    @EJP I did try the BufferedWriter, had a slower run time and still blocked – Cain Jul 27 '15 at 15:13
  • @paulsm4 See the test files I added. The string does occasionally get ridiculously large, is there some limit to String size? Do I need to find another way to accomplish this? – Cain Jul 27 '15 at 15:15
  • Thank you for the update - I'll look at it later. In the meantime, you might find this useful: http://stackoverflow.com/questions/5960554/maximum-line-length-for-bufferedreader-readline-in-java – paulsm4 Jul 27 '15 at 16:45

2 Answers2

3

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 (e.g. "/dev/null" on Linux)

Dici
  • 25,226
  • 7
  • 41
  • 82
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • The other process is reading it by line. But If the string is too big, could the endline character get cut off? – Cain Jul 27 '15 at 15:12
2

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.

user207421
  • 305,947
  • 44
  • 307
  • 483