user878277
user878277

Reputation: 21

Input and Output from Interactive external process in java

I am trying to run a interactive program written in C from java using process builder class. The C program is very simple, it request the user to enter a number and outputs the same number to the user. When i tried to run the java program it hangs, Please let me know the problem.

Java Program

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package testapp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author pradeep
 */
public class TestApp {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException, InterruptedException {

        Process process = new ProcessBuilder("/home/pradeep/a.out").start();
        final InputStream in = process.getInputStream();
        final OutputStream out = process.getOutputStream();
        final BufferedReader br = new BufferedReader(new InputStreamReader(in));
        //Thread.currentThread().sleep(5000);
        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    int ch;
                    String line;
                    System.out.println("Read started");
                    do {
                        //line=br.readLine();
                        ch = in.read();
                        System.out.print((char)ch);
                    }while(ch!=-1);
                    System.out.println("Read ended");

                } catch (IOException ex) {
                    Logger.getLogger(TestApp.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    int i=0;
                    System.out.println("write started");
                        out.write(((i++)+"\n").getBytes());

                    System.out.println("write completed");
                } catch (IOException ex) {
                    Logger.getLogger(TestApp.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

        }).start();

        int result = process.waitFor();
        //out.close();
        //in.close();
        System.out.println("result:"+result);
    }
}

C Program

#include<stdio.h>

int main() {
int number;

int i=10;

printf("Enter number:\n"); scanf("%d",&number); printf("Entered
number:%d\n",number);

return 0;
}

When i modified the write thread in the java program like below, i am getting the output.

new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    int i=0;
                    System.out.println("write started");
                    while(i<2000) {
                        //System.out.println("W->"+i);
                        out.write(((i++)+"\n").getBytes());
                    }

                    System.out.println("write completed");
                } catch (IOException ex) {
                    Logger.getLogger(TestApp.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

        }).start();

Below is the framework code,

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.amphisoft.framework;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author pradeep
 */
public class ProcessManager {

    private static ArrayBlockingQueue<Task> taskQueue = new ArrayBlockingQueue<Task>(10);
    private static Map<Integer,ProcessHolder> processMap = new HashMap<Integer,ProcessHolder>();

    private static ProcessManager instance;
    Integer id = 0;

    Log log = LogFactory.getLog(ProcessManager.class);

    private ProcessManager() {

        new Thread() {

            @Override
            public void run() {
                try {
                    Task task;

                    while((task = taskQueue.take()) != null) {
                        Process process = task.processBuilder.start();
                        ProcessHolder processHolder = new ProcessHolder(process,process.getInputStream(),process.getOutputStream());
                        if(task.hasInput) {
                            WriteThread writeThread = new WriteThread(processHolder);
                            writeThread.start();
                        }
                        if(task.hasOutput) {
                            ReadThread readThread = new ReadThread(processHolder);
                            readThread.start();
                        }
                        processMap.put(task.id,processHolder);
                        System.out.println("waiting for process");
                        process.waitFor();
                        System.out.println("process completed");
                    }
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }

        }.start();

    }

    public static ProcessManager getInstance() {
        if(instance == null) {
            instance = new ProcessManager();
        }

        return instance;
    }

    public Integer addTask(Task task) {
        id++;
        task.id = id;
        taskQueue.add(task);
        return task.id;

    }

    public ProcessHolder getProcessHolder(Integer id) {
        log.info("Get ProcessHolder:"+id);
        log.info("Process Map:"+processMap);

        return processMap.get(id);
    }

}

ProcessHolder.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.amphisoft.framework;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedReader;
import java.io.PipedWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 *
 * @author pradeep
 */
public class ProcessHolder {
    private Process process;
    private PipedReader reader;
    private PipedWriter readerInput;

    private PipedWriter writer;
    private PipedReader writerOutput;

    private BufferedReader in;
    private BufferedWriter out;

    Log log = LogFactory.getLog(ProcessHolder.class);

    public ProcessHolder(Process process, InputStream in, OutputStream out) throws IOException {
        this.process = process;
        this.in = new BufferedReader(new InputStreamReader(in));
        this.out = new BufferedWriter(new OutputStreamWriter(out));

        readerInput = new PipedWriter();
        reader = new PipedReader(readerInput);

        writer = new PipedWriter();
        writerOutput = new PipedReader(writer);
    }

    public void readToPipe() throws IOException, InterruptedException {

        String line = "";
        log.info("Inside readToPipe");
        int ch;
        while((ch = in.read()) != -1) {
            log.info(ch);
            readerInput.write(ch);
            readerInput.flush();
        }


        log.info("Closing the read Pipe");
        in.close();
        readerInput.close();
    }

    public void writeFromPipe() throws IOException, InterruptedException {
        char[] buffer = new char[512];
        int ch;
        log.info("Inside writeFromPipe to write");
        while((ch = writerOutput.read()) != -1) {
            log.info(ch);
            out.write(ch);
            out.flush();
        }

        log.info("Closing the write Pipe");
        out.close();
        writerOutput.close();
    }

    public PipedReader getReader() {
        return reader;
    } 

    public PipedWriter getWriter() {
        return writer;
    }
}

Task.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.amphisoft.framework;

/**
 *
 * @author pradeep
 */
public class Task {
    public ProcessBuilder processBuilder;
    public Integer id;
    public boolean hasInput;
    public boolean hasOutput;
}

WriteThread.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.amphisoft.framework;

/**
 *
 * @author pradeep
 */
public class WriteThread extends Thread{

    ProcessHolder processHolder;

    public WriteThread(ProcessHolder processHolder) {
        this.processHolder = processHolder;
    }

    public void run() {
        try {
            processHolder.writeFromPipe();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

ReadThread.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.amphisoft.framework;

/**
 *
 * @author pradeep
 */
public class ReadThread extends Thread{

    public ProcessHolder processHolder;

    public ReadThread(ProcessHolder processHolder) {
        this.processHolder = processHolder;
    }

    public void run() {
        try {
            processHolder.readToPipe();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

The application add the process to the queue using processmanager class, and it takes care of running the process and starts read and write thread.

Upvotes: 2

Views: 2614

Answers (2)

Luke Woodward
Luke Woodward

Reputation: 64949

The problem is that the input and output streams to the process are buffered.

When you write a single line to the process, the single line isn't enough to fill the buffer. The output stream is waiting for more data to fill the buffer before it writes it all out to the process. In your first example, your Java program is hanging because the C program is waiting for the line of input sent from the Java program, but because of the buffering this never comes.

The solution is to flush the output stream. Flushing an output stream sends to the destination any data currently held in the buffer. To flush the output stream, add the line

out.flush();

after the out.write line in your first example.

In your second example, you write a lot of numbers to the output stream. This is evidently enough to fill the buffer and cause the output stream to send the data to the C program.

Upvotes: 2

PeterMmm
PeterMmm

Reputation: 24630

As you write only 1 number in your first code example (no while loop, only writing "0\n"), you may flush or close the output stream.

Upvotes: 1

Related Questions