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

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

Go to download

Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC

There is a newer version: 3.40.2
Show newest version
/*
 * Copyright (c) 2016-2021 VMware Inc. or its affiliates, 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
 *
 *   https://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.util.Objects;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Disposable;
import reactor.core.Exceptions;
import reactor.util.context.Context;

/**
 * A simple base class for a {@link Subscriber} implementation that lets the user
 * perform a {@link #request(long)} and {@link #cancel()} on it directly. As the targeted
 * use case is to manually handle requests, the {@link #hookOnSubscribe(Subscription)} and
 * {@link #hookOnNext(Object)} hooks are expected to be implemented, but they nonetheless
 * default to an unbounded request at subscription time. If you need to define a {@link Context}
 * for this {@link BaseSubscriber}, simply override its {@link #currentContext()} method.
 * 

* Override the other optional hooks {@link #hookOnComplete()}, * {@link #hookOnError(Throwable)} and {@link #hookOnCancel()} * to customize the base behavior. You also have a termination hook, * {@link #hookFinally(SignalType)}. *

* Most of the time, exceptions triggered inside hooks are propagated to * {@link #onError(Throwable)} (unless there is a fatal exception). The class is in the * {@code reactor.core.publisher} package, as this subscriber is tied to a single * {@link org.reactivestreams.Publisher}. * * @author Simon Baslé */ public abstract class BaseSubscriber implements CoreSubscriber, Subscription, Disposable { volatile Subscription subscription; static AtomicReferenceFieldUpdater S = AtomicReferenceFieldUpdater.newUpdater(BaseSubscriber.class, Subscription.class, "subscription"); /** * Return current {@link Subscription} * @return current {@link Subscription} */ protected Subscription upstream() { return subscription; } @Override public boolean isDisposed() { return subscription == Operators.cancelledSubscription(); } /** * {@link Disposable#dispose() Dispose} the {@link Subscription} by * {@link Subscription#cancel() cancelling} it. */ @Override public void dispose() { cancel(); } /** * Hook for further processing of onSubscribe's Subscription. Implement this method * to call {@link #request(long)} as an initial request. Values other than the * unbounded {@code Long.MAX_VALUE} imply that you'll also call request in * {@link #hookOnNext(Object)}. *

Defaults to request unbounded Long.MAX_VALUE as in {@link #requestUnbounded()} * * @param subscription the subscription to optionally process */ protected void hookOnSubscribe(Subscription subscription){ subscription.request(Long.MAX_VALUE); } /** * Hook for processing of onNext values. You can call {@link #request(long)} here * to further request data from the source {@link org.reactivestreams.Publisher} if * the {@link #hookOnSubscribe(Subscription) initial request} wasn't unbounded. *

Defaults to doing nothing. * * @param value the emitted value to process */ protected void hookOnNext(T value){ // NO-OP } /** * Optional hook for completion processing. Defaults to doing nothing. */ protected void hookOnComplete() { // NO-OP } /** * Optional hook for error processing. Default is to call * {@link Exceptions#errorCallbackNotImplemented(Throwable)}. * * @param throwable the error to process */ protected void hookOnError(Throwable throwable) { throw Exceptions.errorCallbackNotImplemented(throwable); } /** * Optional hook executed when the subscription is cancelled by calling this * Subscriber's {@link #cancel()} method. Defaults to doing nothing. */ protected void hookOnCancel() { //NO-OP } /** * Optional hook executed after any of the termination events (onError, onComplete, * cancel). The hook is executed in addition to and after {@link #hookOnError(Throwable)}, * {@link #hookOnComplete()} and {@link #hookOnCancel()} hooks, even if these callbacks * fail. Defaults to doing nothing. A failure of the callback will be caught by * {@link Operators#onErrorDropped(Throwable, reactor.util.context.Context)}. * * @param type the type of termination event that triggered the hook * ({@link SignalType#ON_ERROR}, {@link SignalType#ON_COMPLETE} or * {@link SignalType#CANCEL}) */ protected void hookFinally(SignalType type) { //NO-OP } @Override public final void onSubscribe(Subscription s) { if (Operators.setOnce(S, this, s)) { try { hookOnSubscribe(s); } catch (Throwable throwable) { onError(Operators.onOperatorError(s, throwable, currentContext())); } } } @Override public final void onNext(T value) { Objects.requireNonNull(value, "onNext"); try { hookOnNext(value); } catch (Throwable throwable) { onError(Operators.onOperatorError(subscription, throwable, value, currentContext())); } } @Override public final void onError(Throwable t) { Objects.requireNonNull(t, "onError"); if (S.getAndSet(this, Operators.cancelledSubscription()) == Operators .cancelledSubscription()) { //already cancelled concurrently Operators.onErrorDropped(t, currentContext()); return; } try { hookOnError(t); } catch (Throwable e) { e = Exceptions.addSuppressed(e, t); Operators.onErrorDropped(e, currentContext()); } finally { safeHookFinally(SignalType.ON_ERROR); } } @Override public final void onComplete() { if (S.getAndSet(this, Operators.cancelledSubscription()) != Operators .cancelledSubscription()) { //we're sure it has not been concurrently cancelled try { hookOnComplete(); } catch (Throwable throwable) { //onError itself will short-circuit due to the CancelledSubscription being set above hookOnError(Operators.onOperatorError(throwable, currentContext())); } finally { safeHookFinally(SignalType.ON_COMPLETE); } } } @Override public final void request(long n) { if (Operators.validate(n)) { Subscription s = this.subscription; if (s != null) { s.request(n); } } } /** * {@link #request(long) Request} an unbounded amount. */ public final void requestUnbounded() { request(Long.MAX_VALUE); } @Override public final void cancel() { if (Operators.terminate(S, this)) { try { hookOnCancel(); } catch (Throwable throwable) { hookOnError(Operators.onOperatorError(subscription, throwable, currentContext())); } finally { safeHookFinally(SignalType.CANCEL); } } } void safeHookFinally(SignalType type) { try { hookFinally(type); } catch (Throwable finallyFailure) { Operators.onErrorDropped(finallyFailure, currentContext()); } } @Override public String toString() { return getClass().getSimpleName(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy