/*
 * Copyright 2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gradle.integtests.fixtures.executer;

import org.apache.commons.io.output.CloseShieldOutputStream;
import org.apache.commons.io.output.TeeOutputStream;
import org.gradle.internal.io.StreamByteBuffer;

import java.io.OutputStream;

class OutputCapturer {
    private final StreamByteBuffer buffer;
    private final OutputStream outputStream;
    private final StringBuilder outputStringBuilder = new StringBuilder();
    private final String outputEncoding;
    private String cachedOutputString;

    public OutputCapturer(OutputStream standardStream, String outputEncoding) {
        this.buffer = new StreamByteBuffer();
        this.outputStream = new CloseShieldOutputStream(new TeeOutputStream(standardStream, buffer.getOutputStream()));
        this.outputEncoding = outputEncoding;
    }

    public OutputStream getOutputStream() {
        return outputStream;
    }

    public String getOutputAsString() {
        if (cachedOutputString == null || buffer.totalBytesUnread() > 0) {
            outputStringBuilder.append(buffer.readAsString(outputEncoding));
            cachedOutputString = outputStringBuilder.toString();
        }
        return cachedOutputString;
    }

    public void reset() {
        buffer.clear();
        outputStringBuilder.setLength(0);
        cachedOutputString = null;
    }
}

@@ -656,24 +656,64 @@
 .executer;%0A%0A
+import net.jcip.annotations.ThreadSafe;%0A
 import org.a
@@ -859,24 +859,52 @@
 yteBuffer;%0A%0A
+import java.io.IOException;%0A
 import java.
@@ -920,16 +920,117 @@
 Stream;%0A
+import java.util.concurrent.locks.Lock;%0Aimport java.util.concurrent.locks.ReentrantLock;%0A%0A@ThreadSafe
 %0Aclass O
@@ -1287,16 +1287,67 @@
 tString;
+%0A    private final Lock lock = new ReentrantLock();
 %0A%0A    pu
@@ -1558,16 +1558,40 @@
 Stream, 
+new LockingOutputStream(
 buffer.g
@@ -1606,16 +1606,23 @@
 Stream()
+, lock)
 ));%0A    
@@ -1786,24 +1786,63 @@
 sString() %7B%0A
+        lock.lock();%0A        try %7B%0A    
         if (
@@ -1908,32 +1908,36 @@
 ) %7B%0A            
+ 
+   
 outputStringBuil
@@ -1989,32 +1989,36 @@
 ));%0A            
+    
 cachedOutputStri
@@ -2066,18 +2066,24 @@
     
-%7D%0A
+    %7D%0A  
         
 retu
@@ -2074,24 +2074,26 @@
 %7D%0A          
+  
 return cache
@@ -2115,37 +2115,133 @@
     
-%7D%0A%0A    public void reset() %7B%0A
+    %7D finally %7B%0A            lock.unlock();%0A        %7D%0A    %7D%0A%0A    public void reset() %7B%0A        lock.lock();%0A        try %7B%0A    
     
@@ -2256,24 +2256,28 @@
 er.clear();%0A
+    
         outp
@@ -2306,16 +2306,20 @@
 gth(0);%0A
+    
         
@@ -2345,16 +2345,1541 @@
 = null;%0A
+        %7D finally %7B%0A            lock.unlock();%0A        %7D%0A    %7D%0A%0A    private static class LockingOutputStream extends OutputStream %7B%0A        private final OutputStream delegate;%0A        private final Lock lock;%0A%0A        LockingOutputStream(OutputStream delegate, Lock lock) %7B%0A            this.delegate = delegate;%0A            this.lock = lock;%0A        %7D%0A%0A        @Override%0A        public void write(int b) throws IOException %7B%0A            lock.lock();%0A            try %7B%0A                delegate.write(b);%0A            %7D finally %7B%0A                lock.unlock();%0A            %7D%0A        %7D%0A%0A        @Override%0A        public void write(byte%5B%5D b) throws IOException %7B%0A            lock.lock();%0A            try %7B%0A                delegate.write(b);%0A            %7D finally %7B%0A                lock.unlock();%0A            %7D%0A        %7D%0A%0A        @Override%0A        public void write(byte%5B%5D b, int off, int len) throws IOException %7B%0A            lock.lock();%0A            try %7B%0A                delegate.write(b, off, len);%0A            %7D finally %7B%0A                lock.unlock();%0A            %7D%0A        %7D%0A%0A        @Override%0A        public void flush() throws IOException %7B%0A            lock.lock();%0A            try %7B%0A                delegate.flush();%0A            %7D finally %7B%0A                lock.unlock();%0A            %7D%0A        %7D%0A%0A        @Override%0A        public void close() throws IOException %7B%0A            lock.lock();%0A            try %7B%0A                delegate.close();%0A            %7D finally %7B%0A                lock.unlock();%0A            %7D%0A        %7D%0A
     %7D%0A%7D%0A

Make OutputCapturer thread safe