標準出力された内容を貰ってくる遊び

プログラムで標準出力(System.out)に対して書き込んだ内容を取得するための方法。
例外安全性とかは考慮していないけれど、おもちゃとしては十分。


System.setOutでPrintStreamを継承したデリゲータを登録する。
ここで、このStreamのwriteメソッドに書かれた内容を、登録したWriterにも書き出すように設定する。(2つのwriteメソッドのオーバーライド)
これで、Writerに書かれた内容はPipedReaderを経由して取得できるようになる。


LineGetterは(標準出力された時にWriter => Readerを経由して)出力された文字列を取得する。この際取得した文字列は、後で使える用にlinesに保持しておく。
外部から文字列群が取得されたら、linesの内容は消去する。
この方法により、コレまでに標準出力された文字列を取得できる。
ただし、別Threadによる取得なので、何時その文字列が生成されるかは確定では無い。
なので、ここではLineGetterが生きている間監視し、文字列の取得を行って見た。
なお、ただの標準出力をしたい場合は、初期設定のSystem.outを取ってあるので、それを用いている。


サブプロセス関連で遊んでいたら出来ていたので記す。
ただし、安全かどうかは自分も知らない。(重要な事なので2回言いました)


以下、ソースコード&出力例。

// ソースコード
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;


public class SystemOutTest {
  
  private final static PrintStream out = System.out;
  
  public static void main(String[] args) throws Exception {
    
    
    PipedWriter pWriter = new PipedWriter();
    final BufferedReader reader = new BufferedReader(new PipedReader(pWriter));
    
    class LineGetter extends Thread {
      
      private final List<String> lines = new ArrayList<String>();
      
      public void run() {
        while(true) {
          try {
            String line = reader.readLine();
            if( line == null ) break;
            synchronized (lines) {
              lines.add(line);
              SystemOutTest.out.println("add: "+ line);
            }
          } catch (IOException e) {
            e.printStackTrace();
          }
        
        }
      };
      
      public List<String> newlines() {
        synchronized (lines) {
          List<String> res = new ArrayList<String>(lines);
          lines.clear();
          return res;
        }
      }
      
    }
    
    final LineGetter getter = new LineGetter();
    getter.start();
    
    new Thread() {
      public void run() { 
        while(getter.isAlive()) {
          for(String str : getter.newlines()) {
            SystemOutTest.out.println("[get]: " + str);
          }
        }
      }
    }.start();
    
    PrintWriterDelegator delegate = new PrintWriterDelegator(pWriter);
    System.setOut(delegate);
    
    Scanner stdin = new Scanner(System.in);
    
    while(true) {
      String line = stdin.nextLine();
      System.out.println(line);
    }
    
  }
}

class PrintWriterDelegator extends PrintStream {

  private final PipedWriter writer;
  
  public PrintWriterDelegator(PipedWriter writer) {
    super(System.out);
    this.writer = writer;
  }

  @Override
  public void write(byte[] buf, int off, int len) {
    super.write(buf, off, len);
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    stream.write(buf, off, len);
    try {
      writer.write(stream.toString());
      writer.flush();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @Override
  public void write(int b) {
    super.write(b);
    try {
      writer.write(b);
      writer.flush();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }  
  
}

// 出力
num
num
add: num
[get]: num
piyo
piyo
add: piyo
[get]: piyo
hoge
hoge
add: hoge
[get]: hoge
exit
exit
add: exit
[get]: exit