fi.jumi.core.stdout.OutputCapturer.java Source code

Java tutorial

Introduction

Here is the source code for fi.jumi.core.stdout.OutputCapturer.java

Source

// Copyright  2011-2014, Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

package fi.jumi.core.stdout;

import fi.jumi.core.util.Boilerplate;
import org.apache.commons.io.output.*;

import javax.annotation.concurrent.ThreadSafe;
import java.io.*;
import java.nio.charset.*;

@ThreadSafe
public class OutputCapturer {

    // PrintStream does not do any buffering of its own; only the OutputStream that it delegates to may buffer.

    private final OutCapturer outCapturer = new OutCapturer();
    private final ErrCapturer errCapturer = new ErrCapturer();
    private final PrintStream out;
    private final PrintStream err;

    public OutputCapturer() {
        this(new NullOutputStream(), new NullOutputStream(), StandardCharsets.UTF_16BE);
    }

    public OutputCapturer(OutputStream realOut, OutputStream realErr, Charset charset) {
        OutputStream capturedOut = new WriterOutputStream(outCapturer, charset);
        OutputStream capturedErr = new WriterOutputStream(errCapturer, charset);
        err = createNonSynchronizedPrintStream(new OutputStreamReplicator(realErr, capturedErr), charset);
        out = SynchronizedPrintStream.create(new OutputStreamReplicator(realOut, capturedOut), charset, err);
    }

    private static PrintStream createNonSynchronizedPrintStream(OutputStream out, Charset charset) {
        try {
            return new PrintStream(out, false, charset.name());
        } catch (UnsupportedEncodingException e) {
            throw Boilerplate.rethrow(e);
        }
    }

    public PrintStream out() {
        return out;
    }

    public PrintStream err() {
        return err;
    }

    public void captureTo(OutputListener listener) {
        outCapturer.setListener(listener);
        errCapturer.setListener(listener);
    }

    @ThreadSafe
    private static class OutCapturer extends AbstractCapturer {
        @Override
        public void write(String text) {
            listener.get().out(text);
        }
    }

    @ThreadSafe
    private static class ErrCapturer extends AbstractCapturer {
        @Override
        public void write(String text) {
            listener.get().err(text);
        }
    }

    @ThreadSafe
    private static abstract class AbstractCapturer extends Writer {
        protected final ThreadLocal<OutputListener> listener = new InitializedInheritableThreadLocal<OutputListener>(
                new NullOutputListener());

        public void setListener(OutputListener listener) {
            this.listener.set(listener);
        }

        @Override
        public void write(char[] cbuf, int off, int len) {
            write(new String(cbuf, off, len));
        }

        @Override
        public abstract void write(String text);

        @Override
        public void flush() {
        }

        @Override
        public void close() {
        }
    }

    @ThreadSafe
    private static class OutputStreamReplicator extends OutputStream {
        private final OutputStream out1;
        private final OutputStream out2;

        public OutputStreamReplicator(OutputStream out1, OutputStream out2) {
            this.out1 = out1;
            this.out2 = out2;
        }

        @Override
        public void write(byte[] bytes, int off, int len) throws IOException {
            out1.write(bytes, off, len);
            out1.flush();
            out2.write(bytes, off, len);
            out2.flush();
        }

        @Override
        public void write(int b) throws IOException {
            out1.write(b);
            out1.flush();
            out2.write(b);
            out2.flush();
        }
    }
}