com.hivemq.client.rx.reactor.FluxWithSingle Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hivemq-mqtt-client-reactor Show documentation
Show all versions of hivemq-mqtt-client-reactor Show documentation
Reactor API for the HiveMQ MQTT Client
The newest version!
/*
* Copyright 2018-present HiveMQ and the HiveMQ Community
*
* 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 com.hivemq.client.rx.reactor;
import com.hivemq.client.internal.rx.reactor.CoreWithSingleStrictSubscriber;
import com.hivemq.client.internal.rx.reactor.operators.FluxWithSingleFrom;
import com.hivemq.client.internal.rx.reactor.operators.FluxWithSingleMap;
import com.hivemq.client.internal.rx.reactor.operators.FluxWithSinglePublishOn;
import com.hivemq.client.internal.util.Checks;
import com.hivemq.client.rx.reactivestreams.PublisherWithSingle;
import com.hivemq.client.rx.reactivestreams.WithSingleSubscriber;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Scheduler;
import reactor.util.concurrent.Queues;
import reactor.util.context.Context;
import java.util.NoSuchElementException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* A {@link Flux} which emits a flow of items of type F
and a single item of type S
.
*
* @param the type of the flow of items.
* @param the type of the single item.
* @author Silvio Giebl
* @since 1.2
*/
public abstract class FluxWithSingle extends Flux implements CorePublisherWithSingle {
/**
* Decorate the specified {@link PublisherWithSingle} with the {@link FluxWithSingle} API.
*
* @param source the source to decorate.
* @param the type of the flow items.
* @param the type of the single item.
* @return a new {@link FluxWithSingle}.
*/
public static @NotNull FluxWithSingle from(
final @NotNull PublisherWithSingle extends F, ? extends S> source) {
if (source instanceof FluxWithSingle) {
//noinspection unchecked
return (FluxWithSingle) source;
}
return new FluxWithSingleFrom<>(source);
}
/**
* Run onSingle, onNext, onComplete and onError on a supplied {@link Scheduler.Worker}.
*
* @param scheduler a {@link Scheduler} providing the {@link Scheduler.Worker} where to publish.
* @return a {@link FluxWithSingle} producing asynchronously.
* @see Flux#publishOn(Scheduler)
*/
public final @NotNull FluxWithSingle publishBothOn(final @NotNull Scheduler scheduler) {
return publishBothOn(scheduler, Queues.SMALL_BUFFER_SIZE);
}
/**
* Run onSingle, onNext, onComplete and onError on a supplied {@link Scheduler.Worker}.
*
* @param scheduler see {@link Flux#publishOn(Scheduler, int)}
* @param prefetch see {@link Flux#publishOn(Scheduler, int)}
* @return a {@link FluxWithSingle} producing asynchronously.
* @see Flux#publishOn(Scheduler, int)
*/
public final @NotNull FluxWithSingle publishBothOn(final @NotNull Scheduler scheduler, final int prefetch) {
return publishBothOn(scheduler, true, prefetch);
}
/**
* Run onSingle, onNext, onComplete and onError on a supplied {@link Scheduler.Worker}.
*
* @param scheduler see {@link Flux#publishOn(Scheduler, boolean, int)}
* @param delayError see {@link Flux#publishOn(Scheduler, boolean, int)}
* @param prefetch see {@link Flux#publishOn(Scheduler, boolean, int)}
* @return a {@link FluxWithSingle} producing asynchronously.
* @see Flux#publishOn(Scheduler, boolean, int)
*/
public final @NotNull FluxWithSingle publishBothOn(
final @NotNull Scheduler scheduler, final boolean delayError, final int prefetch) {
Checks.notNull(scheduler, "Scheduler");
return new FluxWithSinglePublishOn<>(this, scheduler, delayError, prefetch);
}
/**
* Transform the single item emitted by this {@link FluxWithSingle} by applying a synchronous function to it.
*
* @param singleMapper the synchronous transforming {@link Function} for the single item.
* @param the transformed type of the single item.
* @return a transformed {@link FluxWithSingle}.
*/
public final @NotNull FluxWithSingle mapSingle(
final @NotNull Function super S, ? extends SM> singleMapper) {
Checks.notNull(singleMapper, "Single mapper");
return FluxWithSingleMap.mapSingle(this, singleMapper);
}
/**
* Transform the items emitted by this {@link FluxWithSingle} by applying a synchronous function to each item.
*
* @param fluxMapper the synchronous transforming {@link Function} for the flow of items.
* @param singleMapper the synchronous transforming {@link Function} for the single item.
* @param the transformed type of the flow items.
* @param the transformed type of the single item.
* @return a transformed {@link FluxWithSingle}.
*/
public final @NotNull FluxWithSingle mapBoth(
final @NotNull Function super F, ? extends FM> fluxMapper,
final @NotNull Function super S, ? extends SM> singleMapper) {
Checks.notNull(fluxMapper, "Flux mapper");
Checks.notNull(singleMapper, "Single mapper");
return FluxWithSingleMap.mapBoth(this, fluxMapper, singleMapper);
}
/**
* Add behavior (side-effect) triggered when this {@link FluxWithSingle} emits the single item.
*
* @param singleConsumer the callback to call on {@link WithSingleSubscriber#onSingle}.
* @return an observed {@link FluxWithSingle}.
*/
public final @NotNull FluxWithSingle doOnSingle(final @NotNull Consumer super S> singleConsumer) {
Checks.notNull(singleConsumer, "Single consumer");
return FluxWithSingleMap.mapSingle(this, s -> {
singleConsumer.accept(s);
return s;
});
}
@Override
public final void subscribeBoth(final @NotNull WithSingleSubscriber super F, ? super S> subscriber) {
if (subscriber instanceof CoreWithSingleSubscriber) {
//noinspection unchecked
subscribeBoth((CoreWithSingleSubscriber super F, ? super S>) subscriber);
} else {
Checks.notNull(subscriber, "Subscriber");
subscribeBoth(new CoreWithSingleStrictSubscriber<>(subscriber));
}
}
/**
* {@link #subscribe() Subscribes} to this Flowable and returns a future for the single item.
*
* - The future will complete with the single item if this {@link FluxWithSingle} emits a single item.
*
- The future will complete exceptionally with a {@link NoSuchElementException} if this {@link
* FluxWithSingle} completes but no single item was emitted.
*
- The future will complete exceptionally with the exception emitted by this {@link FluxWithSingle} if it
* errors before the single item was emitted.
*
- Cancelling the future will cancel this {@link FluxWithSingle} also when the future already completed
* normally or exceptionally.
*
*
* @return a future for the single item.
* @see #subscribe()
*/
public final @NotNull CompletableFuture subscribeSingleFuture() {
final SingleFutureSubscriber singleFutureSubscriber = new SingleFutureSubscriber<>(this);
final CompletableFuture future = singleFutureSubscriber.getFutureBeforeSubscribe();
singleFutureSubscriber.subscribe();
return future;
}
/**
* {@link #subscribe(Consumer) Subscribes} to this Flowable and returns a future for the single item.
*
* - The future will complete with the single item if this {@link FluxWithSingle} emits a single item.
*
- The future will complete exceptionally with a {@link NoSuchElementException} if this {@link
* FluxWithSingle} completes but no single item was emitted.
*
- The future will complete exceptionally with the exception emitted by this {@link FluxWithSingle} if it
* errors before the single item was emitted.
*
- Cancelling the future will cancel this {@link FluxWithSingle} also when the future already completed
* normally or exceptionally.
*
*
* @param consumer see {@link #subscribe(Consumer)}
* @return a future for the single item.
* @see #subscribe(Consumer)
*/
public final @NotNull CompletableFuture subscribeSingleFuture(final @NotNull Consumer super F> consumer) {
final SingleFutureSubscriber singleFutureSubscriber = new SingleFutureSubscriber<>(this);
final CompletableFuture future = singleFutureSubscriber.getFutureBeforeSubscribe();
singleFutureSubscriber.subscribe(consumer);
return future;
}
/**
* {@link #subscribe(Consumer, Consumer) Subscribes} to this Flowable and returns a future for the single item.
*
* - The future will complete with the single item if this {@link FluxWithSingle} emits a single item.
*
- The future will complete exceptionally with a {@link NoSuchElementException} if this {@link
* FluxWithSingle} completes but no single item was emitted.
*
- The future will complete exceptionally with the exception emitted by this {@link FluxWithSingle} if it
* errors before the single item was emitted.
*
- Cancelling the future will cancel this {@link FluxWithSingle} also when the future already completed
* normally or exceptionally.
*
*
* @param consumer see {@link #subscribe(Consumer, Consumer)}
* @param errorConsumer see {@link #subscribe(Consumer, Consumer)}
* @return a future for the single item.
* @see #subscribe(Consumer, Consumer)
*/
public final @NotNull CompletableFuture subscribeSingleFuture(
final @NotNull Consumer super F> consumer, final @NotNull Consumer super Throwable> errorConsumer) {
final SingleFutureSubscriber singleFutureSubscriber = new SingleFutureSubscriber<>(this);
final CompletableFuture future = singleFutureSubscriber.getFutureBeforeSubscribe();
singleFutureSubscriber.subscribe(consumer, errorConsumer);
return future;
}
/**
* {@link #subscribe(Consumer, Consumer, Runnable) Subscribes} to this Flowable and returns a future for the single
* item.
*
* - The future will complete with the single item if this {@link FluxWithSingle} emits a single item.
*
- The future will complete exceptionally with a {@link NoSuchElementException} if this {@link
* FluxWithSingle} completes but no single item was emitted.
*
- The future will complete exceptionally with the exception emitted by this {@link FluxWithSingle} if it
* errors before the single item was emitted.
*
- Cancelling the future will cancel this {@link FluxWithSingle} also when the future already completed
* normally or exceptionally.
*
*
* @param consumer see {@link #subscribe(Consumer, Consumer, Runnable)}
* @param errorConsumer see {@link #subscribe(Consumer, Consumer, Runnable)}
* @param completeConsumer see {@link #subscribe(Consumer, Consumer, Runnable)}
* @return a future for the single item.
* @see #subscribe(Consumer, Consumer, Runnable)
*/
public final @NotNull CompletableFuture subscribeSingleFuture(
final @NotNull Consumer super F> consumer,
final @NotNull Consumer super Throwable> errorConsumer,
final @NotNull Runnable completeConsumer) {
final SingleFutureSubscriber singleFutureSubscriber = new SingleFutureSubscriber<>(this);
final CompletableFuture future = singleFutureSubscriber.getFutureBeforeSubscribe();
singleFutureSubscriber.subscribe(consumer, errorConsumer, completeConsumer);
return future;
}
/**
* {@link #subscribe(Consumer, Consumer, Runnable, Context) Subscribes} to this Flowable and returns a future for
* the single item.
*
* - The future will complete with the single item if this {@link FluxWithSingle} emits a single item.
*
- The future will complete exceptionally with a {@link NoSuchElementException} if this {@link
* FluxWithSingle} completes but no single item was emitted.
*
- The future will complete exceptionally with the exception emitted by this {@link FluxWithSingle} if it
* errors before the single item was emitted.
*
- Cancelling the future will cancel this {@link FluxWithSingle} also when the future already completed
* normally or exceptionally.
*
*
* @param consumer see {@link #subscribe(Consumer, Consumer, Runnable, Context)}
* @param errorConsumer see {@link #subscribe(Consumer, Consumer, Runnable, Context)}
* @param completeConsumer see {@link #subscribe(Consumer, Consumer, Runnable, Context)}
* @param initialContext see {@link #subscribe(Consumer, Consumer, Runnable, Context)}
* @return a future for the single item.
* @see #subscribe(Consumer, Consumer, Runnable, Context)
*/
public final @NotNull CompletableFuture subscribeSingleFuture(
final @NotNull Consumer super F> consumer,
final @NotNull Consumer super Throwable> errorConsumer,
final @NotNull Runnable completeConsumer,
final @NotNull Context initialContext) {
final SingleFutureSubscriber singleFutureSubscriber = new SingleFutureSubscriber<>(this);
final CompletableFuture future = singleFutureSubscriber.getFutureBeforeSubscribe();
singleFutureSubscriber.subscribe(consumer, errorConsumer, completeConsumer, initialContext);
return future;
}
/**
* {@link #subscribe(Subscriber) Subscribes} to this Flowable and returns a future for the single item.
*
* - The future will complete with the single item if this {@link FluxWithSingle} emits a single item.
*
- The future will complete exceptionally with a {@link NoSuchElementException} if this {@link
* FluxWithSingle} completes but no single item was emitted.
*
- The future will complete exceptionally with the exception emitted by this {@link FluxWithSingle} if it
* errors before the single item was emitted.
*
- Cancelling the future will cancel this {@link FluxWithSingle} also when the future already completed
* normally or exceptionally.
*
*
* @param subscriber see {@link #subscribe(Subscriber)}
* @return a future for the single item.
* @see #subscribe(Subscriber)
*/
public final @NotNull CompletableFuture subscribeSingleFuture(final @NotNull Subscriber super F> subscriber) {
final SingleFutureSubscriber singleFutureSubscriber = new SingleFutureSubscriber<>(this);
final CompletableFuture future = singleFutureSubscriber.getFutureBeforeSubscribe();
singleFutureSubscriber.subscribe(subscriber);
return future;
}
private static class SingleFutureSubscriber extends Flux
implements CoreWithSingleSubscriber, Subscription {
private final @NotNull FluxWithSingle source;
private @Nullable Subscriber super F> subscriber;
private final @NotNull AtomicReference<@Nullable Subscription> subscription = new AtomicReference<>();
private final @NotNull AtomicReference<@Nullable CompletableFuture> future =
new AtomicReference<>(new CompletableFuture() {
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
future.set(null);
SingleFutureSubscriber.this.cancel();
return super.cancel(mayInterruptIfRunning);
}
});
SingleFutureSubscriber(final @NotNull FluxWithSingle source) {
this.source = source;
}
@NotNull CompletableFuture getFutureBeforeSubscribe() {
final CompletableFuture future = this.future.get();
assert future != null;
return future;
}
@Override
public void subscribe(final @NotNull CoreSubscriber super F> subscriber) {
this.subscriber = subscriber;
source.subscribeBoth(this);
}
@Override
public void onSubscribe(final @NotNull Subscription subscription) {
assert subscriber != null;
if (!this.subscription.compareAndSet(null, subscription)) {
cancel(subscription);
}
subscriber.onSubscribe(this);
}
@Override
public void onSingle(final @NotNull S s) {
final CompletableFuture future = this.future.getAndSet(null);
if (future != null) {
future.complete(s);
}
}
@Override
public void onNext(final @NotNull F f) {
assert subscriber != null;
subscriber.onNext(f);
}
@Override
public void onComplete() {
assert subscriber != null;
final CompletableFuture future = this.future.getAndSet(null);
if (future != null) {
future.completeExceptionally(new NoSuchElementException());
}
subscriber.onComplete();
}
@Override
public void onError(final @NotNull Throwable t) {
assert subscriber != null;
final CompletableFuture future = this.future.getAndSet(null);
if (future != null) {
future.completeExceptionally(t);
}
subscriber.onError(t);
}
@Override
public void request(final long n) {
final Subscription subscription = this.subscription.get();
assert subscription != null;
if (subscription != this) {
subscription.request(n);
}
}
@Override
public void cancel() {
final Subscription subscription = this.subscription.getAndSet(this);
if ((subscription != null) && (subscription != this)) {
cancel(subscription);
}
}
private void cancel(final @NotNull Subscription subscription) {
subscription.cancel();
final CompletableFuture future = this.future.getAndSet(null);
if (future != null) {
future.cancel(false);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy