/* * Copyright (c) 2011-2015 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 io.ripc.test.internal; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; /** * Original {@see reactor.core.reactivestreams.PublisherFactory} from {@see http://projectreactor.io}. * <p> * A {@link Subscriber} with a typed stateful context. Some error isolation is also provided * (onSubscribe, onNext and onComplete error is forwarded to onError). * * @author Stephane Maldini */ public class SubscriberWithContext<T, C> implements Subscriber<T> { private volatile int terminated = 0; protected static final AtomicIntegerFieldUpdater<SubscriberWithContext> TERMINAL_UPDATER = AtomicIntegerFieldUpdater .newUpdater(SubscriberWithContext.class, "terminated"); protected final C context; protected final Subscriber<? super T> subscriber; /** * Attach a given arbitrary context (stateful information) to a {@link Subscriber}, all Subscriber methods * will delegate properly. * * @param subscriber the delegate subscriber to invoke on signal * @param context the contextual state of any type to bind for later use * @param <T> Type of data sequence * @param <C> Type of attached stateful context * @return a new Susbscriber with context information */ public static <T, C> SubscriberWithContext<T, C> create(Subscriber<? super T> subscriber, C context) { return new SubscriberWithContext<>(context, subscriber); } protected SubscriberWithContext(C context, Subscriber<? super T> subscriber) { this.context = context; this.subscriber = subscriber; } /** * The stateful context C * * @return the bound context */ public C context() { return context; } @Override public void onSubscribe(Subscription s) { try { subscriber.onSubscribe(s); } catch (Throwable throwable) { subscriber.onError(throwable); } } @Override public void onNext(T t) { try { subscriber.onNext(t); } catch (Throwable throwable) { subscriber.onError(throwable); } } @Override public void onError(Throwable t) { if (TERMINAL_UPDATER.compareAndSet(this, 0, 1)) { subscriber.onError(t); } } @Override public void onComplete() { try { if (TERMINAL_UPDATER.compareAndSet(this, 0, 1)) { subscriber.onComplete(); } } catch (Throwable throwable) { subscriber.onError(throwable); } } public boolean isCancelled() { return terminated == 1; } }