package gobblin.ingestion.google; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; @Slf4j public abstract class AsyncIteratorWithDataSink<T> implements Iterator<T> { private final Object lock = new Object(); private volatile Throwable exceptionInProducerThread = null; private Thread _producerThread; protected LinkedBlockingDeque<T> _dataSink; private final int _pollBlockingTime; protected AsyncIteratorWithDataSink(int queueSize, int pollBlockingTime) { _dataSink = new LinkedBlockingDeque<>(queueSize); _pollBlockingTime = pollBlockingTime; } @Override public boolean hasNext() { initialize(); if (!_dataSink.isEmpty()) { return true; } try { T next = _dataSink.poll(_pollBlockingTime, TimeUnit.SECONDS); while (next == null) { if (_producerThread.isAlive()) { //Job not done yet. Keep waiting... next = _dataSink.poll(_pollBlockingTime, TimeUnit.SECONDS); } else { synchronized (lock) { if (exceptionInProducerThread != null) { throw new RuntimeException( String.format("Found exception in producer thread %s", _producerThread.getName()), exceptionInProducerThread); } } log.info("Producer job has finished. No more query data in the queue."); return false; } } //Must put it back. Implement in this way because LinkedBlockingDeque doesn't support blocking peek. _dataSink.putFirst(next); return true; } catch (InterruptedException e) { throw new RuntimeException(e); } } private void initialize() { if (_producerThread == null) { _producerThread = new Thread(getProducerRunnable()); _producerThread.setUncaughtExceptionHandler(getExceptionHandler()); _producerThread.start(); } } protected abstract Runnable getProducerRunnable(); @Override public T next() { if (hasNext()) { return _dataSink.remove(); } throw new NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException(); } private Thread.UncaughtExceptionHandler getExceptionHandler() { return new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { synchronized (lock) { exceptionInProducerThread = e; } } }; } }