All Downloads are FREE. Search and download functionalities are using the official Maven repository.

reactor.core.publisher.Mono Maven / Gradle / Ivy

/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, All Rights Reserved.
 *
 * 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 reactor.core.publisher;

import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.LongConsumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.stream.LongStream;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Disposable;
import reactor.core.Fuseable;
import reactor.core.Scannable;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Scheduler.Worker;
import reactor.core.scheduler.Schedulers;
import reactor.util.Logger;
import reactor.util.annotation.Nullable;
import reactor.util.concurrent.Queues;
import reactor.util.context.Context;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuple3;
import reactor.util.function.Tuple4;
import reactor.util.function.Tuple5;
import reactor.util.function.Tuple6;
import reactor.util.function.Tuples;

/**
 * A Reactive Streams {@link Publisher} with basic rx operators that completes successfully by emitting an element, or
 * with an error.
 *
 * 

* *

* *

The rx operators will offer aliases for input {@link Mono} type to preserve the "at most one" * property of the resulting {@link Mono}. For instance {@link Mono#flatMap flatMap} returns a * {@link Mono}, while there is a {@link Mono#flatMapMany flatMapMany} alias with possibly more than 1 emission. * *

{@code Mono} should be used for {@link Publisher} that just completes without any value. * *

It is intended to be used in implementations and return types, input parameters should keep using raw {@link * Publisher} as much as possible. * *

Note that using state in the {@code java.util.function} / lambdas used within Mono operators * should be avoided, as these may be shared between several {@link Subscriber Subscribers}. * * @param the type of the single value of this class * * @author Sebastien Deleuze * @author Stephane Maldini * @author David Karnok * @author Simon Baslé * * @see Flux */ public abstract class Mono implements Publisher { // ============================================================================================================== // Static Generators // ============================================================================================================== /** * Creates a deferred emitter that can be used with callback-based * APIs to signal at most one value, a complete or an error signal. *

* Bridging legacy API involves mostly boilerplate code due to the lack * of standard types and methods. There are two kinds of API surfaces: * 1) addListener/removeListener and 2) callback-handler. *

* 1) addListener/removeListener pairs
* To work with such API one has to instantiate the listener, * call the sink from the listener then register it with the source: *


	 * Mono.<String>create(sink -> {
	 *     HttpListener listener = event -> {
	 *         if (event.getResponseCode() >= 400) {
	 *             sink.error(new RuntimeExeption("Failed"));
	 *         } else {
	 *             String body = event.getBody();
	 *             if (body.isEmpty()) {
	 *                 sink.success();
	 *             } else {
	 *                 sink.success(body.toLowerCase());
	 *             }
	 *         }
	 *     };
	 *     
	 *     client.addListener(listener);
	 *     
	 *     sink.onDispose(() -> client.removeListener(listener));
	 * });
	 * 
* Note that this works only with single-value emitting listeners. Otherwise, * all subsequent signals are dropped. You may have to add {@code client.removeListener(this);} * to the listener's body. *

* 2) callback handler
* This requires a similar instantiation pattern such as above, but usually the * successful completion and error are separated into different methods. * In addition, the legacy API may or may not support some cancellation mechanism. *


     * Mono.<String>create(sink -> {
     *     Callback<String> callback = new Callback<String>() {
     *         @Override
     *         public void onResult(String data) {
     *             sink.success(data.toLowerCase());
     *         }
     *         
     *         @Override
     *         public void onError(Exception e) {
     *             sink.error(e);
     *         }
     *     }
     *     
     *     // without cancellation support:
     *     
     *     client.call("query", callback);
     *     
     *     // with cancellation support:
     *     
     *     AutoCloseable cancel = client.call("query", callback);
     *     sink.onDispose(() -> {
     *         try {
     *             cancel.close();
     *         } catch (Exception ex) {
     *             Exceptions.onErrorDropped(ex);
     *         }
     *     });
     * }); 
     * 
* * @param callback Consume the {@link MonoSink} provided per-subscriber by Reactor to generate signals. * @param The type of the value emitted * @return a {@link Mono} */ public static Mono create(Consumer> callback) { return onAssembly(new MonoCreate<>(callback)); } /** * Create a {@link Mono} provider that will {@link Supplier#get supply} a target {@link Mono} to subscribe to for * each {@link Subscriber} downstream. * *

* *

* @param supplier a {@link Mono} factory * * @param the element type of the returned Mono instance * * @return a new {@link Mono} factory */ public static Mono defer(Supplier> supplier) { return onAssembly(new MonoDefer<>(supplier)); } /** * Create a Mono which delays an onNext signal by a given {@link Duration duration} * on a default Scheduler and completes. * If the demand cannot be produced in time, an onError will be signalled instead. * The delay is introduced through the {@link Schedulers#parallel() parallel} default Scheduler. * *

* *

* @param duration the duration of the delay * * @return a new {@link Mono} */ public static Mono delay(Duration duration) { return delay(duration, Schedulers.parallel()); } /** * Create a Mono which delays an onNext signal by a given {@link Duration duration} * on a provided {@link Scheduler} and completes. * If the demand cannot be produced in time, an onError will be signalled instead. * *

* *

* @param duration the {@link Duration} of the delay * @param timer a time-capable {@link Scheduler} instance to run on * * @return a new {@link Mono} */ public static Mono delay(Duration duration, Scheduler timer) { return onAssembly(new MonoDelay(duration.toMillis(), TimeUnit.MILLISECONDS, timer)); } /** * Create a {@link Mono} that completes without emitting any item. * *

* *

* @param the reified {@link Subscriber} type * * @return a completed {@link Mono} */ public static Mono empty() { return MonoEmpty.instance(); } /** * Create a {@link Mono} that terminates with the specified error immediately after * being subscribed to. *

* *

* @param error the onError signal * @param the reified {@link Subscriber} type * * @return a failed {@link Mono} */ public static Mono error(Throwable error) { return onAssembly(new MonoError<>(error)); } /** * Pick the first {@link Mono} to emit any signal (value, empty completion or error) * and replay that signal, effectively behaving like the fastest of these competing * sources. *

* *

* @param monos The deferred monos to use. * @param The type of the function result. * * @return a new {@link Mono} behaving like the fastest of its sources. */ @SafeVarargs public static Mono first(Mono... monos) { return onAssembly(new MonoFirst<>(monos)); } /** * Pick the first available result coming from any of the given monos and populate a new {@literal Mono}. * *

* *

* @param monos The monos to use. * @param The type of the function result. * * @return a {@link Mono}. */ public static Mono first(Iterable> monos) { return onAssembly(new MonoFirst<>(monos)); } /** * Expose the specified {@link Publisher} with the {@link Mono} API, and ensure it will emit 0 or 1 item. * The source emitter will be cancelled on the first `onNext`. *

* *

* @param source the {@link Publisher} source * @param the source type * * @return the next item emitted as a {@link Mono} */ public static Mono from(Publisher source) { if (source instanceof Mono) { @SuppressWarnings("unchecked") Mono casted = (Mono) source; return casted; } if (source instanceof Flux) { @SuppressWarnings("unchecked") Flux casted = (Flux) source; return casted.next(); } return onAssembly(new MonoFromPublisher<>(source)); } /** * Create a {@link Mono} producing its value using the provided {@link Callable}. If * the Callable resolves to {@code null}, the resulting Mono completes empty. * *

* *

* @param supplier {@link Callable} that will produce the value * @param type of the expected value * * @return A {@link Mono}. */ public static Mono fromCallable(Callable supplier) { return onAssembly(new MonoCallable<>(supplier)); } /** * Create a {@link Mono}, producing its value using the provided {@link CompletionStage}. * *

* *

* @param completionStage {@link CompletionStage} that will produce a value (or a null to * complete immediately) * @param type of the expected value * @return A {@link Mono}. */ public static Mono fromCompletionStage(CompletionStage completionStage) { return onAssembly(new MonoCompletionStage<>(completionStage)); } /** * Convert a {@link Publisher} to a {@link Mono} without any cardinality check * (ie this method doesn't check if the source is already a Mono, nor cancels the * source past the first element). Conversion supports {@link Fuseable} sources. * Note this is an advanced interoperability operator that implies you know the * {@link Publisher} you are converting follows the {@link Mono} semantics and only * ever emits one element. * * @param source the Mono-compatible {@link Publisher} to wrap * @param type of the value emitted by the publisher * @return a wrapped {@link Mono} */ public static Mono fromDirect(Publisher source){ if(source instanceof Mono){ @SuppressWarnings("unchecked") Mono m = (Mono)source; return m; } if(source instanceof Flux){ @SuppressWarnings("unchecked") Flux f = (Flux)source; if(source instanceof Fuseable){ return onAssembly(new MonoSourceFluxFuseable<>(f)); } return onAssembly(new MonoSourceFlux<>(f)); } if(source instanceof Fuseable){ return onAssembly(new MonoSourceFuseable<>(source)); } return onAssembly(new MonoSource<>(source)); } /** * Create a {@link Mono}, producing its value using the provided {@link CompletableFuture}. * *

* *

* @param future {@link CompletableFuture} that will produce a value (or a null to * complete immediately) * @param type of the expected value * @return A {@link Mono}. * @see #fromCompletionStage(CompletionStage) fromCompletionStage for a generalization */ public static Mono fromFuture(CompletableFuture future) { return onAssembly(new MonoCompletionStage<>(future)); } /** * Create a {@link Mono} that completes empty once the provided {@link Runnable} has * been executed. * *

* *

* @param runnable {@link Runnable} that will be executed before emitting the completion signal * * @param The generic type of the upstream, which is preserved by this operator * @return A {@link Mono}. */ public static Mono fromRunnable(Runnable runnable) { return onAssembly(new MonoRunnable<>(runnable)); } /** * Create a {@link Mono}, producing its value using the provided {@link Supplier}. If * the Supplier resolves to {@code null}, the resulting Mono completes empty. * *

* *

* @param supplier {@link Supplier} that will produce the value * @param type of the expected value * * @return A {@link Mono}. */ public static Mono fromSupplier(Supplier supplier) { return onAssembly(new MonoSupplier<>(supplier)); } /** * Create a new {@link Mono} that ignores elements from the source (dropping them), * but completes when the source completes. * *

* *

* @param source the {@link Publisher} to ignore * @param the source type of the ignored data * * @return a new completable {@link Mono}. */ public static Mono ignoreElements(Publisher source) { return onAssembly(new MonoIgnorePublisher<>(source)); } /** * Create a new {@link Mono} that emits the specified item, which is captured at * instantiation time. * *

* *

* @param data the only item to onNext * @param the type of the produced item * * @return a {@link Mono}. */ public static Mono just(T data) { return onAssembly(new MonoJust<>(data)); } /** * Create a new {@link Mono} that emits the specified item if {@link Optional#isPresent()} otherwise only emits * onComplete. * *

* *

* @param data the {@link Optional} item to onNext or onComplete if not present * @param the type of the produced item * * @return a {@link Mono}. */ public static Mono justOrEmpty(@Nullable Optional data) { return data != null && data.isPresent() ? just(data.get()) : empty(); } /** * Create a new {@link Mono} that emits the specified item if non null otherwise only emits * onComplete. * *

* *

* @param data the item to onNext or onComplete if null * @param the type of the produced item * * @return a {@link Mono}. */ public static Mono justOrEmpty(@Nullable T data) { return data != null ? just(data) : empty(); } /** * Return a {@link Mono} that will never signal any data, error or completion signal, * essentially running indefinitely. *

* *

* @param the {@link Subscriber} type target * * @return a never completing {@link Mono} */ public static Mono never() { return MonoNever.instance(); } /** * Returns a Mono that emits a Boolean value that indicates whether two Publisher sequences are the * same by comparing the items emitted by each Publisher pairwise. * * @param source1 * the first Publisher to compare * @param source2 * the second Publisher to compare * @param * the type of items emitted by each Publisher * @return a Mono that emits a Boolean value that indicates whether the two sequences are the same */ public static Mono sequenceEqual(Publisher source1, Publisher source2) { return sequenceEqual(source1, source2, equalsBiPredicate(), Queues.SMALL_BUFFER_SIZE); } /** * Returns a Mono that emits a Boolean value that indicates whether two Publisher sequences are the * same by comparing the items emitted by each Publisher pairwise based on the results of a specified * equality function. * * @param source1 * the first Publisher to compare * @param source2 * the second Publisher to compare * @param isEqual * a function used to compare items emitted by each Publisher * @param * the type of items emitted by each Publisher * @return a Mono that emits a Boolean value that indicates whether the two Publisher two sequences * are the same according to the specified function */ public static Mono sequenceEqual(Publisher source1, Publisher source2, BiPredicate isEqual) { return sequenceEqual(source1, source2, isEqual, Queues.SMALL_BUFFER_SIZE); } /** * Returns a Mono that emits a Boolean value that indicates whether two Publisher sequences are the * same by comparing the items emitted by each Publisher pairwise based on the results of a specified * equality function. * * @param source1 * the first Publisher to compare * @param source2 * the second Publisher to compare * @param isEqual * a function used to compare items emitted by each Publisher * @param prefetch * the number of items to prefetch from the first and second source Publisher * @param * the type of items emitted by each Publisher * @return a Mono that emits a Boolean value that indicates whether the two Publisher two sequences * are the same according to the specified function */ public static Mono sequenceEqual(Publisher source1, Publisher source2, BiPredicate isEqual, int prefetch) { return onAssembly(new MonoSequenceEqual<>(source1, source2, isEqual, prefetch)); } /** * Create a {@link Mono} emitting the {@link Context} available on subscribe. * If no Context is available, the mono will simply emit the * {@link Context#empty() empty Context}. * *

* *

* * @return a new {@link Mono} emitting current context * @see #subscribe(CoreSubscriber) */ public static Mono subscriberContext() { return onAssembly(MonoCurrentContext.INSTANCE); } /** * Uses a resource, generated by a supplier for each individual Subscriber, while streaming the value from a * Mono derived from the same resource and makes sure the resource is released if the * sequence terminates or the Subscriber cancels. *

*

  • Eager resource cleanup happens just before the source termination and exceptions raised by the cleanup * Consumer may override the terminal event.
  • Non-eager cleanup will drop any exception.
*

* * * @param resourceSupplier a {@link Callable} that is called on subscribe to create the resource * @param sourceSupplier a {@link Mono} factory to create the Mono depending on the created resource * @param resourceCleanup invoked on completion to clean-up the resource * @param eager push to true to clean before terminating downstream subscribers * @param emitted type * @param resource type * * @return new {@link Mono} */ public static Mono using(Callable resourceSupplier, Function> sourceSupplier, Consumer resourceCleanup, boolean eager) { return onAssembly(new MonoUsing<>(resourceSupplier, sourceSupplier, resourceCleanup, eager)); } /** * Uses a resource, generated by a supplier for each individual Subscriber, while streaming the value from a * Mono derived from the same resource and makes sure the resource is released if the * sequence terminates or the Subscriber cancels. *

* Eager resource cleanup happens just before the source termination and exceptions raised by the cleanup Consumer * may override the terminal event. *

* * * @param resourceSupplier a {@link Callable} that is called on subscribe to create the resource * @param sourceSupplier a {@link Mono} factory to create the Mono depending on the created resource * @param resourceCleanup invoked on completion to clean-up the resource * @param emitted type * @param resource type * * @return new {@link Mono} */ public static Mono using(Callable resourceSupplier, Function> sourceSupplier, Consumer resourceCleanup) { return using(resourceSupplier, sourceSupplier, resourceCleanup, true); } /** * Aggregate given publishers into a new {@literal Mono} that will be fulfilled * when all of the given {@literal sources} have been fulfilled. An error will cause * pending results to be cancelled and immediate error emission to the returned {@link Mono}. *

* *

* @param sources The sources to use. * * @return a {@link Mono}. */ public static Mono when(Publisher... sources) { if (sources.length == 0) { return empty(); } if (sources.length == 1) { return empty(sources[0]); } return onAssembly(new MonoWhen(false, sources)); } /** * Aggregate given publishers into a new {@literal Mono} that will be * fulfilled when all of the given {@literal Publishers} have been fulfilled. * An error will cause pending results to be cancelled and immediate error emission * to the returned {@link Mono}. * *

* *

* * @param sources The sources to use. * * @return a {@link Mono}. */ public static Mono when(final Iterable> sources) { return onAssembly(new MonoWhen(false, sources)); } /** * Aggregate given publishers into a new {@literal Mono} that will be * fulfilled when all of the given {@literal sources} have been fulfilled. If any Publisher * terminates without value, the returned sequence will be terminated immediately and * pending results cancelled. Errors from the sources are delayed. * If several Publishers error, the exceptions are combined (as suppressed exceptions on a root exception). * *

* *

* * @param sources The sources to use. * * @return a {@link Mono}. */ public static Mono whenDelayError(final Iterable> sources) { return onAssembly(new MonoWhen(true, sources)); } /** * Merge given publishers into a new {@literal Mono} that will be fulfilled when * all of the given {@literal sources} have been fulfilled. Errors from the sources are delayed. * If several Publishers error, the exceptions are combined (as suppressed exceptions on a root exception). * *

* *

* @param sources The sources to use. * * @return a {@link Mono}. */ public static Mono whenDelayError(Publisher... sources) { if (sources.length == 0) { return empty(); } if (sources.length == 1) { return empty(sources[0]); } return onAssembly(new MonoWhen(true, sources)); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal Monos} * have been fulfilled, aggregating their values into a {@link Tuple2}. * An error will cause pending results to be cancelled and immediate error emission to the * returned {@link Mono}. * *

