Search code examples
javaswingrunnable

Process output only becomes available after the process has finished


I have a Runnable that reads Console output from an externally called exe (see below) and writes it to both a log file and a JTextArea.

But my Runnable doesn't show the Console output in the JTextArea until the exe completely finishes. How do I get it to print Console output as it happens?

Short Concise Code Example below:

//Main

import java.awt.*;
import java.io.IOException;
import javax.swing.*;

public class Example extends JFrame {

    private static final long serialVersionUID = 1L; 

    public static int maxX, maxY;

    public static JTextArea ta = new JTextArea(20, 60);//For LOG display window

    public static void main(String args[] ) throws IOException
    {   
        new Example();
    }

    public Example() {

        this.setTitle("Example");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //MAIN Panel
        final JPanel main = new JPanel();

        JButton RunButton = button.run(main);
        main.add(RunButton);

        Container container = getContentPane();


        container.add(main);
        this.pack();
        this.setVisible(true);
    }


}

//Button Action Listener

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;


public class button {

    public static JButton run( final JPanel parent ) {
        JButton RunButton = new JButton();      
        RunButton.setText("Start!");

        RunButton.addActionListener(
        new ActionListener()
        {
            public void actionPerformed( ActionEvent event)
            {
                try
                {

                    //Set up LOG Display    
                    JDialog dialog = new JDialog((JFrame)null, "Working...");
                    JPanel temp_panel = new JPanel();
                    temp_panel.add(new JScrollPane(Example.ta));
                    dialog.getContentPane().add(temp_panel);
                    dialog.pack();
                    dialog.setVisible(true);

                    //Build the Command
                    ArrayList<String> command = new ArrayList<String>();
                    command.add("ping");
                    command.add("127.0.0.1");

                    //Start the process
                    Process p = new ProcessBuilder(command).start();

                    //Starts LOG display capture in separate thread 
                    SwingUtilities.invokeLater(new execute(p));

                    //Wait for call to complete
                    p.waitFor();
                }
                catch(Exception err)
                {
                    JOptionPane.showMessageDialog( parent, "Error Executing Run!", "Warning", JOptionPane.ERROR_MESSAGE );  
                }

            }//end ActionPerformed
        });
        return RunButton;
    }
}

//Runnable

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class execute implements Runnable {  
    String line;
    Process p; 

    public execute ( Process process ) {
        p = process;
    }

    public void run() {
        try {
            //Read Process Stream Output and write to LOG file
            BufferedReader is = new BufferedReader( new InputStreamReader(p.getInputStream()));

            while ( (line = is.readLine()) != null ) {
                Example.ta.append(line + "\n");
            }   
            System.out.flush();     
        } catch(Exception ex) { ex.printStackTrace(); }  

    }
}

Solution

  • The problem was not that the thread wasn't capturing the data, it was the JTextArea just not refreshing. repaint(), revalidate(), and updateUI() did not refresh the JTextArea, but the following did:

    Example.ta.update(Example.ta.getGraphics());