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

monix.reactive.internal.rstreams.SubscriberAsReactiveSubscriber.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2014-2016 by its authors. Some rights reserved.
 * See the project homepage at: https://monix.io
 *
 * 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 monix.reactive.internal.rstreams

import monix.execution.Ack.{Continue, Stop}
import monix.reactive.OverflowStrategy.Unbounded
import monix.reactive.observers.{BufferedSubscriber, Subscriber}
import org.reactivestreams.{Subscriber => RSubscriber, Subscription => RSubscription}

/** Wraps a [[monix.reactive.Observer Observer]] instance into an
  * `org.reactiveSubscriber` instance. The resulting
  * subscriber respects the [[http://www.reactive-streams.org/ Reactive Streams]]
  * contract.
  *
  * Given that when emitting [[monix.reactive.Observer.onNext Observer.onNext]] calls,
  * the call may pass asynchronous boundaries, the emitted events need to be buffered.
  * The `requestCount` constructor parameter also represents the buffer size.
  *
  * To async an instance, [[SubscriberAsReactiveSubscriber]] must be used: {{{
  *   // uses the default requestCount of 128
  *   val subscriber = SubscriberAsReactiveSubscriber(new Observer[Int] {
  *     private[this] var sum = 0
  *
  *     def onNext(elem: Int) = {
  *       sum += elem
  *       Continue
  *     }
  *
  *     def onError(ex: Throwable) = {
  *       logger.error(ex)
  *     }
  *
  *     def onComplete() = {
  *       logger.info("Stream completed")
  *     }
  *   })
  * }}}
  *
  * @param subscriber the observer instance that will get wrapped into a
  *                   `org.reactiveSubscriber`, along with the scheduler used
  * @param requestCount the parameter passed to `Subscription.request`,
  *                    also representing the buffer size; MUST BE strictly positive
  */
private[monix] final class SubscriberAsReactiveSubscriber[T] private
  (subscriber: Subscriber[T], requestCount: Int)
  extends RSubscriber[T] {

  require(requestCount > 0, "requestCount must be strictly positive, according to the Reactive Streams contract")

  private[this] val buffer: RSubscriber[T] =
    SyncSubscriberAsReactiveSubscriber(
      BufferedSubscriber.synchronous(subscriber, Unbounded),
      requestCount = requestCount)

  def onSubscribe(s: RSubscription): Unit =
    buffer.onSubscribe(s)

  def onNext(elem: T): Unit = {
    if (elem == null) throw new NullPointerException("onNext(null)")
    buffer.onNext(elem)
  }

  def onError(ex: Throwable): Unit =
    buffer.onError(ex)

  def onComplete(): Unit =
    buffer.onComplete()
}


private[monix] object SubscriberAsReactiveSubscriber {
  /**
    * Wraps a [[monix.reactive.Observer Observer]] instance into a
    * `org.reactiveSubscriber` instance. The resulting
    * subscriber respects the [[http://www.reactive-streams.org/ Reactive Streams]]
    * contract.
    *
    * Given that when emitting [[monix.reactive.Observer.onNext Observer.onNext]] calls,
    * the call may pass asynchronous boundaries, the emitted events need to be buffered.
    * The `requestCount` constructor parameter also represents the buffer size.
    *
    * To async an instance, [[SubscriberAsReactiveSubscriber.apply]] must be used: {{{
    *   // uses the default requestCount of 128
    *   val subscriber = SubscriberAsReactiveSubscriber(new Observer[Int] {
    *     private[this] var sum = 0
    *
    *     def onNext(elem: Int) = {
    *       sum += elem
    *       Continue
    *     }
    *
    *     def onError(ex: Throwable) = {
    *       logger.error(ex)
    *     }
    *
    *     def onComplete() = {
    *       logger.info("Stream completed")
    *     }
    *   })
    * }}}
    *
    * @param subscriber the subscriber instance that will get wrapped into a
    *                  `org.reactiveSubscriber`
    * @param requestCount the parameter passed to each `Subscription.request` call,
    *                    also representing the buffer size; MUST BE strictly positive
    */
  def apply[T](subscriber: Subscriber[T], requestCount: Int = 128): RSubscriber[T] =
    subscriber match {
      case ref: Subscriber.Sync[_] =>
        SyncSubscriberAsReactiveSubscriber(ref.asInstanceOf[Subscriber.Sync[T]], requestCount)
      case _ =>
        new SubscriberAsReactiveSubscriber[T](subscriber, requestCount)
    }
}

