Swingでプロセス実行

Swingでプロセス実行

昨日のプログラムの応用で、外部コマンドの出力を、SwingのJTextAreaに出力するようにしました。

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.text.JTextComponent;

import org.apache.tools.ant.taskdefs.StreamPumper;

/** 外部プロセスを実行し、その標準出力をJTextAreaに表示する. */
public class ProcessOnSwing {
    JFrame frame;
    JTextField text;
    JTextArea area;
    JButton button;
    OutputStreamTextComponent streamArea; 
    
    ProcessOnSwing() {
        text = new JTextField("コマンド文字列を入力してね");
        text.selectAll();
        area = new JTextArea();
        area.setEditable(false);
        button = new JButton("実行");
        button.addActionListener(new ButtonListener());
        frame = new JFrame("Swingでプロセス実行");
        streamArea = new OutputStreamTextComponent(area);

        frame.getContentPane().setLayout(new BorderLayout(5, 5));
        JPanel north = new JPanel(new BorderLayout(5, 5));
        north.add(text, BorderLayout.CENTER);
        north.add(button, BorderLayout.EAST);
        frame.getContentPane().add(north, BorderLayout.NORTH);
        frame.getContentPane().add(new JScrollPane(area), BorderLayout.CENTER);
        
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(640, 480);
        frame.setVisible(true);
    }
    /**
     * 指定されたプロセスを実行する.
     * プロセスは入力を伴わないものとする。
     * 標準出力、標準エラー出力を引数のoutに出力される。
     * @param command コマンド文字列(ex."java.exe -X")
     * @return 戻り値。プロセスの実行に失敗した場合は、Integer.MAX_VALUEを返す。
     */
    int execute(String command, OutputStream out) throws IOException {
        Process process = Runtime.getRuntime().exec(command);
        Thread outThread;
        Thread errThread;
        outThread = new Thread(new StreamPumper(process.getInputStream(), out, false));
        outThread.setDaemon(true);
        errThread = new Thread(new StreamPumper(process.getErrorStream(), out, false));
        errThread.setDaemon(true);
        outThread.start();
        errThread.start();
        try {
            int exitValue = Integer.MAX_VALUE;
            try {
                process.waitFor();
            } catch (InterruptedException e) {
                process.destroy();
            }
            exitValue = process.exitValue();
            try { outThread.join(); } catch (InterruptedException e) {}
            try { errThread.join(); } catch (InterruptedException e) {}
            out.flush();
            try { process.getInputStream().close(); } catch (IOException e) {}
            try { process.getOutputStream().close(); } catch (IOException e) {}
            try { process.getErrorStream().close(); } catch (IOException e) {}
            return exitValue;
        } catch (ThreadDeath t) {
            process.destroy();
            throw t;
        }
    }

    class OutputStreamTextComponent extends OutputStream {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        JTextComponent textComponent;
        OutputStreamTextComponent(JTextComponent textComponent) {
            this.textComponent = textComponent;
        }
        public void write(int b) {
            synchronized(out) {
                out.write(b);
            }
        }
        public void flush() {
            synchronized(out) {
                String str = out.toString();
                area.append(str);
                out.reset();
            }
        }
    }

    class ButtonListener extends AbstractAction {
        public void actionPerformed(ActionEvent e) {
            String command = text.getText();
            try {
                area.setText("");
                int exitValue = execute(command, streamArea);
                JOptionPane.showMessageDialog(frame, "exitValue="+exitValue);
            } catch (IOException ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(frame, "エラーが発生しました"+ex);
            }
        }
    }

    public static void main(String[] args) {
        new ProcessOnSwing();
    }
}

Ant はあれだけ多くのタスクに対応しているので、参考になるソースがたくさんありそうです。Antはツールですが、このようにライブラリとして使うのもいいかも。