* *

* @param p1 The first upstream {@link Publisher} to subscribe to. * @param p2 The second upstream {@link Publisher} to subscribe to. * @param type of the value from source1 * @param type of the value from source2 * * @return a {@link Mono}. */ public static Mono> zip(Mono p1, Mono p2) { return zip(p1, p2, Flux.tuple2Function()); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal Monos} * have been fulfilled, aggregating their values as defined by the combinator function. * An error will cause pending results to be cancelled and immediate error emission to the * returned {@link Mono}. * *

* *

* @param p1 The first upstream {@link Publisher} to subscribe to. * @param p2 The second upstream {@link Publisher} to subscribe to. * @param combinator a {@link BiFunction} combinator function when both sources * complete * @param type of the value from source1 * @param type of the value from source2 * @param output value * * @return a {@link Mono}. */ public static Mono zip(Mono p1, Mono p2, BiFunction combinator) { return onAssembly(new MonoZip(false, p1, p2, combinator)); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal Monos} * have been fulfilled, aggregating their values into a {@link Tuple3}. * An error will cause pending results to be cancelled and immediate error emission to the * returned {@link Mono}. * *

* *

* @param p1 The first upstream {@link Publisher} to subscribe to. * @param p2 The second upstream {@link Publisher} to subscribe to. * @param p3 The third upstream {@link Publisher} to subscribe to. * @param type of the value from source1 * @param type of the value from source2 * @param type of the value from source3 * * @return a {@link Mono}. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static Mono> zip(Mono p1, Mono p2, Mono p3) { return onAssembly(new MonoZip(false, a -> Tuples.fromArray((Object[])a), p1, p2, p3)); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal Monos} * have been fulfilled, aggregating their values into a {@link Tuple4}. * An error will cause pending results to be cancelled and immediate error emission to the * returned {@link Mono}. * *

* *

* @param p1 The first upstream {@link Publisher} to subscribe to. * @param p2 The second upstream {@link Publisher} to subscribe to. * @param p3 The third upstream {@link Publisher} to subscribe to. * @param p4 The fourth upstream {@link Publisher} to subscribe to. * @param type of the value from source1 * @param type of the value from source2 * @param type of the value from source3 * @param type of the value from source4 * * @return a {@link Mono}. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static Mono> zip(Mono p1, Mono p2, Mono p3, Mono p4) { return onAssembly(new MonoZip(false, a -> Tuples.fromArray((Object[])a), p1, p2, p3, p4)); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal Monos} * have been fulfilled, aggregating their values into a {@link Tuple5}. * An error will cause pending results to be cancelled and immediate error emission to the * returned {@link Mono}. * *

* *

* @param p1 The first upstream {@link Publisher} to subscribe to. * @param p2 The second upstream {@link Publisher} to subscribe to. * @param p3 The third upstream {@link Publisher} to subscribe to. * @param p4 The fourth upstream {@link Publisher} to subscribe to. * @param p5 The fifth upstream {@link Publisher} to subscribe to. * @param type of the value from source1 * @param type of the value from source2 * @param type of the value from source3 * @param type of the value from source4 * @param type of the value from source5 * * @return a {@link Mono}. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static Mono> zip(Mono p1, Mono p2, Mono p3, Mono p4, Mono p5) { return onAssembly(new MonoZip(false, a -> Tuples.fromArray((Object[])a), p1, p2, p3, p4, p5)); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal Monos} * have been fulfilled, aggregating their values into a {@link Tuple6}. * An error will cause pending results to be cancelled and immediate error emission to the * returned {@link Mono}. * *

* *

* @param p1 The first upstream {@link Publisher} to subscribe to. * @param p2 The second upstream {@link Publisher} to subscribe to. * @param p3 The third upstream {@link Publisher} to subscribe to. * @param p4 The fourth upstream {@link Publisher} to subscribe to. * @param p5 The fifth upstream {@link Publisher} to subscribe to. * @param p6 The sixth upstream {@link Publisher} to subscribe to. * @param type of the value from source1 * @param type of the value from source2 * @param type of the value from source3 * @param type of the value from source4 * @param type of the value from source5 * @param type of the value from source6 * * @return a {@link Mono}. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static Mono> zip(Mono p1, Mono p2, Mono p3, Mono p4, Mono p5, Mono p6) { return onAssembly(new MonoZip(false, a -> Tuples.fromArray((Object[])a), p1, p2, p3, p4, p5, p6)); } /** * Aggregate given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal * Monos} have been fulfilled, aggregating their values according to the provided combinator function. * If any Mono terminates without value, the returned sequence will be terminated immediately and pending results cancelled. * *

* *

* * @param monos The monos to use. * @param combinator the function to transform the combined array into an arbitrary * object. * @param the combined result * * @return a {@link Mono}. */ public static Mono zip(final Iterable> monos, Function combinator) { return onAssembly(new MonoZip<>(false, combinator, monos)); } /** * Aggregate given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal * Monos} have been fulfilled, aggregating their values according to the provided combinator function. * An error will cause pending results to be cancelled and immediate error emission to the * returned {@link Mono}. *

* *

* @param monos The monos to use. * @param combinator the function to transform the combined array into an arbitrary * object. * @param the combined result * * @return a {@link Mono}. */ public static Mono zip(Function combinator, Mono... monos) { if (monos.length == 0) { return empty(); } if (monos.length == 1) { return monos[0].map(d -> combinator.apply(new Object[]{d})); } return onAssembly(new MonoZip<>(false, combinator, monos)); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal Monos} * have been fulfilled, aggregating their values into a {@link Tuple2} and delaying errors. * If both Monos error, the two exceptions are combined (as suppressed exceptions on a root exception). * *

* *

* @param p1 The first upstream {@link Publisher} to subscribe to. * @param p2 The second upstream {@link Publisher} to subscribe to. * @param type of the value from source1 * @param type of the value from source2 * * @return a {@link Mono}. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static Mono> zipDelayError(Mono p1, Mono p2) { return onAssembly(new MonoZip(true, a -> Tuples.fromArray((Object[])a), p1, p2)); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal Mono Monos} * have been fulfilled, aggregating their values into a {@link Tuple3} and delaying errors. * If several Monos error, the two exceptions are combined (as suppressed exceptions on a root exception). * *

* *

* @param p1 The first upstream {@link Publisher} to subscribe to. * @param p2 The second upstream {@link Publisher} to subscribe to. * @param p3 The third upstream {@link Publisher} to subscribe to. * @param type of the value from source1 * @param type of the value from source2 * @param type of the value from source3 * * @return a {@link Mono}. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static Mono> zipDelayError(Mono p1, Mono p2, Mono p3) { return onAssembly(new MonoZip(true, a -> Tuples.fromArray((Object[])a), p1, p2, p3)); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal Monos} * have been fulfilled, aggregating their values into a {@link Tuple4} and delaying errors. * If several Monos error, the exceptions are combined (as suppressed exceptions on a root exception). * *

* *

* @param p1 The first upstream {@link Publisher} to subscribe to. * @param p2 The second upstream {@link Publisher} to subscribe to. * @param p3 The third upstream {@link Publisher} to subscribe to. * @param p4 The fourth upstream {@link Publisher} to subscribe to. * @param type of the value from source1 * @param type of the value from source2 * @param type of the value from source3 * @param type of the value from source4 * * @return a {@link Mono}. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static Mono> zipDelayError(Mono p1, Mono p2, Mono p3, Mono p4) { return onAssembly(new MonoZip(true, a -> Tuples.fromArray((Object[])a), p1, p2, p3, p4)); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal Monos} * have been fulfilled, aggregating their values into a {@link Tuple5} and delaying errors. * If several Monos error, the exceptions are combined (as suppressed exceptions on a root exception). *

* *

* @param p1 The first upstream {@link Publisher} to subscribe to. * @param p2 The second upstream {@link Publisher} to subscribe to. * @param p3 The third upstream {@link Publisher} to subscribe to. * @param p4 The fourth upstream {@link Publisher} to subscribe to. * @param p5 The fifth upstream {@link Publisher} to subscribe to. * @param type of the value from source1 * @param type of the value from source2 * @param type of the value from source3 * @param type of the value from source4 * @param type of the value from source5 * * @return a {@link Mono}. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static Mono> zipDelayError(Mono p1, Mono p2, Mono p3, Mono p4, Mono p5) { return onAssembly(new MonoZip(true, a -> Tuples.fromArray((Object[])a), p1, p2, p3, p4, p5)); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal Monos} * have been fulfilled, aggregating their values into a {@link Tuple6} and delaying errors. * If several Monos error, the exceptions are combined (as suppressed exceptions on a root exception). * *

* *

* @param p1 The first upstream {@link Publisher} to subscribe to. * @param p2 The second upstream {@link Publisher} to subscribe to. * @param p3 The third upstream {@link Publisher} to subscribe to. * @param p4 The fourth upstream {@link Publisher} to subscribe to. * @param p5 The fifth upstream {@link Publisher} to subscribe to. * @param p6 The sixth upstream {@link Publisher} to subscribe to. * @param type of the value from source1 * @param type of the value from source2 * @param type of the value from source3 * @param type of the value from source4 * @param type of the value from source5 * @param type of the value from source6 * * @return a {@link Mono}. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static Mono> zipDelayError(Mono p1, Mono p2, Mono p3, Mono p4, Mono p5, Mono p6) { return onAssembly(new MonoZip(true, a -> Tuples.fromArray((Object[])a), p1, p2, p3, p4, p5, p6)); } /** * Aggregate given monos into a new {@literal Mono} that will be fulfilled when all of the given {@literal * Monos} have been fulfilled. If any Mono terminates without value, the returned sequence will be terminated * immediately and pending results cancelled. Errors from the sources are delayed. * If several Monos error, the exceptions are combined (as suppressed exceptions on a root exception). * *

* *

* * @param monos The monos to use. * @param combinator the function to transform the combined array into an arbitrary * object. * @param the combined result * * @return a {@link Mono}. */ public static Mono zipDelayError(final Iterable> monos, Function combinator) { return onAssembly(new MonoZip<>(true, combinator, monos)); } /** * Merge given monos into a new {@literal Mono} that will be fulfilled when all of the * given {@literal Monos} have been fulfilled, aggregating their values according to * the provided combinator function and delaying errors. * If several Monos error, the exceptions are combined (as suppressed exceptions on a root exception). * *

* *

* @param monos The monos to use. * @param combinator the function to transform the combined array into an arbitrary * object. * @param the combined result * * @return a combined {@link Mono}. */ public static Mono zipDelayError(Function combinator, Mono... monos) { if (monos.length == 0) { return empty(); } if (monos.length == 1) { return monos[0].map(d -> combinator.apply(new Object[]{d})); } return onAssembly(new MonoZip<>(true, combinator, monos)); } // ============================================================================================================== // Operators // ============================================================================================================== /** * Transform this {@link Mono} into a target type. * *

	 * {@code mono.as(Flux::from).subscribe() }
	 * 
* * @param transformer the {@link Function} to immediately map this {@link Mono} * into a target type * @param

the returned instance type * * @return the {@link Mono} transformed to an instance of P * @see #compose for a bounded conversion to {@link Publisher} */ public final

