/*
* Copyright 2016 the original author or authors.
*
* 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 ratpack.stream.internal;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ratpack.func.Action;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
public abstract class ManagedSubscription<T> implements Subscription {
private static final Logger LOGGER = LoggerFactory.getLogger(ManagedSubscription.class);
private volatile boolean open;
private final AtomicLong demand = new AtomicLong();
private final Action<? super T> disposer;
private final Subscriber<? super T> subscriber;
private final AtomicBoolean done = new AtomicBoolean();
public ManagedSubscription(Subscriber<? super T> subscriber, Action<? super T> disposer) {
this.subscriber = subscriber;
this.disposer = disposer;
}
@Override
public final void request(long n) {
if (n < 1) {
subscriber.onError(new IllegalArgumentException("3.9 While the Subscription is not cancelled, Subscription.request(long n) MUST throw a java.lang.IllegalArgumentException if the argument is <= 0."));
cancel();
return;
}
if (!open) {
if (n == Long.MAX_VALUE) {
open = true;
demand.set(Long.MAX_VALUE);
} else {
long newDemand = demand.addAndGet(n);
if (newDemand < 1 || newDemand == Long.MAX_VALUE) {
open = true;
n = Long.MAX_VALUE;
demand.set(Long.MAX_VALUE);
}
}
onRequest(n);
}
}
protected long getDemand() {
return isDone() ? 0 : demand.get();
}
protected boolean shouldEmit() {
return isDone() || demand.get() > 0;
}
protected boolean isDone() {
return done.get();
}
protected boolean hasDemand() {
return getDemand() > 0;
}
protected abstract void onRequest(long n);
protected abstract void onCancel();
protected void emitNext(T item) {
if (isDone()) {
dispose(item);
} else {
if (!open) {
demand.decrementAndGet();
}
subscriber.onNext(item);
}
}
protected void emitError(Throwable error) {
if (fireDone()) {
subscriber.onError(error);
}
}
protected void emitComplete() {
if (fireDone()) {
subscriber.onComplete();
}
}
protected void dispose(T item) {
try {
disposer.execute(item);
} catch (Exception e) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("exception raised disposing of " + item + " - will be ignored", e);
}
}
}
private boolean fireDone() {
return done.compareAndSet(false, true);
}
@Override
public void cancel() {
onCancel();
fireDone();
}
}