package com.aol.micro.server.reactive; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.Supplier; import cyclops.async.Future; import cyclops.async.LazyReact; import cyclops.async.Pipes; import cyclops.async.QueueFactory; import cyclops.control.Eval; import cyclops.control.Maybe; import cyclops.function.FluentFunctions; import cyclops.stream.FutureStream; import cyclops.stream.ReactiveSeq; import lombok.AccessLevel; import lombok.AllArgsConstructor; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** * Class for pusing information across threads to various consumers. * Push data onto a queue on one thread for a Stream or Consumer on another to react to. * * @author johnmcclean * * @param <T> */ @AllArgsConstructor(access=AccessLevel.PRIVATE) public class EventQueueManager<T> { public final static Supplier<LazyReact> io = FluentFunctions.of(()->new LazyReact(100,100)) .memoize(); public static <T> EventQueueManager<T> of(Executor ex, QueueFactory<T> factory){ return new EventQueueManager<>(ex,factory); } private final Pipes<String,T> pipes = Pipes.of(); private final Executor ex; private final QueueFactory<T> factory; /** * Push data onto the named queue. * * @param key Queue name * @param value Data to push */ public void push(String key, T value){ pipes.push(key,value); } /** * React to any data pushed onto the named Queue with the supplied consumer. * * @param key Queu name * @param reactor Consumer to react to new data */ public void forEach(String key,Consumer<? super T> reactor){ if(!pipes.get(key).isPresent()) pipes.register(key, factory.build()); pipes.reactiveSeq(key) .get() .futureOperations(ex) .forEachX(Long.MAX_VALUE,reactor); } /** * @param key Register a new queue with supplied key */ public void register(String key){ pipes.register(key, factory.build()); } /** * Asynchronously extract a single data point from the named Queue. * * * @param key Queue name * @param ex Executor to run on * @return FutureW that will eventually have data from the Queue */ public Future<T> future(String key, Executor ex){ return pipes.oneOrErrorAsync(key, ex); } /** * Asynchronously extract a single data point from the named Queue. * * * @param key Queue name * @param ex Executor to run on * @return Mono that will eventually have data from the Queue */ public Mono<T> mono(String key,Executor ex){ return Mono.fromFuture(pipes.oneOrErrorAsync(key, ex) .getFuture()); } /** * Generate a Reactor Stream (Flux) from the supplied Queue name. * Any data pushed to the Queue will pass on to the returned Flux. * * @param key Queue name * @return Flux which will recieve data from the Queue */ public Flux<T> flux(String key){ if(!pipes.get(key).isPresent()) pipes.register(key, factory.build()); return Flux.from(pipes.reactiveSeq(key) .get()); } /** * Generate a cyclops-react Stream (ReactiveSeq) from the supplied Queue name. * Any data pushed to the Queue will pass on to the returned ReactiveSeq. * * @param key Queue name * @return ReactiveSeq which will recieve data from the Queue */ public ReactiveSeq<T> stream(String key){ if(!pipes.get(key).isPresent()) pipes.register(key, factory.build()); return pipes.reactiveSeq(key) .get(); } /** * Lazily extract a single value from the named Queue. * Maybe will be none if the Queue is closed, otherwise will retrieve the next value. * * @param key Queue name * @return Maybe with next data point */ public Maybe<T> maybe(String key){ if(!pipes.get(key).isPresent()) pipes.register(key, factory.build()); return pipes.oneValue(key); } /** * Lazily extract a single value from the named Queue. * Eval will be contain null if the Queue is closed, otherwise will retrieve the next value. * * @param key Queue name * @return Eval with next data point with next data point */ public Eval<T> lazy(String key){ if(!pipes.get(key).isPresent()) pipes.register(key, factory.build()); return pipes.nextOrNull(key); } /** * Generate a Stream of Futures from the supplied key name * * @param key Queue name * @return Stream of futures */ public FutureStream<T> ioFutureStream(String key){ if(!pipes.get(key).isPresent()) pipes.register(key, factory.build()); return pipes.futureStream(key, io.get()) .get(); } }