P as(Function, P> transformer) { return transformer.apply(this); } /** * Join the termination signals from this mono and another source into the returned * void mono * *

* *

* @param other the {@link Publisher} to wait for * complete * @return a new combined Mono * @see #when */ public final Mono and(Publisher other) { if (this instanceof MonoWhen) { @SuppressWarnings("unchecked") MonoWhen o = (MonoWhen) this; Mono result = o.whenAdditionalSource(other); if (result != null) { return result; } } return when(this, other); } /** * Subscribe to this {@link Mono} and block indefinitely until a next signal is * received. Returns that value, or null if the Mono completes empty. In case the Mono * errors, the original exception is thrown (wrapped in a {@link RuntimeException} if * it was a checked exception). * *

* *

* Note that each block() will trigger a new subscription: in other words, the result * might miss signal from hot publishers. * * @return T the result */ @Nullable public T block() { BlockingMonoSubscriber subscriber = new BlockingMonoSubscriber<>(); onLastAssembly(this).subscribe(Operators.toCoreSubscriber(subscriber)); return subscriber.blockingGet(); } /** * Subscribe to this {@link Mono} and block until a next signal is * received or a timeout expires. Returns that value, or null if the Mono completes * empty. In case the Mono errors, the original exception is thrown (wrapped in a * {@link RuntimeException} if it was a checked exception). * If the provided timeout expires,a {@link RuntimeException} is thrown. * *

* *

* Note that each block() will trigger a new subscription: in other words, the result * might miss signal from hot publishers. * * @param timeout maximum time period to wait for before raising a {@link RuntimeException} * * @return T the result */ @Nullable public T block(Duration timeout) { BlockingMonoSubscriber subscriber = new BlockingMonoSubscriber<>(); onLastAssembly(this).subscribe(Operators.toCoreSubscriber(subscriber)); return subscriber.blockingGet(timeout.toMillis(), TimeUnit.MILLISECONDS); } /** * Cast the current {@link Mono} produced type into a target produced type. * *

* * * @param the {@link Mono} output type * @param clazz the target type to cast to * * @return a casted {@link Mono} */ public final Mono cast(Class clazz) { Objects.requireNonNull(clazz, "clazz"); return map(clazz::cast); } /** * Turn this {@link Mono} into a hot source and cache last emitted signals for further {@link Subscriber}. * Completion and Error will also be replayed. *

* * * @return a replaying {@link Mono} */ public final Mono cache() { return onAssembly(new MonoProcessor<>(this)); } public final Mono cache(Duration ttl) { return onAssembly(new MonoCacheTime<>(this, ttl, Schedulers.parallel())); } /** * Prepare this {@link Mono} so that subscribers will cancel from it on a * specified * {@link Scheduler}. * * @param scheduler the {@link Scheduler} to signal cancel on * * @return a scheduled cancel {@link Mono} */ public final Mono cancelOn(Scheduler scheduler) { return onAssembly(new MonoCancelOn<>(this, scheduler)); } /** * Activate assembly tracing for this particular {@link Mono}, in case of an error * upstream of the checkpoint. Tracing incurs the cost of an exception stack trace * creation. *

* It should be placed towards the end of the reactive chain, as errors * triggered downstream of it cannot be observed and augmented with assembly trace. * * @return the assembly tracing {@link Mono} */ public final Mono checkpoint() { return checkpoint(null, true); } /** * Activate assembly marker for this particular {@link Mono} by giving it a description that * will be reflected in the assembly traceback in case of an error upstream of the * checkpoint. Note that unlike {@link #checkpoint()}, this doesn't create a * filled stack trace, avoiding the main cost of the operator. * However, as a trade-off the description must be unique enough for the user to find * out where this Mono was assembled. If you only want a generic description, and * still rely on the stack trace to find the assembly site, use the * {@link #checkpoint(String, boolean)} variant. *

* It should be placed towards the end of the reactive chain, as errors * triggered downstream of it cannot be observed and augmented with assembly trace. * * @param description a unique enough description to include in the light assembly traceback. * @return the assembly marked {@link Mono} */ public final Mono checkpoint(String description) { return checkpoint(Objects.requireNonNull(description), false); } /** * Activate assembly tracing or the lighter assembly marking depending on the * {@code forceStackTrace} option. *

* By setting the {@code forceStackTrace} parameter to {@literal true}, activate assembly * tracing for this particular {@link Mono} and give it a description that * will be reflected in the assembly traceback in case of an error upstream of the * checkpoint. Note that unlike {@link #checkpoint(String)}, this will incur * the cost of an exception stack trace creation. The description could for * example be a meaningful name for the assembled mono or a wider correlation ID, * since the stack trace will always provide enough information to locate where this * Flux was assembled. *

* By setting {@code forceStackTrace} to {@literal false}, behaves like * {@link #checkpoint(String)} and is subject to the same caveat in choosing the * description. *

* It should be placed towards the end of the reactive chain, as errors * triggered downstream of it cannot be observed and augmented with assembly marker. * * @param description a description (must be unique enough if forceStackTrace is push * to false). * @param forceStackTrace false to make a light checkpoint without a stacktrace, true * to use a stack trace. * @return the assembly marked {@link Mono}. */ public final Mono checkpoint(@Nullable String description, boolean forceStackTrace) { return new MonoOnAssembly<>(this, description, !forceStackTrace); } /** * Defer the given transformation to this {@link Mono} in order to generate a * target {@link Mono} type. A transformation will occur for each * {@link Subscriber}. For instance: * *

	 * {@code flux.compose(Mono::from).subscribe() }
	 * 
* * @param transformer the {@link Function} to lazily map this {@link Mono} into a target {@link Mono} * instance upon subscription. * @param the item type in the returned {@link Publisher} * * @return a new {@link Mono} * @see #as as() for a loose conversion to an arbitrary type * @see #transform(Function) */ public final Mono compose(Function, ? extends Publisher> transformer) { return defer(() -> from(transformer.apply(this))); } /** * Concatenate emissions of this {@link Mono} with the provided {@link Publisher} * (no interleave). *

* * * @param other the {@link Publisher} sequence to concat after this {@link Flux} * * @return a concatenated {@link Flux} */ public final Flux concatWith(Publisher other) { return Flux.concat(this, other); } /** * Provide a default single value if this mono is completed without any data * *

* *

* @param defaultV the alternate value if this sequence is empty * * @return a new {@link Mono} * * @see Flux#defaultIfEmpty(Object) */ public final Mono defaultIfEmpty(T defaultV) { if (this instanceof Fuseable.ScalarCallable) { try { T v = block(); if (v == null) { return Mono.just(defaultV); } } catch (Throwable e) { //leave MonoError returns as this } return this; } return onAssembly(new MonoDefaultIfEmpty<>(this, defaultV)); } /** * Delay this {@link Mono} element ({@link Subscriber#onNext} signal) by a given * duration. Empty Monos or error signals are not delayed. * *

* * *

* Note that the scheduler on which the Mono chain continues execution will be the * {@link Schedulers#parallel() parallel} scheduler if the mono is valued, or the * current scheduler if the mono completes empty or errors. * * @param delay duration by which to delay the {@link Subscriber#onNext} signal * @return a delayed {@link Mono} */ public final Mono delayElement(Duration delay) { return delayElement(delay, Schedulers.parallel()); } /** * Delay this {@link Mono} element ({@link Subscriber#onNext} signal) by a given * {@link Duration}, on a particular {@link Scheduler}. Empty monos or error signals are not delayed. * *

* * *

* Note that the scheduler on which the mono chain continues execution will be the * scheduler provided if the mono is valued, or the current scheduler if the mono * completes empty or errors. * * @param delay {@link Duration} by which to delay the {@link Subscriber#onNext} signal * @param timer a time-capable {@link Scheduler} instance to delay the value signal on * @return a delayed {@link Mono} */ public final Mono delayElement(Duration delay, Scheduler timer) { return onAssembly(new MonoDelayElement<>(this, delay.toMillis(), TimeUnit.MILLISECONDS, timer)); } /** * Subscribe to this {@link Mono} and another {@link Publisher} that is generated from * this Mono's element and which will be used as a trigger for relaying said element. *

* That is to say, the resulting {@link Mono} delays until this Mono's element is * emitted, generates a trigger Publisher and then delays again until the trigger * Publisher terminates. *

* Note that contiguous calls to all delayUntil are fused together. * The triggers are generated and subscribed to in sequence, once the previous trigger * completes. Error is propagated immediately * downstream. In both cases, an error in the source is immediately propagated. *

* * * @param triggerProvider a {@link Function} that maps this Mono's value into a * {@link Publisher} whose termination will trigger relaying the value. * * @return this Mono, but delayed until the derived publisher terminates. */ public final Mono delayUntil(Function> triggerProvider) { Objects.requireNonNull(triggerProvider, "triggerProvider required"); if (this instanceof MonoDelayUntil) { return ((MonoDelayUntil) this).copyWithNewTriggerGenerator(false,triggerProvider); } return onAssembly(new MonoDelayUntil<>(this, triggerProvider)); } /** * Delay the {@link Mono#subscribe(Subscriber) subscription} to this {@link Mono} source until the given * period elapses. * *

* * * @param delay duration before subscribing this {@link Mono} * * @return a delayed {@link Mono} * */ public final Mono delaySubscription(Duration delay) { return delaySubscription(delay, Schedulers.parallel()); } /** * Delay the {@link Mono#subscribe(Subscriber) subscription} to this {@link Mono} source until the given * {@link Duration} elapses. * *

* * * @param delay {@link Duration} before subscribing this {@link Mono} * @param timer a time-capable {@link Scheduler} instance to run on * * @return a delayed {@link Mono} * */ public final Mono delaySubscription(Duration delay, Scheduler timer) { return delaySubscription(Mono.delay(delay, timer)); } /** * Delay the subscription to this {@link Mono} until another {@link Publisher} * signals a value or completes. * *

* * * @param subscriptionDelay a * {@link Publisher} to signal by next or complete this {@link Mono#subscribe(Subscriber)} * @param the other source type * * @return a delayed {@link Mono} * */ public final Mono delaySubscription(Publisher subscriptionDelay) { return onAssembly(new MonoDelaySubscription<>(this, subscriptionDelay)); } /** * An operator working only if this {@link Mono} emits onNext, onError or onComplete {@link Signal} * instances, transforming these {@link #materialize() materialized} signals into * real signals on the {@link Subscriber}. * The error {@link Signal} will trigger onError and complete {@link Signal} will trigger * onComplete. * *

* * @param the dematerialized type * * @return a dematerialized {@link Mono} * @see #materialize() */ public final Mono dematerialize() { @SuppressWarnings("unchecked") Mono> thiz = (Mono>) this; return onAssembly(new MonoDematerialize<>(thiz)); } /** * Add behavior triggered after the {@link Mono} terminates, either by completing downstream successfully or with an error. * The arguments will be null depending on success, success with data and error: *

    *
  • null, null : completed without data
  • *
  • T, null : completed with data
  • *
  • null, Throwable : failed with/without data
  • *
* *

* *

* @param afterSuccessOrError the callback to call after {@link Subscriber#onNext}, {@link Subscriber#onComplete} without preceding {@link Subscriber#onNext} or {@link Subscriber#onError} * * @return a new {@link Mono} */ public final Mono doAfterSuccessOrError(BiConsumer afterSuccessOrError) { return onAssembly(new MonoPeekTerminal<>(this, null, null, afterSuccessOrError)); } /** * Add behavior (side-effect) triggered after the {@link Mono} terminates, either by * completing downstream successfully or with an error. *

* *

* @param afterTerminate the callback to call after {@link Subscriber#onComplete} or {@link Subscriber#onError} * * @return an observed {@link Flux} */ public final Mono doAfterTerminate(Runnable afterTerminate) { Objects.requireNonNull(afterTerminate, "afterTerminate"); return doAfterSuccessOrError((s, e) -> afterTerminate.run()); } /** * Add behavior triggering after the {@link Mono} terminates for any reason, * including cancellation. The terminating event ({@link SignalType#ON_COMPLETE}, * {@link SignalType#ON_ERROR} and {@link SignalType#CANCEL}) is passed to the consumer, * which is executed after the signal has been passed downstream. *

* Note that the fact that the signal is propagated downstream before the callback is * executed means that several doFinally in a row will be executed in * reverse order. If you want to assert the execution of the callback * please keep in mind that the Mono will complete before it is executed, so its * effect might not be visible immediately after eg. a {@link #block()}. * * @param onFinally the callback to execute after a terminal signal (complete, error * or cancel) * @return an observed {@link Mono} */ public final Mono doFinally(Consumer onFinally) { Objects.requireNonNull(onFinally, "onFinally"); if (this instanceof Fuseable) { return onAssembly(new MonoDoFinallyFuseable<>(this, onFinally)); } return onAssembly(new MonoDoFinally<>(this, onFinally)); } /** * Add behavior triggered when the {@link Mono} is cancelled. * * *

* *

* @param onCancel the callback to call on {@link Subscription#cancel()} * * @return a new {@link Mono} */ public final Mono doOnCancel(Runnable onCancel) { Objects.requireNonNull(onCancel, "onCancel"); return doOnSignal(this, null, null, null, null, null, onCancel); } /** * Add behavior triggered when the {@link Mono} emits a data successfully. * *

* *

* @param onNext the callback to call on {@link Subscriber#onNext} * * @return a new {@link Mono} */ public final Mono doOnNext(Consumer onNext) { Objects.requireNonNull(onNext, "onNext"); return doOnSignal(this, null, onNext, null, null, null, null); } /** * Add behavior triggered when the {@link Mono} completes successfully. * *

    *
  • null : completed without data
  • *
  • T: completed with data
  • *
* *

* *

* @param onSuccess the callback to call on, argument is null if the {@link Mono} * completes without data * {@link Subscriber#onNext} or {@link Subscriber#onComplete} without preceding {@link Subscriber#onNext} * * @return a new {@link Mono} */ public final Mono doOnSuccess(Consumer onSuccess) { Objects.requireNonNull(onSuccess, "onSuccess"); return onAssembly(new MonoPeekTerminal<>(this, onSuccess, null, null)); } /** * Add behavior triggered when the {@link Mono} emits an item, fails with an error * or completes successfully. All these events are represented as a {@link Signal} * that is passed to the side-effect callback. Note that this is an advanced operator, * typically used for monitoring of a Mono. * * @param signalConsumer the mandatory callback to call on * {@link Subscriber#onNext(Object)}, {@link Subscriber#onError(Throwable)} and * {@link Subscriber#onComplete()} * @return an observed {@link Mono} * @see #doOnNext(Consumer) * @see #doOnError(Consumer) * @see #materialize() * @see Signal */ public final Mono doOnEach(Consumer> signalConsumer) { Objects.requireNonNull(signalConsumer, "signalConsumer"); return doOnSignal(this, null, v -> signalConsumer.accept(Signal.next(v)), e -> signalConsumer.accept(Signal.error(e)), () -> signalConsumer.accept(Signal.complete()), null, null); } /** * Add behavior triggered when the {@link Mono} completes with an error. * *

* *

* @param onError the error callback to call on {@link Subscriber#onError(Throwable)} * * @return a new {@link Mono} */ public final Mono doOnError(Consumer onError) { Objects.requireNonNull(onError, "onError"); return doOnSignal(this, null, null, onError, null, null, null); } /** * Add behavior triggered when the {@link Mono} completes with an error matching the given exception type. *

* * * @param exceptionType the type of exceptions to handle * @param onError the error handler for relevant errors * @param type of the error to handle * * @return an observed {@link Mono} * */ public final Mono doOnError(Class exceptionType, final Consumer onError) { Objects.requireNonNull(exceptionType, "type"); @SuppressWarnings("unchecked") Consumer handler = (Consumer)onError; return doOnError(exceptionType::isInstance, handler); } /** * Add behavior triggered when the {@link Mono} completes with an error matching the given predicate. *

* * * @param predicate the matcher for exceptions to handle * @param onError the error handler for relevant error * * @return an observed {@link Mono} * */ public final Mono doOnError(Predicate predicate, final Consumer onError) { Objects.requireNonNull(predicate, "predicate"); return doOnError(t -> { if (predicate.test(t)) { onError.accept(t); } }); } /** * Add behavior triggering a {@link LongConsumer} when the {@link Mono} receives any request. *

* Note that non fatal error raised in the callback will not be propagated and * will simply trigger {@link Operators#onOperatorError(Throwable, Context)}. * *

* * * @param consumer the consumer to invoke on each request * * @return an observed {@link Mono} */ public final Mono doOnRequest(final LongConsumer consumer) { Objects.requireNonNull(consumer, "consumer"); return doOnSignal(this, null, null, null, null, consumer, null); } /** * Add behavior triggered when the {@link Mono} is subscribed. * *

* *

* @param onSubscribe the callback to call on {@link Subscriber#onSubscribe(Subscription)} * * @return a new {@link Mono} */ public final Mono doOnSubscribe(Consumer onSubscribe) { Objects.requireNonNull(onSubscribe, "onSubscribe"); return doOnSignal(this, onSubscribe, null, null, null, null, null); } /** * Add behavior triggered when the {@link Mono} terminates, either by completing successfully or with an error. * *

    *
  • null, null : completing without data
  • *
  • T, null : completing with data
  • *
  • null, Throwable : failing with/without data
  • *
* *

* *

* @param onSuccessOrError the callback to call {@link Subscriber#onNext}, {@link Subscriber#onComplete} without preceding {@link Subscriber#onNext} or {@link Subscriber#onError} * * @return a new {@link Mono} */ public final Mono doOnSuccessOrError(BiConsumer onSuccessOrError) { Objects.requireNonNull(onSuccessOrError, "onSuccessOrError"); return onAssembly(new MonoPeekTerminal<>(this, null, onSuccessOrError, null)); } /** * Add behavior triggered when the {@link Mono} terminates, either by completing successfully or with an error. * *

* *

* @param onTerminate the callback to call {@link Subscriber#onNext}, {@link Subscriber#onComplete} without preceding {@link Subscriber#onNext} or {@link Subscriber#onError} * * @return a new {@link Mono} */ public final Mono doOnTerminate(Runnable onTerminate) { Objects.requireNonNull(onTerminate, "onTerminate"); return doOnSignal(this, null, null, e -> onTerminate.run(), onTerminate, null, null); } /** * Map this {@link Mono} into {@link reactor.util.function.Tuple2 Tuple2<Long, T>} * of timemillis and source data. The timemillis corresponds to the elapsed time between * the subscribe and the first next signal, as measured by the {@link Schedulers#parallel() parallel} scheduler. * *

* * * @return a new {@link Mono} that emits a tuple of time elapsed in milliseconds and matching data */ public final Mono> elapsed() { return elapsed(Schedulers.parallel()); } /** * Map this {@link Mono} sequence into {@link reactor.util.function.Tuple2 Tuple2<Long, T>} * of timemillis and source data. The timemillis corresponds to the elapsed time between the subscribe and the first * next signal, as measured by the provided {@link Scheduler}. * *

* * * @param scheduler a {@link Scheduler} instance to read time from * @return a new {@link Mono} that emits a tuple of time elapsed in milliseconds and matching data */ public final Mono> elapsed(Scheduler scheduler) { Objects.requireNonNull(scheduler, "scheduler"); return onAssembly(new MonoElapsed<>(this, scheduler)); } /** * Recursively expand elements into a graph and emit all the resulting element, * in a depth-first traversal order. *

* That is: emit the value from this {@link Mono}, expand it and emit the first value * at this first level of recursion, and so on... When no more recursion is possible, * backtrack to the previous level and re-apply the strategy. *

* For example, given the hierarchical structure *

	 *  A
	 *   - AA
	 *     - aa1
	 *   - AB
	 *     - ab1
	 *   - a1
	 * 
* * Expands {@code Mono.just(A)} into *
	 *  A
	 *  AA
	 *  aa1
	 *  AB
	 *  ab1
	 *  a1
	 * 
* * @param expander the {@link Function} applied at each level of recursion to expand * values into a {@link Publisher}, producing a graph. * @param capacityHint a capacity hint to prepare the inner queues to accommodate n * elements per level of recursion. * * @return this Mono expanded depth-first to a {@link Flux} */ public final Flux expandDeep(Function> expander, int capacityHint) { return Flux.onAssembly(new MonoExpand<>(this, expander, false, capacityHint)); } /** * Recursively expand elements into a graph and emit all the resulting element, * in a depth-first traversal order. *

* That is: emit the value from this {@link Mono}, expand it and emit the first value * at this first level of recursion, and so on... When no more recursion is possible, * backtrack to the previous level and re-apply the strategy. *

* For example, given the hierarchical structure *

	 *  A
	 *   - AA
	 *     - aa1
	 *   - AB
	 *     - ab1
	 *   - a1
	 * 
* * Expands {@code Mono.just(A)} into *
	 *  A
	 *  AA
	 *  aa1
	 *  AB
	 *  ab1
	 *  a1
	 * 
* * @param expander the {@link Function} applied at each level of recursion to expand * values into a {@link Publisher}, producing a graph. * * @return this Mono expanded depth-first to a {@link Flux} */ public final Flux expandDeep(Function> expander) { return expandDeep(expander, Queues.SMALL_BUFFER_SIZE); } /** * Recursively expand elements into a graph and emit all the resulting element using * a breadth-first traversal strategy. *

* That is: emit the value from this {@link Mono} first, then it each at a first level of * recursion and emit all of the resulting values, then expand all of these at a * second level and so on... *

* For example, given the hierarchical structure *

	 *  A
	 *   - AA
	 *     - aa1
	 *   - AB
	 *     - ab1
	 *   - a1
	 * 
* * Expands {@code Mono.just(A)} into *
	 *  A
	 *  AA
	 *  AB
	 *  a1
	 *  aa1
	 *  ab1
	 * 
* * @param expander the {@link Function} applied at each level of recursion to expand * values into a {@link Publisher}, producing a graph. * @param capacityHint a capacity hint to prepare the inner queues to accommodate n * elements per level of recursion. * * @return this Mono expanded breadth-first to a {@link Flux} */ public final Flux expand(Function> expander, int capacityHint) { return Flux.onAssembly(new MonoExpand<>(this, expander, true, capacityHint)); } /** * Recursively expand elements into a graph and emit all the resulting element using * a breadth-first traversal strategy. *

* That is: emit the value from this {@link Mono} first, then it each at a first level of * recursion and emit all of the resulting values, then expand all of these at a * second level and so on... *

* For example, given the hierarchical structure *

	 *  A
	 *   - AA
	 *     - aa1
	 *   - AB
	 *     - ab1
	 *   - a1
	 * 
* * Expands {@code Mono.just(A)} into *
	 *  A
	 *  AA
	 *  AB
	 *  a1
	 *  aa1
	 *  ab1
	 * 
* * @param expander the {@link Function} applied at each level of recursion to expand * values into a {@link Publisher}, producing a graph. * * @return this Mono expanded breadth-first to a {@link Flux} */ public final Flux expand(Function> expander) { return expand(expander, Queues.SMALL_BUFFER_SIZE); } /** * If this {@link Mono} is valued, test the result and replay it if predicate returns true. * Otherwise complete without value. * *

* *

* @param tester the predicate to evaluate * * @return a filtered {@link Mono} */ public final Mono filter(final Predicate tester) { if (this instanceof Fuseable) { return onAssembly(new MonoFilterFuseable<>(this, tester)); } return onAssembly(new MonoFilter<>(this, tester)); } /** * If this {@link Mono} is valued, test the value asynchronously using a generated * {@code Publisher} test. The value from the Mono is replayed if the * first item emitted by the test is {@literal true}. It is dropped if the test is * either empty or its first emitted value is {@literal false}. *

* Note that only the first value of the test publisher is considered, and unless it * is a {@link Mono}, test will be cancelled after receiving that first value. * * @param asyncPredicate the function generating a {@link Publisher} of {@link Boolean} * to filter the Mono with * @return a filtered {@link Mono} */ public final Mono filterWhen(Function> asyncPredicate) { return onAssembly(new MonoFilterWhen<>(this, asyncPredicate)); } /** * Transform the item emitted by this {@link Mono} asynchronously, returning the * value emitted by another {@link Mono} (possibly changing the value type). * *

* *

* @param transformer the function to dynamically bind a new {@link Mono} * @param the result type bound * * @return a new {@link Mono} with an asynchronously mapped value. */ public final Mono flatMap(Function> transformer) { return onAssembly(new MonoFlatMap<>(this, transformer)); } /** * Transform the item emitted by this {@link Mono} into a Publisher, then forward * its emissions into the returned {@link Flux}. * *

* *

* @param mapper the * {@link Function} to produce a sequence of R from the the eventual passed {@link Subscriber#onNext} * @param the merged sequence type * * @return a new {@link Flux} as the sequence is not guaranteed to be single at most */ public final Flux flatMapMany(Function> mapper) { return Flux.onAssembly(new MonoFlatMapMany<>(this, mapper)); } /** * Transform the signals emitted by this {@link Mono} into signal-specific Publishers, * then forward the applicable Publisher's emissions into the returned {@link Flux}. * *

* *

* @param mapperOnNext the {@link Function} to call on next data and returning a sequence to merge * @param mapperOnError the {@link Function} to call on error signal and returning a sequence to merge * @param mapperOnComplete the {@link Function} to call on complete signal and returning a sequence to merge * @param the type of the produced inner sequence * * @return a new {@link Flux} as the sequence is not guaranteed to be single at most * * @see Flux#flatMap(Function, Function, Supplier) */ public final Flux flatMapMany(Function> mapperOnNext, Function> mapperOnError, Supplier> mapperOnComplete) { return flux().flatMap(mapperOnNext, mapperOnError, mapperOnComplete); } /** * Transform the item emitted by this {@link Mono} into {@link Iterable}, then forward * its elements into the returned {@link Flux}. The prefetch argument allows to * give an arbitrary prefetch size to the inner {@link Iterable}. * *

* * * @param mapper the {@link Function} to transform input item into a sequence {@link Iterable} * @param the merged output sequence type * * @return a merged {@link Flux} * */ public final Flux flatMapIterable(Function> mapper) { return Flux.onAssembly(new MonoFlattenIterable<>(this, mapper, Integer .MAX_VALUE, Queues.one())); } /** * Convert this {@link Mono} to a {@link Flux} * * @return a {@link Flux} variant of this {@link Mono} */ public final Flux flux() { if (this instanceof Callable) { if (this instanceof Fuseable.ScalarCallable) { T v; try { v = block(); } catch (Throwable t) { return Flux.error(t); } if (v == null) { return Flux.empty(); } return Flux.just(v); } @SuppressWarnings("unchecked") Callable thiz = (Callable) this; return Flux.onAssembly(new FluxCallable<>(thiz)); } return Flux.wrap(this); } /** * Emit a single boolean true if this {@link Mono} has an element. * *

* * * @return a new {@link Mono} with true if a value is emitted and false * otherwise */ public final Mono hasElement() { return onAssembly(new MonoHasElement<>(this)); } /** * Handle the items emitted by this {@link Mono} by calling a biconsumer with the * output sink for each onNext. At most one {@link SynchronousSink#next(Object)} * call must be performed and/or 0 or 1 {@link SynchronousSink#error(Throwable)} or * {@link SynchronousSink#complete()}. * * @param handler the handling {@link BiConsumer} * @param the transformed type * * @return a transformed {@link Mono} */ public final Mono handle(BiConsumer> handler) { if (this instanceof Fuseable) { return onAssembly(new MonoHandleFuseable<>(this, handler)); } return onAssembly(new MonoHandle<>(this, handler)); } /** * Hides the identity of this {@link Mono} instance. * *

The main purpose of this operator is to prevent certain identity-based * optimizations from happening, mostly for diagnostic purposes. * * @return a new {@link Mono} preventing {@link Publisher} / {@link Subscription} based Reactor optimizations */ public final Mono hide() { return onAssembly(new MonoHide<>(this)); } /** * Ignores onNext signal (dropping it) and only propagates termination events. * *

* *

* * @return a new empty {@link Mono} representing the completion of this {@link Mono}. */ public final Mono ignoreElement() { return onAssembly(new MonoIgnoreElement<>(this)); } /** * Observe all Reactive Streams signals and trace them using {@link Logger} support. * Default will use {@link Level#INFO} and {@code java.util.logging}. * If SLF4J is available, it will be used instead. * *

* *

* The default log category will be "reactor.Mono", followed by a suffix generated from * the source operator, e.g. "reactor.Mono.Map". * * @return a new {@link Mono} that logs signals * * @see Flux#log() */ public final Mono log() { return log(null, Level.INFO); } /** * Observe all Reactive Streams signals and use {@link Logger} support to handle trace implementation. Default will * use {@link Level#INFO} and java.util.logging. If SLF4J is available, it will be used instead. * *

* *

* @param category to be mapped into logger configuration (e.g. org.springframework * .reactor). If category ends with "." like "reactor.", a generated operator * suffix will complete, e.g. "reactor.Flux.Map". * * @return a new {@link Mono} */ public final Mono log(@Nullable String category) { return log(category, Level.INFO); } /** * Observe Reactive Streams signals matching the passed flags {@code options} and use * {@link Logger} support to handle trace implementation. Default will use the passed * {@link Level} and java.util.logging. If SLF4J is available, it will be used instead. * * Options allow fine grained filtering of the traced signal, for instance to only capture onNext and onError: *

	 *     mono.log("category", SignalType.ON_NEXT, SignalType.ON_ERROR)
	 * 

* *

* @param category to be mapped into logger configuration (e.g. org.springframework * .reactor). If category ends with "." like "reactor.", a generated operator * suffix will complete, e.g. "reactor.Flux.Map". * @param level the {@link Level} to enforce for this tracing Mono (only FINEST, FINE, * INFO, WARNING and SEVERE are taken into account) * @param options a vararg {@link SignalType} option to filter log messages * * @return a new {@link Mono} * */ public final Mono log(@Nullable String category, Level level, SignalType... options) { return log(category, level, false, options); } /** * Observe Reactive Streams signals matching the passed filter {@code options} and * use {@link Logger} support to * handle trace * implementation. Default will * use the passed {@link Level} and java.util.logging. If SLF4J is available, it will be used instead. * * Options allow fine grained filtering of the traced signal, for instance to only capture onNext and onError: *

	 *     mono.log("category", Level.INFO, SignalType.ON_NEXT, SignalType.ON_ERROR)
	 * 

* *

* @param category to be mapped into logger configuration (e.g. org.springframework * .reactor). If category ends with "." like "reactor.", a generated operator * suffix will complete, e.g. "reactor.Mono.Map". * @param level the {@link Level} to enforce for this tracing Mono (only FINEST, FINE, * INFO, WARNING and SEVERE are taken into account) * @param showOperatorLine capture the current stack to display operator * class/line number. * @param options a vararg {@link SignalType} option to filter log messages * * @return a new unaltered {@link Mono} */ public final Mono log(@Nullable String category, Level level, boolean showOperatorLine, SignalType... options) { SignalLogger log = new SignalLogger<>(this, category, level, showOperatorLine, options); if (this instanceof Fuseable) { return onAssembly(new MonoLogFuseable<>(this, log)); } return onAssembly(new MonoLog<>(this, log)); } /** * Transform the item emitted by this {@link Mono} by applying a synchronous function to it. * *

* *

* @param mapper the synchronous transforming {@link Function} * @param the transformed type * * @return a new {@link Mono} */ public final Mono map(Function mapper) { if (this instanceof Fuseable) { return onAssembly(new MonoMapFuseable<>(this, mapper)); } return onAssembly(new MonoMap<>(this, mapper)); } /** * Transform incoming onNext, onError and onComplete signals into {@link Signal} instances, * materializing these signals. * Since the error is materialized as a {@code Signal}, the propagation will be stopped and onComplete will be * emitted. Complete signal will first emit a {@code Signal.complete()} and then effectively complete the flux. * *

* * * @return a {@link Mono} of materialized {@link Signal} * @see #dematerialize() */ public final Mono> materialize() { return onAssembly(new MonoMaterialize<>(this)); } /** * Merge emissions of this {@link Mono} with the provided {@link Publisher}. * The element from the Mono may be interleaved with the elements of the Publisher. * *

* *

* @param other the {@link Publisher} to merge with * * @return a new {@link Flux} as the sequence is not guaranteed to be at most 1 */ public final Flux mergeWith(Publisher other) { return Flux.merge(this, other); } /** * Give a name to this sequence, which can be retrieved using {@link Scannable#name()} * as long as this is the first reachable {@link Scannable#parents()}. * * @param name a name for the sequence * @return the same sequence, but bearing a name */ public final Mono name(String name) { return MonoName.createOrAppend(this, name); } /** * Emit the first available result from this mono or the other mono. * *

* *

* @param other the racing other {@link Mono} to compete with for the result * * @return a new {@link Mono} * @see #first */ public final Mono or(Mono other) { if (this instanceof MonoFirst) { MonoFirst a = (MonoFirst) this; Mono result = a.orAdditionalSource(other); if (result != null) { return result; } } return first(this, other); } /** * Evaluate the emitted value against the given {@link Class} type. If the * value matches the type, it is passed into the new {@link Mono}. Otherwise the * value is ignored. * *

* * * @param clazz the {@link Class} type to test values against * * @return a new {@link Mono} filtered on the requested type */ public final Mono ofType(final Class clazz) { Objects.requireNonNull(clazz, "clazz"); return filter(o -> clazz.isAssignableFrom(o.getClass())).cast(clazz); } /** * Transform an error emitted by this {@link Mono} by synchronously applying a function * to it if the error matches the given predicate. Otherwise let the error pass through. *

* * * @param predicate the error predicate * @param mapper the error transforming {@link Function} * * @return a {@link Mono} that transforms some source errors to other errors */ public final Mono onErrorMap(Predicate predicate, Function mapper) { return onErrorResume(predicate, e -> Mono.error(mapper.apply(e))); } /** * Transform any error emitted by this {@link Mono} by synchronously applying a function to it. *

* *

* @param mapper the error transforming {@link Function} * * @return a {@link Mono} that transforms source errors to other errors */ public final Mono onErrorMap(Function mapper) { return onErrorResume(e -> Mono.error(mapper.apply(e))); } /** * Transform an error emitted by this {@link Mono} by synchronously applying a function * to it if the error matches the given type. Otherwise let the error pass through. *

* *

* @param type the class of the exception type to react to * @param mapper the error transforming {@link Function} * @param the error type * * @return a {@link Mono} that transforms some source errors to other errors */ public final Mono onErrorMap(Class type, Function mapper) { @SuppressWarnings("unchecked") Function handler = (Function)mapper; return onErrorMap(type::isInstance, handler); } /** * Subscribe to a fallback publisher when any error occurs, using a function to * choose the fallback depending on the error. * *

* *

* @param fallback the function to choose the fallback to an alternative {@link Mono} * * @return a {@link Mono} falling back upon source onError * * @see Flux#onErrorResume */ public final Mono onErrorResume(Function> fallback) { return onAssembly(new MonoOnErrorResume<>(this, fallback)); } /** * Subscribe to a fallback publisher when an error matching the given type * occurs, using a function to choose the fallback depending on the error. *

* * * @param type the error type to match * @param fallback the function to choose the fallback to an alternative {@link Mono} * @param the error type * * @return a {@link Mono} falling back upon source onError * @see Flux#onErrorResume */ public final Mono onErrorResume(Class type, Function> fallback) { Objects.requireNonNull(type, "type"); @SuppressWarnings("unchecked") Function> handler = (Function>)fallback; return onErrorResume(type::isInstance, handler); } /** * Subscribe to a fallback publisher when an error matching a given predicate * occurs. *

* * * @param predicate the error predicate to match * @param fallback the function to choose the fallback to an alternative {@link Mono} * @return a {@link Mono} falling back upon source onError * @see Flux#onErrorResume */ public final Mono onErrorResume(Predicate predicate, Function> fallback) { Objects.requireNonNull(predicate, "predicate"); return onErrorResume(e -> predicate.test(e) ? fallback.apply(e) : error(e)); } /** * Simply emit a captured fallback value when any error is observed on this {@link Mono}. * *

* *

* @param fallback the value to emit if an error occurs * * @return a new falling back {@link Mono} */ public final Mono onErrorReturn(final T fallback) { return onErrorResume(throwable -> just(fallback)); } /** * Simply emit a captured fallback value when an error of the specified type is * observed on this {@link Mono}. *

* * @param type the error type to match * @param fallbackValue the value to emit if an error occurs that matches the type * @param the error type * * @return a new falling back {@link Mono} */ public final Mono onErrorReturn(Class type, T fallbackValue) { return onErrorResume(type, throwable -> just(fallbackValue)); } /** * Simply emit a captured fallback value when an error matching the given predicate is * observed on this {@link Mono}. *

* * @param predicate the error predicate to match * @param fallbackValue the value to emit if an error occurs that matches the predicate * * @return a new {@link Mono} */ public final Mono onErrorReturn(Predicate predicate, T fallbackValue) { return onErrorResume(predicate, throwable -> just(fallbackValue)); } /** * Detaches both the child {@link Subscriber} and the {@link Subscription} on * termination or cancellation. *

This should help with odd retention scenarios when running * with non-reactor {@link Subscriber}. * * @return a detachable {@link Mono} */ public final Mono onTerminateDetach() { return new MonoDetach<>(this); } /** * Share a {@link Mono} for the duration of a function that may transform it and * consume it as many times as necessary without causing multiple subscriptions * to the upstream. * * @param transform the transformation function * @param the output value type * * @return a new {@link Mono} */ public final Mono publish(Function, ? extends Mono> transform) { return onAssembly(new MonoPublishMulticast<>(this, transform)); } /** * Run onNext, onComplete and onError on a supplied {@link Scheduler} * {@link Worker Worker}. *

* This operator influences the threading context where the rest of the operators in * the chain below it will execute, up to a new occurrence of {@code publishOn}. *

* *

* Typically used for fast publisher, slow consumer(s) scenarios. * *

	 * {@code mono.publishOn(Schedulers.single()).subscribe() }
	 * 
* * @param scheduler a {@link Scheduler} providing the {@link Worker} where to publish * * @return an asynchronously producing {@link Mono} */ public final Mono publishOn(Scheduler scheduler) { if(this instanceof Callable) { if (this instanceof Fuseable.ScalarCallable) { try { T value = block(); return onAssembly(new MonoSubscribeOnValue<>(value, scheduler)); } catch (Throwable t) { //leave MonoSubscribeOnCallable defer error } } @SuppressWarnings("unchecked") Callable c = (Callable)this; return onAssembly(new MonoSubscribeOnCallable<>(c, scheduler)); } return onAssembly(new MonoPublishOn<>(this, scheduler)); } /** * Repeatedly and indefinitely subscribe to the source upon completion of the * previous subscription. * *

* * * @return an indefinitely repeated {@link Flux} on onComplete */ public final Flux repeat() { return repeat(Flux.ALWAYS_BOOLEAN_SUPPLIER); } /** * Repeatedly subscribe to the source if the predicate returns true after completion of the previous subscription. * *

* * * @param predicate the boolean to evaluate on onComplete. * * @return a {@link Flux} that repeats on onComplete while the predicate matches * */ public final Flux repeat(BooleanSupplier predicate) { return Flux.onAssembly(new MonoRepeatPredicate<>(this, predicate)); } /** * Repeatedly subscribe to the source {@literal numRepeat} times. * *

* * * @param numRepeat the number of times to re-subscribe on onComplete * * @return a {@link Flux} that repeats on onComplete, up to the specified number of repetitions */ public final Flux repeat(long numRepeat) { if(numRepeat == 0){ return Flux.empty(); } return Flux.onAssembly(new MonoRepeat<>(this, numRepeat)); } /** * Repeatedly subscribe to the source if the predicate returns true after completion of the previous * subscription. A specified maximum of repeat will limit the number of re-subscribe. * *

* * * @param numRepeat the number of times to re-subscribe on complete * @param predicate the boolean to evaluate on onComplete * * @return a {@link Flux} that repeats on onComplete while the predicate matches, * up to the specified number of repetitions */ public final Flux repeat(long numRepeat, BooleanSupplier predicate) { return Flux.defer(() -> repeat(Flux.countingBooleanSupplier(predicate, numRepeat))); } /** * Repeatedly subscribe to this {@link Mono} when a companion sequence emits elements in * response to the flux completion signal. Any terminal signal from the companion * sequence will terminate the resulting {@link Flux} with the same signal immediately. *

If the companion sequence signals when this {@link Mono} is active, the repeat * attempt is suppressed. * *

* * * @param repeatFactory the {@link Function} that returns the associated {@link Publisher} * companion, given a {@link Flux} that signals each onComplete as a {@link Long} * representing the number of source elements emitted in the latest attempt (0 or 1). * * @return a {@link Flux} that repeats on onComplete when the companion {@link Publisher} produces an * onNext signal */ public final Flux repeatWhen(Function, ? extends Publisher> repeatFactory) { return Flux.onAssembly(new MonoRepeatWhen<>(this, repeatFactory)); } /** * Repeatedly subscribe to this {@link Mono} as long as the current subscription to this * {@link Mono} completes empty and the companion {@link Publisher} produces an onNext signal. *

* Any terminal signal will terminate the resulting {@link Mono} with the same signal immediately. * *

* * * @param repeatFactory the {@link Function} that returns the associated {@link Publisher} * companion, given a {@link Flux} that signals each onComplete as a 0-based incrementing {@link Long}. * * @return a {@link Mono} that resubscribes to this {@link Mono} if the previous subscription was empty, * as long as the companion {@link Publisher} produces an onNext signal * */ public final Mono repeatWhenEmpty(Function, ? extends Publisher> repeatFactory) { return repeatWhenEmpty(Integer.MAX_VALUE, repeatFactory); } /** * Repeatedly subscribe to this {@link Mono} as long as the current subscription to this * {@link Mono} completes empty and the companion {@link Publisher} produces an onNext signal. *

* Any terminal signal will terminate the resulting {@link Mono} with the same signal immediately. *

* Emits an {@link IllegalStateException} if {@code maxRepeat} is exceeded (provided * it is different from {@code Integer.MAX_VALUE}). * *

* * * @param maxRepeat the maximum number of repeats (infinite if {@code Integer.MAX_VALUE}) * @param repeatFactory the {@link Function} that returns the associated {@link Publisher} * companion, given a {@link Flux} that signals each onComplete as a 0-based incrementing {@link Long}. * * @return a {@link Mono} that resubscribes to this {@link Mono} if the previous subscription was empty, * as long as the companion {@link Publisher} produces an onNext signal and the maximum number of repeats isn't exceeded. */ public final Mono repeatWhenEmpty(int maxRepeat, Function, ? extends Publisher> repeatFactory) { return Mono.defer(() -> { Flux iterations; if(maxRepeat == Integer.MAX_VALUE) { iterations = Flux.fromStream(LongStream.range(0, Long.MAX_VALUE).boxed()); } else { iterations = Flux .range(0, maxRepeat) .map(Integer::longValue) .concatWith(Flux.error(new IllegalStateException("Exceeded maximum number of repeats"), true)); } return this.repeatWhen(o -> repeatFactory.apply(o .zipWith(iterations, 1, (c, i) -> i))) .next(); }); } /** * Re-subscribes to this {@link Mono} sequence if it signals any error, indefinitely. *

* * * @return a {@link Mono} that retries on onError */ public final Mono retry() { return retry(Long.MAX_VALUE); } /** * Re-subscribes to this {@link Mono} sequence if it signals any error, for a fixed * number of times. *

* Note that passing {@literal Long.MAX_VALUE} is treated as infinite retry. *

* * * @param numRetries the number of times to tolerate an error * * @return a {@link Mono} that retries on onError up to the specified number of retry attempts. */ public final Mono retry(long numRetries) { return onAssembly(new MonoRetry<>(this, numRetries)); } /** * Re-subscribes to this {@link Mono} sequence if it signals any error * that matches the given {@link Predicate}, otherwise push the error downstream. * *

* * * @param retryMatcher the predicate to evaluate if retry should occur based on a given error signal * * @return a {@link Mono} that retries on onError if the predicates matches. */ public final Mono retry(Predicate retryMatcher) { return onAssembly(new MonoRetryPredicate<>(this, retryMatcher)); } /** * Re-subscribes to this {@link Mono} sequence up to the specified number of retries if it signals any * error that match the given {@link Predicate}, otherwise push the error downstream. * *

* * * @param numRetries the number of times to tolerate an error * @param retryMatcher the predicate to evaluate if retry should occur based on a given error signal * * @return a {@link Mono} that retries on onError up to the specified number of retry * attempts, only if the predicate matches. * */ public final Mono retry(long numRetries, Predicate retryMatcher) { return defer(() -> retry(Flux.countingPredicate(retryMatcher, numRetries))); } /** * Retries this {@link Mono} when a companion sequence signals * an item in response to this {@link Mono} error signal *

If the companion sequence signals when the {@link Mono} is active, the retry * attempt is suppressed and any terminal signal will terminate the {@link Mono} source with the same signal * immediately. * *

* * * @param whenFactory the {@link Function} that returns the associated {@link Publisher} * companion, given a {@link Flux} that signals each onError as a {@link Throwable}. * * @return a {@link Mono} that retries on onError when the companion {@link Publisher} produces an * onNext signal */ public final Mono retryWhen(Function, ? extends Publisher> whenFactory) { return onAssembly(new MonoRetryWhen<>(this, whenFactory)); } /** * Subscribe to this {@link Mono} and request unbounded demand. *

* This version doesn't specify any consumption behavior for the events from the * chain, especially no error handling, so other variants should usually be preferred. * *

* *

* * @return a new {@link Disposable} that can be used to cancel the underlying {@link Subscription} */ public final Disposable subscribe() { if(this instanceof MonoProcessor){ MonoProcessor s = (MonoProcessor)this; s.connect(); return s; } else{ return subscribeWith(new LambdaMonoSubscriber<>(null, null, null, null)); } } /** * Subscribe a {@link Consumer} to this {@link Mono} that will consume all the * sequence. It will request an unbounded demand ({@code Long.MAX_VALUE}). *

* For a passive version that observe and forward incoming data see {@link #doOnNext(java.util.function.Consumer)}. *

* Keep in mind that since the sequence can be asynchronous, this will immediately * return control to the calling thread. This can give the impression the consumer is * not invoked when executing in a main thread or a unit test for instance. * *

* * * @param consumer the consumer to invoke on each value (onNext signal) * * @return a new {@link Disposable} that can be used to cancel the underlying {@link Subscription} */ public final Disposable subscribe(Consumer consumer) { Objects.requireNonNull(consumer, "consumer"); return subscribe(consumer, null, null); } /** * Subscribe to this {@link Mono} with a {@link Consumer} that will consume all the * elements in the sequence, as well as a {@link Consumer} that will handle errors. * The subscription will request an unbounded demand ({@code Long.MAX_VALUE}). *

* For a passive version that observe and forward incoming data see {@link #doOnSuccess(Consumer)} and * {@link #doOnError(java.util.function.Consumer)}. *

* Keep in mind that since the sequence can be asynchronous, this will immediately * return control to the calling thread. This can give the impression the consumer is * not invoked when executing in a main thread or a unit test for instance. * *

* * * @param consumer the consumer to invoke on each next signal * @param errorConsumer the consumer to invoke on error signal * * @return a new {@link Disposable} that can be used to cancel the underlying {@link Subscription} */ public final Disposable subscribe(@Nullable Consumer consumer, Consumer errorConsumer) { Objects.requireNonNull(errorConsumer, "errorConsumer"); return subscribe(consumer, errorConsumer, null); } /** * Subscribe {@link Consumer} to this {@link Mono} that will respectively consume all the * elements in the sequence, handle errors and react to completion. The subscription * will request unbounded demand ({@code Long.MAX_VALUE}). *

* For a passive version that observe and forward incoming data see {@link #doOnSuccess(Consumer)} and * {@link #doOnError(java.util.function.Consumer)}. *

* Keep in mind that since the sequence can be asynchronous, this will immediately * return control to the calling thread. This can give the impression the consumer is * not invoked when executing in a main thread or a unit test for instance. * *

* * * @param consumer the consumer to invoke on each value * @param errorConsumer the consumer to invoke on error signal * @param completeConsumer the consumer to invoke on complete signal * * @return a new {@link Disposable} that can be used to cancel the underlying {@link Subscription} */ public final Disposable subscribe( @Nullable Consumer consumer, @Nullable Consumer errorConsumer, @Nullable Runnable completeConsumer) { return subscribe(consumer, errorConsumer, completeConsumer, null); } /** * Subscribe {@link Consumer} to this {@link Mono} that will respectively consume all the * elements in the sequence, handle errors, react to completion, and request upon subscription. * It will let the provided {@link Subscription subscriptionConsumer} * request the adequate amount of data, or request unbounded demand * {@code Long.MAX_VALUE} if no such consumer is provided. *

* For a passive version that observe and forward incoming data see {@link #doOnSuccess(Consumer)} and * {@link #doOnError(java.util.function.Consumer)}. *

* Keep in mind that since the sequence can be asynchronous, this will immediately * return control to the calling thread. This can give the impression the consumer is * not invoked when executing in a main thread or a unit test for instance. * *

* * * @param consumer the consumer to invoke on each value * @param errorConsumer the consumer to invoke on error signal * @param completeConsumer the consumer to invoke on complete signal * @param subscriptionConsumer the consumer to invoke on subscribe signal, to be used * for the initial {@link Subscription#request(long) request}, or null for max request * * @return a new {@link Disposable} that can be used to cancel the underlying {@link Subscription} */ public final Disposable subscribe( @Nullable Consumer consumer, @Nullable Consumer errorConsumer, @Nullable Runnable completeConsumer, @Nullable Consumer subscriptionConsumer) { return subscribeWith(new LambdaMonoSubscriber<>(consumer, errorConsumer, completeConsumer, subscriptionConsumer)); } @Override public final void subscribe(Subscriber actual) { onLastAssembly(this).subscribe(Operators.toCoreSubscriber(actual)); } /** * An internal {@link Publisher#subscribe(Subscriber)} that will bypass * {@link Hooks#onLastOperator(Function)} pointcut. *

* In addition to behave as expected by {@link Publisher#subscribe(Subscriber)} * in a controlled manner, it supports direct subscribe-time {@link Context} passing. * * @param actual the {@link Subscriber} interested into the published sequence * @see Publisher#subscribe(Subscriber) */ public abstract void subscribe(CoreSubscriber actual); /** * Enrich a potentially empty downstream {@link Context} by adding all values * from the given {@link Context}, producing a new {@link Context} that is propagated * upstream. *

* The {@link Context} propagation happens once per subscription (not on each onNext): * it is done during the {@code subscribe(Subscriber)} phase, which runs from * the last operator of a chain towards the first. *

* So this operator enriches a {@link Context} coming from under it in the chain * (downstream, by default an empty one) and passes the new enriched {@link Context} * to operators above it in the chain (upstream, by way of them using * {@code Flux#subscribe(Subscriber,Context)}). * * @param mergeContext the {@link Context} to merge with a previous {@link Context} * state, returning a new one. * * @return a contextualized {@link Mono} * @see Context */ public final Mono subscriberContext(Context mergeContext) { return subscriberContext(c -> c.putAll(mergeContext)); } /** * Enrich a potentially empty downstream {@link Context} by applying a {@link Function} * to it, producing a new {@link Context} that is propagated upstream. *

* The {@link Context} propagation happens once per subscription (not on each onNext): * it is done during the {@code subscribe(Subscriber)} phase, which runs from * the last operator of a chain towards the first. *

* So this operator enriches a {@link Context} coming from under it in the chain * (downstream, by default an empty one) and passes the new enriched {@link Context} * to operators above it in the chain (upstream, by way of them using * {@code Flux#subscribe(Subscriber,Context)}). * * @param doOnContext the function taking a previous {@link Context} state * and returning a new one. * * @return a contextualized {@link Mono} * @see Context */ public final Mono subscriberContext(Function doOnContext) { return new MonoSubscriberContext<>(this, doOnContext); } /** * Run subscribe, onSubscribe and request on a specified {@link Scheduler}'s {@link Worker}. * As such, placing this operator anywhere in the chain will also impact the execution * context of onNext/onError/onComplete signals from the beginning of the chain up to * the next occurrence of a {@link #publishOn(Scheduler) publishOn}. *

* * *

	 * {@code mono.subscribeOn(Schedulers.parallel()).subscribe()) }
	 * 
* * @param scheduler a {@link Scheduler} providing the {@link Worker} where to subscribe * * @return a {@link Flux} requesting asynchronously * @see #publishOn(Scheduler) */ public final Mono subscribeOn(Scheduler scheduler) { if(this instanceof Callable) { if (this instanceof Fuseable.ScalarCallable) { try { T value = block(); return onAssembly(new MonoSubscribeOnValue<>(value, scheduler)); } catch (Throwable t) { //leave MonoSubscribeOnCallable defer error } } @SuppressWarnings("unchecked") Callable c = (Callable)this; return onAssembly(new MonoSubscribeOnCallable<>(c, scheduler)); } return onAssembly(new MonoSubscribeOn<>(this, scheduler)); } /** * Subscribe the given {@link Subscriber} to this {@link Mono} and return said * {@link Subscriber} (eg. a {@link MonoProcessor}). * * @param subscriber the {@link Subscriber} to subscribe with * @param the reified type of the {@link Subscriber} for chaining * * @return the passed {@link Subscriber} after subscribing it to this {@link Mono} */ public final > E subscribeWith(E subscriber) { subscribe(subscriber); return subscriber; } /** * Fallback to an alternative {@link Mono} if this mono is completed without data * *

* *

* @param alternate the alternate mono if this mono is empty * * @return a {@link Mono} falling back upon source completing without elements * @see Flux#switchIfEmpty */ public final Mono switchIfEmpty(Mono alternate) { return onAssembly(new MonoSwitchIfEmpty<>(this, alternate)); } /** * Tag this mono with a key/value pair. These can be retrieved as a {@link Set} of * all tags throughout the publisher chain by using {@link Scannable#tags()} (as * traversed * by {@link Scannable#parents()}). * * @param key a tag key * @param value a tag value * @return the same sequence, but bearing tags */ public final Mono tag(String key, String value) { return MonoName.createOrAppend(this, key, value); } /** * Give this Mono a chance to resolve within a specified time frame but complete if it * doesn't. This works a bit like {@link #timeout(Duration)} except that the resulting * {@link Mono} completes rather than errors when the timer expires. *

* The timeframe is evaluated using the {@link Schedulers#parallel() parallel Scheduler}. * * @param duration the maximum duration to wait for the source Mono to resolve. * @return a new {@link Mono} that will propagate the signals from the source unless * no signal is received for {@code duration}, in which case it completes. */ public final Mono take(Duration duration) { return take(duration, Schedulers.parallel()); } /** * Give this Mono a chance to resolve within a specified time frame but complete if it * doesn't. This works a bit like {@link #timeout(Duration)} except that the resulting * {@link Mono} completes rather than errors when the timer expires. *

* The timeframe is evaluated using the provided {@link Scheduler}. * * @param duration the maximum duration to wait for the source Mono to resolve. * @param timer the {@link Scheduler} on which to measure the duration. * * @return a new {@link Mono} that will propagate the signals from the source unless * no signal is received for {@code duration}, in which case it completes. */ public final Mono take(Duration duration, Scheduler timer) { return takeUntilOther(Mono.delay(duration, timer)); } /** * Give this Mono a chance to resolve before a companion {@link Publisher} emits. If * the companion emits before any signal from the source, the resulting Mono will * complete. Otherwise, it will relay signals from the source. * * @param other a companion {@link Publisher} that shortcircuits the source with an * onComplete signal if it emits before the source emits. * * @return a new {@link Mono} that will propagate the signals from the source unless * a signal is first received from the companion {@link Publisher}, in which case it * completes. */ public final Mono takeUntilOther(Publisher other) { return onAssembly(new MonoTakeUntilOther<>(this, other)); } /** * Return a {@code Mono} which only replays complete and error signals * from this {@link Mono}. * *

* *

* @return a {@link Mono} ignoring its payload (actively dropping) */ public final Mono then() { return empty(this); } /** * Let this {@link Mono} complete then play another Mono. *

* In other words ignore element from this {@link Mono} and transform its completion signal into the * emission and completion signal of a provided {@code Mono}. Error signal is * replayed in the resulting {@code Mono}. * *

* * * @param other a {@link Mono} to emit from after termination * @param the element type of the supplied Mono * * @return a new {@link Mono} that emits from the supplied {@link Mono} */ public final Mono then(Mono other) { if (this instanceof MonoIgnoreThen) { MonoIgnoreThen a = (MonoIgnoreThen) this; return a.shift(other); } return onAssembly(new MonoIgnoreThen<>(new Publisher[] { this }, other)); } /** * Return a {@code Mono} that waits for this {@link Mono} to complete then * for a supplied {@link Publisher Publisher<Void>} to also complete. The * second completion signal is replayed, or any error signal that occurs instead. *

* * * @param other a {@link Publisher} to wait for after this Mono's termination * @return a new {@link Mono} completing when both publishers have completed in * sequence */ public final Mono thenEmpty(Publisher other) { return then(fromDirect(other)); } /** * Let this {@link Mono} complete then play another {@link Publisher}. *

* In other words ignore element from this mono and transform the completion signal into a * {@code Flux} that will emit elements from the provided {@link Publisher}. * *

* * * @param other a {@link Publisher} to emit from after termination * @param the element type of the supplied Publisher * * @return a new {@link Flux} that emits from the supplied {@link Publisher} after * this Mono completes. */ public final Flux thenMany(Publisher other) { @SuppressWarnings("unchecked") Flux concat = (Flux)Flux.concat(ignoreElement(), other); return Flux.onAssembly(concat); } /** * Propagate a {@link TimeoutException} in case no item arrives within the given * {@link Duration}. * *

* * * @param timeout the timeout before the onNext signal from this {@link Mono} * * @return a {@link Mono} that can time out */ public final Mono timeout(Duration timeout) { return timeout(timeout, Schedulers.parallel()); } /** * Switch to a fallback {@link Mono} in case no item arrives within the given {@link Duration}. * *

* If the fallback {@link Mono} is null, signal a {@link TimeoutException} instead. * *

* * * @param timeout the timeout before the onNext signal from this {@link Mono} * @param fallback the fallback {@link Mono} to subscribe to when a timeout occurs * * @return a {@link Mono} that will fallback to a different {@link Mono} in case of timeout */ public final Mono timeout(Duration timeout, Mono fallback) { return timeout(timeout, fallback, Schedulers.parallel()); } /** * Signal a {@link TimeoutException} error in case an item doesn't arrive before the given period, * as measured on the provided {@link Scheduler}. * *

* * * @param timeout the timeout before the onNext signal from this {@link Mono} * @param timer a time-capable {@link Scheduler} instance to run the delay on * * @return an expirable {@link Mono} */ public final Mono timeout(Duration timeout, Scheduler timer) { return timeout(timeout, null, timer); } /** * Switch to a fallback {@link Mono} in case an item doesn't arrive before the given period, * as measured on the provided {@link Scheduler}. * *

If the given {@link Mono} is null, signal a {@link TimeoutException}. * *

* * * @param timeout the timeout before the onNext signal from this {@link Mono} * @param fallback the fallback {@link Mono} to subscribe when a timeout occurs * @param timer a time-capable {@link Scheduler} instance to run on * * @return an expirable {@link Mono} with a fallback {@link Mono} */ public final Mono timeout(Duration timeout, @Nullable Mono fallback, Scheduler timer) { final Mono _timer = Mono.delay(timeout, timer).onErrorReturn(0L); if(fallback == null) { return onAssembly(new MonoTimeout<>(this, _timer)); } return onAssembly(new MonoTimeout<>(this, _timer, fallback)); } /** * Signal a {@link TimeoutException} in case the item from this {@link Mono} has * not been emitted before the given {@link Publisher} emits. * *

* * * @param firstTimeout the timeout {@link Publisher} that must not emit before the first signal from this {@link Mono} * @param the element type of the timeout Publisher * * @return an expirable {@link Mono} if the item does not come before a {@link Publisher} signals * */ public final Mono timeout(Publisher firstTimeout) { return onAssembly(new MonoTimeout<>(this, firstTimeout)); } /** * Switch to a fallback {@link Publisher} in case the item from this {@link Mono} has * not been emitted before the given {@link Publisher} emits. * *

* * * @param firstTimeout the timeout * {@link Publisher} that must not emit before the first signal from this {@link Mono} * @param fallback the fallback {@link Publisher} to subscribe when a timeout occurs * @param the element type of the timeout Publisher * * @return an expirable {@link Mono} with a fallback {@link Mono} if the item doesn't * come before a {@link Publisher} signals * */ public final Mono timeout(Publisher firstTimeout, Mono fallback) { return onAssembly(new MonoTimeout<>(this, firstTimeout, fallback)); } /** * If this {@link Mono} is valued, emit a {@link reactor.util.function.Tuple2} pair of * T1 the current clock time in millis (as a {@link Long} measured by the * {@link Schedulers#parallel() parallel} Scheduler) and T2 the emitted data (as a {@code T}). * *

* * * @return a timestamped {@link Mono} */ public final Mono> timestamp() { return timestamp(Schedulers.parallel()); } /** * If this {@link Mono} is valued, emit a {@link reactor.util.function.Tuple2} pair of * T1 the current clock time in millis (as a {@link Long} measured by the * provided {@link Scheduler}) and T2 the emitted data (as a {@code T}). * *

* * * @param scheduler a {@link Scheduler} instance to read time from * @return a timestamped {@link Mono} */ public final Mono> timestamp(Scheduler scheduler) { Objects.requireNonNull(scheduler, "scheduler"); return map(d -> Tuples.of(scheduler.now(TimeUnit.MILLISECONDS), d)); } /** * Transform this {@link Mono} into a {@link CompletableFuture} completing on onNext or onComplete and failing on * onError. * *

* *

* * @return a {@link CompletableFuture} */ public final CompletableFuture toFuture() { return subscribeWith(new MonoToCompletableFuture<>()); } /** * Wrap this {@link Mono} into a {@link MonoProcessor} (turning it hot and allowing to block, * cancel, as well as many other operations). Note that the {@link MonoProcessor} is * {@link MonoProcessor#connect() connected to} (which is equivalent to calling subscribe * on it). * *

* *

* * @return a {@link MonoProcessor} to use to either retrieve value or cancel the underlying {@link Subscription} */ public final MonoProcessor toProcessor() { MonoProcessor result; if (this instanceof MonoProcessor) { result = (MonoProcessor)this; } else { result = new MonoProcessor<>(this); } result.connect(); return result; } /** * Transform this {@link Mono} in order to generate a target {@link Mono}. Unlike {@link #compose(Function)}, the * provided function is executed as part of assembly. * *

	 * {@code
	 * Function applySchedulers = mono -> mono.subscribeOn(Schedulers.io())
	 *                                                    .publishOn(Schedulers.parallel());
	 * mono.transform(applySchedulers).map(v -> v * v).subscribe();
	 * }
	 * 
* * @param transformer the {@link Function} to immediately map this {@link Mono} into a target {@link Mono} * instance. * @param the item type in the returned {@link Mono} * * @return a new {@link Mono} * @see #compose(Function) compose(Function) for deferred composition of {@link Mono} for each {@link Subscriber} * @see #as(Function) as(Function) for a loose conversion to an arbitrary type */ public final Mono transform(Function, ? extends Publisher> transformer) { return onAssembly(from(transformer.apply(this))); } /** * Wait for the result from this mono, use it to create a second mono via the * provided {@code rightGenerator} function and combine both results into a {@link Tuple2}. * *

* *

* @param rightGenerator the {@link Function} to generate a {@code Mono} to combine with * @param the element type of the other Mono instance * * @return a new combined Mono */ public final Mono> zipWhen(Function> rightGenerator) { return zipWhen(rightGenerator, Tuples::of); } /** * Wait for the result from this mono, use it to create a second mono via the * provided {@code rightGenerator} function and combine both results into an arbitrary * {@code O} object, as defined by the provided {@code combinator} function. * *

* *

* @param rightGenerator the {@link Function} to generate a {@code Mono} to combine with * @param combinator a {@link BiFunction} combinator function when both sources complete * @param the element type of the other Mono instance * @param the element type of the combination * * @return a new combined Mono */ public final Mono zipWhen(Function> rightGenerator, BiFunction combinator) { Objects.requireNonNull(rightGenerator, "rightGenerator function is mandatory to get the right-hand side Mono"); Objects.requireNonNull(combinator, "combinator function is mandatory to combine results from both Monos"); return flatMap(t -> rightGenerator.apply(t).map(t2 -> combinator.apply(t, t2))); } /** * Combine the result from this mono and another into a {@link Tuple2}. * *

* *

* @param other the {@link Mono} to combine with * @param the element type of the other Mono instance * * @return a new combined Mono */ public final Mono> zipWith(Mono other) { return zipWith(other, Flux.tuple2Function()); } /** * Combine the result from this mono and another into an arbitrary {@code O} object, * as defined by the provided {@code combinator} function. * *

* *

* @param other the {@link Mono} to combine with * @param combinator a {@link BiFunction} combinator function when both sources * complete * @param the element type of the other Mono instance * @param the element type of the combination * * @return a new combined Mono */ public final Mono zipWith(Mono other, BiFunction combinator) { if (this instanceof MonoZip) { @SuppressWarnings("unchecked") MonoZip o = (MonoZip) this; Mono result = o.zipAdditionalSource(other, combinator); if (result != null) { return result; } } return zip(this, other, combinator); } /** * To be used by custom operators: invokes assembly {@link Hooks} pointcut given a * {@link Mono}, potentially returning a new {@link Mono}. This is for example useful * to activate cross-cutting concerns at assembly time, eg. a generalized * {@link #checkpoint()}. * * @param the value type * @param source the source to apply assembly hooks onto * * @return the source, potentially wrapped with assembly time cross-cutting behavior */ @SuppressWarnings("unchecked") protected static Mono onAssembly(Mono source) { Function hook = Hooks.onEachOperatorHook; if(hook == null) { return source; } return (Mono)hook.apply(source); } /** * To be used by custom operators: invokes assembly {@link Hooks} pointcut given a * {@link Mono}, potentially returning a new {@link Mono}. This is for example useful * to activate cross-cutting concerns at assembly time, eg. a generalized * {@link #checkpoint()}. * * @param the value type * @param source the source to apply assembly hooks onto * * @return the source, potentially wrapped with assembly time cross-cutting behavior */ @SuppressWarnings("unchecked") protected static Mono onLastAssembly(Mono source) { Function hook = Hooks.onLastOperatorHook; if(hook == null) { return source; } return (Mono)Objects.requireNonNull(hook.apply(source), "LastOperator hook returned null"); } @Override public String toString() { return getClass().getSimpleName(); } static Mono empty(Publisher source) { @SuppressWarnings("unchecked") Mono then = (Mono)ignoreElements(source); return then; } @SuppressWarnings("unchecked") static Mono doOnSignal(Mono source, @Nullable Consumer onSubscribe, @Nullable Consumer onNext, @Nullable Consumer onError, @Nullable Runnable onComplete, @Nullable LongConsumer onRequest, @Nullable Runnable onCancel) { if (source instanceof Fuseable) { return onAssembly(new MonoPeekFuseable<>(source, onSubscribe, onNext, onError, onComplete, onRequest, onCancel)); } return onAssembly(new MonoPeek<>(source, onSubscribe, onNext, onError, onComplete, onRequest, onCancel)); } @SuppressWarnings("unchecked") static BiPredicate equalsBiPredicate(){ return EQUALS_BIPREDICATE; } static final BiPredicate EQUALS_BIPREDICATE = Object::equals; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy