Search code examples
javastdoutstdinmaxima

Programatically interact with an I/O program in Java


I am writing a program that utilizes a third party mathematics software, "Maxima". This program is a command line interface, and so it can communicate through my Java program with simple I/O routing. I have already figured out how to run the program from within Java, and I have read a lot about how I can reconfigure System.out and how InputStreams/OutputStreams work, but I can't figure out how to do the following (what I think should be a pretty simple task):

  1. Output to Maxima a command from Java, (like the string "5 + 5;")
  2. Retrieve Maxima's output, and deal with it from Java code (like maybe printing the given string + "blah").
  3. Output another command to Maxima from Java...
  4. etc.

- Below is code which will run Maxima and allow me to interact with it on the Eclipse Console

public static void main(final String[] args) {

    // An idea I had for manipulaing how the printstream works.
    // Set the system.out to be a custom Prinstream.
    // final PrintStream interceptor = new Interceptor(origOut);
    // System.setOut(interceptor);

    // Run the program:
    final String programLocation = "\"C:\\Program Files (x86)\\Maxima-sbcl-5.37.2\\bin\\maxima.bat\"";
    final ProcessBuilder pb = new ProcessBuilder();
    pb.redirectInput(Redirect.INHERIT); // Inherit I/O
    pb.redirectOutput(Redirect.INHERIT);
    pb.command(programLocation);

    try {
        // Start the program and allow it to run in Eclipse's/the program's
        // console.
        pb.start().waitFor();
    } catch (final InterruptedException e) {
        e.printStackTrace();
    } catch (final IOException e) {
        e.printStackTrace();
    }

}

This allows for the following style of interaction: Image of Console Interaction


Solution

  • Thanks to the words of wisdom from @RealSkeptic, I think I worked out a solution here.

    The key was building a BufferedWriter, and a BufferedReader to interact with the I/O of Maxima. That is:

    BufferedWriter w = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
    BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream()));
    

    Those two lines of code create buffered readers and writers which can input data to Maxima, and read what Maxima output. Here is a (fairly lengthy) use case of this method, which I use to do basically what I asked in the question:

    public class TestClass {
    
    public static void main(final String[] args) {
        @SuppressWarnings("unused")
        final TestClass ts = new TestClass();
    }
    
    private BufferedWriter w;
    private BufferedReader r;
    
    public TestClass() {
        // Start the process using process builder
        final String programLocation = "\"C:\\Program Files (x86)\\Maxima-sbcl-5.37.2\\bin\\maxima.bat\"";
        final ProcessBuilder pb = new ProcessBuilder();
        pb.command(programLocation);
        Process process;
        try {
            process = pb.start();
        } catch (final IOException e) {
            e.printStackTrace();
            process = null;
            // killProgram();
        }
    
        // Build your own wrappers for communicating with the program.
        w = new BufferedWriter(
                new OutputStreamWriter(process.getOutputStream()));
        r = new BufferedReader(new InputStreamReader(process.getInputStream()));
    
        // Print the five starting messages.
        printFromBuffer();
        printFromBuffer();
        printFromBuffer();
        printFromBuffer();
        printFromBuffer();
    
        // Run the following three commands in Maxima
        runCommand("5+5;");
        runCommand("2*65;");
        runCommand("quit();");
    }
    
    /**
     * Runs the given string and prints out the returned answer.
     */
    private void runCommand(final String s) {
        try {
            w.write(s);
            w.flush();
            printFromBuffer();
            printFromBuffer();
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }
    
    private void printFromBuffer() {
        try {
            final String s = r.readLine();
    
            System.out.println(s + " -blah");
    
        } catch (final IOException e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }
    }