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

net.pincette.rs.ProcessorBase Maven / Gradle / Ivy

There is a newer version: 3.7.1
Show newest version
package net.pincette.rs;

import java.util.concurrent.Flow.Processor;
import java.util.concurrent.Flow.Subscriber;
import java.util.concurrent.Flow.Subscription;

/**
 * A base class for reactive streams processors.
 *
 * @param  the value type for the incoming elements.
 * @param  the value type for the outgoing elements.
 * @author Werner Donné
 * @since 3.0
 */
public abstract class ProcessorBase implements Processor {
  protected Subscriber subscriber;
  protected Subscription subscription;
  private boolean completed;
  private boolean error;
  private Throwable pendingException;

  /** Cancels the upstream. */
  public void cancel() {
    cancelling();
  }

  /**
   * Provides subclasses the opportunity to flush stuff when the stream is cancelled. The
   * implementation cancels the subscription, which subclasses should also do after doing their own
   * thing.
   */
  protected void cancelling() {
    dispatch(subscription::cancel);
  }

  /** Completes the downstream and cancels the upstream. */
  protected void complete() {
    onComplete();
    cancel();
  }

  protected void dispatch(final Runnable action) {
    Serializer.dispatch(action::run, this::onError);
  }

  protected abstract void emit(final long number);

  protected boolean getError() {
    return error;
  }

  private void notifySubscriber() {
    if (subscriber != null && subscription != null) {
      subscriber.onSubscribe(new Backpressure());

      if (pendingException != null) {
        subscriber.onError(pendingException);
      }
    }
  }

  public void onComplete() {
    if (!getError()) {
      if (subscriber != null) {
        subscriber.onComplete();
      } else {
        completed = true;
      }
    }
  }

  public void onError(final Throwable t) {
    if (t == null) {
      throw new NullPointerException("Can't throw null.");
    }

    setError(true);

    if (subscriber != null) {
      subscriber.onError(t);
    } else {
      pendingException = t;
    }
  }

  public void onSubscribe(final Subscription subscription) {
    if (subscription == null) {
      throw new NullPointerException("A subscription can't be null.");
    }

    if (this.subscription != null) {
      cancel();
      this.subscription = subscription;
    } else {
      this.subscription = subscription;
      notifySubscriber();
    }
  }

  protected void setError(final boolean value) {
    error = value;
  }

  public void subscribe(final Subscriber subscriber) {
    if (subscriber == null) {
      throw new NullPointerException("A subscriber can't be null.");
    }

    this.subscriber = subscriber;
    notifySubscriber();
  }

  private class Backpressure implements Subscription {
    public void cancel() {
      ProcessorBase.this.cancel();
    }

    public void request(final long number) {
      if (number <= 0) {
        throw new IllegalArgumentException("A request must be strictly positive.");
      }

      if (!getError()) {
        if (completed) {
          onComplete();
        } else {
          emit(number);
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy