public class Sout {

    public void hi(){
        System.out.println("hi");
    }

}

이렇게 콘솔에 어떤 메시지를 출력하는 경우가 있을 때 저걸 애플리케이션에서 캡춰할 수 있는 걸 만들어 보는 과제가 떨어졌다.

public class SoutTest {

    SoutInterceptor soutInterceptor = new SoutInterceptor();

    @Test
    public void sout() throws IOException {
        soutInterceptor.active();

        Sout sout = new Sout();
       sout.hi();

        assertThat(soutInterceptor.getMessages(), is("hi"));
    }

}

간단하게 테스트를 만들고 돌려보기 시작했다. 캡춰한 메시지를 어떻게 가져올지가 고민이었는데 그냥 생각난 가장 단순한 방법으로 가져오게했다. 이제 남은건 SoutInterceptor라는 녀석을 만드는 일이다. 뭘 어찌해야 한담 @_@;

가장 먼저 떠오른 방법은 콘솔을 모니터링 하는것이다. 그런데.. 넘 복잡할 것같고 막연하다. 다음으로 떠오른게 AOP. out.println()을 할 때 가로챌 수 있지 않을까? 하지만 힌트가 전달됐다. out을 교체할 수 있단다. 크헉.. 이건 뭐 거의 정답 수준의 힌트이지만 그렇게 간단하지는 않다고 한다. 좋아 해보자.

코딩은 구글신과 함께.. (또는 사부님 말씀대로 이클립스 코드를 뒤지면 나올지도 모른다. 사부님은 이미 뒤져본 것 같다. 자신이 생각한 방법과 동일한 방법을 사용했다고 한다. 어떤 건지는 안 찾아봐서 모르겠다;; 수천 수만 개나 되는 소스 코드를 받아오기도 귀찮고 그걸 IDE에 로딩하는데 엄청 오래 걸릴 것이며 잘못해서 뻑나거나 빌드가 안되고 컴파일 에러잡고 그러면서 삼천포로 가고 싶진 않았다.)

public class SoutInterceptor {

    private PipedInputStream pipedInputStream;
    private PrintStream originalPrint;

    public SoutInterceptor() {
        originalPrint = System.out;
        this.pipedInputStream = new PipedInputStream();
    }

    public String getMessages() throws IOException {
        byte[] messages = new byte[pipedInputStream.available()];
        pipedInputStream.read(messages, 0, messages.length);
        return new String(messages);
    }

    public void active() throws IOException {
        final PipedOutputStream pipedOutputStream = new PipedOutputStream(pipedInputStream);
        PrintStream saveStream = new PrintStream(pipedOutputStream) {
            @Override
            public void println(String x) {
                try {
                    pipedOutputStream.write(x.getBytes());
                } catch (IOException e) {
                    System.out.println("error");
                }
                originalPrint.println(x);
            }
        };
        System.setOut(saveStream);
    }

}

오호.. 잘 돌아간다.. +_+.

테스트를 좀 더 해보자.

        sout.hello();
       
        assertThat(soutInterceptor.getMessages(), is("hello"));

아래에 이렇게 추가하고 hello() 메서드 안에서는 hello를 출력하게 했다. 또 테스트가 잘 돌아간다. 흠.. 이제 맞게 한건가?

println(Stirng x)를 재정의 했는데 print(int), println(boolean) 를 호출할 때도 잘 동작한다. 왜그럴까?