/*
* Copyright (C) 2015 SoftIndex LLC.
*
* 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 io.datakernel.stream;
import io.datakernel.async.*;
import io.datakernel.eventloop.Eventloop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
import static io.datakernel.async.AsyncIterators.asyncIteratorOfIterator;
public final class StreamProducers {
private StreamProducers() {
}
/**
* Returns producer which doing nothing - not sending any data and not closing itself.
*
* @param eventloop event loop in which will run it
*/
public static <T> StreamProducer<T> idle(Eventloop eventloop) {
return new Idle<>(eventloop);
}
/**
* Returns producer which closes itself
*
* @param eventloop event loop in which will run it
* @param <T> type of item for send
*/
public static <T> StreamProducer<T> closing(Eventloop eventloop) {
return new EndOfStream<>(eventloop);
}
public static <T> StreamProducer<T> closingWithError(Eventloop eventloop, Exception e) {
return new ClosingWithError<>(eventloop, e);
}
/**
* Creates producer which sends value and closes itself
*
* @param eventloop event loop in which will run it
* @param value value for sending
* @param <T> type of value
*/
public static <T> StreamProducer<T> ofValue(Eventloop eventloop, final T value) {
return new OfValue<>(eventloop, value);
}
/**
* Creates producer which sends value and closes itself
*
* @param eventloop event loop in which will run it
* @param value value for sending
* @param close if producer is closed
* @param <T> type of value
*/
public static <T> StreamProducer<T> ofValue(Eventloop eventloop, final T value, boolean close) {
return new OfValue<>(eventloop, value, close);
}
/**
* Returns new {@link OfIterator} which sends items from iterator
*
* @param eventloop event loop in which will run it
* @param iterator iterator with items for sending
* @param <T> type of item
*/
public static <T> StreamProducer<T> ofIterator(Eventloop eventloop, Iterator<T> iterator) {
return new OfIterator<>(eventloop, iterator);
}
/**
* Returns new {@link OfIterator} which sends items from {@code iterable}
*
* @param eventloop event loop in which will run it
* @param iterable iterable with items for sending
* @param <T> type of item
*/
public static <T> StreamProducer<T> ofIterable(Eventloop eventloop, Iterable<T> iterable) {
return new OfIterator<>(eventloop, iterable.iterator());
}
/**
* Represents asynchronously resolving producer.
*
* @param eventloop event loop in which will run it
* @param producerCallable callable with producer
* @param <T> type of output data
*/
public static <T> StreamProducer<T> asynchronouslyResolving(final Eventloop eventloop, final AsyncCallable<StreamProducer<T>> producerCallable) {
final StreamForwarder<T> forwarder = StreamForwarder.create(eventloop);
eventloop.post(new Runnable() {
@Override
public void run() {
producerCallable.call(new ResultCallback<StreamProducer<T>>() {
@Override
protected void onResult(StreamProducer<T> actualProducer) {
actualProducer.streamTo(forwarder.getInput());
}
@Override
protected void onException(Exception exception) {
new ClosingWithError<T>(eventloop, exception).streamTo(forwarder.getInput());
}
});
}
});
return forwarder.getOutput();
}
/**
* Returns {@link StreamProducerConcat} with producers from asyncIterator which will stream to this
*
* @param eventloop event loop in which will run it
* @param asyncIterator iterator with producers
* @param <T> type of output data
*/
public static <T> StreamProducer<T> concat(Eventloop eventloop, AsyncIterator<StreamProducer<T>> asyncIterator) {
return new StreamProducerConcat<>(eventloop, asyncIterator);
}
/**
* Returns {@link StreamProducerConcat} with producers from AsyncIterable which will stream to this
*
* @param eventloop event loop in which will run it
* @param asyncIterator iterable with producers
* @param <T> type of output data
*/
public static <T> StreamProducer<T> concat(Eventloop eventloop, AsyncIterable<StreamProducer<T>> asyncIterator) {
return concat(eventloop, asyncIterator.asyncIterator());
}
/**
* Returns {@link StreamProducerConcat} with producers from Iterator which will stream to this
*
* @param eventloop event loop in which will run it
* @param iterator iterator with producers
* @param <T> type of output data
*/
public static <T> StreamProducer<T> concat(Eventloop eventloop, Iterator<StreamProducer<T>> iterator) {
return concat(eventloop, asyncIteratorOfIterator(iterator));
}
/**
* Returns {@link StreamProducerConcat} with producers from Iterable which will stream to this
*
* @param eventloop event loop in which will run it
* @param producers list of producers
* @param <T> type of output data
*/
public static <T> StreamProducer<T> concat(Eventloop eventloop, List<StreamProducer<T>> producers) {
return concat(eventloop, producers.iterator());
}
@SafeVarargs
public static <T> StreamProducer<T> concat(Eventloop eventloop, StreamProducer<T>... producers) {
return concat(eventloop, Arrays.asList(producers));
}
/**
* Represent a {@link AbstractStreamProducer} which once sends to consumer end of stream.
*
* @param <T>
*/
public static class EndOfStream<T> extends AbstractStreamProducer<T> {
public EndOfStream(Eventloop eventloop) {
super(eventloop);
}
@Override
protected void onStarted() {
sendEndOfStream();
}
}
/**
* Represent producer which sends specified exception to consumer.
*
* @param <T>
*/
public static class ClosingWithError<T> extends AbstractStreamProducer<T> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Exception exception;
public ClosingWithError(Eventloop eventloop, Exception exception) {
super(eventloop);
this.exception = exception;
}
@Override
protected void onStarted() {
logger.trace("{} close with error {}", this, exception.getMessage());
closeWithError(exception);
}
}
public static class Idle<T> extends AbstractStreamProducer<T> {
public Idle(Eventloop eventloop) {
super(eventloop);
}
}
/**
* Represents a {@link AbstractStreamProducer} which will send all values from iterator.
*
* @param <T> type of output data
*/
public static class OfIterator<T> extends AbstractStreamProducer<T> {
private final Iterator<T> iterator;
private boolean sendEndOfStream = true;
/**
* Creates a new instance of StreamProducerOfIterator
*
* @param eventloop event loop where producer will run
* @param iterator iterator with object which need to send
*/
public OfIterator(Eventloop eventloop, Iterator<T> iterator) {
this(eventloop, iterator, true);
}
public OfIterator(Eventloop eventloop, Iterator<T> iterator, boolean sendEndOfStream) {
super(eventloop);
this.iterator = checkNotNull(iterator);
this.sendEndOfStream = sendEndOfStream;
}
@Override
protected void doProduce() {
for (; ; ) {
if (!iterator.hasNext())
break;
if (!isStatusReady())
return;
T item = iterator.next();
send(item);
}
if (sendEndOfStream)
sendEndOfStream();
}
@Override
protected void onStarted() {
produce();
}
@Override
protected void onResumed() {
resumeProduce();
}
}
/**
* It is {@link AbstractStreamProducer} which sends specified single value to its consumer, followed by end-of-stream
*
* @param <T> type of value for send
*/
public static class OfValue<T> extends AbstractStreamProducer<T> {
private final T value;
private final boolean sendEndOfStream;
/**
* Creates producer which sends value and closes itself
*
* @param eventloop event loop in which this producer will run
* @param value value for sending
*/
public OfValue(Eventloop eventloop, T value) {
this(eventloop, value, true);
}
/**
* Creates producer which sends value and optionally closes itself
*
* @param eventloop event loop in which this producer will run
* @param value value for sending
* @param sendEndOfStream if producer is closed
*/
public OfValue(Eventloop eventloop, T value, boolean sendEndOfStream) {
super(eventloop);
this.value = value;
this.sendEndOfStream = sendEndOfStream;
}
@Override
protected void doProduce() {
send(value);
if (sendEndOfStream)
sendEndOfStream();
}
@Override
protected void onStarted() {
produce();
}
@Override
protected void onResumed() {
resumeProduce();
}
}
/**
* Represents {@link AbstractStreamTransformer_1_1}, which created with iterator with {@link AbstractStreamProducer}
* which will stream to this
*
* @param <T> type of received data
*/
public static class StreamProducerConcat<T> extends StreamProducerDecorator<T> {
private final AsyncIterator<StreamProducer<T>> iterator;
private final ForwarderConcat forwarderConcat;
public StreamProducerConcat(Eventloop eventloop, AsyncIterator<StreamProducer<T>> iterator) {
this.forwarderConcat = new ForwarderConcat(eventloop);
this.iterator = iterator;
setActualProducer(forwarderConcat.getOutput());
}
private class ForwarderConcat extends AbstractStreamTransformer_1_1<T, T> {
protected InputConsumer inputConsumer;
protected OutputProducer outputProducer;
protected ForwarderConcat(Eventloop eventloop) {
super(eventloop);
inputConsumer = new InputConsumer();
outputProducer = new OutputProducer();
}
@Override
protected AbstractInputConsumer getInputImpl() {
return inputConsumer;
}
@Override
protected AbstractOutputProducer getOutputImpl() {
return outputProducer;
}
private class InputConsumer extends AbstractInputConsumer {
private void doNext() {
eventloop.post(new Runnable() {
@Override
public void run() {
iterator.next(new IteratorCallback<StreamProducer<T>>() {
@Override
protected void onNext(StreamProducer<T> actualProducer) {
actualProducer.streamTo(new StreamConsumerDecorator<T>(ForwarderConcat.this.getInput()) {
@Override
public void onProducerEndOfStream() {
inputConsumer.onUpstreamEndOfStream();
}
});
}
@Override
protected void onEnd() {
outputProducer.sendEndOfStream();
}
@Override
protected void onException(Exception e) {
closeWithError(e);
}
});
}
});
}
@Override
protected void onUpstreamEndOfStream() {
doNext();
}
@Override
public StreamDataReceiver<T> getDataReceiver() {
return outputProducer.getDownstreamDataReceiver();
}
}
private class OutputProducer extends AbstractOutputProducer {
@Override
protected void onDownstreamStarted() {
inputConsumer.doNext();
}
@Override
protected void onDownstreamSuspended() {
inputConsumer.suspend();
}
@Override
protected void onDownstreamResumed() {
inputConsumer.resume();
}
}
}
}
}