/**
  * Wraps a [[monix.reactive.observers.Observer.Sync Observer.Sync]] instance into a
  * `org.reactiveSubscriber` instance. The resulting
  * subscriber respects the [[http://www.reactive-streams.org/ Reactive Streams]]
  * contract.
  *
  * Given that we can guarantee a [[monix.reactive.observers.Observer.Sync Observer.Sync]]
  * is used, then no buffering is needed and thus the implementation is very efficient.
  *
  * To async an instance, [[SyncSubscriberAsReactiveSubscriber]] must be used: {{{
  *   // uses the default requestCount of 128
  *   val subscriber = SyncSubscriberAsReactiveSubscriber(new Observer[Int] {
  *     private[this] var sum = 0
  *
  *     def onNext(elem: Int) = {
  *       sum += elem
  *       Continue
  *     }
  *
  *     def onError(ex: Throwable) = {
  *       logger.error(ex)
  *     }
  *
  *     def onComplete() = {
  *       logger.info("Stream completed")
  *     }
  *   })
  * }}}
  */
private[monix] final class SyncSubscriberAsReactiveSubscriber[T] private
  (subscriber: Subscriber.Sync[T], requestCount: Int)
  extends RSubscriber[T] {

  require(requestCount > 0, "requestCount must be strictly positive, according to the Reactive Streams contract")

  private[this] implicit val s = subscriber.scheduler

  private[this] var subscription = null : RSubscription
  private[this] var expectingCount = 0
  @volatile private[this] var isCanceled = false

  def onSubscribe(s: RSubscription): Unit = {
    if (subscription == null && !isCanceled) {
      subscription = s
      expectingCount = requestCount
      s.request(requestCount)
    }
    else {
      s.cancel()
    }
  }

  def onNext(elem: T): Unit = {
    if (subscription == null) throw new NullPointerException("onSubscription never happened")
    if (elem == null) throw new NullPointerException("onNext(null)")

    if (!isCanceled) {
      if (expectingCount > 0) expectingCount -= 1

      subscriber.onNext(elem) match {
        case Continue =>
          // should it request more events?
          if (expectingCount == 0) {
            expectingCount = requestCount
            subscription.request(requestCount)
          }
        case Stop =>
          // downstream canceled, so we MUST cancel too
          isCanceled = true
          subscription.cancel()
      }
    }
  }

  def onError(ex: Throwable): Unit = {
    if (ex == null) throw new NullPointerException("onError(null)")

    if (!isCanceled) {
      isCanceled = true
      subscriber.onError(ex)
    }
  }

  def onComplete(): Unit =
    if (!isCanceled) {
      isCanceled = true
      subscriber.onComplete()
    }
}


private[monix] object SyncSubscriberAsReactiveSubscriber {
  /**
    * Wraps a [[monix.reactive.observers.Observer.Sync Observer.Sync]] instance into a
    * `org.reactiveSubscriber` instance. The resulting
    * subscriber respects the [[http://www.reactive-streams.org/ Reactive Streams]]
    * contract.
    *
    * Given that we can guarantee a [[monix.reactive.observers.Observer.Sync Observer.Sync]]
    * is used, then no buffering is needed and thus the implementation is very efficient.
    *
    * To async an instance, [[SyncSubscriberAsReactiveSubscriber.apply]] must be used: {{{
    *   // uses the default requestCount of 128
    *   val subscriber = SyncSubscriberAsReactiveSubscriber(new Observer[Int] {
    *     private[this] var sum = 0
    *
    *     def onNext(elem: Int) = {
    *       sum += elem
    *       Continue
    *     }
    *
    *     def onError(ex: Throwable) = {
    *       logger.error(ex)
    *     }
    *
    *     def onComplete() = {
    *       logger.info("Stream completed")
    *     }
    *   })
    * }}}
    *
    * @param subscriber the observer instance that will get wrapped into a
    *                  `org.reactiveSubscriber`, along with the
    *                  used scheduler
    * @param requestCount the parameter passed to `Subscription.request`
    */
  def apply[T](subscriber: Subscriber.Sync[T], requestCount: Int = 128): RSubscriber[T] = {
    new SyncSubscriberAsReactiveSubscriber[T](subscriber, requestCount)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy