package org.testcontainers.containers.output;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.core.async.ResultCallbackTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
/**
* This class can be used as a generic callback for docker-java commands that produce Frames.
*/
public class FrameConsumerResultCallback extends ResultCallbackTemplate<FrameConsumerResultCallback, Frame> {
private final static Logger LOGGER = LoggerFactory.getLogger(FrameConsumerResultCallback.class);
private Map<OutputFrame.OutputType, Consumer<OutputFrame>> consumers;
private CountDownLatch completionLatch = new CountDownLatch(1);
public FrameConsumerResultCallback() {
consumers = new HashMap<>();
}
/**
* Set this callback to use the specified consumer for the given output type.
* The same consumer can be configured for more than one output type.
* @param outputType the output type to configure
* @param consumer the consumer to use for that output type
*/
public void addConsumer(OutputFrame.OutputType outputType, Consumer<OutputFrame> consumer) {
consumers.put(outputType, consumer);
}
@Override
public void onNext(Frame frame) {
if (frame != null) {
OutputFrame outputFrame = OutputFrame.forFrame(frame);
if (outputFrame != null) {
Consumer<OutputFrame> consumer = consumers.get(outputFrame.getType());
if (consumer == null) {
LOGGER.error("got frame with type " + frame.getStreamType() + ", for which no handler is configured");
} else {
consumer.accept(outputFrame);
}
}
}
}
@Override
public void onError(Throwable throwable) {
// Sink any errors
try {
close();
} catch (IOException ignored) { }
}
@Override
public void close() throws IOException {
// send an END frame to every consumer... but only once per consumer.
for (Consumer<OutputFrame> consumer : new HashSet<>(consumers.values())) {
consumer.accept(OutputFrame.END);
}
super.close();
completionLatch.countDown();
}
/**
* @return a {@link CountDownLatch} that may be used to wait until {@link #close()} has been called.
*/
public CountDownLatch getCompletionLatch() {
return completionLatch;
}
}