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

io.vertx.rx.java.ReadStreamSubscriber Maven / Gradle / Ivy

The newest version!
package io.vertx.rx.java;

import io.vertx.core.Handler;
import io.vertx.core.streams.ReadStream;
import rx.Observable;
import rx.Subscriber;

import java.util.ArrayDeque;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * An RxJava {@code Subscriber} that turns an {@code Observable} into a {@link ReadStream}.
 * 

* The stream implements the {@link #pause()} and {@link #resume()} operation by maintaining * a buffer of {@link #BUFFER_SIZE} elements between the {@code Observable} and the {@code ReadStream}. *

* When the subscriber is created it requests {@code 0} elements to activate the subscriber's back-pressure. * Setting the handler initially on the {@code ReadStream} triggers a request of {@link #BUFFER_SIZE} elements. * When the item buffer is half empty, new elements are requested to fill the buffer back to {@link #BUFFER_SIZE} * elements. *

* The {@link #endHandler(Handler)} is called when the {@code Observable} is completed or has failed and * no pending elements, emitted before the completion or failure, are still in the buffer, i.e the handler * is not called when the stream is paused. * * @author Julien Viet */ public class ReadStreamSubscriber extends Subscriber implements ReadStream { private static final Runnable NOOP_ACTION = () -> {}; private static final Throwable DONE_SENTINEL = new Throwable(); public static final int BUFFER_SIZE = 16; public static ReadStream asReadStream(Observable observable, Function adapter) { return new ReadStreamSubscriber<>(adapter, observable::subscribe); } private final Function adapter; private Handler endHandler; private Handler exceptionHandler; private Handler elementHandler; private long demand = Long.MAX_VALUE; private Throwable completed; private ArrayDeque pending = new ArrayDeque<>(); private int requested = 0; private Consumer> doSubscribe; private Thread emitting; public ReadStreamSubscriber(Function adapter, Consumer> doSubscribe) { this.adapter = adapter; this.doSubscribe = doSubscribe; request(0); } @Override public ReadStream handler(Handler handler) { Runnable action; synchronized (this) { elementHandler = handler; if (handler != null) { action = () -> doSubscribe.accept(this); } else { action = this::unsubscribe; } } action.run(); serializedCheckStatus(); return this; } @Override public ReadStream pause() { synchronized (this) { demand = 0L; } return this; } @Override public ReadStream fetch(long amount) { if (amount < 0L) { throw new IllegalArgumentException("Invalid amount: " + amount); } synchronized (this) { demand += amount; if (demand < 0L) { demand = Long.MAX_VALUE; } } serializedCheckStatus(); return this; } @Override public ReadStream resume() { return fetch(Long.MAX_VALUE); } /** Ensure checkStatus is never called concurrently. */ private void serializedCheckStatus() { synchronized (this) { if (emitting != null && emitting != Thread.currentThread()) { return; } emitting = Thread.currentThread(); } try { checkStatus(); } finally { synchronized (this) { emitting = null; } } } private void checkStatus() { Runnable action = NOOP_ACTION; while (true) { J adapted; Handler handler; synchronized (this) { if (demand > 0L && (handler = elementHandler) != null && pending.size() > 0) { if (demand != Long.MAX_VALUE) { demand--; } requested--; R item = pending.poll(); adapted = adapter.apply(item); } else { if (completed != null) { if (pending.isEmpty()) { Handler onError; Throwable result; if (completed != DONE_SENTINEL) { onError = exceptionHandler; result = completed; exceptionHandler = null; } else { onError = null; result = null; } Handler onCompleted = endHandler; endHandler = null; action = () -> { try { if (onError != null) { onError.handle(result); } } finally { if (onCompleted != null) { onCompleted.handle(null); } } }; } } else if (elementHandler != null && requested < BUFFER_SIZE / 2) { int request = BUFFER_SIZE - requested; action = () -> request(request); requested = BUFFER_SIZE; } break; } } handler.handle(adapted); } action.run(); } @Override public ReadStream endHandler(Handler handler) { synchronized (this) { if (completed == null || pending.size() > 0) { endHandler = handler; } else { if (handler != null) { throw new IllegalStateException(); } } } return this; } @Override public ReadStream exceptionHandler(Handler handler) { synchronized (this) { if (completed == null || pending.size() > 0) { exceptionHandler = handler; } else { if (handler != null) { throw new IllegalStateException(); } } } return this; } @Override public void onCompleted() { onError(DONE_SENTINEL); } @Override public void onError(Throwable e) { synchronized (this) { if (completed != null) { return; } completed = e; } serializedCheckStatus(); } @Override public void onNext(R item) { synchronized (this) { pending.add(item); } serializedCheckStatus(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy