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

hu.akarnokd.rxjava2.debug.validator.RxJavaProtocolValidator Maven / Gradle / Ivy

There is a newer version: 0.20.10
Show newest version
/*
 * Copyright 2016-2018 David Karnok
 *
 * 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 hu.akarnokd.rxjava2.debug.validator;

import hu.akarnokd.rxjava2.debug.SavedHooks;
import hu.akarnokd.rxjava2.functions.PlainConsumer;
import io.reactivex.*;
import io.reactivex.annotations.Nullable;
import io.reactivex.flowables.ConnectableFlowable;
import io.reactivex.functions.Function;
import io.reactivex.internal.functions.Functions;
import io.reactivex.observables.ConnectableObservable;
import io.reactivex.parallel.ParallelFlowable;
import io.reactivex.plugins.RxJavaPlugins;

/**
 * Installs assembly hooks that validate the Reactive-Streams and derived
 * protocols are honored.
 *
 * @since 0.17.4
 * @see #setOnViolationHandler(PlainConsumer)
 * @see #enable()
 */
public final class RxJavaProtocolValidator {

    /** Utility class. */
    private RxJavaProtocolValidator() {
        throw new IllegalStateException("No instances!");
    }

    static volatile boolean enabled;

    static volatile PlainConsumer onViolation;

    static final PlainConsumer DEFAULT = new PlainConsumer() {
        @Override
        public void accept(ProtocolNonConformanceException e) {
            RxJavaPlugins.onError(e);
        }
    };

    /**
     * Enable the protocol violation hooks.
     * @see #enableAndChain()
     * @see #disable()
     */
    public static void enable() {
        enable(false);
    }

    /**
     * Enable the protocol violation hooks by chaining it
     * before any existing hook.
     * @return the SavedHooks instance that allows restoring the previous assembly
     * hook handlers overridden by this method
     * @see #enable()
     */
    public static SavedHooks enableAndChain() {
        return enable(true);
    }

    @SuppressWarnings("rawtypes")
    static SavedHooks enable(boolean chain) {
        PlainConsumer h = onViolation;
        if (h == null) {
            h = DEFAULT;
        }
        final PlainConsumer handler = h;

        // ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

        final Function saveC = RxJavaPlugins.getOnCompletableAssembly();
        Function oldCompletable = saveC;
        if (oldCompletable == null || !chain) {
            oldCompletable = Functions.identity();
        }
        final Function oldC = oldCompletable;

        RxJavaPlugins.setOnCompletableAssembly(new Function() {
            @Override
            public Completable apply(Completable c) throws Exception {
                return oldC.apply(new CompletableValidator(c, handler));
            }
        });

        // ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

        final Function saveM = RxJavaPlugins.getOnMaybeAssembly();
        Function oldMaybe = saveM;
        if (oldMaybe == null || !chain) {
            oldMaybe = Functions.identity();
        }
        final Function oldM = oldMaybe;

        RxJavaPlugins.setOnMaybeAssembly(new Function() {
            @SuppressWarnings("unchecked")
            @Override
            public Maybe apply(Maybe c) throws Exception {
                return oldM.apply(new MaybeValidator(c, handler));
            }
        });

        // ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

        final Function saveS = RxJavaPlugins.getOnSingleAssembly();
        Function oldSingle = saveS;
        if (oldSingle == null || !chain) {
            oldSingle = Functions.identity();
        }
        final Function oldS = oldSingle;

        RxJavaPlugins.setOnSingleAssembly(new Function() {
            @SuppressWarnings("unchecked")
            @Override
            public Single apply(Single c) throws Exception {
                return oldS.apply(new SingleValidator(c, handler));
            }
        });

        // ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

        final Function saveO = RxJavaPlugins.getOnObservableAssembly();
        Function oldObservable = saveO;
        if (oldObservable == null || !chain) {
            oldObservable = Functions.identity();
        }
        final Function oldO = oldObservable;

        RxJavaPlugins.setOnObservableAssembly(new Function() {
            @SuppressWarnings("unchecked")
            @Override
            public Observable apply(Observable c) throws Exception {
                return oldO.apply(new ObservableValidator(c, handler));
            }
        });

        // ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

        final Function saveF = RxJavaPlugins.getOnFlowableAssembly();
        Function oldFlowable = saveF;
        if (oldFlowable == null || !chain) {
            oldFlowable = Functions.identity();
        }
        final Function oldF = oldFlowable;

        RxJavaPlugins.setOnFlowableAssembly(new Function() {
            @SuppressWarnings("unchecked")
            @Override
            public Flowable apply(Flowable c) throws Exception {
                return oldF.apply(new FlowableValidator(c, handler));
            }
        });

        // ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

        final Function saveCF = RxJavaPlugins.getOnConnectableFlowableAssembly();
        Function oldConnFlow = saveCF;
        if (oldConnFlow == null || !chain) {
            oldConnFlow = Functions.identity();
        }
        final Function oldCF = oldConnFlow;

        RxJavaPlugins.setOnConnectableFlowableAssembly(new Function() {
            @SuppressWarnings("unchecked")
            @Override
            public ConnectableFlowable apply(ConnectableFlowable c) throws Exception {
                return oldCF.apply(new ConnectableFlowableValidator(c, handler));
            }
        });

        // ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

        final Function saveCO = RxJavaPlugins.getOnConnectableObservableAssembly();
        Function oldConnObs = saveCO;
        if (oldConnObs == null || !chain) {
            oldConnObs = Functions.identity();
        }
        final Function oldCO = oldConnObs;

        RxJavaPlugins.setOnConnectableObservableAssembly(new Function() {
            @SuppressWarnings("unchecked")
            @Override
            public ConnectableObservable apply(ConnectableObservable c) throws Exception {
                return oldCO.apply(new ConnectableObservableValidator(c, handler));
            }
        });

        // ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo

        final Function savePF = RxJavaPlugins.getOnParallelAssembly();
        Function oldParFlow = savePF;
        if (oldParFlow == null || !chain) {
            oldParFlow = Functions.identity();
        }
        final Function oldPF = oldParFlow;

        RxJavaPlugins.setOnParallelAssembly(new Function() {
            @SuppressWarnings("unchecked")
            @Override
            public ParallelFlowable apply(ParallelFlowable c) throws Exception {
                return oldPF.apply(new ParallelFlowableValidator(c, handler));
            }
        });

        enabled = true;

        return new SavedHooks() {
            @Override
            public void restore() {
                RxJavaPlugins.setOnCompletableAssembly(saveC);
                RxJavaPlugins.setOnSingleAssembly(saveS);
                RxJavaPlugins.setOnMaybeAssembly(saveM);
                RxJavaPlugins.setOnObservableAssembly(saveO);
                RxJavaPlugins.setOnFlowableAssembly(saveF);

                RxJavaPlugins.setOnConnectableObservableAssembly(saveCO);
                RxJavaPlugins.setOnConnectableFlowableAssembly(saveCF);

                RxJavaPlugins.setOnParallelAssembly(savePF);
            }
        };
    }

    /**
     * Disables the validation hooks be resetting the assembly hooks
     * to none.
     */
    public static void disable() {
        RxJavaPlugins.setOnCompletableAssembly(null);
        RxJavaPlugins.setOnSingleAssembly(null);
        RxJavaPlugins.setOnMaybeAssembly(null);
        RxJavaPlugins.setOnObservableAssembly(null);
        RxJavaPlugins.setOnFlowableAssembly(null);

        RxJavaPlugins.setOnConnectableObservableAssembly(null);
        RxJavaPlugins.setOnConnectableFlowableAssembly(null);

        RxJavaPlugins.setOnParallelAssembly(null);
        enabled = false;
    }

    /**
     * Returns true if the validation hooks have been installed.
     * @return true if the validation hooks have been installed
     */
    public static boolean isEnabled() {
        return enabled;
    }

    /**
     * Set a custom violation callback handler.
     * 

* Call this method before enabling the validation hooks via {@link #enable()}. *

* Calling with {@code null} restores the default handler which forwards the exception * to the global {@link RxJavaPlugins#onError(Throwable)} handler. * @param handler the handler that will receive the {@link ProtocolNonConformanceException} * violation exceptions */ public static void setOnViolationHandler(@Nullable PlainConsumer handler) { onViolation = handler; } /** * Returns the current custom violation callback handler. * @return the current custom violation callback handler or null if not set */ @Nullable public static PlainConsumer getOnViolationHandler() { return onViolation; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy