
io.datakernel.csp.ChannelSupplier Maven / Gradle / Ivy
Show all versions of datakernel-csp Show documentation
/*
* Copyright (C) 2015-2019 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.csp;
import io.datakernel.async.function.AsyncSupplier;
import io.datakernel.async.process.AsyncExecutor;
import io.datakernel.async.process.Cancellable;
import io.datakernel.bytebuf.ByteBuf;
import io.datakernel.common.exception.UncheckedException;
import io.datakernel.csp.dsl.ChannelSupplierTransformer;
import io.datakernel.csp.queue.ChannelQueue;
import io.datakernel.net.AsyncTcpSocket;
import io.datakernel.promise.Promise;
import io.datakernel.promise.SettablePromise;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static io.datakernel.common.Recyclable.tryRecycle;
import static io.datakernel.common.collection.CollectionUtils.asIterator;
/**
* This interface represents supplier of {@link Promise} of data that should be used serially
* (each consecutive {@link #get()}) operation should be called only after previous
* {@link #get()} operation finishes.
*
* After supplier is closed, all subsequent calls to {@link #get()} will return promise,
* completed exceptionally.
*
* If any exception is caught while supplying data items, {@link #close(Throwable)} method
* should be called. All resources should be freed and the caught exception should be
* propagated to all related processes.
*
* If {@link #get()} returns {@link Promise} of {@code null}, it represents end-of-stream
* and means that no additional data should be queried.
*/
public interface ChannelSupplier extends Cancellable {
@NotNull
Promise get();
/**
* @see #of(AsyncSupplier, Cancellable)
*/
static ChannelSupplier of(AsyncSupplier supplier) {
return of(supplier, null);
}
/**
* Wraps {@link AsyncSupplier} in ChannelSupplier, when {@code get()}
* is called, {@code AsyncSupplier}'s {@code get()} will be executed.
*
* @param supplier an {@code AsyncSupplier} to be wrapped in ChannelSupplier
* @param cancellable a {@code Cancellable} which will be set
* for the ChannelSupplier wrapper
* @param data type wrapped in {@code AsyncSupplier} and ChannelSupplier
* @return ChannelSupplier which wraps {@code AsyncSupplier}
*/
static ChannelSupplier of(AsyncSupplier supplier, @Nullable Cancellable cancellable) {
return new AbstractChannelSupplier(cancellable) {
@Override
protected Promise doGet() {
return supplier.get();
}
};
}
/**
* Returns a ChannelSupplier received from {@link ChannelQueue}.
*/
static ChannelSupplier ofConsumer(Consumer> consumer, ChannelQueue queue) {
consumer.accept(queue.getConsumer());
return queue.getSupplier();
}
/**
* Wraps provided default {@link Supplier} to ChannelSupplier.
*/
static ChannelSupplier ofSupplier(Supplier extends Promise> supplier) {
return of(supplier::get);
}
/**
* Returns a {@link ChannelSuppliers.ChannelSupplierEmpty}.
*/
static ChannelSupplier of() {
return new ChannelSuppliers.ChannelSupplierEmpty<>();
}
/**
* Wraps provided {@code value} to a {@link ChannelSuppliers.ChannelSupplierOfValue}.
*
* @param value a value to be wrapped in ChannelSupplier
* @return a {@code ChannelSupplierOfValue} which wraps the {@code value}
*/
static ChannelSupplier of(T value) {
return new ChannelSuppliers.ChannelSupplierOfValue<>(value);
}
/**
* @see #ofIterator(Iterator)
*/
@SafeVarargs
static ChannelSupplier of(T... values) {
return ofIterator(asIterator(values));
}
/**
* Returns a {@link ChannelSuppliers.ChannelSupplierOfException}
* of provided exception.
*
* @param e a {@link Throwable} to be wrapped in ChannelSupplier
*/
static ChannelSupplier ofException(Throwable e) {
return new ChannelSuppliers.ChannelSupplierOfException<>(e);
}
/**
* @see #ofIterator(Iterator)
*/
static ChannelSupplier ofIterable(Iterable extends T> iterable) {
return ofIterator(iterable.iterator());
}
/**
* @see #ofIterator(Iterator)
*/
static ChannelSupplier ofStream(Stream extends T> stream) {
return ofIterator(stream.iterator());
}
/**
* Wraps provided {@code Iterator} into
* {@link ChannelSuppliers.ChannelSupplierOfIterator}.
*
* @param iterator an iterator to be wrapped in ChannelSupplier
* @return a ChannelSupplier which wraps elements of type
*/
static ChannelSupplier ofIterator(Iterator extends T> iterator) {
return new ChannelSuppliers.ChannelSupplierOfIterator<>(iterator);
}
/**
* Wraps {@link AsyncTcpSocket#read()} operation into {@link ChannelSupplier}
*
* @return {@link ChannelSupplier} of ByteBufs that are read from network
*/
static ChannelSupplier ofSocket(AsyncTcpSocket socket) {
return ChannelSuppliers.prefetch(ChannelSupplier.of(socket::read, socket));
}
/**
* Wraps {@code promise} of ChannelSupplier in ChannelSupplier or
* returns the ChannelSupplier from {@code promise} itself.
*
* If {@code promise} is completed, it will be materialized and its result
* (a ChannelSupplier) will be returned.
*
* Otherwise, when {@code get()} is called, it will wait until {@code promise}
* completes and {@code promise} result's (a ChannelSupplier) {@code get()}
* operation will be executed. If the {@code promise} completes exceptionally,
* a {@code promise} of exception will be returned.
*
* @param promise wraps a {@code ChannelSupplier}
* @return a ChannelSupplier of {@code promise} or a wrapper ChannelSupplier
*/
static ChannelSupplier ofPromise(Promise extends ChannelSupplier> promise) {
if (promise.isResult()) return promise.getResult();
return new AbstractChannelSupplier() {
ChannelSupplier supplier;
Throwable exception;
@Override
protected Promise doGet() {
if (supplier != null) return supplier.get();
return promise.thenEx((supplier, e) -> {
if (e == null) {
this.supplier = supplier;
return supplier.get();
} else {
return Promise.ofException(e);
}
});
}
@Override
protected void onClosed(@NotNull Throwable e) {
exception = e;
promise.whenResult(supplier -> supplier.close(e));
}
};
}
/**
* Returns a {@code ChannelSupplier} wrapped in {@link Supplier}
* and calls its {@code get()} when {@code get()} method is called.
*
* @param provider a provider of {@code ChannelSupplier}
* @return a {@code ChannelSupplier} that was wrapped in
* the {@code provider}
*/
static ChannelSupplier ofLazyProvider(Supplier extends ChannelSupplier> provider) {
return new AbstractChannelSupplier() {
private ChannelSupplier supplier;
@Override
protected Promise doGet() {
if (supplier == null) supplier = provider.get();
return supplier.get();
}
@Override
protected void onClosed(@NotNull Throwable e) {
if (supplier != null) {
supplier.close(e);
}
}
};
}
/**
* Transforms this ChannelSupplier with the provided {@code fn}.
*
* @param returned result after transformation
* @param fn {@link ChannelSupplierTransformer} applied to the ChannelSupplier
*/
default R transformWith(ChannelSupplierTransformer fn) {
return fn.transform(this);
}
/**
* Creates and returns a new {@link AbstractChannelSupplier}
* based on current ChannelSupplier and makes its promise
* complete asynchronously.
*/
default ChannelSupplier async() {
return new AbstractChannelSupplier(this) {
@Override
protected Promise doGet() {
return ChannelSupplier.this.get().async();
}
};
}
/**
* Creates and returns a new {@link AbstractChannelSupplier}
* based on current ChannelSupplier and makes its promise
* executed by the provided {@code asyncExecutor}.
*/
default ChannelSupplier withExecutor(AsyncExecutor asyncExecutor) {
return new AbstractChannelSupplier(this) {
@Override
protected Promise doGet() {
return asyncExecutor.execute(ChannelSupplier.this::get);
}
};
}
/**
* Creates and returns a new {@link AbstractChannelSupplier}
* based on current ChannelSupplier and when its Promise completes
* successfully, the result is accepted by the provided {@code fn}.
*/
default ChannelSupplier peek(Consumer super T> fn) {
return new AbstractChannelSupplier(this) {
@Override
protected Promise doGet() {
return ChannelSupplier.this.get()
.whenResult(value -> { if (value != null) fn.accept(value);});
}
};
}
/**
* Creates and returns a new {@link AbstractChannelSupplier}
* based on current ChannelSupplier and when its Promise completes,
* applies provided {@code fn} to the result.
*/
default ChannelSupplier map(Function super @NotNull T, ? extends V> fn) {
return new AbstractChannelSupplier(this) {
@Override
protected Promise doGet() {
return ChannelSupplier.this.get()
.map(value -> {
if (value != null) {
try {
return fn.apply(value);
} catch (UncheckedException u) {
ChannelSupplier.this.close(u.getCause());
throw u;
}
} else {
return null;
}
});
}
};
}
/**
* Creates and returns a new {@link AbstractChannelSupplier}
* based on current ChannelSupplier and applies provided {@code fn}
* to its Promise asynchronously.
*/
default ChannelSupplier mapAsync(Function super @NotNull T, ? extends Promise> fn) {
return new AbstractChannelSupplier(this) {
@Override
protected Promise doGet() {
return ChannelSupplier.this.get()
.then(value -> value != null ?
fn.apply(value) :
Promise.of(null));
}
};
}
/**
* Creates and returns a new {@link AbstractChannelSupplier}
* based on current ChannelSupplier and checks if its Promise's value(s)
* match(es) the predicate, leaving only those value(s) which pass the test.
*/
default ChannelSupplier filter(Predicate super T> predicate) {
return new AbstractChannelSupplier(this) {
@Override
protected Promise doGet() {
while (true) {
Promise promise = ChannelSupplier.this.get();
if (promise.isResult()) {
T value = promise.getResult();
if (value == null || predicate.test(value)) return promise;
tryRecycle(value);
continue;
}
return promise.then(value -> {
if (value == null || predicate.test(value)) {
return Promise.of(value);
} else {
tryRecycle(value);
return get();
}
});
}
}
};
}
/**
* Creates and returns a new {@link AbstractChannelSupplier} based on current
* ChannelSupplier, when its {@code get} is called, its values will be returned
* until they don't fit the {@code predicate}. If one of the results passed the
* {@code predicate test}, consequent {@code get} operations will return {@code null}.
*/
default ChannelSupplier until(Predicate super T> predicate) {
return new AbstractChannelSupplier(this) {
boolean stop = false;
@Override
protected Promise doGet() {
if (stop) {
return Promise.of(null);
}
return ChannelSupplier.this.get()
.map(value -> {
if (value == null) {
return null;
}
if (predicate.test(value)) {
stop = true;
}
return value;
});
}
};
}
/**
* Creates and returns a new {@link AbstractChannelSupplier}
* based on current ChannelSupplier. Even if its Promise completes
* with an exception, {@code get()} method will return a successfully
* completed Promise (in case of exception, with {@code null} result value).
*/
default ChannelSupplier lenient() {
return new AbstractChannelSupplier(this) {
@Override
protected Promise doGet() {
return ChannelSupplier.this.get().thenEx((value, e) -> Promise.of(value));
}
};
}
/**
* @see ChannelSuppliers#streamTo(ChannelSupplier, ChannelConsumer)
*/
default Promise streamTo(ChannelConsumer consumer) {
return ChannelSuppliers.streamTo(this, consumer);
}
default Promise streamTo(Promise extends ChannelConsumer> consumer) {
return ChannelSuppliers.streamTo(this, ChannelConsumer.ofPromise(consumer));
}
/**
* Binds this ChannelSupplier to provided {@link ChannelInput}
*/
default Promise bindTo(ChannelInput to) {
return to.set(this);
}
/**
* @see ChannelSuppliers#collect
*/
default Promise toCollector(Collector collector) {
return ChannelSuppliers.collect(this,
collector.supplier().get(), collector.accumulator(), collector.finisher());
}
/**
* @see #toCollector(Collector)
*/
default Promise> toList() {
return toCollector(Collectors.toList());
}
default ChannelSupplier withEndOfStream(Function, Promise> fn) {
SettablePromise endOfStream = new SettablePromise<>();
Promise newEndOfStream = fn.apply(endOfStream);
return new AbstractChannelSupplier(this) {
@SuppressWarnings("unchecked")
@Override
protected Promise doGet() {
return ChannelSupplier.this.get()
.thenEx((item, e) -> {
if (e == null) {
if (item != null) return Promise.of(item);
endOfStream.trySet(null);
return (Promise) newEndOfStream;
} else {
endOfStream.trySetException(e);
return (Promise) newEndOfStream;
}
});
}
@Override
protected void onClosed(@NotNull Throwable e) {
endOfStream.trySetException(e);
}
};
}
static Promise getEndOfStream(Consumer, Promise>> fn) {
return Promise.ofCallback(cb ->
fn.accept(endOfStream -> endOfStream.whenComplete(cb)));
}
}