akka.stream.StreamRefs.scala Maven / Gradle / Ivy
/*
* Copyright (C) 2018-2020 Lightbend Inc.
*/
package akka.stream
import akka.NotUsed
import akka.actor.ActorRef
import akka.actor.ActorSystem
import akka.actor.ClassicActorSystemProvider
import akka.actor.ExtendedActorSystem
import akka.actor.Extension
import akka.actor.ExtensionId
import akka.annotation.DoNotInherit
import akka.stream.impl.streamref.StreamRefResolverImpl
import akka.stream.scaladsl.{ Sink, Source }
import scala.language.implicitConversions
/**
* See full documentation on [[SinkRef]].
*/
object SinkRef {
/** Implicitly converts a [[SinkRef]] to a [[Sink]]. The same can be achieved by calling `.sink` on the reference. */
implicit def convertRefToSink[T](sinkRef: SinkRef[T]): Sink[T, NotUsed] = sinkRef.sink()
}
/**
* A [[SinkRef]] allows sharing a "reference" to a [[Sink]] with others, with the main purpose of crossing a network boundary.
* Usually obtaining a SinkRef would be done via Actor messaging, in which one system asks a remote one,
* to accept some data from it, and the remote one decides to accept the request to send data in a back-pressured
* streaming fashion -- using a sink ref.
*
* To create a [[SinkRef]] you have to materialize the `Sink` that you want to obtain a reference to by attaching it to a `StreamRefs.sinkRef()`.
*
* Stream refs can be seen as Reactive Streams over network boundaries.
* See also [[akka.stream.SourceRef]] which is the dual of a `SinkRef`.
*
* For additional configuration see `reference.conf` as well as [[akka.stream.StreamRefAttributes]].
*
* Not for user extension.
*/
@DoNotInherit
trait SinkRef[In] {
/** Scala API: Get [[Sink]] underlying to this source ref. */
def sink(): Sink[In, NotUsed]
/** Java API: Get [[javadsl.Sink]] underlying to this source ref. */
final def getSink(): javadsl.Sink[In, NotUsed] = sink().asJava
}
/**
* See full documentation on [[SourceRef]].
*/
object SourceRef {
/** Implicitly converts a SourceRef to a Source. The same can be achieved by calling `.source` on the SourceRef itself. */
implicit def convertRefToSource[T](ref: SourceRef[T]): Source[T, NotUsed] =
ref.source
}
/**
* A SourceRef allows sharing a "reference" with others, with the main purpose of crossing a network boundary.
* Usually obtaining a SourceRef would be done via Actor messaging, in which one system asks a remote one,
* to share some data with it, and the remote one decides to do so in a back-pressured streaming fashion -- using a stream ref.
*
* To create a [[SourceRef]] you have to materialize the `Source` that you want to obtain a reference to by attaching it to a `Sink.sourceRef`.
*
* Stream refs can be seen as Reactive Streams over network boundaries.
* See also [[akka.stream.SinkRef]] which is the dual of a `SourceRef`.
*
* For additional configuration see `reference.conf` as well as [[akka.stream.StreamRefAttributes]].
*
* Not for user extension.
*/
@DoNotInherit
trait SourceRef[T] {
/** Scala API: Get [[Source]] underlying to this source ref. */
def source: Source[T, NotUsed]
/** Java API: Get [[javadsl.Source]] underlying to this source ref. */
final def getSource: javadsl.Source[T, NotUsed] = source.asJava
}
// --- exceptions ---
final case class TargetRefNotInitializedYetException()
extends IllegalStateException(
"Internal remote target actor ref not yet resolved, yet attempted to send messages to it. " +
"This should not happen due to proper flow-control, please open a ticket on the issue tracker: https://github.com/akka/akka")
final case class StreamRefSubscriptionTimeoutException(msg: String) extends IllegalStateException(msg)
final case class RemoteStreamRefActorTerminatedException(msg: String) extends RuntimeException(msg)
final case class InvalidSequenceNumberException(expectedSeqNr: Long, gotSeqNr: Long, msg: String)
extends IllegalStateException(
s"$msg (expected: $expectedSeqNr, got: $gotSeqNr). " +
s"In most cases this means that message loss on this connection has occurred and the stream will fail eagerly.")
/**
* Stream refs establish a connection between a local and remote actor, representing the origin and remote sides
* of a stream. Each such actor refers to the other side as its "partner". We make sure that no other actor than
* the initial partner can send demand/messages to the other side accidentally.
*
* This exception is thrown when a message is received from a non-partner actor,
* which could mean a bug or some actively malicient behavior from the other side.
*
* This is not meant as a security feature, but rather as plain sanity-check.
*/
final case class InvalidPartnerActorException(expectedRef: ActorRef, gotRef: ActorRef, msg: String)
extends IllegalStateException(
s"$msg (expected: $expectedRef, got: $gotRef). " +
s"This may happen due to 'double-materialization' on the other side of this stream ref. " +
s"Do note that stream refs are one-shot references and have to be paired up in 1:1 pairs. " +
s"Multi-cast such as broadcast etc can be implemented by sharing multiple new stream references. ")
/**
* The stream ref resolver extension provides a way to serialize and deserialize streamrefs in user serializers.
*/
object StreamRefResolver extends ExtensionId[StreamRefResolver] {
override def get(system: ActorSystem): StreamRefResolver = super.get(system)
override def get(system: ClassicActorSystemProvider): StreamRefResolver = super.get(system)
override def createExtension(system: ExtendedActorSystem): StreamRefResolver =
new StreamRefResolverImpl(system)
}
/**
* The stream ref resolver provides a way to serialize and deserialize streamrefs in user serializers.
*
* Not for user extension
*/
@DoNotInherit trait StreamRefResolver extends Extension {
/**
* Generate full String representation of the `SourceRef`.
* This representation should be used as serialized representation.
*/
def toSerializationFormat[T](ref: SourceRef[T]): String
/**
* Generate full String representation of the `SinkRef`.
* This representation should be used as serialized representation.
*/
def toSerializationFormat[T](ref: SinkRef[T]): String
/**
* Deserialize an `SourceRef` in the [[#toSerializationFormat]].
*/
def resolveSourceRef[T](serializedSourceRef: String): SourceRef[T]
/**
* Deserialize an `SinkRef` in the [[#toSerializationFormat]].
*/
def resolveSinkRef[T](serializedSinkRef: String): SinkRef[T]
}