reactor.rx.action.combination.SwitchAction Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2015 Pivotal Software Inc., 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.rx.action.combination;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.Dispatcher;
import reactor.core.processor.CancelException;
import reactor.core.reactivestreams.SerializedSubscriber;
import reactor.core.support.Exceptions;
import reactor.core.support.NonBlocking;
import reactor.rx.action.Action;
import reactor.rx.broadcast.Broadcaster;
/**
* @author Stephane Maldini
* @since 2.0
*/
public class SwitchAction extends Action, T> {
private final Dispatcher dispatcher;
private long pendingRequests = 0l;
private SwitchSubscriber switchSubscriber;
public SwitchAction(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public SwitchSubscriber getSwitchSubscriber() {
return switchSubscriber;
}
@Override
public void subscribe(Subscriber super T> subscriber) {
final SwitchSubscriber switcher;
final boolean toSubscribe;
synchronized (this) {
switcher = switchSubscriber;
toSubscribe = switcher != null && switcher.s == null;
}
if(toSubscribe){
switcher.publisher.subscribe(switcher);
}
super.subscribe(SerializedSubscriber.create(subscriber));
}
@Override
public void onNext(Publisher extends T> ev) {
if (ev == null) {
throw new NullPointerException("Spec 2.13: Signal cannot be null");
}
try {
doNext(ev);
} catch (CancelException uae){
throw uae;
} catch (Throwable cause) {
doError(Exceptions.addValueAsLastCause(cause, ev));
}
}
@Override
public void cancel() {
SwitchSubscriber subscriber;
synchronized (this) {
subscriber = switchSubscriber;
}
if(subscriber != null){
subscriber.cancel();
}
if(upstreamSubscription != Broadcaster.HOT_SUBSCRIPTION){
super.cancel();
}
}
@Override
protected void doNext(Publisher extends T> ev) {
SwitchSubscriber subscriber, nextSubscriber;
synchronized (this) {
if(switchSubscriber != null && switchSubscriber.publisher == ev) return;
if(pendingRequests != Long.MAX_VALUE) pendingRequests--;
subscriber = switchSubscriber;
switchSubscriber = nextSubscriber= new SwitchSubscriber(ev);
}
if (subscriber != null) {
subscriber.cancel();
}
ev.subscribe(nextSubscriber);
}
@Override
protected void doShutdown() {
SwitchSubscriber subscriber;
synchronized (this) {
subscriber = switchSubscriber;
if (subscriber != null) {
switchSubscriber = null;
}
}
if (subscriber != null) {
subscriber.cancel();
}
super.doShutdown();
}
@Override
protected void doComplete() {
SwitchSubscriber subscriber;
synchronized (this) {
subscriber = switchSubscriber;
}
if (subscriber == null) {
super.doComplete();
} else {
cancel();
}
}
@Override
protected void requestUpstream(long capacity, boolean terminated, long elements) {
SwitchSubscriber subscriber;
synchronized (this) {
if ((pendingRequests += elements) < 0) pendingRequests = Long.MAX_VALUE;
subscriber = switchSubscriber;
}
super.requestUpstream(capacity, terminated, elements);
if (subscriber != null) {
subscriber.request(elements);
}
}
@Override
public final Dispatcher getDispatcher() {
return dispatcher;
}
public class SwitchSubscriber implements NonBlocking, Subscriber, Subscription {
final Publisher extends T> publisher;
Subscription s;
public SwitchSubscriber(Publisher extends T> publisher) {
this.publisher = publisher;
}
@Override
public boolean isReactivePull(Dispatcher dispatcher, long producerCapacity) {
return SwitchAction.this.isReactivePull(dispatcher, producerCapacity);
}
@Override
public long getCapacity() {
return capacity;
}
@Override
public void onSubscribe(final Subscription s) {
this.s = s;
long pending;
synchronized (SwitchAction.this) {
pending = pendingRequests;
}
if (pending > 0 && downstreamSubscription != null) {
s.request(pending);
}
}
@Override
public void onNext(T t) {
synchronized (SwitchAction.this) {
if (pendingRequests > 0 && pendingRequests != Long.MAX_VALUE) {
pendingRequests--;
}
}
broadcastNext(t);
}
@Override
public void onError(Throwable t) {
broadcastError(t);
}
@Override
public void onComplete() {
synchronized (SwitchAction.this) {
switchSubscriber = null;
}
cancel();
if (upstreamSubscription == null) {
broadcastComplete();
}
}
@Override
public void request(long n) {
s.request(n);
}
public void cancel() {
Subscription s = this.s;
if (s != null) {
this.s = null;
s.cancel();
}
}
public Subscription getSubscription() {
return s;
}
}
}