/** * Copyright (C) 2018 Lightbend Inc. */ package docs.stream import akka.NotUsed import akka.actor.{ Actor, Props } import akka.stream.ActorMaterializer import akka.stream.scaladsl._ import akka.testkit.AkkaSpec import docs.CompileOnlySpec import scala.concurrent.Future class FlowStreamRefsDocSpec extends AkkaSpec with CompileOnlySpec { "offer a source ref" in compileOnlySpec { //#offer-source import akka.stream.SourceRef import akka.pattern.pipe case class RequestLogs(streamId: Int) case class LogsOffer(streamId: Int, sourceRef: SourceRef[String]) class DataSource extends Actor { import context.dispatcher implicit val mat = ActorMaterializer()(context) def receive = { case RequestLogs(streamId) ⇒ // obtain the source you want to offer: val source: Source[String, NotUsed] = streamLogs(streamId) // materialize the SourceRef: val ref: Future[SourceRef[String]] = source.runWith(StreamRefs.sourceRef()) // wrap the SourceRef in some domain message, such that the sender knows what source it is val reply: Future[LogsOffer] = ref.map(LogsOffer(streamId, _)) // reply to sender reply pipeTo sender() } def streamLogs(streamId: Long): Source[String, NotUsed] = ??? } //#offer-source implicit val mat = ActorMaterializer() //#offer-source-use val sourceActor = system.actorOf(Props[DataSource], "dataSource") sourceActor ! RequestLogs(1337) val offer = expectMsgType[LogsOffer] // implicitly converted to a Source: offer.sourceRef.runWith(Sink.foreach(println)) // alternatively explicitly obtain Source from SourceRef: // offer.sourceRef.source.runWith(Sink.foreach(println)) //#offer-source-use } "offer a sink ref" in compileOnlySpec { //#offer-sink import akka.pattern.pipe import akka.stream.SinkRef case class PrepareUpload(id: String) case class MeasurementsSinkReady(id: String, sinkRef: SinkRef[String]) class DataReceiver extends Actor { import context.dispatcher implicit val mat = ActorMaterializer()(context) def receive = { case PrepareUpload(nodeId) ⇒ // obtain the source you want to offer: val sink: Sink[String, NotUsed] = logsSinkFor(nodeId) // materialize the SinkRef (the remote is like a source of data for us): val ref: Future[SinkRef[String]] = StreamRefs.sinkRef[String]().to(sink).run() // wrap the SinkRef in some domain message, such that the sender knows what source it is val reply: Future[MeasurementsSinkReady] = ref.map(MeasurementsSinkReady(nodeId, _)) // reply to sender reply pipeTo sender() } def logsSinkFor(nodeId: String): Sink[String, NotUsed] = ??? } //#offer-sink implicit val mat = ActorMaterializer() def localMetrics(): Source[String, NotUsed] = Source.single("") //#offer-sink-use val receiver = system.actorOf(Props[DataReceiver], "receiver") receiver ! PrepareUpload("system-42-tmp") val ready = expectMsgType[MeasurementsSinkReady] // stream local metrics to Sink's origin: localMetrics().runWith(ready.sinkRef) //#offer-sink-use } "show how to configure timeouts with attrs" in compileOnlySpec { implicit val mat: ActorMaterializer = null //#attr-sub-timeout // configure the timeout for source import scala.concurrent.duration._ import akka.stream.StreamRefAttributes // configuring Sink.sourceRef (notice that we apply the attributes to the Sink!): Source.repeat("hello") .runWith(StreamRefs.sourceRef().addAttributes(StreamRefAttributes.subscriptionTimeout(5.seconds))) // configuring SinkRef.source: StreamRefs.sinkRef().addAttributes(StreamRefAttributes.subscriptionTimeout(5.seconds)) .runWith(Sink.ignore) // not very interesting Sink, just an example //#attr-sub-timeout } }