monifu.reactive.observables.GroupedObservable.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 monifu.reactive.observables
import monifu.concurrent.{Cancelable, Scheduler}
import monifu.reactive._
import monifu.reactive.internals._
import monifu.reactive.observers.CacheUntilConnectSubscriber
import scala.concurrent.Future
/**
* A `GroupedObservable` is an observable type generated
* by `Observable.groupBy`. It has the following properties:
*
* - comes accompanied with a `key` property after which
* the grouping was made
*
* - supports a single subscriber, throwing `IllegalStateException`
* if you attempt multiple subscriptions
*/
trait GroupedObservable[K, +V] extends Observable[V]
with LiftOperators2[K,V,GroupedObservable] { self =>
/**
* Returns the key associated with this grouped observable.
*/
def key: K
protected def liftToSelf[U](f: (Observable[V]) => Observable[U]): GroupedObservable[K, U] =
new GroupedObservable[K, U] {
val key = self.key
private[this] val lifted = f(self)
def onSubscribe(subscriber: Subscriber[U]): Unit =
lifted.onSubscribe(subscriber)
}
}
object GroupedObservable {
/** Builder returning an input+output pair */
private[reactive] def broadcast[K,V](key: K, onCancel: Cancelable)
(implicit s: Scheduler): (Subscriber[V], GroupedObservable[K,V]) = {
val ref = new Implementation[K,V](key, onCancel)
(ref, ref)
}
/** Implementation for [[GroupedObservable]] */
private final class Implementation[K, V](val key: K, onCancel: Cancelable)
(implicit val scheduler: Scheduler)
extends GroupedObservable[K,V] with Subscriber[V] { self =>
// needs to be set upon subscription
private[this] var ref: Subscriber[V] = null
private[this] val underlying = {
val o = new Observer[V] {
def onNext(elem: V) = {
val downstream = if (ref == null) self.synchronized(ref) else ref
downstream.onNext(elem)
.ifCanceledDoCancel(onCancel)
}
def onError(ex: Throwable): Unit = {
val downstream = if (ref == null) self.synchronized(ref) else ref
downstream.onError(ex)
}
def onComplete(): Unit = {
val downstream = if (ref == null) self.synchronized(ref) else ref
downstream.onComplete()
}
}
CacheUntilConnectSubscriber(Subscriber(o, scheduler))
}
def onNext(elem: V): Future[Ack] = underlying.onNext(elem)
def onError(ex: Throwable): Unit = underlying.onError(ex)
def onComplete(): Unit = underlying.onComplete()
def onSubscribe(subscriber: Subscriber[V]): Unit =
self.synchronized {
if (ref != null) {
subscriber.onError(
new IllegalStateException(
s"Cannot subscribe twice to a GroupedObservable"))
}
else {
ref = subscriber
underlying.connect()
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy