@Override public FluxSink<T> onDispose(Disposable d) { sink.onDispose(d); return this; }
/** * Wrapper around {@link FluxSink#onDispose(Disposable)}. * * @param disposable to be passed to the delegate sink */ public void onDispose(Disposable disposable) { fluxSink.onDispose(disposable); } }
/** * Write the given stream of {@link DataBuffer DataBuffers} to the given {@code AsynchronousFileChannel}. * Does <strong>not</strong> close the channel when the flux is terminated, and does * <strong>not</strong> {@linkplain #release(DataBuffer) release} the data buffers in the * source. If releasing is required, then subscribe to the returned {@code Flux} with a * {@link #releaseConsumer()}. * <p>Note that the writing process does not start until the returned {@code Flux} is subscribed to. * @param source the stream of data buffers to be written * @param channel the channel to write to * @param position the file position at which the write is to begin; must be non-negative * @return a flux containing the same buffers as in {@code source}, that starts the writing * process when subscribed to, and that publishes any writing errors and the completion signal */ public static Flux<DataBuffer> write( Publisher<DataBuffer> source, AsynchronousFileChannel channel, long position) { Assert.notNull(source, "'source' must not be null"); Assert.notNull(channel, "'channel' must not be null"); Assert.isTrue(position >= 0, "'position' must be >= 0"); Flux<DataBuffer> flux = Flux.from(source); return Flux.create(sink -> { AsynchronousFileChannelWriteCompletionHandler completionHandler = new AsynchronousFileChannelWriteCompletionHandler(sink, channel, position); sink.onDispose(completionHandler); flux.subscribe(completionHandler); }); }
/** * Write the given stream of {@link DataBuffer DataBuffers} to the given {@code WritableByteChannel}. Does * <strong>not</strong> close the channel when the flux is terminated, and does * <strong>not</strong> {@linkplain #release(DataBuffer) release} the data buffers in the * source. If releasing is required, then subscribe to the returned {@code Flux} with a * {@link #releaseConsumer()}. * <p>Note that the writing process does not start until the returned {@code Flux} is subscribed to. * @param source the stream of data buffers to be written * @param channel the channel to write to * @return a flux containing the same buffers as in {@code source}, that starts the writing * process when subscribed to, and that publishes any writing errors and the completion signal */ public static Flux<DataBuffer> write(Publisher<DataBuffer> source, WritableByteChannel channel) { Assert.notNull(source, "'source' must not be null"); Assert.notNull(channel, "'channel' must not be null"); Flux<DataBuffer> flux = Flux.from(source); return Flux.create(sink -> { WritableByteChannelSubscriber subscriber = new WritableByteChannelSubscriber(sink, channel); sink.onDispose(subscriber); flux.subscribe(subscriber); }); }
/** * Obtain a {@code AsynchronousFileChannel} from the given supplier, and read it into a * {@code Flux} of {@code DataBuffer}s, starting at the given position. Closes the * channel when the flux is terminated. * @param channelSupplier the supplier for the channel to read from * @param position the position to start reading from * @param dataBufferFactory the factory to create data buffers with * @param bufferSize the maximum size of the data buffers * @return a flux of data buffers read from the given channel */ public static Flux<DataBuffer> readAsynchronousFileChannel(Callable<AsynchronousFileChannel> channelSupplier, long position, DataBufferFactory dataBufferFactory, int bufferSize) { Assert.notNull(channelSupplier, "'channelSupplier' must not be null"); Assert.notNull(dataBufferFactory, "'dataBufferFactory' must not be null"); Assert.isTrue(position >= 0, "'position' must be >= 0"); Assert.isTrue(bufferSize > 0, "'bufferSize' must be > 0"); DataBuffer dataBuffer = dataBufferFactory.allocateBuffer(bufferSize); ByteBuffer byteBuffer = dataBuffer.asByteBuffer(0, bufferSize); Flux<DataBuffer> result = Flux.using(channelSupplier, channel -> Flux.create(sink -> { AsynchronousFileChannelReadCompletionHandler completionHandler = new AsynchronousFileChannelReadCompletionHandler(channel, sink, position, dataBufferFactory, bufferSize); channel.read(byteBuffer, position, dataBuffer, completionHandler); sink.onDispose(completionHandler::dispose); }), DataBufferUtils::closeChannel); return result.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release); }
/** * Write the given stream of {@link DataBuffer DataBuffers} to the given {@code AsynchronousFileChannel}. * Does <strong>not</strong> close the channel when the flux is terminated, and does * <strong>not</strong> {@linkplain #release(DataBuffer) release} the data buffers in the * source. If releasing is required, then subscribe to the returned {@code Flux} with a * {@link #releaseConsumer()}. * <p>Note that the writing process does not start until the returned {@code Flux} is subscribed to. * @param source the stream of data buffers to be written * @param channel the channel to write to * @param position the file position at which the write is to begin; must be non-negative * @return a flux containing the same buffers as in {@code source}, that starts the writing * process when subscribed to, and that publishes any writing errors and the completion signal */ public static Flux<DataBuffer> write( Publisher<DataBuffer> source, AsynchronousFileChannel channel, long position) { Assert.notNull(source, "'source' must not be null"); Assert.notNull(channel, "'channel' must not be null"); Assert.isTrue(position >= 0, "'position' must be >= 0"); Flux<DataBuffer> flux = Flux.from(source); return Flux.create(sink -> { AsynchronousFileChannelWriteCompletionHandler completionHandler = new AsynchronousFileChannelWriteCompletionHandler(sink, channel, position); sink.onDispose(completionHandler); flux.subscribe(completionHandler); }); }
/** * Write the given stream of {@link DataBuffer DataBuffers} to the given {@code WritableByteChannel}. Does * <strong>not</strong> close the channel when the flux is terminated, and does * <strong>not</strong> {@linkplain #release(DataBuffer) release} the data buffers in the * source. If releasing is required, then subscribe to the returned {@code Flux} with a * {@link #releaseConsumer()}. * <p>Note that the writing process does not start until the returned {@code Flux} is subscribed to. * @param source the stream of data buffers to be written * @param channel the channel to write to * @return a flux containing the same buffers as in {@code source}, that starts the writing * process when subscribed to, and that publishes any writing errors and the completion signal */ public static Flux<DataBuffer> write(Publisher<DataBuffer> source, WritableByteChannel channel) { Assert.notNull(source, "'source' must not be null"); Assert.notNull(channel, "'channel' must not be null"); Flux<DataBuffer> flux = Flux.from(source); return Flux.create(sink -> { WritableByteChannelSubscriber subscriber = new WritableByteChannelSubscriber(sink, channel); sink.onDispose(subscriber); flux.subscribe(subscriber); }); }
/** * Obtain a {@code AsynchronousFileChannel} from the given supplier, and read it into a * {@code Flux} of {@code DataBuffer}s, starting at the given position. Closes the * channel when the flux is terminated. * @param channelSupplier the supplier for the channel to read from * @param position the position to start reading from * @param dataBufferFactory the factory to create data buffers with * @param bufferSize the maximum size of the data buffers * @return a flux of data buffers read from the given channel */ public static Flux<DataBuffer> readAsynchronousFileChannel(Callable<AsynchronousFileChannel> channelSupplier, long position, DataBufferFactory dataBufferFactory, int bufferSize) { Assert.notNull(channelSupplier, "'channelSupplier' must not be null"); Assert.notNull(dataBufferFactory, "'dataBufferFactory' must not be null"); Assert.isTrue(position >= 0, "'position' must be >= 0"); Assert.isTrue(bufferSize > 0, "'bufferSize' must be > 0"); DataBuffer dataBuffer = dataBufferFactory.allocateBuffer(bufferSize); ByteBuffer byteBuffer = dataBuffer.asByteBuffer(0, bufferSize); Flux<DataBuffer> result = Flux.using(channelSupplier, channel -> Flux.create(sink -> { AsynchronousFileChannelReadCompletionHandler completionHandler = new AsynchronousFileChannelReadCompletionHandler(channel, sink, position, dataBufferFactory, bufferSize); channel.read(byteBuffer, position, dataBuffer, completionHandler); sink.onDispose(completionHandler::dispose); }), DataBufferUtils::closeChannel); return result.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release); }
@Override public Flux<PatternMessage<K, V>> observePatterns(FluxSink.OverflowStrategy overflowStrategy) { return Flux.create(sink -> { RedisPubSubAdapter<K, V> listener = new RedisPubSubAdapter<K, V>() { @Override public void message(K pattern, K channel, V message) { sink.next(new PatternMessage<>(pattern, channel, message)); } }; StatefulRedisPubSubConnection<K, V> statefulConnection = getStatefulConnection(); statefulConnection.addListener(listener); sink.onDispose(() -> { statefulConnection.removeListener(listener); }); }, overflowStrategy); }
@Override public Flux<ChannelMessage<K, V>> observeChannels(FluxSink.OverflowStrategy overflowStrategy) { return Flux.create(sink -> { RedisPubSubAdapter<K, V> listener = new RedisPubSubAdapter<K, V>() { @Override public void message(K channel, V message) { sink.next(new ChannelMessage<>(channel, message)); } }; StatefulRedisPubSubConnection<K, V> statefulConnection = getStatefulConnection(); statefulConnection.addListener(listener); sink.onDispose(() -> { statefulConnection.removeListener(listener); }); }, overflowStrategy); }
@Override public <U> UpdateHandlerRegistration<U> registerUpdateHandler( SubscriptionQueryMessage<?, ?, ?> query, SubscriptionQueryBackpressure backpressure, int updateBufferSize) { EmitterProcessor<SubscriptionQueryUpdateMessage<U>> processor = EmitterProcessor.create(updateBufferSize); FluxSink<SubscriptionQueryUpdateMessage<U>> sink = processor.sink(backpressure.getOverflowStrategy()); sink.onDispose(() -> updateHandlers.remove(query)); FluxSinkWrapper<SubscriptionQueryUpdateMessage<U>> fluxSinkWrapper = new FluxSinkWrapper<>(sink); updateHandlers.put(query, fluxSinkWrapper); Registration registration = () -> { fluxSinkWrapper.complete(); return true; }; return new UpdateHandlerRegistration<>(registration, processor.replay(updateBufferSize).autoConnect()); }
@Test public void fluxCreateDisposables() { AtomicInteger dispose1 = new AtomicInteger(); AtomicInteger dispose2 = new AtomicInteger(); AtomicInteger cancel1 = new AtomicInteger(); AtomicInteger cancel2 = new AtomicInteger(); AtomicInteger cancellation = new AtomicInteger(); Flux<String> created = Flux.create(s -> { s.onDispose(dispose1::getAndIncrement) .onCancel(cancel1::getAndIncrement); s.onDispose(dispose2::getAndIncrement); assertThat(dispose2.get()).isEqualTo(1); s.onCancel(cancel2::getAndIncrement); assertThat(cancel2.get()).isEqualTo(1); s.onDispose(cancellation::getAndIncrement); assertThat(cancellation.get()).isEqualTo(1); assertThat(dispose1.get()).isEqualTo(0); assertThat(cancel1.get()).isEqualTo(0); s.next("test1"); s.complete(); }); StepVerifier.create(created) .expectNext("test1") .verifyComplete(); assertThat(dispose1.get()).isEqualTo(1); assertThat(cancel1.get()).isEqualTo(0); }
@Test(timeout = 3000L) public void cancelOnDedicatedScheduler() throws Exception { CountDownLatch latch = new CountDownLatch(1); AtomicReference<Thread> threadHash = new AtomicReference<>(Thread.currentThread()); Schedulers.single().schedule(() -> threadHash.set(Thread.currentThread())); Flux.create(sink -> { sink.onDispose(() -> { if (threadHash.compareAndSet(Thread.currentThread(), null)) { latch.countDown(); } }); }) .cancelOn(Schedulers.single()) .subscribeWith(AssertSubscriber.create()) .cancel(); latch.await(); Assert.assertNull(threadHash.get()); }
@Test public void fluxCreateBufferedCancelled() { AtomicInteger onDispose = new AtomicInteger(); AtomicInteger onCancel = new AtomicInteger(); Flux<String> created = Flux.create(s -> { s.onDispose(() -> { onDispose.getAndIncrement(); assertThat(s.isCancelled()).isTrue(); }); s.onCancel(() -> { onCancel.getAndIncrement(); assertThat(s.isCancelled()).isTrue(); }); s.next("test1"); s.next("test2"); s.next("test3"); s.complete(); }); StepVerifier.create(created) .expectNext("test1", "test2", "test3") .thenCancel() .verify(); assertThat(onDispose.get()).isEqualTo(1); assertThat(onCancel.get()).isEqualTo(1); }
@Test public void fluxCreateBuffered() { AtomicInteger onDispose = new AtomicInteger(); AtomicInteger onCancel = new AtomicInteger(); Flux<String> created = Flux.create(s -> { s.onDispose(onDispose::getAndIncrement) .onCancel(onCancel::getAndIncrement); s.next("test1"); s.next("test2"); s.next("test3"); s.complete(); }); assertThat(created.getPrefetch()).isEqualTo(-1); StepVerifier.create(created) .expectNext("test1", "test2", "test3") .verifyComplete(); assertThat(onDispose.get()).isEqualTo(1); assertThat(onCancel.get()).isEqualTo(0); }
@Test public void fluxCreateBuffered2() { AtomicInteger cancellation = new AtomicInteger(); AtomicInteger onCancel = new AtomicInteger(); StepVerifier.create(Flux.create(s -> { s.onDispose(cancellation::getAndIncrement); s.onCancel(onCancel::getAndIncrement); s.next("test1"); s.next("test2"); s.next("test3"); s.complete(); }).publishOn(Schedulers.parallel())) .expectNext("test1", "test2", "test3") .verifyComplete(); assertThat(cancellation.get()).isEqualTo(1); assertThat(onCancel.get()).isEqualTo(0); }
@Test public void fluxCreateIgnoreCancelled() { AtomicInteger onDispose = new AtomicInteger(); AtomicInteger onCancel = new AtomicInteger(); Flux<String> created = Flux.create(s -> { s.onDispose(() -> { onDispose.getAndIncrement(); assertThat(s.isCancelled()).isTrue(); }); s.onCancel(() -> { onCancel.getAndIncrement(); assertThat(s.isCancelled()).isTrue(); }); s.next("test1"); s.next("test2"); s.next("test3"); s.complete(); }, FluxSink.OverflowStrategy.IGNORE); StepVerifier.create(created) .expectNext("test1", "test2", "test3") .thenCancel() .verify(); assertThat(onDispose.get()).isEqualTo(1); assertThat(onCancel.get()).isEqualTo(1); }
@Test public void fluxCreateLatestCancelled() { AtomicInteger onDispose = new AtomicInteger(); AtomicInteger onCancel = new AtomicInteger(); Flux<String> created = Flux.create(s -> { s.onDispose(() -> { onDispose.getAndIncrement(); assertThat(s.isCancelled()).isTrue(); }); s.onCancel(() -> { onCancel.getAndIncrement(); assertThat(s.isCancelled()).isTrue(); }); s.next("test1"); s.next("test2"); s.next("test3"); s.complete(); }, FluxSink.OverflowStrategy.LATEST); StepVerifier.create(created) .expectNext("test1", "test2", "test3") .thenCancel() .verify(); assertThat(onDispose.get()).isEqualTo(1); assertThat(onCancel.get()).isEqualTo(1); }
@Test public void fluxCreateDropCancelled() { AtomicInteger onDispose = new AtomicInteger(); AtomicInteger onCancel = new AtomicInteger(); Flux<String> created = Flux.create(s -> { s.onDispose(() -> { onDispose.getAndIncrement(); assertThat(s.isCancelled()).isTrue(); }); s.onCancel(() -> { onCancel.getAndIncrement(); assertThat(s.isCancelled()).isTrue(); }); s.next("test1"); s.next("test2"); s.next("test3"); s.complete(); }, FluxSink.OverflowStrategy.DROP); StepVerifier.create(created) .expectNext("test1", "test2", "test3") .thenCancel() .verify(); assertThat(onDispose.get()).isEqualTo(1); assertThat(onCancel.get()).isEqualTo(1); }
@Test public void fluxCreateSerializedCancelled() { AtomicInteger onDispose = new AtomicInteger(); AtomicInteger onCancel = new AtomicInteger(); Flux<String> created = Flux.create(s -> { s.onDispose(onDispose::getAndIncrement) .onCancel(onCancel::getAndIncrement); s.next("test1"); s.next("test2"); s.next("test3"); assertThat(s.isCancelled()).isTrue(); s.complete(); }); StepVerifier.create(created) .expectNext("test1", "test2", "test3") .thenCancel() .verify(); assertThat(onDispose.get()).isEqualTo(1); assertThat(onCancel.get()).isEqualTo(1); }